Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-44834] Proofs for reflection queries #6296

Merged
merged 1 commit into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -174,8 +175,9 @@ private boolean canInitializeWithoutSideEffects(ResolvedJavaMethod clinit, Set<C
plugins.appendNodePlugin(new EarlyConstantFoldLoadFieldPlugin(originalProviders.getMetaAccess()));

SubstrateGraphBuilderPlugins.registerClassDesiredAssertionStatusPlugin(invocationPlugins, originalProviders.getSnippetReflection());
FallbackFeature fallbackFeature = ImageSingletons.contains(FallbackFeature.class) ? ImageSingletons.lookup(FallbackFeature.class) : null;
ReflectionPlugins.registerInvocationPlugins(classInitializationSupport.loader, originalProviders.getSnippetReflection(), null, classInitializationPlugin, invocationPlugins, null,
ParsingReason.EarlyClassInitializerAnalysis);
ParsingReason.EarlyClassInitializerAnalysis, fallbackFeature);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,9 @@ public String getDeletionReason(Field reflectionField) {

@Override
public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, Plugins plugins, ParsingReason reason) {
FallbackFeature fallbackFeature = ImageSingletons.contains(FallbackFeature.class) ? ImageSingletons.lookup(FallbackFeature.class) : null;
ReflectionPlugins.registerInvocationPlugins(loader, snippetReflection, annotationSubstitutions,
plugins.getClassInitializationPlugin(), plugins.getInvocationPlugins(), aUniverse, reason);
plugins.getClassInitializationPlugin(), plugins.getInvocationPlugins(), aUniverse, reason, fallbackFeature);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -71,9 +72,12 @@
import com.oracle.svm.core.hub.PredefinedClassesSupport;
import com.oracle.svm.core.jdk.StackTraceUtils;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.ExceptionSynthesizer;
import com.oracle.svm.hosted.FallbackFeature;
import com.oracle.svm.hosted.ImageClassLoader;
import com.oracle.svm.hosted.ReachabilityRegistrationNode;
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor;
import com.oracle.svm.hosted.substitute.DeletedElementException;
Expand Down Expand Up @@ -118,19 +122,21 @@ static class Options {
private final ClassInitializationPlugin classInitializationPlugin;
private final AnalysisUniverse aUniverse;
private final ParsingReason reason;
private final FallbackFeature fallbackFeature;

private ReflectionPlugins(ImageClassLoader imageClassLoader, SnippetReflectionProvider snippetReflection, AnnotationSubstitutionProcessor annotationSubstitutions,
ClassInitializationPlugin classInitializationPlugin, AnalysisUniverse aUniverse, ParsingReason reason) {
ClassInitializationPlugin classInitializationPlugin, AnalysisUniverse aUniverse, ParsingReason reason, FallbackFeature fallbackFeature) {
this.imageClassLoader = imageClassLoader;
this.snippetReflection = snippetReflection;
this.annotationSubstitutions = annotationSubstitutions;
this.classInitializationPlugin = classInitializationPlugin;
this.aUniverse = aUniverse;
this.reason = reason;
this.fallbackFeature = fallbackFeature;
}

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

ReflectionPlugins rp = new ReflectionPlugins(imageClassLoader, snippetReflection, annotationSubstitutions, classInitializationPlugin, aUniverse, reason);
ReflectionPlugins rp = new ReflectionPlugins(imageClassLoader, snippetReflection, annotationSubstitutions, classInitializationPlugin, aUniverse, reason, fallbackFeature);
rp.registerMethodHandlesPlugins(plugins);
rp.registerClassPlugins(plugins);
}
Expand Down Expand Up @@ -255,6 +261,21 @@ private void registerClassPlugins(InvocationPlugins plugins) {
"getField", "getMethod", "getConstructor",
"getDeclaredField", "getDeclaredMethod", "getDeclaredConstructor");

if (MissingReflectionRegistrationUtils.throwMissingRegistrationErrors()) {
registerBulkInvocationPlugin(plugins, Class.class, "getClasses", RuntimeReflection::registerAllClasses);
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredClasses", RuntimeReflection::registerAllDeclaredClasses);
registerBulkInvocationPlugin(plugins, Class.class, "getConstructors", RuntimeReflection::registerAllConstructors);
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredConstructors", RuntimeReflection::registerAllDeclaredConstructors);
registerBulkInvocationPlugin(plugins, Class.class, "getFields", RuntimeReflection::registerAllFields);
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredFields", RuntimeReflection::registerAllDeclaredFields);
registerBulkInvocationPlugin(plugins, Class.class, "getMethods", RuntimeReflection::registerAllMethods);
registerBulkInvocationPlugin(plugins, Class.class, "getDeclaredMethods", RuntimeReflection::registerAllDeclaredMethods);
registerBulkInvocationPlugin(plugins, Class.class, "getNestMembers", RuntimeReflection::registerAllNestMembers);
registerBulkInvocationPlugin(plugins, Class.class, "getPermittedSubclasses", RuntimeReflection::registerAllPermittedSubclasses);
registerBulkInvocationPlugin(plugins, Class.class, "getRecordComponents", RuntimeReflection::registerAllRecordComponents);
registerBulkInvocationPlugin(plugins, Class.class, "getSigners", RuntimeReflection::registerAllSigners);
}

Registration r = new Registration(plugins, Class.class);
r.register(new RequiredInvocationPlugin("forName", String.class) {
@Override
Expand Down Expand Up @@ -390,7 +411,7 @@ private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method refl
}

private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method reflectionMethod, Predicate<Object[]> 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);
Expand All @@ -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<Object[]> allowConstantFolding) {
assert b.getMetaAccess().lookupJavaMethod(reflectionMethod).equals(targetMethod) : "Fold method mismatch: " + reflectionMethod + " != " + targetMethod;
Expand Down Expand Up @@ -469,6 +494,48 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav
return pushConstant(b, targetMethod, targetParameters, returnKind, returnValue, false) != null;
}

private <T> void registerBulkInvocationPlugin(InvocationPlugins plugins, Class<T> declaringClass, String methodName, Consumer<T> 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 <T> boolean registerConstantBulkReflectionQuery(GraphBuilderContext b, Receiver receiver, Consumer<T> 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 <T> void registerForRuntimeReflection(T receiver, Consumer<T> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down