Skip to content

Commit 7bc32da

Browse files
committed
[GR-69094] Basic Reflection support for Crema.
PullRequest: graal/18878
2 parents 831b011 + 5944fbc commit 7bc32da

28 files changed

+1797
-331
lines changed

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

Lines changed: 62 additions & 156 deletions
Large diffs are not rendered by default.

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,11 @@ public final class DynamicHubCompanion {
7373
*/
7474
final int classFileAccessFlags;
7575

76-
/** The class that serves as the host for the nest. All nestmates have the same host. */
77-
final Class<?> nestHost;
76+
/**
77+
* The class that serves as the host for the nest. All nestmates have the same host. Always
78+
* encoded with null for Dynamic hubs allocated at runtime.
79+
*/
80+
@Stable Class<?> nestHost;
7881

7982
/** The simple binary name of this class, as returned by {@code Class.getSimpleBinaryName0}. */
8083
final String simpleBinaryName;
@@ -126,11 +129,11 @@ public final class DynamicHubCompanion {
126129
* Metadata for querying the reflection data. When using layered images this field is always
127130
* null and should not be queried. Instead, use {@link LayeredReflectionMetadataSingleton}.
128131
*/
129-
@UnknownObjectField(canBeNull = true, availability = BuildPhaseProvider.AfterCompilation.class) //
130-
@Stable DynamicHub.ReflectionMetadata reflectionMetadata;
132+
@UnknownObjectField(canBeNull = true, types = ImageReflectionMetadata.class, availability = BuildPhaseProvider.AfterCompilation.class) //
133+
@Stable ReflectionMetadata reflectionMetadata;
131134

132-
@UnknownObjectField(canBeNull = true, availability = BuildPhaseProvider.AfterCompilation.class) //
133-
@Stable DynamicHub.DynamicHubMetadata hubMetadata;
135+
@UnknownObjectField(canBeNull = true, types = ImageDynamicHubMetadata.class, availability = BuildPhaseProvider.AfterCompilation.class) //
136+
@Stable DynamicHubMetadata hubMetadata;
134137

135138
/**
136139
* Classloader used for loading this class. Most classes have the correct class loader set
@@ -159,9 +162,9 @@ static DynamicHubCompanion createHosted(Module module, DynamicHub superHub, Stri
159162
}
160163

161164
static DynamicHubCompanion createAtRuntime(Module module, DynamicHub superHub, String sourceFileName, int modifiers, int classFileAccessFlags,
162-
ClassLoader classLoader, Class<?> nestHost, String simpleBinaryName, Object declaringClass, String signature) {
165+
ClassLoader classLoader, String simpleBinaryName, Object declaringClass, String signature) {
163166
assert RuntimeClassLoading.isSupported();
164-
return new DynamicHubCompanion(module, superHub, sourceFileName, modifiers, classFileAccessFlags, classLoader, nestHost, simpleBinaryName, declaringClass, signature);
167+
return new DynamicHubCompanion(module, superHub, sourceFileName, modifiers, classFileAccessFlags, classLoader, null, simpleBinaryName, declaringClass, signature);
165168
}
166169

167170
private DynamicHubCompanion(Module module, DynamicHub superHub, String sourceFileName, int modifiers, int classFileAccessFlags,
@@ -178,4 +181,12 @@ private DynamicHubCompanion(Module module, DynamicHub superHub, String sourceFil
178181

179182
this.classLoader = classLoader;
180183
}
184+
185+
public void setHubMetadata(RuntimeDynamicHubMetadata hubMetadata) {
186+
this.hubMetadata = hubMetadata;
187+
}
188+
189+
public void setReflectionMetadata(RuntimeReflectionMetadata reflectionMetadata) {
190+
this.reflectionMetadata = reflectionMetadata;
191+
}
181192
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package com.oracle.svm.core.hub;
27+
28+
interface DynamicHubMetadata {
29+
30+
Object[] getEnclosingMethod(DynamicHub declaringClass);
31+
32+
Object[] getSigners(DynamicHub declaringClass);
33+
34+
byte[] getRawAnnotations(DynamicHub declaringClass);
35+
36+
byte[] getRawTypeAnnotations(DynamicHub declaringClass);
37+
38+
Class<?>[] getDeclaredClasses(DynamicHub declaringClass);
39+
40+
Class<?>[] getNestMembers(DynamicHub declaringClass);
41+
42+
Class<?>[] getPermittedSubClasses(DynamicHub declaringClass);
43+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.hub;
26+
27+
import com.oracle.svm.core.BuildPhaseProvider;
28+
import com.oracle.svm.core.heap.UnknownPrimitiveField;
29+
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder;
30+
import org.graalvm.nativeimage.ImageSingletons;
31+
32+
import static com.oracle.svm.core.reflect.RuntimeMetadataDecoder.NO_DATA;
33+
34+
final class ImageDynamicHubMetadata implements DynamicHubMetadata {
35+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class) //
36+
final int enclosingMethodInfoIndex;
37+
38+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
39+
final int annotationsIndex;
40+
41+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
42+
final int typeAnnotationsIndex;
43+
44+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
45+
final int classesEncodingIndex;
46+
47+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
48+
final int permittedSubclassesEncodingIndex;
49+
50+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
51+
final int nestMembersEncodingIndex;
52+
53+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
54+
final int signersEncodingIndex;
55+
56+
ImageDynamicHubMetadata(int enclosingMethodInfoIndex, int annotationsIndex, int typeAnnotationsIndex, int classesEncodingIndex, int permittedSubclassesEncodingIndex,
57+
int nestMembersEncodingIndex, int signersEncodingIndex) {
58+
this.enclosingMethodInfoIndex = enclosingMethodInfoIndex;
59+
this.annotationsIndex = annotationsIndex;
60+
this.typeAnnotationsIndex = typeAnnotationsIndex;
61+
this.classesEncodingIndex = classesEncodingIndex;
62+
this.permittedSubclassesEncodingIndex = permittedSubclassesEncodingIndex;
63+
this.nestMembersEncodingIndex = nestMembersEncodingIndex;
64+
this.signersEncodingIndex = signersEncodingIndex;
65+
}
66+
67+
@Override
68+
public Object[] getEnclosingMethod(DynamicHub declaringClass) {
69+
if (enclosingMethodInfoIndex == NO_DATA) {
70+
return null;
71+
}
72+
Object[] enclosingMethod = ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseEnclosingMethod(enclosingMethodInfoIndex, declaringClass);
73+
if (enclosingMethod != null) {
74+
PredefinedClassesSupport.throwIfUnresolvable((Class<?>) enclosingMethod[0], declaringClass.getClassLoader0());
75+
}
76+
return enclosingMethod;
77+
}
78+
79+
@Override
80+
public Object[] getSigners(DynamicHub declaringClass) {
81+
if (signersEncodingIndex == NO_DATA) {
82+
return null;
83+
}
84+
return ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseObjects(signersEncodingIndex, declaringClass);
85+
}
86+
87+
@Override
88+
public byte[] getRawAnnotations(DynamicHub declaringClass) {
89+
if (annotationsIndex == NO_DATA) {
90+
return null;
91+
}
92+
return ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseByteArray(annotationsIndex, declaringClass);
93+
}
94+
95+
@Override
96+
public byte[] getRawTypeAnnotations(DynamicHub declaringClass) {
97+
if (typeAnnotationsIndex == NO_DATA) {
98+
return null;
99+
}
100+
return ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseByteArray(typeAnnotationsIndex, declaringClass);
101+
}
102+
103+
@Override
104+
public Class<?>[] getDeclaredClasses(DynamicHub declaringClass) {
105+
if (classesEncodingIndex == NO_DATA) {
106+
return new Class<?>[0];
107+
}
108+
Class<?>[] declaredClasses = ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseClasses(classesEncodingIndex, declaringClass);
109+
for (Class<?> clazz : declaredClasses) {
110+
PredefinedClassesSupport.throwIfUnresolvable(clazz, declaringClass.getClassLoader0());
111+
}
112+
return declaredClasses;
113+
}
114+
115+
@Override
116+
public Class<?>[] getNestMembers(DynamicHub declaringClass) {
117+
if (nestMembersEncodingIndex == NO_DATA) {
118+
return new Class<?>[]{DynamicHub.toClass(declaringClass)};
119+
}
120+
Class<?>[] nestMembers = ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseClasses(nestMembersEncodingIndex, declaringClass);
121+
for (Class<?> clazz : nestMembers) {
122+
PredefinedClassesSupport.throwIfUnresolvable(clazz, declaringClass.getClassLoader0());
123+
}
124+
return nestMembers;
125+
}
126+
127+
@Override
128+
public Class<?>[] getPermittedSubClasses(DynamicHub declaringClass) {
129+
if (permittedSubclassesEncodingIndex == NO_DATA) {
130+
return new Class<?>[0];
131+
}
132+
Class<?>[] permittedSubclasses = ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseClasses(permittedSubclassesEncodingIndex, declaringClass);
133+
for (Class<?> clazz : permittedSubclasses) {
134+
PredefinedClassesSupport.throwIfUnresolvable(clazz, declaringClass.getClassLoader0());
135+
}
136+
return permittedSubclasses;
137+
}
138+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.hub;
26+
27+
import com.oracle.svm.core.BuildPhaseProvider;
28+
import com.oracle.svm.core.heap.UnknownPrimitiveField;
29+
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder;
30+
import org.graalvm.nativeimage.ImageSingletons;
31+
32+
import java.lang.reflect.Constructor;
33+
import java.lang.reflect.Field;
34+
import java.lang.reflect.Method;
35+
import java.lang.reflect.RecordComponent;
36+
37+
import static com.oracle.svm.core.reflect.RuntimeMetadataDecoder.NO_DATA;
38+
39+
/**
40+
* Instances of this class are used to represent the reflection metadata for Dynamic hubs prepared
41+
* at build time and included in the image. For dynamic hubs loaded dynamically at runtime
42+
* {@link RuntimeReflectionMetadata} is used.
43+
*/
44+
public final class ImageReflectionMetadata implements ReflectionMetadata {
45+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
46+
final int fieldsEncodingIndex;
47+
48+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
49+
final int methodsEncodingIndex;
50+
51+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
52+
final int constructorsEncodingIndex;
53+
54+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
55+
final int recordComponentsEncodingIndex;
56+
57+
@UnknownPrimitiveField(availability = BuildPhaseProvider.CompileQueueFinished.class)//
58+
final int classFlags;
59+
60+
ImageReflectionMetadata(int fieldsEncodingIndex, int methodsEncodingIndex, int constructorsEncodingIndex, int recordComponentsEncodingIndex, int classFlags) {
61+
this.fieldsEncodingIndex = fieldsEncodingIndex;
62+
this.methodsEncodingIndex = methodsEncodingIndex;
63+
this.constructorsEncodingIndex = constructorsEncodingIndex;
64+
this.recordComponentsEncodingIndex = recordComponentsEncodingIndex;
65+
this.classFlags = classFlags;
66+
}
67+
68+
public int getClassFlags() {
69+
return classFlags;
70+
}
71+
72+
@Override
73+
public Field[] getDeclaredFields(DynamicHub declaringClass, boolean publicOnly, int layerNum) {
74+
if (fieldsEncodingIndex == NO_DATA) {
75+
return new Field[0];
76+
}
77+
return ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseFields(declaringClass, fieldsEncodingIndex, publicOnly, layerNum);
78+
}
79+
80+
@Override
81+
public Method[] getDeclaredMethods(DynamicHub declaringClass, boolean publicOnly, int layerNum) {
82+
if (methodsEncodingIndex == NO_DATA) {
83+
return new Method[0];
84+
}
85+
return ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseMethods(declaringClass, methodsEncodingIndex, publicOnly, layerNum);
86+
}
87+
88+
@Override
89+
public Constructor<?>[] getDeclaredConstructors(DynamicHub declaringClass, boolean publicOnly, int layerNum) {
90+
if (constructorsEncodingIndex == NO_DATA) {
91+
return new Constructor<?>[0];
92+
}
93+
return ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseConstructors(declaringClass, constructorsEncodingIndex, publicOnly, layerNum);
94+
}
95+
96+
@Override
97+
public RecordComponent[] getRecordComponents(DynamicHub declaringClass, int layerNum) {
98+
if (recordComponentsEncodingIndex == NO_DATA) {
99+
/* See ReflectionDataBuilder.buildRecordComponents() for details. */
100+
throw DynamicHub.recordsNotAvailable(declaringClass);
101+
}
102+
return ImageSingletons.lookup(RuntimeMetadataDecoder.class).parseRecordComponents(declaringClass, recordComponentsEncodingIndex, layerNum);
103+
}
104+
}

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.graalvm.nativeimage.Platforms;
3737

3838
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
39-
import com.oracle.svm.core.hub.DynamicHub.ReflectionMetadata;
4039
import com.oracle.svm.core.imagelayer.BuildingInitialLayerPredicate;
4140
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
4241
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
@@ -53,7 +52,7 @@ public class LayeredReflectionMetadataSingleton implements MultiLayeredImageSing
5352
private static final String LAYERED_REFLECTION_METADATA_HUBS = "layered reflection metadata hubs";
5453
private static final String LAYERED_REFLECTION_METADATA_CLASS_FLAGS = "layered reflection metadata classFlags";
5554

56-
private final EconomicMap<Integer, ReflectionMetadata> reflectionMetadataMap = EconomicMap.create();
55+
private final EconomicMap<Integer, ImageReflectionMetadata> reflectionMetadataMap = EconomicMap.create();
5756

5857
/**
5958
* The class flags registered in previous layers. This map is used to check if the class flags
@@ -83,7 +82,7 @@ public static LayeredReflectionMetadataSingleton[] singletons() {
8382
}
8483

8584
@Platforms(Platform.HOSTED_ONLY.class)
86-
public void setReflectionMetadata(DynamicHub hub, ReflectionMetadata reflectionMetadata) {
85+
public void setReflectionMetadata(DynamicHub hub, ImageReflectionMetadata reflectionMetadata) {
8786
/* GR-63472: Two different classes could have the same name in different class loaders */
8887
assert !reflectionMetadataMap.containsKey(hub.getTypeID()) : "The hub %s was added twice in the same layered reflection metadata".formatted(hub);
8988
if (isClassFlagsSubsetOfPreviousLayer(hub.getTypeID(), reflectionMetadata) && isReflectionMetadataEmpty(reflectionMetadata)) {
@@ -98,15 +97,15 @@ private boolean isClassFlagsSubsetOfPreviousLayer(int hub, ReflectionMetadata re
9897
}
9998

10099
private static int getCombinedClassFlags(ReflectionMetadata reflectionMetadata, int previousLayerFlags) {
101-
return previousLayerFlags | reflectionMetadata.classFlags;
100+
return previousLayerFlags | reflectionMetadata.getClassFlags();
102101
}
103102

104-
private static boolean isReflectionMetadataEmpty(ReflectionMetadata reflectionMetadata) {
103+
private static boolean isReflectionMetadataEmpty(ImageReflectionMetadata reflectionMetadata) {
105104
return reflectionMetadata.fieldsEncodingIndex == -1 && reflectionMetadata.methodsEncodingIndex == -1 &&
106105
reflectionMetadata.constructorsEncodingIndex == -1 && reflectionMetadata.recordComponentsEncodingIndex == -1;
107106
}
108107

109-
public ReflectionMetadata getReflectionMetadata(DynamicHub hub) {
108+
public ImageReflectionMetadata getReflectionMetadata(DynamicHub hub) {
110109
return reflectionMetadataMap.get(hub.getTypeID());
111110
}
112111

0 commit comments

Comments
 (0)