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,46 @@ 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 ) {
339333 mhw = fallbackSupplier .get ();
340334 }
341335
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 ());
336+ if (mhw .isCanSetTarget () && (callSite .getTarget () != mhw .getTargetMethodHandle ())) {
337+ // GROOVY-11935: Set invokedynamic call site target immediately for static method calls to enable earlier JIT inlining
338+ if (receiver instanceof Class ) {
339+ var method = mhw .getMethod ();
340+ if (method != null && Modifier .isStatic (method .getModifiers ())) {
341+ callSite .setTarget (mhw .getTargetMethodHandle ());
347342 }
348- } else {
349- callSite .setTarget (mhw .getTargetMethodHandle ());
350- if (LOG_ENABLED ) LOG .info ("call site target set, preparing outside invocation" );
351343 }
352344
353- mhw .resetLatestHitCount ();
345+ if (mhw .getLatestHitCount () > INDY_OPTIMIZE_THRESHOLD ) {
346+ if (callSite .getFallbackRound ().get () > INDY_FALLBACK_CUTOFF ) {
347+ if (callSite .getTarget () != callSite .getDefaultTarget ()) {
348+ // reset the call site target to default forever to avoid JIT deoptimization storm further
349+ callSite .setTarget (callSite .getDefaultTarget ());
350+ }
351+ } else {
352+ callSite .setTarget (mhw .getTargetMethodHandle ());
353+ if (LOG_ENABLED ) LOG .info ("call site target set, preparing outside invocation" );
354+ }
355+
356+ mhw .resetLatestHitCount ();
357+ }
354358 }
355359
356360 return mhw .getCachedMethodHandle ();
357361 }
358362
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-
365363 /**
366364 * Core method for indy method selection using runtime types.
367365 * @deprecated Use the new bootHandle-based approach instead.
@@ -391,7 +389,9 @@ private static MethodHandle selectMethodHandle(CacheableCallSite callSite, Class
391389 // it is important but impacts the performance somehow when cache misses frequently
392390 Object receiver = arguments [0 ];
393391 String key = receiver != null ? receiver .getClass ().getName () : NULL_OBJECT_CLASS_NAME ;
394- callSite .put (key , mhw );
392+
393+ // Avoid PIC pollution: don't write back uncached wrappers, e.g. for instance-level metaClass dispatches.
394+ callSite .put (key , mhw .isCanSetTarget () ? mhw : NULL_METHOD_HANDLE_WRAPPER );
395395 }
396396
397397 return mhw .getCachedMethodHandle ();
@@ -404,6 +404,7 @@ private static MethodHandleWrapper fallback(CacheableCallSite callSite, Class<?>
404404 return new MethodHandleWrapper (
405405 selector .handle .asSpreader (Object [].class , arguments .length ).asType (MethodType .methodType (Object .class , Object [].class )),
406406 selector .handle ,
407+ selector .method ,
407408 selector .cache
408409 );
409410 }
0 commit comments