Skip to content

Commit 6474ced

Browse files
committed
Support for Static fields of runtime-loaded classes. Includes reflection.
1 parent 41271d9 commit 6474ced

File tree

8 files changed

+168
-14
lines changed

8 files changed

+168
-14
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ interface CremaDispatchTable {
6969

7070
Class<?> findLoadedClass(JavaType unresolvedJavaType, ResolvedJavaType accessingClass);
7171

72+
Object getStaticStorage(Class<?> cls, boolean primitives);
73+
7274
static CremaSupport singleton() {
7375
return ImageSingletons.lookup(CremaSupport.class);
7476
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_jdk_internal_misc_Unsafe_Reflection.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import com.oracle.svm.core.SubstrateUtil;
3232
import com.oracle.svm.core.annotate.Substitute;
3333
import com.oracle.svm.core.annotate.TargetClass;
34+
import com.oracle.svm.core.hub.RuntimeClassLoading;
35+
import com.oracle.svm.core.hub.crema.CremaSupport;
3436
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
3537
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
3638
import com.oracle.svm.core.reflect.UnsafeFieldUtil;
@@ -55,6 +57,10 @@ public Object staticFieldBase(Target_java_lang_reflect_Field field) {
5557
throw new NullPointerException();
5658
}
5759
int layerNumber = ImageLayerBuildingSupport.buildingImageLayer() ? field.installedLayerNumber : MultiLayeredImageSingleton.UNUSED_LAYER_NUMBER;
60+
if (RuntimeClassLoading.isSupported()) {
61+
Field reflectField = SubstrateUtil.cast(field, Field.class);
62+
return CremaSupport.singleton().getStaticStorage(reflectField.getDeclaringClass(), reflectField.getType().isPrimitive());
63+
}
5864
if (SubstrateUtil.cast(field, Field.class).getType().isPrimitive()) {
5965
return StaticFieldsSupport.getStaticPrimitiveFieldsAtRuntime(layerNumber);
6066
} else {

substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaResolvedObjectType.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,20 @@
3737
import jdk.vm.ci.meta.ResolvedJavaType;
3838

3939
public final class CremaResolvedObjectType extends InterpreterResolvedObjectType implements CremaResolvedJavaType {
40+
private final byte[] primitiveStatics;
41+
private final Object[] referenceStatics;
4042

4143
public CremaResolvedObjectType(Symbol<Type> type, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, InterpreterResolvedObjectType[] interfaces,
42-
InterpreterConstantPool constantPool, Class<?> javaClass, boolean isWordType) {
44+
InterpreterConstantPool constantPool, Class<?> javaClass, boolean isWordType,
45+
int staticReferenceFields, int staticPrimitiveFieldsSize) {
4346
super(type, modifiers, componentType, superclass, interfaces, constantPool, javaClass, isWordType);
47+
this.primitiveStatics = new byte[staticPrimitiveFieldsSize];
48+
this.referenceStatics = new Object[staticReferenceFields];
49+
}
50+
51+
@Override
52+
public Object getStaticStorage(boolean primitives) {
53+
return primitives ? primitiveStatics : referenceStatics;
4454
}
4555

4656
@Override

substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
import org.graalvm.nativeimage.Platforms;
3131
import org.graalvm.word.WordBase;
3232

33+
import com.oracle.svm.core.StaticFieldsSupport;
3334
import com.oracle.svm.core.heap.UnknownObjectField;
3435
import com.oracle.svm.core.hub.DynamicHub;
3536
import com.oracle.svm.core.hub.registry.SymbolsSupport;
37+
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
3638
import com.oracle.svm.core.util.VMError;
3739
import com.oracle.svm.espresso.classfile.ParserKlass;
3840
import com.oracle.svm.espresso.classfile.descriptors.ByteSequence;
@@ -150,8 +152,9 @@ public static InterpreterResolvedObjectType createForInterpreter(String name, in
150152
}
151153

152154
public static CremaResolvedObjectType createForCrema(ParserKlass parserKlass, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass,
153-
InterpreterResolvedObjectType[] interfaces, Class<?> javaClass) {
154-
return new CremaResolvedObjectType(parserKlass.getType(), modifiers, componentType, superclass, interfaces, null, javaClass, false);
155+
InterpreterResolvedObjectType[] interfaces, Class<?> javaClass,
156+
int staticReferenceFields, int staticPrimitiveFieldsSize) {
157+
return new CremaResolvedObjectType(parserKlass.getType(), modifiers, componentType, superclass, interfaces, null, javaClass, false, staticReferenceFields, staticPrimitiveFieldsSize);
155158
}
156159

157160
@VisibleForSerialization
@@ -225,6 +228,14 @@ public final boolean isAssignableFrom(ResolvedJavaType other) {
225228
return false;
226229
}
227230

231+
public Object getStaticStorage(boolean primitives) {
232+
if (primitives) {
233+
return StaticFieldsSupport.getStaticPrimitiveFieldsAtRuntime(MultiLayeredImageSingleton.UNKNOWN_LAYER_NUMBER);
234+
} else {
235+
return StaticFieldsSupport.getStaticObjectFieldsAtRuntime(MultiLayeredImageSingleton.UNKNOWN_LAYER_NUMBER);
236+
}
237+
}
238+
228239
private static boolean isSubTypeOf(InterpreterResolvedObjectType superType, InterpreterResolvedObjectType subType) {
229240
if (subType.equals(superType)) {
230241
return true;

substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@
5252
import com.oracle.svm.core.hub.registry.ClassRegistries;
5353
import com.oracle.svm.core.hub.registry.SymbolsSupport;
5454
import com.oracle.svm.core.meta.MethodPointer;
55+
import com.oracle.svm.espresso.classfile.ConstantPool;
5556
import com.oracle.svm.espresso.classfile.ParserField;
5657
import com.oracle.svm.espresso.classfile.ParserKlass;
5758
import com.oracle.svm.espresso.classfile.ParserMethod;
59+
import com.oracle.svm.espresso.classfile.attributes.Attribute;
60+
import com.oracle.svm.espresso.classfile.attributes.ConstantValueAttribute;
5861
import com.oracle.svm.espresso.classfile.descriptors.ByteSequence;
5962
import com.oracle.svm.espresso.classfile.descriptors.Name;
6063
import com.oracle.svm.espresso.classfile.descriptors.ParserSymbols;
@@ -203,7 +206,8 @@ public void fillDynamicHubInfo(DynamicHub hub, CremaDispatchTable dispatchTable,
203206
table.getParserKlass(),
204207
hub.getModifiers(),
205208
componentType, superType, interfaces,
206-
DynamicHub.toClass(hub));
209+
DynamicHub.toClass(hub),
210+
table.layout.getStaticReferenceFieldCount(), table.layout.getStaticPrimitiveFieldSize());
207211

208212
ParserKlass parserKlass = table.partialType.parserKlass;
209213
thisType.setConstantPool(new RuntimeInterpreterConstantPool(thisType, parserKlass.getConstantPool()));
@@ -241,6 +245,8 @@ public void fillDynamicHubInfo(DynamicHub hub, CremaDispatchTable dispatchTable,
241245
thisType.setAfterFieldsOffset(table.layout().afterInstanceFieldsOffset());
242246
thisType.setDeclaredFields(declaredFields);
243247

248+
initStaticFields(thisType, table.getParserKlass().getFields());
249+
244250
// Done
245251
hub.setInterpreterType(thisType);
246252

@@ -266,6 +272,86 @@ public CremaDispatchTable getDispatchTable(ParserKlass parsed, Class<?> superCla
266272
}
267273
}
268274

275+
private static void initStaticFields(CremaResolvedObjectType type, ParserField[] fields) {
276+
// GR-61367: Currently done eagerly, but should be done during linking.
277+
InterpreterResolvedJavaField[] declaredFields = type.getDeclaredFields();
278+
for (int i = 0; i < declaredFields.length; i++) {
279+
InterpreterResolvedJavaField resolvedField = declaredFields[i];
280+
ParserField parsedField = fields[i];
281+
assert resolvedField.getSymbolicName() == parsedField.getName();
282+
assert resolvedField.isStatic() == parsedField.isStatic();
283+
if (resolvedField.isStatic()) {
284+
ConstantValueAttribute cva = null;
285+
for (Attribute attribute : parsedField.getAttributes()) {
286+
if (attribute.getName() == ConstantValueAttribute.NAME) {
287+
assert attribute instanceof ConstantValueAttribute;
288+
cva = (ConstantValueAttribute) attribute;
289+
break;
290+
}
291+
}
292+
if (cva != null) {
293+
int constantValueIndex = cva.getConstantValueIndex();
294+
switch (parsedField.getKind()) {
295+
case Boolean: {
296+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.INTEGER;
297+
boolean c = type.getConstantPool().intAt(constantValueIndex) != 0;
298+
InterpreterToVM.setFieldBoolean(c, type.getStaticStorage(true), resolvedField);
299+
break;
300+
}
301+
case Byte: {
302+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.INTEGER;
303+
byte c = (byte) type.getConstantPool().intAt(constantValueIndex);
304+
InterpreterToVM.setFieldByte(c, type.getStaticStorage(true), resolvedField);
305+
break;
306+
}
307+
case Short: {
308+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.INTEGER;
309+
short c = (short) type.getConstantPool().intAt(constantValueIndex);
310+
InterpreterToVM.setFieldShort(c, type.getStaticStorage(true), resolvedField);
311+
break;
312+
}
313+
case Char: {
314+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.INTEGER;
315+
char c = (char) type.getConstantPool().intAt(constantValueIndex);
316+
InterpreterToVM.setFieldChar(c, type.getStaticStorage(true), resolvedField);
317+
break;
318+
}
319+
case Int: {
320+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.INTEGER;
321+
int c = type.getConstantPool().intAt(constantValueIndex);
322+
InterpreterToVM.setFieldInt(c, type.getStaticStorage(true), resolvedField);
323+
break;
324+
}
325+
case Float: {
326+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.FLOAT;
327+
float c = type.getConstantPool().floatAt(constantValueIndex);
328+
InterpreterToVM.setFieldFloat(c, type.getStaticStorage(true), resolvedField);
329+
break;
330+
}
331+
case Long: {
332+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.LONG;
333+
long c = type.getConstantPool().longAt(constantValueIndex);
334+
InterpreterToVM.setFieldLong(c, type.getStaticStorage(true), resolvedField);
335+
break;
336+
}
337+
case Double: {
338+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.DOUBLE;
339+
double c = type.getConstantPool().doubleAt(constantValueIndex);
340+
InterpreterToVM.setFieldDouble(c, type.getStaticStorage(true), resolvedField);
341+
break;
342+
}
343+
case Object: {
344+
assert type.getConstantPool().tagAt(constantValueIndex) == ConstantPool.Tag.STRING;
345+
String c = type.getConstantPool().resolveStringAt(constantValueIndex);
346+
InterpreterToVM.setFieldObject(c, type.getStaticStorage(false), resolvedField);
347+
break;
348+
}
349+
}
350+
}
351+
}
352+
}
353+
}
354+
269355
static final class CremaPartialType implements PartialType<InterpreterResolvedJavaType, InterpreterResolvedJavaMethod, InterpreterResolvedJavaField> {
270356
private final Class<?> superClass;
271357
private final ParserKlass parserKlass;
@@ -598,6 +684,11 @@ public Class<?> findLoadedClass(JavaType unresolvedJavaType, ResolvedJavaType ac
598684
return registry.findLoadedClass(symbolicType);
599685
}
600686

687+
@Override
688+
public Object getStaticStorage(Class<?> cls, boolean primitives) {
689+
return ((InterpreterResolvedObjectType) DynamicHub.fromClass(cls).getInterpreterType()).getStaticStorage(primitives);
690+
}
691+
601692
@Override
602693
public Object execute(ResolvedJavaMethod targetMethod, Object[] args) {
603694
return Interpreter.execute((InterpreterResolvedJavaMethod) targetMethod, args);

substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,7 @@
264264
import static com.oracle.svm.interpreter.metadata.Bytecodes.TABLESWITCH;
265265
import static com.oracle.svm.interpreter.metadata.Bytecodes.WIDE;
266266

267-
import com.oracle.svm.core.StaticFieldsSupport;
268267
import com.oracle.svm.core.jdk.InternalVMMethod;
269-
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
270268
import com.oracle.svm.core.util.VMError;
271269
import com.oracle.svm.espresso.classfile.ConstantPool;
272270
import com.oracle.svm.interpreter.debug.DebuggerEvents;
@@ -1503,8 +1501,7 @@ private static int putField(InterpreterFrame frame, int top, InterpreterResolved
15031501

15041502
int slotCount = kind.getSlotCount();
15051503
Object receiver = (opcode == PUTSTATIC)
1506-
? (kind.isPrimitive() ? StaticFieldsSupport.getStaticPrimitiveFieldsAtRuntime(MultiLayeredImageSingleton.UNKNOWN_LAYER_NUMBER)
1507-
: StaticFieldsSupport.getStaticObjectFieldsAtRuntime(MultiLayeredImageSingleton.UNKNOWN_LAYER_NUMBER))
1504+
? field.getDeclaringClass().getStaticStorage(kind.isPrimitive())
15081505
: nullCheck(popObject(frame, top - slotCount - 1));
15091506

15101507
if (field.isStatic()) {
@@ -1547,8 +1544,7 @@ private static int getField(InterpreterFrame frame, int top, InterpreterResolved
15471544
assert kind != JavaKind.Illegal;
15481545

15491546
Object receiver = opcode == GETSTATIC
1550-
? (kind.isPrimitive() ? StaticFieldsSupport.getStaticPrimitiveFieldsAtRuntime(MultiLayeredImageSingleton.UNKNOWN_LAYER_NUMBER)
1551-
: StaticFieldsSupport.getStaticObjectFieldsAtRuntime(MultiLayeredImageSingleton.UNKNOWN_LAYER_NUMBER))
1547+
? field.getDeclaringClass().getStaticStorage(kind.isPrimitive())
15521548
: nullCheck(popObject(frame, top - 1));
15531549

15541550
if (field.isStatic()) {

substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/fieldlayout/FieldLayout.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ public final class FieldLayout {
4141
private final int afterInstanceFieldsOffset;
4242
private final int[] offsets;
4343
private final int[] referenceFieldsOffsets;
44+
private final int staticReferenceFieldCount;
45+
private final int staticPrimitiveFieldSize;
4446

45-
FieldLayout(int afterInstanceFieldsOffset, int[] offsets, int[] referenceFieldsOffsets) {
47+
FieldLayout(int afterInstanceFieldsOffset, int[] offsets, int[] referenceFieldsOffsets, int staticReferenceFieldCount, int staticPrimitiveFieldSize) {
4648
this.afterInstanceFieldsOffset = afterInstanceFieldsOffset;
4749
this.offsets = offsets;
4850
this.referenceFieldsOffsets = referenceFieldsOffsets;
51+
this.staticReferenceFieldCount = staticReferenceFieldCount;
52+
this.staticPrimitiveFieldSize = staticPrimitiveFieldSize;
4953
}
5054

5155
/**
@@ -85,4 +89,18 @@ public int getOffset(int idx) {
8589
public int[] getReferenceFieldsOffsets() {
8690
return referenceFieldsOffsets;
8791
}
92+
93+
/**
94+
* @return The number of static reference fields.
95+
*/
96+
public int getStaticReferenceFieldCount() {
97+
return staticReferenceFieldCount;
98+
}
99+
100+
/**
101+
* @return The size of static primitive fields to allocate.
102+
*/
103+
public int getStaticPrimitiveFieldSize() {
104+
return staticPrimitiveFieldSize;
105+
}
88106
}

substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/fieldlayout/GreedyFieldLayoutStrategy.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@
4242
*/
4343
/* GR-69569: This should move closer to UniverseBuilder.layoutInstanceFields */
4444
final class GreedyFieldLayoutStrategy {
45+
4546
static FieldLayout build(ParserField[] declaredParsedFields, long startOffset) {
4647
FieldLayoutImpl.ForInstanceFields forInstance = new FieldLayoutImpl.ForInstanceFields(declaredParsedFields, startOffset);
47-
FieldLayoutImpl.ForStaticReferenceFields forStaticReferences = new FieldLayoutImpl.ForStaticReferenceFields(declaredParsedFields, /*- GR-69003 */ 0);
48-
FieldLayoutImpl.ForStaticPrimitiveFields forStaticPrimitives = new FieldLayoutImpl.ForStaticPrimitiveFields(declaredParsedFields, /*- GR-69003 */ 0);
48+
FieldLayoutImpl.ForStaticReferenceFields forStaticReferences = new FieldLayoutImpl.ForStaticReferenceFields(declaredParsedFields,
49+
ConfigurationValues.getObjectLayout().getArrayBaseOffset(jdk.vm.ci.meta.JavaKind.Object));
50+
FieldLayoutImpl.ForStaticPrimitiveFields forStaticPrimitives = new FieldLayoutImpl.ForStaticPrimitiveFields(declaredParsedFields,
51+
ConfigurationValues.getObjectLayout().getArrayBaseOffset(jdk.vm.ci.meta.JavaKind.Byte));
4952

5053
int[] offsets = new int[declaredParsedFields.length];
5154
int[] referenceOffsets = new int[forInstance.getCountFor(JavaKind.Object)];
@@ -71,7 +74,8 @@ static FieldLayout build(ParserField[] declaredParsedFields, long startOffset) {
7174
offsets[i] = offset;
7275
}
7376
assert referencePos == referenceOffsets.length;
74-
return new FieldLayout(NumUtil.safeToInt(forInstance.getAfterFieldsOffset()), offsets, referenceOffsets);
77+
return new FieldLayout(NumUtil.safeToInt(forInstance.getAfterFieldsOffset()), offsets, referenceOffsets,
78+
forStaticReferences.getTotalCount(), forStaticPrimitives.getTotalSize());
7579
}
7680

7781
private abstract static class FieldLayoutImpl {
@@ -117,6 +121,22 @@ public final int getCountFor(JavaKind kind) {
117121
return counts[indexOf(kind)];
118122
}
119123

124+
public final int getTotalCount() {
125+
long total = 0;
126+
for (int i = 0; i < COUNT_LEN; i++) {
127+
total = total + counts[i];
128+
}
129+
return NumUtil.safeToInt(total);
130+
}
131+
132+
public final int getTotalSize() {
133+
long total = 0;
134+
for (int i = 0; i < COUNT_LEN; i++) {
135+
total += counts[i] * (long) SIZES_IN_BYTES[i];
136+
}
137+
return NumUtil.safeToInt(total);
138+
}
139+
120140
FieldLayoutImpl(ParserField[] declaredParsedFields, long startOffset) {
121141
this.startOffset = startOffset;
122142
this.afterFieldsOffset = init(declaredParsedFields);

0 commit comments

Comments
 (0)