Skip to content

[WIP][GR-45250] Implement a bytecode-level analysis for constant reflection calls #10878

New issue

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

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

Already on GitHub? # to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

graalvmbot
Copy link
Collaborator

Currently, the constant reflection analysis used by Native Image is optimization dependent. This can lead to unexpected results during image run-time when using reflection. For example, the Class.forName call in the following snippet will be folded by the analysis:

static boolean isEven(int n) {
    return n % 2 == 0;
}

Class<?> grabClass() throws ClassNotFoundException {
    var className = isEven(4) ? "A" : "B";
    return Class.forName(className); // returns Class A
}

However, adding a simple printing statement to isEven or toggling different optimizations during build-time can cause the method to be non-inlinable and Class.forName call won't be folded:

static boolean isEven(int n) {
    System.out.print("isEven was called");
    return n % 2 == 0;
}

Class<?> grabClass() throws ClassNotFoundException {
    var className = isEven(4) ? "A" : "B";
    return Class.forName(className); // throws ClassNotFoundException / MissingReflectionRegistrationError
}

In order to prevent this behavior, we can run a constant reflection analysis directly on the bytecode used as input to Native Image.

This PR implements a JVMTI agent (com.oracle.svm.reflectionagent.NativeImageReflectionAgent) which intercepts user provided class files, analyzes them for constant reflection usage and marks such reflective methods as constant by redirecting them to their counterparts in the org.graalvm.nativeimage.impl.reflectiontags.ConstantTags) class. If a reflective method invocation in a user provided class gets folded by com.oracle.svm.hosted.snippets.ReflectionPlugins without it being marked as constant by the agent, the user gets a warning during build-time:

Warning: Call to java.lang.Class.forName(String) reached in Demo.main(Demo.java:14) with arguments (A) was reduced to the constant class A outside of the strict constant reflection mode. Consider adding the appropriate entry to your reachability metadata (https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection).

To enable the agent and warnings, use the -H:+EnableStrictReflection. In addition, three new options are provided for more accurate logging of constant reflection folding done by ReflectionPlugins:

  • -H:ReflectionPluginTraceLocation=<log_location> - Location for the log file. If not set, the log isn't created.
  • -H:ReflectionPluginTraceFormat=json|plain - Specify the format of the location log. Default value is json. If ReflectionPluginTraceLocation isn't set, this option has no effect.
  • -H:+ReflectionPluginTraceUserOnly - Log only the constant folding which occurred in user classes. Default value is true. If ReflectionPluginTraceLocation isn't set, this option has no effect.

@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Mar 18, 2025
@graalvmbot graalvmbot force-pushed the alekstef/GT-45250-strict-reflection-mode branch from 5e6a66b to 34d8c35 Compare March 25, 2025 10:02
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
OCA Verified All contributors have signed the Oracle Contributor Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants