Skip to content

Commit 5d53f89

Browse files
committed
[GR-69003] Support for static fields in runtime loaded classes.
PullRequest: graal/22188
2 parents 5f3166b + a75a349 commit 5d53f89

File tree

18 files changed

+276
-46
lines changed

18 files changed

+276
-46
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/ClassInitializationInfo.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.oracle.svm.core.hub.DynamicHub;
4343
import com.oracle.svm.core.hub.PredefinedClassesSupport;
4444
import com.oracle.svm.core.hub.RuntimeClassLoading;
45+
import com.oracle.svm.core.hub.crema.CremaSupport;
4546
import com.oracle.svm.core.jdk.InternalVMMethod;
4647
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
4748
import com.oracle.svm.core.stack.StackOverflowCheck;
@@ -54,6 +55,7 @@
5455

5556
import jdk.graal.compiler.word.Word;
5657
import jdk.internal.reflect.Reflection;
58+
import jdk.vm.ci.meta.ResolvedJavaMethod;
5759

5860
/**
5961
* Information about the runtime class initialization state of a {@link DynamicHub class}, and
@@ -86,6 +88,12 @@ public final class ClassInitializationInfo {
8688
private static final ClassInitializationInfo FAILED_NO_CLINIT_NO_TRACKING = new ClassInitializationInfo(InitState.InitializationError, false, false);
8789
private static final ClassInitializationInfo FAILED_HAS_CLINIT_NO_TRACKING = new ClassInitializationInfo(InitState.InitializationError, true, false);
8890

91+
/**
92+
* Marks that a runtime-loaded class has a class initializer that should be executed in the
93+
* interpreter.
94+
*/
95+
private static final FunctionPointerHolder INTERPRETER_INITIALIZATION_MARKER = new FunctionPointerHolder(null);
96+
8997
/**
9098
* Function pointer to the class initializer that should be called at run-time. In some cases,
9199
* this may be an arbitrary helper method instead of the {@code <clinit>} of the class. This
@@ -188,12 +196,12 @@ private ClassInitializationInfo(CFunctionPointer runtimeClassInitializer, boolea
188196
}
189197

190198
/** For classes that are loaded at run-time. */
191-
private ClassInitializationInfo(boolean typeReachedTracked) {
199+
private ClassInitializationInfo(boolean typeReachedTracked, boolean hasClassInitializer) {
192200
assert RuntimeClassLoading.isSupported();
193201

194202
this.buildTimeInitialized = false;
195-
this.hasInitializer = true;
196-
this.runtimeClassInitializer = null;
203+
this.hasInitializer = hasClassInitializer;
204+
this.runtimeClassInitializer = hasClassInitializer ? INTERPRETER_INITIALIZATION_MARKER : null;
197205
this.slowPathRequired = true;
198206
this.initLock = new ReentrantLock();
199207
/* GR-59739: Needs a new state "Loaded". */
@@ -224,8 +232,8 @@ public static ClassInitializationInfo forRuntimeTimeInitializedClass(CFunctionPo
224232
return new ClassInitializationInfo(methodPointer, typeReachedTracked);
225233
}
226234

227-
public static ClassInitializationInfo forRuntimeLoadedClass(boolean typeReachedTracked) {
228-
return new ClassInitializationInfo(typeReachedTracked);
235+
public static ClassInitializationInfo forRuntimeLoadedClass(boolean typeReachedTracked, boolean hasClassInitializer) {
236+
return new ClassInitializationInfo(typeReachedTracked, hasClassInitializer);
229237
}
230238

231239
public boolean isBuildTimeInitialized() {
@@ -533,7 +541,7 @@ private void tryInitialize0(DynamicHub hub) {
533541
*/
534542
Throwable exception = null;
535543
try {
536-
invokeClassInitializer();
544+
invokeClassInitializer(hub);
537545
} catch (Throwable ex) {
538546
exception = ex;
539547
}
@@ -694,27 +702,35 @@ private void setInitializationStateAndNotify0(InitState state) {
694702
}
695703

696704
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+13/src/hotspot/share/oops/instanceKlass.cpp#L1675-L1715")
697-
private void invokeClassInitializer() {
705+
private void invokeClassInitializer(DynamicHub hub) {
698706
if (runtimeClassInitializer == null) {
699707
return;
700708
}
701709

702-
ClassInitializerFunctionPointer functionPointer = (ClassInitializerFunctionPointer) runtimeClassInitializer.functionPointer;
703-
VMError.guarantee(functionPointer.isNonNull());
704-
705710
/* Protect the yellow zone before executing arbitrary Java code. */
706711
if (Platform.includedIn(NATIVE_ONLY.class)) {
707712
StackOverflowCheck.singleton().protectYellowZone();
708713
}
709714
try {
710-
functionPointer.invoke();
715+
invokeClassInitializer0(hub);
711716
} finally {
712717
if (Platform.includedIn(NATIVE_ONLY.class)) {
713718
StackOverflowCheck.singleton().makeYellowZoneAvailable();
714719
}
715720
}
716721
}
717722

723+
private void invokeClassInitializer0(DynamicHub hub) {
724+
if (RuntimeClassLoading.isSupported() && runtimeClassInitializer == INTERPRETER_INITIALIZATION_MARKER) {
725+
ResolvedJavaMethod classInitializer = hub.getInterpreterType().getClassInitializer();
726+
CremaSupport.singleton().execute(classInitializer, new Object[0]);
727+
} else {
728+
ClassInitializerFunctionPointer functionPointer = (ClassInitializerFunctionPointer) runtimeClassInitializer.functionPointer;
729+
VMError.guarantee(functionPointer.isNonNull());
730+
functionPointer.invoke();
731+
}
732+
}
733+
718734
public enum InitState {
719735
/**
720736
* Successfully linked/verified (but not initialized yet). Linking happens during image

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,9 @@ public DynamicHub(Class<?> hostedJavaClass, String name, byte hubType, Reference
472472
@NeverInline("Fields of DynamicHub are immutable. Immutable reads could float above ANY_LOCATION writes.")
473473
public static DynamicHub allocate(String name, DynamicHub superHub, Object interfacesEncoding, DynamicHub componentHub, String sourceFileName,
474474
int modifiers, short flags, ClassLoader classLoader, String simpleBinaryName, Module module,
475-
Object declaringClass, String signature, int typeID, int interfaceID,
475+
Object declaringClass, String signature,
476+
int typeID, int interfaceID,
477+
boolean hasClassInitializer,
476478
short numClassTypes,
477479
short typeIDDepth,
478480
short numIterableInterfaceTypes,
@@ -506,7 +508,7 @@ public static DynamicHub allocate(String name, DynamicHub superHub, Object inter
506508

507509
/* Always allow unsafe allocation for classes that were loaded at run-time. */
508510
companion.canUnsafeAllocate = true;
509-
companion.classInitializationInfo = ClassInitializationInfo.forRuntimeLoadedClass(false);
511+
companion.classInitializationInfo = ClassInitializationInfo.forRuntimeLoadedClass(false, hasClassInitializer);
510512

511513
assert !isFlagSet(flags, IS_PRIMITIVE_FLAG_BIT);
512514
boolean isInterface = isFlagSet(flags, IS_INTERFACE_FLAG_BIT);

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, int layerNum);
73+
7274
static CremaSupport singleton() {
7375
return ImageSingletons.lookup(CremaSupport.class);
7476
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ private Class<?> createClass(ParserKlass parsed, ClassDefinitionInfo info, Symbo
404404

405405
DynamicHub hub = DynamicHub.allocate(externalName, superHub, interfacesEncoding, null,
406406
sourceFile, modifiers, flags, getClassLoader(), simpleBinaryName, module, enclosingClass, classSignature,
407-
typeID, interfaceID, numClassTypes, typeIDDepth, numIterableInterfaces, openTypeWorldTypeCheckSlots, openTypeWorldInterfaceHashTable, openTypeWorldInterfaceHashParam,
407+
typeID, interfaceID,
408+
hasClassInitializer(parsed), numClassTypes, typeIDDepth, numIterableInterfaces, openTypeWorldTypeCheckSlots, openTypeWorldInterfaceHashTable, openTypeWorldInterfaceHashParam,
408409
dispatchTableLength,
409410
dispatchTable.getDeclaredInstanceReferenceFieldOffsets(), afterFieldsOffset, isValueBased);
410411

@@ -430,6 +431,15 @@ private static void checkNotHybrid(ParserKlass parsed) {
430431

431432
}
432433

434+
private static boolean hasClassInitializer(ParserKlass parsed) {
435+
for (ParserMethod method : parsed.getMethods()) {
436+
if (method.getName() == ParserNames._clinit_) {
437+
return true;
438+
}
439+
}
440+
return false;
441+
}
442+
433443
private static boolean declaresDefaultMethods(ParserKlass parsed) {
434444
for (ParserMethod method : parsed.getMethods()) {
435445
int flags = method.getFlags();

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(), field.installedLayerNumber);
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/CremaResolvedJavaFieldImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
import com.oracle.svm.core.hub.DynamicHub;
2828
import com.oracle.svm.core.hub.crema.CremaResolvedJavaField;
2929
import com.oracle.svm.core.hub.crema.CremaSupport;
30+
import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo;
3031
import com.oracle.svm.espresso.classfile.ParserField;
3132

33+
import jdk.graal.compiler.core.common.NumUtil;
3234
import jdk.vm.ci.meta.JavaType;
3335
import jdk.vm.ci.meta.UnresolvedJavaType;
3436

@@ -42,6 +44,7 @@ public class CremaResolvedJavaFieldImpl extends InterpreterResolvedJavaField imp
4244
offset,
4345
/*- constantValue */ null,
4446
/*- isWordStorage */ false);
47+
this.layerNum = NumUtil.safeToByte(DynamicImageLayerInfo.CREMA_LAYER_ID);
4548
}
4649

4750
public static CremaResolvedJavaFieldImpl createAtRuntime(InterpreterResolvedObjectType declaringClass, ParserField f, int offset) {

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,31 @@
2929
import com.oracle.svm.core.hub.crema.CremaResolvedJavaMethod;
3030
import com.oracle.svm.core.hub.crema.CremaResolvedJavaRecordComponent;
3131
import com.oracle.svm.core.hub.crema.CremaResolvedJavaType;
32+
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
3233
import com.oracle.svm.core.util.VMError;
3334
import com.oracle.svm.espresso.classfile.descriptors.Symbol;
3435
import com.oracle.svm.espresso.classfile.descriptors.Type;
3536

3637
import jdk.vm.ci.meta.JavaType;
38+
import jdk.vm.ci.meta.ResolvedJavaMethod;
3739
import jdk.vm.ci.meta.ResolvedJavaType;
3840

3941
public final class CremaResolvedObjectType extends InterpreterResolvedObjectType implements CremaResolvedJavaType {
42+
private final byte[] primitiveStatics;
43+
private final Object[] referenceStatics;
4044

4145
public CremaResolvedObjectType(Symbol<Type> type, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, InterpreterResolvedObjectType[] interfaces,
42-
InterpreterConstantPool constantPool, Class<?> javaClass, boolean isWordType) {
46+
InterpreterConstantPool constantPool, Class<?> javaClass, boolean isWordType,
47+
int staticReferenceFields, int staticPrimitiveFieldsSize) {
4348
super(type, modifiers, componentType, superclass, interfaces, constantPool, javaClass, isWordType);
49+
this.primitiveStatics = new byte[staticPrimitiveFieldsSize];
50+
this.referenceStatics = new Object[staticReferenceFields];
51+
}
52+
53+
@Override
54+
public Object getStaticStorage(boolean primitives, int layerNum) {
55+
assert layerNum != MultiLayeredImageSingleton.NONSTATIC_FIELD_LAYER_NUMBER;
56+
return primitives ? primitiveStatics : referenceStatics;
4457
}
4558

4659
@Override
@@ -71,6 +84,16 @@ public CremaResolvedJavaMethod[] getDeclaredConstructors() {
7184
return result.toArray(new CremaResolvedJavaMethod[0]);
7285
}
7386

87+
@Override
88+
public ResolvedJavaMethod getClassInitializer() {
89+
for (InterpreterResolvedJavaMethod method : getDeclaredMethods(false)) {
90+
if (method.isClassInitializer()) {
91+
return method;
92+
}
93+
}
94+
return null;
95+
}
96+
7497
@Override
7598
public CremaResolvedJavaRecordComponent[] getRecordComponents() {
7699
// (GR-69095)

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.interpreter.metadata;
2626

2727
import java.lang.annotation.Annotation;
28+
import java.lang.reflect.Modifier;
2829
import java.util.function.Function;
2930

3031
import org.graalvm.nativeimage.Platform;
@@ -33,12 +34,14 @@
3334
import com.oracle.graal.pointsto.meta.AnalysisField;
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.descriptors.Name;
3840
import com.oracle.svm.espresso.classfile.descriptors.Symbol;
3941
import com.oracle.svm.espresso.classfile.descriptors.Type;
4042
import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols;
4143

44+
import jdk.graal.compiler.core.common.NumUtil;
4245
import jdk.vm.ci.meta.JavaConstant;
4346
import jdk.vm.ci.meta.JavaKind;
4447
import jdk.vm.ci.meta.JavaType;
@@ -59,6 +62,7 @@ public class InterpreterResolvedJavaField implements ResolvedJavaField, CremaFie
5962

6063
// Computed after analysis.
6164
private int offset;
65+
protected byte layerNum;
6266

6367
private final InterpreterResolvedObjectType declaringClass;
6468
protected InterpreterResolvedJavaType resolvedType;
@@ -96,6 +100,9 @@ protected InterpreterResolvedJavaField(
96100
// Primitive types are trivially resolved.
97101
this.resolvedType = InterpreterResolvedPrimitiveType.fromKind(CremaTypeAccess.symbolToJvmciKind(typeSymbol));
98102
}
103+
this.layerNum = NumUtil.safeToByte(Modifier.isStatic(modifiers) /*- Prevents 'this-escape' warning. */
104+
? MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED
105+
: MultiLayeredImageSingleton.NONSTATIC_FIELD_LAYER_NUMBER);
99106
}
100107

101108
@Platforms(Platform.HOSTED_ONLY.class)
@@ -117,13 +124,18 @@ public static InterpreterResolvedJavaField createForInterpreter(String name, int
117124
JavaType type, InterpreterResolvedObjectType declaringClass,
118125
int offset,
119126
JavaConstant constant,
120-
boolean isWordStorage) {
127+
boolean isWordStorage,
128+
int layerNum) {
121129
MetadataUtil.requireNonNull(type);
122130
MetadataUtil.requireNonNull(declaringClass);
123131
Symbol<Name> nameSymbol = SymbolsSupport.getNames().getOrCreate(name);
124132
InterpreterResolvedJavaType resolvedType = type instanceof InterpreterResolvedJavaType ? (InterpreterResolvedJavaType) type : null;
125133
Symbol<Type> symbolicType = resolvedType == null ? CremaTypeAccess.jvmciNameToType(type.getName()) : resolvedType.getSymbolicType();
126-
return new InterpreterResolvedJavaField(nameSymbol, symbolicType, modifiers, resolvedType, declaringClass, offset, constant, isWordStorage);
134+
InterpreterResolvedJavaField result = new InterpreterResolvedJavaField(nameSymbol, symbolicType, modifiers, resolvedType, declaringClass, offset, constant, isWordStorage);
135+
if (result.isStatic()) {
136+
result.layerNum = NumUtil.safeToByte(layerNum);
137+
}
138+
return result;
127139
}
128140

129141
@Platforms(Platform.HOSTED_ONLY.class)
@@ -163,6 +175,14 @@ public final void setOffset(int offset) {
163175
this.offset = offset;
164176
}
165177

178+
@Platforms(Platform.HOSTED_ONLY.class)
179+
public final void setInstalledLayerNum(int layerNum) {
180+
assert isStatic();
181+
VMError.guarantee(this.layerNum == MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED || this.layerNum == layerNum);
182+
this.layerNum = NumUtil.safeToByte(layerNum);
183+
184+
}
185+
166186
@Platforms(Platform.HOSTED_ONLY.class)
167187
public final void setResolvedType(InterpreterResolvedJavaType resolvedType) {
168188
VMError.guarantee(this.resolvedType == null || this.resolvedType.equals(resolvedType),
@@ -180,6 +200,10 @@ public final int getOffset() {
180200
return offset;
181201
}
182202

203+
public final int getInstalledLayerNum() {
204+
return layerNum;
205+
}
206+
183207
@Override
184208
public final String getName() {
185209
return name.toString();

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.oracle.svm.espresso.classfile.ParserMethod;
4545
import com.oracle.svm.espresso.classfile.attributes.CodeAttribute;
4646
import com.oracle.svm.espresso.classfile.descriptors.Name;
47+
import com.oracle.svm.espresso.classfile.descriptors.ParserSymbols;
4748
import com.oracle.svm.espresso.classfile.descriptors.Signature;
4849
import com.oracle.svm.espresso.classfile.descriptors.Symbol;
4950
import com.oracle.svm.espresso.shared.vtable.PartialMethod;
@@ -330,12 +331,12 @@ public final boolean isDeclared() {
330331

331332
@Override
332333
public final boolean isClassInitializer() {
333-
return "<clinit>".equals(getName()) && isStatic();
334+
return ParserSymbols.ParserNames._clinit_ == getSymbolicName() && isStatic();
334335
}
335336

336337
@Override
337338
public final boolean isConstructor() {
338-
return "<init>".equals(getName()) && !isStatic();
339+
return ParserSymbols.ParserNames._init_ == getSymbolicName() && !isStatic();
339340
}
340341

341342
@Override

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ public List<ResolvedJavaMethod> getAllMethods(boolean forceLink) {
304304
}
305305

306306
@Override
307-
public final ResolvedJavaMethod getClassInitializer() {
307+
public ResolvedJavaMethod getClassInitializer() {
308+
// We currently do not expect this to be called for any other type than
309+
// CremaResolvedObjectType.
308310
throw VMError.intentionallyUnimplemented();
309311
}
310312

0 commit comments

Comments
 (0)