4242import java .util .List ;
4343import java .util .Objects ;
4444import java .util .Set ;
45+ import java .util .function .Predicate ;
4546import java .util .function .Supplier ;
4647import java .util .stream .Collectors ;
4748import java .util .stream .Stream ;
5859import org .graalvm .compiler .nodes .graphbuilderconf .InvocationPlugins .Registration ;
5960import org .graalvm .compiler .options .Option ;
6061import org .graalvm .nativeimage .ImageSingletons ;
62+ import org .graalvm .nativeimage .hosted .RuntimeReflection ;
63+ import org .graalvm .nativeimage .impl .RuntimeClassInitializationSupport ;
6164
6265import com .oracle .graal .pointsto .infrastructure .OriginalClassProvider ;
6366import com .oracle .graal .pointsto .meta .AnalysisUniverse ;
7275import com .oracle .svm .core .util .VMError ;
7376import com .oracle .svm .hosted .ExceptionSynthesizer ;
7477import com .oracle .svm .hosted .ImageClassLoader ;
78+ import com .oracle .svm .hosted .classinitialization .ClassInitializationSupport ;
7579import com .oracle .svm .hosted .substitute .AnnotationSubstitutionProcessor ;
7680import com .oracle .svm .hosted .substitute .DeletedElementException ;
7781import 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