Skip to content

Commit 492384d

Browse files
committed
[GR-39967] Skip constant folding of reflection methods with side effects.
PullRequest: graal/12834
2 parents da530a5 + 758223b commit 492384d

File tree

1 file changed

+78
-4
lines changed

1 file changed

+78
-4
lines changed

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

Lines changed: 78 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.Predicate;
4546
import java.util.function.Supplier;
4647
import java.util.stream.Collectors;
4748
import java.util.stream.Stream;
@@ -58,6 +59,8 @@
5859
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
5960
import org.graalvm.compiler.options.Option;
6061
import org.graalvm.nativeimage.ImageSingletons;
62+
import org.graalvm.nativeimage.hosted.RuntimeReflection;
63+
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
6164

6265
import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider;
6366
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
@@ -72,6 +75,7 @@
7275
import com.oracle.svm.core.util.VMError;
7376
import com.oracle.svm.hosted.ExceptionSynthesizer;
7477
import com.oracle.svm.hosted.ImageClassLoader;
78+
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
7579
import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor;
7680
import com.oracle.svm.hosted.substitute.DeletedElementException;
7781
import com.oracle.svm.util.ModuleSupport;
@@ -165,6 +169,7 @@ public static void registerInvocationPlugins(ImageClassLoader imageClassLoader,
165169
ByteOrder.class));
166170

167171
private void registerMethodHandlesPlugins(InvocationPlugins plugins) {
172+
168173
registerFoldInvocationPlugins(plugins, MethodHandles.class,
169174
"publicLookup", "privateLookupIn",
170175
"arrayConstructor", "arrayLength", "arrayElementGetter", "arrayElementSetter", "arrayElementVarHandle",
@@ -174,16 +179,18 @@ private void registerMethodHandlesPlugins(InvocationPlugins plugins) {
174179
"in",
175180
"findStatic", "findVirtual", "findConstructor", "findClass", "accessClass", "findSpecial",
176181
"findGetter", "findSetter", "findVarHandle",
177-
"findStaticGetter", "findStaticSetter", "findStaticVarHandle",
182+
"findStaticGetter", "findStaticSetter",
178183
"unreflect", "unreflectSpecial", "unreflectConstructor",
179-
"unreflectGetter", "unreflectSetter", "unreflectVarHandle");
184+
"unreflectGetter", "unreflectSetter");
180185

181186
registerFoldInvocationPlugins(plugins, MethodType.class,
182187
"methodType", "genericMethodType",
183188
"changeParameterType", "insertParameterTypes", "appendParameterTypes", "replaceParameterTypes", "dropParameterTypes",
184189
"changeReturnType", "erase", "generic", "wrap", "unwrap",
185190
"parameterType", "parameterCount", "returnType", "lastParameterType");
186191

192+
registerConditionalFoldInvocationPlugins(plugins);
193+
187194
Registration r = new Registration(plugins, MethodHandles.class);
188195
r.register(new RequiredInlineOnlyInvocationPlugin("lookup") {
189196
@Override
@@ -193,6 +200,53 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
193200
});
194201
}
195202

203+
/**
204+
* For some methods check if folding an invocation using reflection, i.e., by executing the
205+
* target method and capturing the result, has undesired side effects, such as triggering
206+
* initialization of classes that should be initialized at run time. This is based on knowledge
207+
* about the reflection API methods implementation.
208+
*/
209+
private void registerConditionalFoldInvocationPlugins(InvocationPlugins plugins) {
210+
Method methodHandlesLookupFindStaticVarHandle = ReflectionUtil.lookupMethod(MethodHandles.Lookup.class, "findStaticVarHandle", Class.class, String.class, Class.class);
211+
registerFoldInvocationPlugin(plugins, methodHandlesLookupFindStaticVarHandle, (args) -> {
212+
/* VarHandles.makeFieldHandle() triggers init of receiver class (JDK-8291065). */
213+
Object classArg = args[0];
214+
if (classArg instanceof Class<?>) {
215+
if (shouldInitializeAtRuntime((Class<?>) classArg)) {
216+
/* Skip the folding and register the field for run time reflection. */
217+
if (reason == ParsingReason.PointsToAnalysis) {
218+
Field field = ReflectionUtil.lookupField(true, (Class<?>) args[0], (String) args[1]);
219+
if (field != null) {
220+
RuntimeReflection.register(field);
221+
}
222+
}
223+
return false;
224+
}
225+
}
226+
return true;
227+
});
228+
229+
Method methodHandlesLookupUnreflectVarHandle = ReflectionUtil.lookupMethod(MethodHandles.Lookup.class, "unreflectVarHandle", Field.class);
230+
registerFoldInvocationPlugin(plugins, methodHandlesLookupUnreflectVarHandle, (args) -> {
231+
/*
232+
* VarHandles.makeFieldHandle() triggers init of static field's declaring class
233+
* (JDK-8291065).
234+
*/
235+
Object fieldArg = args[0];
236+
if (fieldArg instanceof Field) {
237+
Field field = (Field) fieldArg;
238+
if (isStatic(field) && shouldInitializeAtRuntime(field.getDeclaringClass())) {
239+
/* Skip the folding and register the field for run time reflection. */
240+
if (reason == ParsingReason.PointsToAnalysis) {
241+
RuntimeReflection.register(field);
242+
}
243+
return false;
244+
}
245+
}
246+
return true;
247+
});
248+
}
249+
196250
private void registerClassPlugins(InvocationPlugins plugins) {
197251
registerFoldInvocationPlugins(plugins, Class.class,
198252
"getField", "getMethod", "getConstructor",
@@ -326,7 +380,13 @@ private void registerFoldInvocationPlugins(InvocationPlugins plugins, Class<?> d
326380
}
327381
}
328382

383+
private static final Predicate<Object[]> alwaysAllowConstantFolding = args -> true;
384+
329385
private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method reflectionMethod) {
386+
registerFoldInvocationPlugin(plugins, reflectionMethod, alwaysAllowConstantFolding);
387+
}
388+
389+
private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method reflectionMethod, Predicate<Object[]> allowConstantFolding) {
330390
if (!ALLOWED_CONSTANT_CLASSES.contains(reflectionMethod.getReturnType()) && !reflectionMethod.getReturnType().isPrimitive()) {
331391
throw VMError.shouldNotReachHere("Return type of method " + reflectionMethod + " is not on the allow-list for types that are immutable");
332392
}
@@ -341,12 +401,13 @@ private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method refl
341401
plugins.register(reflectionMethod.getDeclaringClass(), new RequiredInvocationPlugin(reflectionMethod.getName(), parameterTypes.toArray(new Class<?>[0])) {
342402
@Override
343403
public boolean defaultHandler(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode... args) {
344-
return foldInvocationUsingReflection(b, targetMethod, reflectionMethod, receiver, args);
404+
return foldInvocationUsingReflection(b, targetMethod, reflectionMethod, receiver, args, allowConstantFolding);
345405
}
346406
});
347407
}
348408

349-
private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Method reflectionMethod, Receiver receiver, ValueNode[] args) {
409+
private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Method reflectionMethod, Receiver receiver, ValueNode[] args,
410+
Predicate<Object[]> allowConstantFolding) {
350411
assert b.getMetaAccess().lookupJavaMethod(reflectionMethod).equals(targetMethod) : "Fold method mismatch: " + reflectionMethod + " != " + targetMethod;
351412

352413
Object receiverValue;
@@ -376,6 +437,10 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav
376437
}
377438
}
378439

440+
if (!allowConstantFolding.test(argValues)) {
441+
return false;
442+
}
443+
379444
/* String representation of the parameters for debug printing. */
380445
Supplier<String> targetParameters = () -> (receiverValue == null ? "" : receiverValue.toString() + "; ") +
381446
Stream.of(argValues).map(arg -> arg instanceof Object[] ? Arrays.toString((Object[]) arg) : Objects.toString(arg)).collect(Collectors.joining(", "));
@@ -401,6 +466,15 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav
401466
return pushConstant(b, targetMethod, targetParameters, returnKind, returnValue, false) != null;
402467
}
403468

469+
private static boolean shouldInitializeAtRuntime(Class<?> classArg) {
470+
ClassInitializationSupport classInitializationSupport = (ClassInitializationSupport) ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
471+
return classInitializationSupport.shouldInitializeAtRuntime(classArg);
472+
}
473+
474+
private static boolean isStatic(Field field) {
475+
return Modifier.isStatic(field.getModifiers());
476+
}
477+
404478
private Object unbox(GraphBuilderContext b, ValueNode arg, JavaKind argKind) {
405479
if (!arg.isJavaConstant()) {
406480
/*

0 commit comments

Comments
 (0)