Skip to content

Commit d8a49ed

Browse files
committed
Skip constant folding of reflection methods with side effects.
1 parent 297fb2b commit d8a49ed

File tree

1 file changed

+71
-4
lines changed

1 file changed

+71
-4
lines changed

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.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,46 @@ 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+
RuntimeReflection.register(ReflectionUtil.lookupField((Class<?>) args[0], (String) args[1]));
218+
return false;
219+
}
220+
}
221+
return true;
222+
});
223+
224+
Method methodHandlesLookupUnreflectVarHandle = ReflectionUtil.lookupMethod(MethodHandles.Lookup.class, "unreflectVarHandle", Field.class);
225+
registerFoldInvocationPlugin(plugins, methodHandlesLookupUnreflectVarHandle, (args) -> {
226+
/*
227+
* VarHandles.makeFieldHandle() triggers init of static field's declaring class
228+
* (JDK-8291065).
229+
*/
230+
Object fieldArg = args[0];
231+
if (fieldArg instanceof Field) {
232+
Field field = (Field) fieldArg;
233+
if (isStatic(field) && shouldInitializeAtRuntime(field.getDeclaringClass())) {
234+
/* Skip the folding and register the field for run time reflection. */
235+
RuntimeReflection.register(field);
236+
return false;
237+
}
238+
}
239+
return true;
240+
});
241+
}
242+
196243
private void registerClassPlugins(InvocationPlugins plugins) {
197244
registerFoldInvocationPlugins(plugins, Class.class,
198245
"getField", "getMethod", "getConstructor",
@@ -326,7 +373,13 @@ private void registerFoldInvocationPlugins(InvocationPlugins plugins, Class<?> d
326373
}
327374
}
328375

376+
private static final Predicate<Object[]> alwaysAllowConstantFolding = args -> true;
377+
329378
private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method reflectionMethod) {
379+
registerFoldInvocationPlugin(plugins, reflectionMethod, alwaysAllowConstantFolding);
380+
}
381+
382+
private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method reflectionMethod, Predicate<Object[]> allowConstantFolding) {
330383
if (!ALLOWED_CONSTANT_CLASSES.contains(reflectionMethod.getReturnType()) && !reflectionMethod.getReturnType().isPrimitive()) {
331384
throw VMError.shouldNotReachHere("Return type of method " + reflectionMethod + " is not on the allow-list for types that are immutable");
332385
}
@@ -341,12 +394,13 @@ private void registerFoldInvocationPlugin(InvocationPlugins plugins, Method refl
341394
plugins.register(reflectionMethod.getDeclaringClass(), new RequiredInvocationPlugin(reflectionMethod.getName(), parameterTypes.toArray(new Class<?>[0])) {
342395
@Override
343396
public boolean defaultHandler(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode... args) {
344-
return foldInvocationUsingReflection(b, targetMethod, reflectionMethod, receiver, args);
397+
return foldInvocationUsingReflection(b, targetMethod, reflectionMethod, receiver, args, allowConstantFolding);
345398
}
346399
});
347400
}
348401

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

352406
Object receiverValue;
@@ -376,6 +430,10 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav
376430
}
377431
}
378432

433+
if (!allowConstantFolding.test(argValues)) {
434+
return false;
435+
}
436+
379437
/* String representation of the parameters for debug printing. */
380438
Supplier<String> targetParameters = () -> (receiverValue == null ? "" : receiverValue.toString() + "; ") +
381439
Stream.of(argValues).map(arg -> arg instanceof Object[] ? Arrays.toString((Object[]) arg) : Objects.toString(arg)).collect(Collectors.joining(", "));
@@ -401,6 +459,15 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav
401459
return pushConstant(b, targetMethod, targetParameters, returnKind, returnValue, false) != null;
402460
}
403461

462+
private static boolean shouldInitializeAtRuntime(Class<?> classArg) {
463+
ClassInitializationSupport classInitializationSupport = (ClassInitializationSupport) ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
464+
return classInitializationSupport.shouldInitializeAtRuntime(classArg);
465+
}
466+
467+
private static boolean isStatic(Field field) {
468+
return Modifier.isStatic(field.getModifiers());
469+
}
470+
404471
private Object unbox(GraphBuilderContext b, ValueNode arg, JavaKind argKind) {
405472
if (!arg.isJavaConstant()) {
406473
/*

0 commit comments

Comments
 (0)