From 110aad34fdb5900646f521c88b8567041c606651 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Tue, 14 Mar 2023 12:10:17 +0100 Subject: [PATCH] Proofs for reflection queries --- .../oracle/svm/hosted/FallbackFeature.java | 7 +- .../EarlyClassInitializerAnalysis.java | 4 +- .../svm/hosted/reflect/ReflectionFeature.java | 3 +- .../hosted/snippets/ReflectionPlugins.java | 75 ++++++++++++++++++- .../UnsafeAutomaticSubstitutionProcessor.java | 5 +- 5 files changed, 85 insertions(+), 9 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java index 5c6f16cdfd19..cf4b3e554134 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FallbackFeature.java @@ -43,8 +43,8 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.svm.core.FallbackExecutor; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl; @@ -283,6 +283,7 @@ public void beforeAnalysis(BeforeAnalysisAccess a) { } public FallbackImageRequest reflectionFallback = null; + public boolean ignoreReflectionFallback = false; public FallbackImageRequest resourceFallback = null; public FallbackImageRequest jniFallback = null; public FallbackImageRequest proxyFallback = null; @@ -313,7 +314,9 @@ public void afterAnalysis(AfterAnalysisAccess a) { if (!reflectionCalls.isEmpty()) { reflectionCalls.add(ABORT_MSG_PREFIX + " due to reflection use without configuration."); - reflectionFallback = new FallbackImageRequest(reflectionCalls); + if (!ignoreReflectionFallback) { + reflectionFallback = new FallbackImageRequest(reflectionCalls); + } } if (!resourceCalls.isEmpty()) { resourceCalls.add(ABORT_MSG_PREFIX + " due to accessing resources without configuration."); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/EarlyClassInitializerAnalysis.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/EarlyClassInitializerAnalysis.java index 557d78148236..f3ea57041bba 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/EarlyClassInitializerAnalysis.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/EarlyClassInitializerAnalysis.java @@ -69,6 +69,7 @@ import com.oracle.svm.core.graal.thread.VMThreadLocalAccess; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FallbackFeature; import com.oracle.svm.hosted.phases.EarlyConstantFoldLoadFieldPlugin; import com.oracle.svm.hosted.snippets.ReflectionPlugins; import com.oracle.svm.hosted.snippets.SubstrateGraphBuilderPlugins; @@ -174,8 +175,9 @@ private boolean canInitializeWithoutSideEffects(ResolvedJavaMethod clinit, Set allowConstantFolding) { - if (!ALLOWED_CONSTANT_CLASSES.contains(reflectionMethod.getReturnType()) && !reflectionMethod.getReturnType().isPrimitive()) { + if (!isAllowedReturnType(reflectionMethod.getReturnType())) { throw VMError.shouldNotReachHere("Return type of method " + reflectionMethod + " is not on the allow-list for types that are immutable"); } reflectionMethod.setAccessible(true); @@ -409,6 +430,10 @@ public boolean defaultHandler(GraphBuilderContext b, ResolvedJavaMethod targetMe }); } + private static boolean isAllowedReturnType(Class returnType) { + return ALLOWED_CONSTANT_CLASSES.contains(returnType) || returnType.isPrimitive(); + } + private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Method reflectionMethod, Receiver receiver, ValueNode[] args, Predicate allowConstantFolding) { assert b.getMetaAccess().lookupJavaMethod(reflectionMethod).equals(targetMethod) : "Fold method mismatch: " + reflectionMethod + " != " + targetMethod; @@ -469,6 +494,48 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav return pushConstant(b, targetMethod, targetParameters, returnKind, returnValue, false) != null; } + private void registerBulkInvocationPlugin(InvocationPlugins plugins, Class declaringClass, String methodName, Consumer registrationCallback) { + plugins.register(declaringClass, new RequiredInvocationPlugin(methodName, new Class[]{Receiver.class}) { + @Override + public boolean isDecorator() { + return true; + } + + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { + VMError.guarantee(!targetMethod.isStatic(), "Bulk reflection queries are not static"); + return registerConstantBulkReflectionQuery(b, receiver, registrationCallback); + } + }); + } + + @SuppressWarnings("unchecked") + private boolean registerConstantBulkReflectionQuery(GraphBuilderContext b, Receiver receiver, Consumer registrationCallback) { + /* + * Calling receiver.get(true) can add a null check guard, i.e., modifying the graph in the + * process. It is an error for invocation plugins that do not replace the call to modify the + * graph. + */ + Object receiverValue = unbox(b, receiver.get(false), JavaKind.Object); + if (receiverValue == null || receiverValue == NULL_MARKER) { + return false; + } + + b.add(new ReachabilityRegistrationNode(() -> registerForRuntimeReflection((T) receiverValue, registrationCallback))); + return true; + } + + private void registerForRuntimeReflection(T receiver, Consumer registrationCallback) { + try { + registrationCallback.accept(receiver); + if (fallbackFeature != null) { + fallbackFeature.ignoreReflectionFallback = true; + } + } catch (LinkageError e) { + // Ignore, the call should be registered manually + } + } + private static boolean shouldInitializeAtRuntime(Class classArg) { ClassInitializationSupport classInitializationSupport = (ClassInitializationSupport) ImageSingletons.lookup(RuntimeClassInitializationSupport.class); return classInitializationSupport.shouldInitializeAtRuntime(classArg); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java index a4a9f4ea5807..edc794ad80d9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java @@ -73,6 +73,7 @@ import org.graalvm.compiler.phases.tiers.HighTierContext; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.ImageSingletons; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -87,6 +88,7 @@ import com.oracle.svm.core.jdk.RecordSupport; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FallbackFeature; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.SVMHost; @@ -284,8 +286,9 @@ public void init(ImageClassLoader loader, MetaAccessProvider originalMetaAccess) NoClassInitializationPlugin classInitializationPlugin = new NoClassInitializationPlugin(); plugins.setClassInitializationPlugin(classInitializationPlugin); + FallbackFeature fallbackFeature = ImageSingletons.contains(FallbackFeature.class) ? ImageSingletons.lookup(FallbackFeature.class) : null; ReflectionPlugins.registerInvocationPlugins(loader, snippetReflection, annotationSubstitutions, classInitializationPlugin, plugins.getInvocationPlugins(), null, - ParsingReason.UnsafeSubstitutionAnalysis); + ParsingReason.UnsafeSubstitutionAnalysis, fallbackFeature); /* * Note: ConstantFoldLoadFieldPlugin should not be installed because it will disrupt