2121import groovy .lang .GroovySystem ;
2222import org .apache .groovy .util .SystemUtil ;
2323import org .codehaus .groovy .GroovyBugError ;
24- import org .codehaus .groovy .reflection .ClassInfo ;
2524import org .codehaus .groovy .runtime .GeneratedClosure ;
2625
2726import java .lang .invoke .CallSite ;
3130import java .lang .invoke .MethodType ;
3231import java .lang .invoke .MutableCallSite ;
3332import java .lang .invoke .SwitchPoint ;
33+ import java .lang .reflect .Modifier ;
3434import java .util .Map ;
3535import java .util .function .Function ;
3636import java .util .logging .Level ;
@@ -320,48 +320,48 @@ public static Object fromCache(CacheableCallSite callSite, Class<?> sender, Stri
320320 */
321321 private static MethodHandle fromCacheHandle (CacheableCallSite callSite , Class <?> sender , String methodName , int callID , Boolean safeNavigation , Boolean thisCall , Boolean spreadCall , Object dummyReceiver , Object [] arguments ) throws Throwable {
322322 FallbackSupplier fallbackSupplier = new FallbackSupplier (callSite , sender , methodName , callID , safeNavigation , thisCall , spreadCall , dummyReceiver , arguments );
323+ final Object receiver = arguments [0 ];
323324
324- MethodHandleWrapper mhw ;
325- if (bypassCache (spreadCall , arguments )) {
326- mhw = NULL_METHOD_HANDLE_WRAPPER ;
327- } else {
328- Object receiver = arguments [0 ];
329- String receiverClassName = receiver != null ? receiver .getClass ().getName () : NULL_OBJECT_CLASS_NAME ;
330-
331- mhw = callSite .getAndPut (receiverClassName , (theName ) -> {
332- MethodHandleWrapper fallback = fallbackSupplier .get ();
333- if (fallback .isCanSetTarget ()) return fallback ;
334- return NULL_METHOD_HANDLE_WRAPPER ;
335- });
336- }
325+ String receiverClassName = receiver != null ? receiver .getClass ().getName () : NULL_OBJECT_CLASS_NAME ;
326+ MethodHandleWrapper mhw = callSite .getAndPut (receiverClassName , (theName ) -> {
327+ MethodHandleWrapper fallback = fallbackSupplier .get ();
328+ if (fallback .isCanSetTarget ()) return fallback ;
329+ return NULL_METHOD_HANDLE_WRAPPER ;
330+ });
337331
338332 if (mhw == NULL_METHOD_HANDLE_WRAPPER ) {
333+ // The PIC stores a sentinel to remember "do not relink this receiver shape";
334+ // execution still needs a real handle for the current invocation.
339335 mhw = fallbackSupplier .get ();
340336 }
341337
342- if (mhw .isCanSetTarget () && (callSite .getTarget () != mhw .getTargetMethodHandle ()) && (mhw .getLatestHitCount () > INDY_OPTIMIZE_THRESHOLD )) {
343- if (callSite .getFallbackRound ().get () > INDY_FALLBACK_CUTOFF ) {
344- if (callSite .getTarget () != callSite .getDefaultTarget ()) {
345- // reset the call site target to default forever to avoid JIT deoptimization storm further
346- callSite .setTarget (callSite .getDefaultTarget ());
338+ if (mhw .isCanSetTarget () && (callSite .getTarget () != mhw .getTargetMethodHandle ())) {
339+ // GROOVY-11935: Set invokedynamic call site target immediately for static method calls to enable earlier JIT inlining
340+ if (receiver instanceof Class ) {
341+ var method = mhw .getMethod ();
342+ if (method != null && Modifier .isStatic (method .getModifiers ())) {
343+ callSite .setTarget (mhw .getTargetMethodHandle ());
347344 }
348- } else {
349- callSite .setTarget (mhw .getTargetMethodHandle ());
350- if (LOG_ENABLED ) LOG .info ("call site target set, preparing outside invocation" );
351345 }
352346
353- mhw .resetLatestHitCount ();
347+ if (mhw .getLatestHitCount () > INDY_OPTIMIZE_THRESHOLD ) {
348+ if (callSite .getFallbackRound ().get () > INDY_FALLBACK_CUTOFF ) {
349+ if (callSite .getTarget () != callSite .getDefaultTarget ()) {
350+ // reset the call site target to default forever to avoid JIT deoptimization storm further
351+ callSite .setTarget (callSite .getDefaultTarget ());
352+ }
353+ } else {
354+ callSite .setTarget (mhw .getTargetMethodHandle ());
355+ if (LOG_ENABLED ) LOG .info ("call site target set, preparing outside invocation" );
356+ }
357+
358+ mhw .resetLatestHitCount ();
359+ }
354360 }
355361
356362 return mhw .getCachedMethodHandle ();
357363 }
358364
359- private static boolean bypassCache (Boolean spreadCall , Object [] arguments ) {
360- if (spreadCall ) return true ;
361- Object receiver = arguments [0 ];
362- return receiver != null && ClassInfo .getClassInfo (receiver .getClass ()).hasPerInstanceMetaClasses ();
363- }
364-
365365 /**
366366 * Core method for indy method selection using runtime types.
367367 * @deprecated Use the new bootHandle-based approach instead.
@@ -391,7 +391,9 @@ private static MethodHandle selectMethodHandle(CacheableCallSite callSite, Class
391391 // it is important but impacts the performance somehow when cache misses frequently
392392 Object receiver = arguments [0 ];
393393 String key = receiver != null ? receiver .getClass ().getName () : NULL_OBJECT_CLASS_NAME ;
394- callSite .put (key , mhw );
394+
395+ // Avoid PIC pollution: don't write back uncached wrappers, e.g. for instance-level metaClass dispatches.
396+ callSite .put (key , mhw .isCanSetTarget () ? mhw : NULL_METHOD_HANDLE_WRAPPER );
395397 }
396398
397399 return mhw .getCachedMethodHandle ();
@@ -404,6 +406,7 @@ private static MethodHandleWrapper fallback(CacheableCallSite callSite, Class<?>
404406 return new MethodHandleWrapper (
405407 selector .handle .asSpreader (Object [].class , arguments .length ).asType (MethodType .methodType (Object .class , Object [].class )),
406408 selector .handle ,
409+ selector .method ,
407410 selector .cache
408411 );
409412 }
0 commit comments