Skip to content

Commit 087e740

Browse files
committed
[GR-44834] Proofs for reflection queries
PullRequest: graal/14050
2 parents 65fa7a0 + 110aad3 commit 087e740

File tree

5 files changed

+85
-9
lines changed

5 files changed

+85
-9
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
import com.oracle.graal.pointsto.meta.AnalysisMethod;
4444
import com.oracle.svm.core.FallbackExecutor;
4545
import com.oracle.svm.core.SubstrateOptions;
46-
import com.oracle.svm.core.feature.InternalFeature;
4746
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
47+
import com.oracle.svm.core.feature.InternalFeature;
4848
import com.oracle.svm.core.util.UserError;
4949
import com.oracle.svm.core.util.VMError;
5050
import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl;
@@ -283,6 +283,7 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
283283
}
284284

285285
public FallbackImageRequest reflectionFallback = null;
286+
public boolean ignoreReflectionFallback = false;
286287
public FallbackImageRequest resourceFallback = null;
287288
public FallbackImageRequest jniFallback = null;
288289
public FallbackImageRequest proxyFallback = null;
@@ -313,7 +314,9 @@ public void afterAnalysis(AfterAnalysisAccess a) {
313314

314315
if (!reflectionCalls.isEmpty()) {
315316
reflectionCalls.add(ABORT_MSG_PREFIX + " due to reflection use without configuration.");
316-
reflectionFallback = new FallbackImageRequest(reflectionCalls);
317+
if (!ignoreReflectionFallback) {
318+
reflectionFallback = new FallbackImageRequest(reflectionCalls);
319+
}
317320
}
318321
if (!resourceCalls.isEmpty()) {
319322
resourceCalls.add(ABORT_MSG_PREFIX + " due to accessing resources without configuration.");

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/EarlyClassInitializerAnalysis.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import com.oracle.svm.core.graal.thread.VMThreadLocalAccess;
7070
import com.oracle.svm.core.option.HostedOptionValues;
7171
import com.oracle.svm.core.util.VMError;
72+
import com.oracle.svm.hosted.FallbackFeature;
7273
import com.oracle.svm.hosted.phases.EarlyConstantFoldLoadFieldPlugin;
7374
import com.oracle.svm.hosted.snippets.ReflectionPlugins;
7475
import com.oracle.svm.hosted.snippets.SubstrateGraphBuilderPlugins;
@@ -174,8 +175,9 @@ private boolean canInitializeWithoutSideEffects(ResolvedJavaMethod clinit, Set<C
174175
plugins.appendNodePlugin(new EarlyConstantFoldLoadFieldPlugin(originalProviders.getMetaAccess()));
175176

176177
SubstrateGraphBuilderPlugins.registerClassDesiredAssertionStatusPlugin(invocationPlugins, originalProviders.getSnippetReflection());
178+
FallbackFeature fallbackFeature = ImageSingletons.contains(FallbackFeature.class) ? ImageSingletons.lookup(FallbackFeature.class) : null;
177179
ReflectionPlugins.registerInvocationPlugins(classInitializationSupport.loader, originalProviders.getSnippetReflection(), null, classInitializationPlugin, invocationPlugins, null,
178-
ParsingReason.EarlyClassInitializerAnalysis);
180+
ParsingReason.EarlyClassInitializerAnalysis, fallbackFeature);
179181

180182
GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
181183

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,9 @@ public String getDeletionReason(Field reflectionField) {
339339

340340
@Override
341341
public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, Plugins plugins, ParsingReason reason) {
342+
FallbackFeature fallbackFeature = ImageSingletons.contains(FallbackFeature.class) ? ImageSingletons.lookup(FallbackFeature.class) : null;
342343
ReflectionPlugins.registerInvocationPlugins(loader, snippetReflection, annotationSubstitutions,
343-
plugins.getClassInitializationPlugin(), plugins.getInvocationPlugins(), aUniverse, reason);
344+
plugins.getClassInitializationPlugin(), plugins.getInvocationPlugins(), aUniverse, reason, fallbackFeature);
344345
}
345346
}
346347

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.util.List;
4343
import java.util.Objects;
4444
import java.util.Set;
45+
import java.util.function.Consumer;
4546
import java.util.function.Predicate;
4647
import java.util.function.Supplier;
4748
import java.util.stream.Collectors;
@@ -71,9 +72,12 @@
7172
import com.oracle.svm.core.hub.PredefinedClassesSupport;
7273
import com.oracle.svm.core.jdk.StackTraceUtils;
7374
import com.oracle.svm.core.option.HostedOptionKey;
75+
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
7476
import com.oracle.svm.core.util.VMError;
7577
import com.oracle.svm.hosted.ExceptionSynthesizer;
78+
import com.oracle.svm.hosted.FallbackFeature;
7679
import com.oracle.svm.hosted.ImageClassLoader;
80+
import com.oracle.svm.hosted.ReachabilityRegistrationNode;
7781
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
7882
import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor;
7983
import com.oracle.svm.hosted.substitute.DeletedElementException;
@@ -118,19 +122,21 @@ static class Options {
118122
private final ClassInitializationPlugin classInitializationPlugin;
119123
private final AnalysisUniverse aUniverse;
120124
private final ParsingReason reason;
125+
private final FallbackFeature fallbackFeature;
121126

122127
private ReflectionPlugins(ImageClassLoader imageClassLoader, SnippetReflectionProvider snippetReflection, AnnotationSubstitutionProcessor annotationSubstitutions,
123-
ClassInitializationPlugin classInitializationPlugin, AnalysisUniverse aUniverse, ParsingReason reason) {
128+
ClassInitializationPlugin classInitializationPlugin, AnalysisUniverse aUniverse, ParsingReason reason, FallbackFeature fallbackFeature) {
124129
this.imageClassLoader = imageClassLoader;
125130
this.snippetReflection = snippetReflection;
126131
this.annotationSubstitutions = annotationSubstitutions;
127132
this.classInitializationPlugin = classInitializationPlugin;
128133
this.aUniverse = aUniverse;
129134
this.reason = reason;
135+
this.fallbackFeature = fallbackFeature;
130136
}
131137

132138
public static void registerInvocationPlugins(ImageClassLoader imageClassLoader, SnippetReflectionProvider snippetReflection, AnnotationSubstitutionProcessor annotationSubstitutions,
133-
ClassInitializationPlugin classInitializationPlugin, InvocationPlugins plugins, AnalysisUniverse aUniverse, ParsingReason reason) {
139+
ClassInitializationPlugin classInitializationPlugin, InvocationPlugins plugins, AnalysisUniverse aUniverse, ParsingReason reason, FallbackFeature fallbackFeature) {
134140
/*
135141
* Initialize the registry if we are during analysis. If hosted is false, i.e., we are
136142
* analyzing the static initializers, then we always intrinsify, so don't need a registry.
@@ -141,7 +147,7 @@ public static void registerInvocationPlugins(ImageClassLoader imageClassLoader,
141147
}
142148
}
143149

144-
ReflectionPlugins rp = new ReflectionPlugins(imageClassLoader, snippetReflection, annotationSubstitutions, classInitializationPlugin, aUniverse, reason);
150+
ReflectionPlugins rp = new ReflectionPlugins(imageClassLoader, snippetReflection, annotationSubstitutions, classInitializationPlugin, aUniverse, reason, fallbackFeature);
145151
rp.registerMethodHandlesPlugins(plugins);
146152
rp.registerClassPlugins(plugins);
147153
}
@@ -255,6 +261,21 @@ private void registerClassPlugins(InvocationPlugins plugins) {
255261
"getField", "getMethod", "getConstructor",
256262
"getDeclaredField", "getDeclaredMethod", "getDeclaredConstructor");
257263

264+
if (MissingReflectionRegistrationUtils.throwMissingRegistrationErrors()) {
265+
registerBulkInvocationPlugin(plugins, Class.class, "getClasses", RuntimeReflection::registerAllClasses);
266+
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredClasses", RuntimeReflection::registerAllDeclaredClasses);
267+
registerBulkInvocationPlugin(plugins, Class.class, "getConstructors", RuntimeReflection::registerAllConstructors);
268+
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredConstructors", RuntimeReflection::registerAllDeclaredConstructors);
269+
registerBulkInvocationPlugin(plugins, Class.class, "getFields", RuntimeReflection::registerAllFields);
270+
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredFields", RuntimeReflection::registerAllDeclaredFields);
271+
registerBulkInvocationPlugin(plugins, Class.class, "getMethods", RuntimeReflection::registerAllMethods);
272+
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredMethods", RuntimeReflection::registerAllDeclaredMethods);
273+
registerBulkInvocationPlugin(plugins, Class.class, "getNestMembers", RuntimeReflection::registerAllNestMembers);
274+
registerBulkInvocationPlugin(plugins, Class.class, "getPermittedSubclasses", RuntimeReflection::registerAllPermittedSubclasses);
275+
registerBulkInvocationPlugin(plugins, Class.class, "getRecordComponents", RuntimeReflection::registerAllRecordComponents);
276+
registerBulkInvocationPlugin(plugins, Class.class, "getSigners", RuntimeReflection::registerAllSigners);
277+
}
278+
258279
Registration r = new Registration(plugins, Class.class);
259280
r.register(new RequiredInvocationPlugin("forName", String.class) {
260281
@Override
@@ -390,7 +411,7 @@ private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method refl
390411
}
391412

392413
private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method reflectionMethod, Predicate<Object[]> allowConstantFolding) {
393-
if (!ALLOWED_CONSTANT_CLASSES.contains(reflectionMethod.getReturnType()) && !reflectionMethod.getReturnType().isPrimitive()) {
414+
if (!isAllowedReturnType(reflectionMethod.getReturnType())) {
394415
throw VMError.shouldNotReachHere("Return type of method " + reflectionMethod + " is not on the allow-list for types that are immutable");
395416
}
396417
reflectionMethod.setAccessible(true);
@@ -409,6 +430,10 @@ public boolean defaultHandler(GraphBuilderContext b, ResolvedJavaMethod targetMe
409430
});
410431
}
411432

433+
private static boolean isAllowedReturnType(Class<?> returnType) {
434+
return ALLOWED_CONSTANT_CLASSES.contains(returnType) || returnType.isPrimitive();
435+
}
436+
412437
private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Method reflectionMethod, Receiver receiver, ValueNode[] args,
413438
Predicate<Object[]> allowConstantFolding) {
414439
assert b.getMetaAccess().lookupJavaMethod(reflectionMethod).equals(targetMethod) : "Fold method mismatch: " + reflectionMethod + " != " + targetMethod;
@@ -469,6 +494,48 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav
469494
return pushConstant(b, targetMethod, targetParameters, returnKind, returnValue, false) != null;
470495
}
471496

497+
private <T> void registerBulkInvocationPlugin(InvocationPlugins plugins, Class<T> declaringClass, String methodName, Consumer<T> registrationCallback) {
498+
plugins.register(declaringClass, new RequiredInvocationPlugin(methodName, new Class<?>[]{Receiver.class}) {
499+
@Override
500+
public boolean isDecorator() {
501+
return true;
502+
}
503+
504+
@Override
505+
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
506+
VMError.guarantee(!targetMethod.isStatic(), "Bulk reflection queries are not static");
507+
return registerConstantBulkReflectionQuery(b, receiver, registrationCallback);
508+
}
509+
});
510+
}
511+
512+
@SuppressWarnings("unchecked")
513+
private <T> boolean registerConstantBulkReflectionQuery(GraphBuilderContext b, Receiver receiver, Consumer<T> registrationCallback) {
514+
/*
515+
* Calling receiver.get(true) can add a null check guard, i.e., modifying the graph in the
516+
* process. It is an error for invocation plugins that do not replace the call to modify the
517+
* graph.
518+
*/
519+
Object receiverValue = unbox(b, receiver.get(false), JavaKind.Object);
520+
if (receiverValue == null || receiverValue == NULL_MARKER) {
521+
return false;
522+
}
523+
524+
b.add(new ReachabilityRegistrationNode(() -> registerForRuntimeReflection((T) receiverValue, registrationCallback)));
525+
return true;
526+
}
527+
528+
private <T> void registerForRuntimeReflection(T receiver, Consumer<T> registrationCallback) {
529+
try {
530+
registrationCallback.accept(receiver);
531+
if (fallbackFeature != null) {
532+
fallbackFeature.ignoreReflectionFallback = true;
533+
}
534+
} catch (LinkageError e) {
535+
// Ignore, the call should be registered manually
536+
}
537+
}
538+
472539
private static boolean shouldInitializeAtRuntime(Class<?> classArg) {
473540
ClassInitializationSupport classInitializationSupport = (ClassInitializationSupport) ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
474541
return classInitializationSupport.shouldInitializeAtRuntime(classArg);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import org.graalvm.compiler.phases.tiers.HighTierContext;
7474
import org.graalvm.compiler.phases.util.Providers;
7575
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
76+
import org.graalvm.nativeimage.ImageSingletons;
7677

7778
import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor;
7879
import com.oracle.graal.pointsto.meta.AnalysisType;
@@ -87,6 +88,7 @@
8788
import com.oracle.svm.core.jdk.RecordSupport;
8889
import com.oracle.svm.core.option.HostedOptionKey;
8990
import com.oracle.svm.core.util.VMError;
91+
import com.oracle.svm.hosted.FallbackFeature;
9092
import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl;
9193
import com.oracle.svm.hosted.ImageClassLoader;
9294
import com.oracle.svm.hosted.SVMHost;
@@ -284,8 +286,9 @@ public void init(ImageClassLoader loader, MetaAccessProvider originalMetaAccess)
284286
NoClassInitializationPlugin classInitializationPlugin = new NoClassInitializationPlugin();
285287
plugins.setClassInitializationPlugin(classInitializationPlugin);
286288

289+
FallbackFeature fallbackFeature = ImageSingletons.contains(FallbackFeature.class) ? ImageSingletons.lookup(FallbackFeature.class) : null;
287290
ReflectionPlugins.registerInvocationPlugins(loader, snippetReflection, annotationSubstitutions, classInitializationPlugin, plugins.getInvocationPlugins(), null,
288-
ParsingReason.UnsafeSubstitutionAnalysis);
291+
ParsingReason.UnsafeSubstitutionAnalysis, fallbackFeature);
289292

290293
/*
291294
* Note: ConstantFoldLoadFieldPlugin should not be installed because it will disrupt

0 commit comments

Comments
 (0)