Skip to content

Commit e9a1174

Browse files
committed
Trace reflective accesses
1 parent 0baa135 commit e9a1174

File tree

5 files changed

+137
-7
lines changed

5 files changed

+137
-7
lines changed

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
5050
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
5151
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
52+
import com.oracle.svm.core.metadata.MetadataTracer;
5253
import com.oracle.svm.core.option.HostedOptionKey;
5354
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
5455
import com.oracle.svm.core.util.ImageHeapMap;
@@ -341,17 +342,22 @@ private static Class<?> forName(String className, ClassLoader classLoader, boole
341342
}
342343

343344
private Object forName0(String className, ClassLoader classLoader) {
344-
var conditional = knownClasses.get(className);
345-
Object result = conditional == null ? null : conditional.getValue();
346345
if (className.endsWith("[]")) {
347346
/* Querying array classes with their "TypeName[]" name always throws */
348-
result = NEGATIVE_QUERY;
347+
return new ClassNotFoundException(className);
349348
}
349+
var conditional = knownClasses.get(className);
350+
Object result = conditional == null ? null : conditional.getValue();
350351
if (result == null) {
351352
result = PredefinedClassesSupport.getLoadedForNameOrNull(className, classLoader);
352353
}
353354
if (result == null && !ClassNameSupport.isValidReflectionName(className)) {
354-
result = NEGATIVE_QUERY;
355+
/* Invalid class names always throw, no need for reflection data */
356+
return new ClassNotFoundException(className);
357+
}
358+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
359+
// NB: the early returns above ensure we do not trace calls with bad type args.
360+
MetadataTracer.singleton().traceReflectionType(className);
355361
}
356362
return result == NEGATIVE_QUERY ? new ClassNotFoundException(className) : result;
357363
}
@@ -445,7 +451,13 @@ public static boolean canUnsafeInstantiateAsInstance(DynamicHub hub) {
445451
break;
446452
}
447453
}
448-
return conditionSet != null && conditionSet.satisfied();
454+
if (conditionSet != null) {
455+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
456+
MetadataTracer.singleton().traceReflectionType(clazz.getName()).setUnsafeAllocated();
457+
}
458+
return conditionSet.satisfied();
459+
}
460+
return false;
449461
}
450462

451463
@Override

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

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.hub;
2626

27+
import static com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility;
28+
import static com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration;
2729
import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors;
2830
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
2931
import static com.oracle.svm.core.annotate.TargetElement.CONSTRUCTOR_NAME;
@@ -84,19 +86,21 @@
8486
import java.util.function.BiFunction;
8587
import java.util.function.IntFunction;
8688

87-
import com.oracle.svm.core.TrackDynamicAccessEnabled;
8889
import org.graalvm.nativeimage.AnnotationAccess;
8990
import org.graalvm.nativeimage.ImageSingletons;
9091
import org.graalvm.nativeimage.Platform;
9192
import org.graalvm.nativeimage.Platforms;
9293
import org.graalvm.word.WordBase;
9394

95+
import com.oracle.svm.configure.config.ConfigurationType;
96+
import com.oracle.svm.configure.config.SignatureUtil;
9497
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
9598
import com.oracle.svm.core.BuildPhaseProvider.CompileQueueFinished;
9699
import com.oracle.svm.core.NeverInline;
97100
import com.oracle.svm.core.NeverInlineTrivial;
98101
import com.oracle.svm.core.RuntimeAssertionsSupport;
99102
import com.oracle.svm.core.SubstrateUtil;
103+
import com.oracle.svm.core.TrackDynamicAccessEnabled;
100104
import com.oracle.svm.core.Uninterruptible;
101105
import com.oracle.svm.core.annotate.Alias;
102106
import com.oracle.svm.core.annotate.Delete;
@@ -121,6 +125,7 @@
121125
import com.oracle.svm.core.jdk.ProtectionDomainSupport;
122126
import com.oracle.svm.core.jdk.Resources;
123127
import com.oracle.svm.core.meta.SharedType;
128+
import com.oracle.svm.core.metadata.MetadataTracer;
124129
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
125130
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder;
126131
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder.ConstructorDescriptor;
@@ -704,6 +709,9 @@ private ReflectionMetadata reflectionMetadata() {
704709
}
705710

706711
private void checkClassFlag(int mask, String methodName) {
712+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
713+
traceClassFlagQuery(mask);
714+
}
707715
if (throwMissingRegistrationErrors() && !(isClassFlagSet(mask) && getConditions().satisfied())) {
708716
MissingReflectionRegistrationUtils.forBulkQuery(DynamicHub.toClass(this), methodName);
709717
}
@@ -726,6 +734,27 @@ private static boolean isClassFlagSet(int mask, ReflectionMetadata reflectionMet
726734
return reflectionMetadata != null && (reflectionMetadata.classFlags & mask) != 0;
727735
}
728736

737+
private void traceClassFlagQuery(int mask) {
738+
ConfigurationType type = MetadataTracer.singleton().traceReflectionType(getName());
739+
// TODO (GR-64765): We over-approximate member accessibility here because we don't trace
740+
// accesses. Once we trace accesses, it will suffice to register the class for reflection.
741+
switch (mask) {
742+
case ALL_FIELDS_FLAG -> type.setAllPublicFields(ConfigurationMemberAccessibility.ACCESSED);
743+
case ALL_DECLARED_FIELDS_FLAG -> type.setAllDeclaredFields(ConfigurationMemberAccessibility.ACCESSED);
744+
case ALL_METHODS_FLAG -> type.setAllPublicMethods(ConfigurationMemberAccessibility.ACCESSED);
745+
case ALL_DECLARED_METHODS_FLAG -> type.setAllDeclaredMethods(ConfigurationMemberAccessibility.ACCESSED);
746+
case ALL_CONSTRUCTORS_FLAG -> type.setAllPublicConstructors(ConfigurationMemberAccessibility.ACCESSED);
747+
case ALL_DECLARED_CONSTRUCTORS_FLAG -> type.setAllDeclaredConstructors(ConfigurationMemberAccessibility.ACCESSED);
748+
case ALL_CLASSES_FLAG -> type.setAllPublicClasses();
749+
case ALL_DECLARED_CLASSES_FLAG -> type.setAllDeclaredClasses();
750+
case ALL_RECORD_COMPONENTS_FLAG -> type.setAllRecordComponents();
751+
case ALL_PERMITTED_SUBCLASSES_FLAG -> type.setAllPermittedSubclasses();
752+
case ALL_NEST_MEMBERS_FLAG -> type.setAllNestMembers();
753+
case ALL_SIGNERS_FLAG -> type.setAllSigners();
754+
default -> throw VMError.shouldNotReachHere("unknown class flag " + mask);
755+
}
756+
}
757+
729758
/** Executed at runtime. */
730759
private static Object initEnumConstantsAtRuntime(Method values) {
731760
try {
@@ -1282,6 +1311,11 @@ public Field getField(String fieldName) throws NoSuchFieldException, SecurityExc
12821311
private void checkField(String fieldName, Field field, boolean publicOnly) throws NoSuchFieldException {
12831312
boolean throwMissingErrors = throwMissingRegistrationErrors();
12841313
Class<?> clazz = DynamicHub.toClass(this);
1314+
1315+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1316+
traceFieldLookup(fieldName, field, publicOnly);
1317+
}
1318+
12851319
if (field == null) {
12861320
if (throwMissingErrors && !allElementsRegistered(publicOnly, ALL_DECLARED_FIELDS_FLAG, ALL_FIELDS_FLAG)) {
12871321
MissingReflectionRegistrationUtils.forField(clazz, fieldName);
@@ -1305,6 +1339,21 @@ private void checkField(String fieldName, Field field, boolean publicOnly) throw
13051339
}
13061340
}
13071341

1342+
private void traceFieldLookup(String fieldName, Field field, boolean publicOnly) {
1343+
ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED;
1344+
if (field != null) {
1345+
// register declaring type and field
1346+
ConfigurationType declaringType = MetadataTracer.singleton().traceReflectionType(field.getDeclaringClass().getName());
1347+
declaringType.addField(fieldName, declaration, false);
1348+
// register receiver type
1349+
MetadataTracer.singleton().traceReflectionType(getName());
1350+
} else {
1351+
// register receiver type and negative field query
1352+
ConfigurationType receiverType = MetadataTracer.singleton().traceReflectionType(getName());
1353+
receiverType.addField(fieldName, declaration, false);
1354+
}
1355+
}
1356+
13081357
@Substitute
13091358
private Method getMethod(String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
13101359
Objects.requireNonNull(methodName);
@@ -1340,6 +1389,11 @@ private void checkConstructor(Class<?>[] parameterTypes, Constructor<?> construc
13401389
private boolean checkExecutableExists(String methodName, Class<?>[] parameterTypes, Executable method, boolean publicOnly) {
13411390
boolean throwMissingErrors = throwMissingRegistrationErrors();
13421391
Class<?> clazz = DynamicHub.toClass(this);
1392+
1393+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1394+
traceMethodLookup(methodName, parameterTypes, method, publicOnly);
1395+
}
1396+
13431397
if (method == null) {
13441398
boolean isConstructor = methodName.equals(CONSTRUCTOR_NAME);
13451399
int allDeclaredFlag = isConstructor ? ALL_DECLARED_CONSTRUCTORS_FLAG : ALL_DECLARED_METHODS_FLAG;
@@ -1366,6 +1420,34 @@ private boolean checkExecutableExists(String methodName, Class<?>[] parameterTyp
13661420
}
13671421
}
13681422

1423+
private void traceMethodLookup(String methodName, Class<?>[] parameterTypes, Executable method, boolean publicOnly) {
1424+
ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED;
1425+
if (method != null) {
1426+
// register declaring type and method
1427+
ConfigurationType declaringType = MetadataTracer.singleton().traceReflectionType(method.getDeclaringClass().getName());
1428+
declaringType.addMethod(methodName, toInternalSignature(parameterTypes), declaration);
1429+
// register receiver type
1430+
MetadataTracer.singleton().traceReflectionType(getName());
1431+
} else {
1432+
// register receiver type and negative method query
1433+
ConfigurationType receiverType = MetadataTracer.singleton().traceReflectionType(getName());
1434+
receiverType.addMethod(methodName, toInternalSignature(parameterTypes), declaration, ConfigurationMemberAccessibility.QUERIED);
1435+
}
1436+
}
1437+
1438+
private static String toInternalSignature(Class<?>[] classes) {
1439+
List<String> names;
1440+
if (classes == null) {
1441+
names = List.of();
1442+
} else {
1443+
names = new ArrayList<>(classes.length);
1444+
for (Class<?> aClass : classes) {
1445+
names.add(aClass.getName());
1446+
}
1447+
}
1448+
return SignatureUtil.toInternalSignature(names);
1449+
}
1450+
13691451
private boolean allElementsRegistered(boolean publicOnly, int allDeclaredElementsFlag, int allPublicElementsFlag) {
13701452
return isClassFlagSet(allDeclaredElementsFlag) || (publicOnly && isClassFlagSet(allPublicElementsFlag));
13711453
}
@@ -1882,6 +1964,8 @@ public DynamicHub arrayType() {
18821964
}
18831965
if (companion.arrayHub == null) {
18841966
MissingReflectionRegistrationUtils.forClass(getTypeName() + "[]");
1967+
} else if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1968+
MetadataTracer.singleton().traceReflectionType(companion.arrayHub.getTypeName());
18851969
}
18861970
return companion.arrayHub;
18871971
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangReflectSubstitutions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.oracle.svm.core.config.ConfigurationValues;
3838
import com.oracle.svm.core.hub.DynamicHub;
3939
import com.oracle.svm.core.hub.LayoutEncoding;
40+
import com.oracle.svm.core.metadata.MetadataTracer;
4041
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
4142
import com.oracle.svm.core.snippets.KnownIntrinsics;
4243

@@ -386,6 +387,9 @@ private static void set(Object a, int index, Object value) {
386387
@Substitute
387388
private static Object newArray(Class<?> componentType, int length)
388389
throws NegativeArraySizeException {
390+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
391+
MetadataTracer.singleton().traceReflectionType(componentType.arrayType().getName());
392+
}
389393
return KnownIntrinsics.unvalidatedNewArray(componentType, length);
390394
}
391395

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333
import org.graalvm.nativeimage.ImageSingletons;
3434
import org.graalvm.nativeimage.hosted.Feature;
3535

36+
import com.oracle.svm.configure.ConfigurationTypeDescriptor;
3637
import com.oracle.svm.configure.NamedConfigurationTypeDescriptor;
38+
import com.oracle.svm.configure.ProxyConfigurationTypeDescriptor;
3739
import com.oracle.svm.configure.UnresolvedConfigurationCondition;
3840
import com.oracle.svm.configure.config.ConfigurationSet;
41+
import com.oracle.svm.configure.config.ConfigurationType;
3942
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4043
import com.oracle.svm.core.feature.InternalFeature;
4144
import com.oracle.svm.core.jdk.RuntimeSupport;
@@ -79,6 +82,22 @@ public boolean enabled() {
7982
return config != null;
8083
}
8184

85+
public ConfigurationType traceReflectionType(String className) {
86+
return traceReflectionTypeImpl(new NamedConfigurationTypeDescriptor(className));
87+
}
88+
89+
/**
90+
* Marks the given proxy type as reachable from reflection.
91+
*/
92+
public void traceProxyType(List<String> interfaceNames) {
93+
traceReflectionTypeImpl(new ProxyConfigurationTypeDescriptor(interfaceNames));
94+
}
95+
96+
private ConfigurationType traceReflectionTypeImpl(ConfigurationTypeDescriptor typeDescriptor) {
97+
assert enabled();
98+
return config.getReflectionConfiguration().getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), new NamedConfigurationTypeDescriptor(className));
99+
}
100+
82101
public void traceResource(String resourceName, String moduleName) {
83102
assert enabled();
84103
config.getResourceConfiguration().addGlobPattern(UnresolvedConfigurationCondition.alwaysTrue(), resourceName, moduleName);
@@ -91,7 +110,7 @@ public void traceResourceBundle(String baseName) {
91110

92111
public void traceSerializationType(String className) {
93112
assert enabled();
94-
config.getReflectionConfiguration().getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), new NamedConfigurationTypeDescriptor(className)).setSerializable();
113+
traceReflectionType(className).setSerializable();
95114
}
96115

97116
private static void initialize() {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626

2727
import java.lang.reflect.InvocationHandler;
2828
import java.lang.reflect.Proxy;
29+
import java.util.ArrayList;
2930
import java.util.Arrays;
3031
import java.util.EnumSet;
32+
import java.util.List;
3133
import java.util.regex.Pattern;
3234

3335
import org.graalvm.collections.EconomicMap;
@@ -46,6 +48,7 @@
4648
import com.oracle.svm.core.layeredimagesingleton.DuplicableImageSingleton;
4749
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
4850
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
51+
import com.oracle.svm.core.metadata.MetadataTracer;
4952
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
5053
import com.oracle.svm.core.util.ImageHeapMap;
5154
import com.oracle.svm.core.util.VMError;
@@ -188,6 +191,14 @@ private static ClassLoader getCommonClassLoaderOrFail(ClassLoader loader, Class<
188191

189192
@Override
190193
public Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) {
194+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
195+
List<String> interfaceNames = new ArrayList<>(interfaces.length);
196+
for (Class<?> iface : interfaces) {
197+
interfaceNames.add(iface.getName());
198+
}
199+
MetadataTracer.singleton().traceProxyType(interfaceNames);
200+
}
201+
191202
ProxyCacheKey key = new ProxyCacheKey(interfaces);
192203
ConditionalRuntimeValue<Object> clazzOrError = proxyCache.get(key);
193204

0 commit comments

Comments
 (0)