11021315c11e619e0735bea1349e04023178c4067Alexander Blompackage org.robolectric.internal.bytecode; 21021315c11e619e0735bea1349e04023178c4067Alexander Blom 31021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static java.lang.invoke.MethodHandles.catchException; 4704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williamsimport static java.lang.invoke.MethodHandles.constant; 51021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static java.lang.invoke.MethodHandles.dropArguments; 61021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static java.lang.invoke.MethodHandles.exactInvoker; 71021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static java.lang.invoke.MethodHandles.filterArguments; 81021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static java.lang.invoke.MethodHandles.foldArguments; 91021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static java.lang.invoke.MethodHandles.throwException; 101021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static java.lang.invoke.MethodType.methodType; 111021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static org.robolectric.internal.bytecode.MethodCallSite.Kind.REGULAR; 121021315c11e619e0735bea1349e04023178c4067Alexander Blomimport static org.robolectric.internal.bytecode.MethodCallSite.Kind.STATIC; 131021315c11e619e0735bea1349e04023178c4067Alexander Blom 14851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.lang.invoke.CallSite; 15851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.lang.invoke.ConstantCallSite; 16851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.lang.invoke.MethodHandle; 17851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.lang.invoke.MethodHandles; 18851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.lang.invoke.MethodType; 19851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport java.lang.invoke.SwitchPoint; 20851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport org.robolectric.util.ReflectionHelpers; 21851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williams 221021315c11e619e0735bea1349e04023178c4067Alexander Blompublic class InvokeDynamicSupport { 23feff94bc310afb5b46109a1496d2a309e5fdb3a8Christian Williams @SuppressWarnings("unused") 24feff94bc310afb5b46109a1496d2a309e5fdb3a8Christian Williams private static Interceptors INTERCEPTORS; 25feff94bc310afb5b46109a1496d2a309e5fdb3a8Christian Williams 261021315c11e619e0735bea1349e04023178c4067Alexander Blom private static final MethodHandle BIND_CALL_SITE; 271021315c11e619e0735bea1349e04023178c4067Alexander Blom private static final MethodHandle BIND_INIT_CALL_SITE; 281021315c11e619e0735bea1349e04023178c4067Alexander Blom private static final MethodHandle EXCEPTION_HANDLER; 291021315c11e619e0735bea1349e04023178c4067Alexander Blom private static final MethodHandle GET_SHADOW; 301021315c11e619e0735bea1349e04023178c4067Alexander Blom 311021315c11e619e0735bea1349e04023178c4067Alexander Blom static { 321021315c11e619e0735bea1349e04023178c4067Alexander Blom try { 331021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodHandles.Lookup lookup = MethodHandles.lookup(); 341021315c11e619e0735bea1349e04023178c4067Alexander Blom 351021315c11e619e0735bea1349e04023178c4067Alexander Blom BIND_CALL_SITE = lookup.findStatic(InvokeDynamicSupport.class, "bindCallSite", 361021315c11e619e0735bea1349e04023178c4067Alexander Blom methodType(MethodHandle.class, MethodCallSite.class)); 371021315c11e619e0735bea1349e04023178c4067Alexander Blom BIND_INIT_CALL_SITE = lookup.findStatic(InvokeDynamicSupport.class, "bindInitCallSite", 381021315c11e619e0735bea1349e04023178c4067Alexander Blom methodType(MethodHandle.class, RoboCallSite.class)); 391021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodHandle cleanStackTrace = lookup.findStatic(RobolectricInternals.class, "cleanStackTrace", 401021315c11e619e0735bea1349e04023178c4067Alexander Blom methodType(Throwable.class, Throwable.class)); 411021315c11e619e0735bea1349e04023178c4067Alexander Blom EXCEPTION_HANDLER = filterArguments(throwException(void.class, Throwable.class), 0, cleanStackTrace); 421021315c11e619e0735bea1349e04023178c4067Alexander Blom GET_SHADOW = lookup.findVirtual(ShadowedObject.class, "$$robo$getData", methodType(Object.class)); 431021315c11e619e0735bea1349e04023178c4067Alexander Blom } catch (NoSuchMethodException | IllegalAccessException e) { 441021315c11e619e0735bea1349e04023178c4067Alexander Blom throw new AssertionError(e); 451021315c11e619e0735bea1349e04023178c4067Alexander Blom } 461021315c11e619e0735bea1349e04023178c4067Alexander Blom } 471021315c11e619e0735bea1349e04023178c4067Alexander Blom 481021315c11e619e0735bea1349e04023178c4067Alexander Blom @SuppressWarnings("UnusedDeclaration") 491021315c11e619e0735bea1349e04023178c4067Alexander Blom public static CallSite bootstrapInit(MethodHandles.Lookup caller, String name, MethodType type) { 501021315c11e619e0735bea1349e04023178c4067Alexander Blom RoboCallSite site = new RoboCallSite(type, caller.lookupClass()); 511021315c11e619e0735bea1349e04023178c4067Alexander Blom 521021315c11e619e0735bea1349e04023178c4067Alexander Blom bindInitCallSite(site); 531021315c11e619e0735bea1349e04023178c4067Alexander Blom 541021315c11e619e0735bea1349e04023178c4067Alexander Blom return site; 551021315c11e619e0735bea1349e04023178c4067Alexander Blom } 561021315c11e619e0735bea1349e04023178c4067Alexander Blom 571021315c11e619e0735bea1349e04023178c4067Alexander Blom @SuppressWarnings("UnusedDeclaration") 581021315c11e619e0735bea1349e04023178c4067Alexander Blom public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type, 591021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodHandle original) throws IllegalAccessException { 601021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodCallSite site = new MethodCallSite(type, caller.lookupClass(), name, original, REGULAR); 611021315c11e619e0735bea1349e04023178c4067Alexander Blom 621021315c11e619e0735bea1349e04023178c4067Alexander Blom bindCallSite(site); 631021315c11e619e0735bea1349e04023178c4067Alexander Blom 641021315c11e619e0735bea1349e04023178c4067Alexander Blom return site; 651021315c11e619e0735bea1349e04023178c4067Alexander Blom } 661021315c11e619e0735bea1349e04023178c4067Alexander Blom 671021315c11e619e0735bea1349e04023178c4067Alexander Blom @SuppressWarnings("UnusedDeclaration") 681021315c11e619e0735bea1349e04023178c4067Alexander Blom public static CallSite bootstrapStatic(MethodHandles.Lookup caller, String name, MethodType type, 691021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodHandle original) throws IllegalAccessException { 701021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodCallSite site = new MethodCallSite(type, caller.lookupClass(), name, original, STATIC); 711021315c11e619e0735bea1349e04023178c4067Alexander Blom 721021315c11e619e0735bea1349e04023178c4067Alexander Blom bindCallSite(site); 731021315c11e619e0735bea1349e04023178c4067Alexander Blom 741021315c11e619e0735bea1349e04023178c4067Alexander Blom return site; 751021315c11e619e0735bea1349e04023178c4067Alexander Blom } 761021315c11e619e0735bea1349e04023178c4067Alexander Blom 771021315c11e619e0735bea1349e04023178c4067Alexander Blom @SuppressWarnings("UnusedDeclaration") 781021315c11e619e0735bea1349e04023178c4067Alexander Blom public static CallSite bootstrapIntrinsic(MethodHandles.Lookup caller, String name, 791021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodType type, String callee) throws IllegalAccessException { 801021315c11e619e0735bea1349e04023178c4067Alexander Blom 81704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams MethodHandle mh = getMethodHandle(callee, name, type); 821021315c11e619e0735bea1349e04023178c4067Alexander Blom if (mh == null) { 831021315c11e619e0735bea1349e04023178c4067Alexander Blom throw new IllegalArgumentException("Could not find intrinsic for " + callee + ":" + name); 841021315c11e619e0735bea1349e04023178c4067Alexander Blom } 851021315c11e619e0735bea1349e04023178c4067Alexander Blom 861021315c11e619e0735bea1349e04023178c4067Alexander Blom return new ConstantCallSite(mh.asType(type)); 871021315c11e619e0735bea1349e04023178c4067Alexander Blom } 881021315c11e619e0735bea1349e04023178c4067Alexander Blom 89704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams private static final MethodHandle NOTHING = constant(Void.class, null).asType(methodType(void.class)); 90704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams 91704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams private static MethodHandle getMethodHandle(String className, String methodName, MethodType type) { 92704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams Interceptor interceptor = INTERCEPTORS.findInterceptor(className, methodName); 93704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams if (interceptor != null) { 94704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams try { 95704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams // reload interceptor in sandbox... 96704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams Class<Interceptor> theClass = 97704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams (Class<Interceptor>) ReflectionHelpers.loadClass( 98704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams RobolectricInternals.getClassLoader(), 99704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams interceptor.getClass().getName()).asSubclass(Interceptor.class); 100704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams return ReflectionHelpers.newInstance(theClass).getMethodHandle(methodName, type); 101704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams } catch (NoSuchMethodException | IllegalAccessException e) { 102704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams throw new RuntimeException(e); 103704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams } 104704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams } 105704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams 106704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams if (type.parameterCount() != 0) { 107704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams return dropArguments(NOTHING, 0, type.parameterArray()); 108704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams } else { 109704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams return NOTHING; 110704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams } 111704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams } 112704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williams 1131021315c11e619e0735bea1349e04023178c4067Alexander Blom private static MethodHandle bindInitCallSite(RoboCallSite site) { 1141021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodHandle mh = RobolectricInternals.getShadowCreator(site.getCaller()); 1151021315c11e619e0735bea1349e04023178c4067Alexander Blom return bindWithFallback(mh, site, BIND_INIT_CALL_SITE); 1161021315c11e619e0735bea1349e04023178c4067Alexander Blom } 1171021315c11e619e0735bea1349e04023178c4067Alexander Blom 1181021315c11e619e0735bea1349e04023178c4067Alexander Blom private static MethodHandle bindCallSite(MethodCallSite site) throws IllegalAccessException { 1191021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodHandle mh = 1201021315c11e619e0735bea1349e04023178c4067Alexander Blom RobolectricInternals.findShadowMethod(site.getCaller(), site.getName(), site.type(), 1211021315c11e619e0735bea1349e04023178c4067Alexander Blom site.isStatic()); 1221021315c11e619e0735bea1349e04023178c4067Alexander Blom 1231021315c11e619e0735bea1349e04023178c4067Alexander Blom if (mh == null) { 1241021315c11e619e0735bea1349e04023178c4067Alexander Blom // Call original code and make sure to clean stack traces 1251021315c11e619e0735bea1349e04023178c4067Alexander Blom mh = cleanStackTraces(site.getOriginal()); 1261021315c11e619e0735bea1349e04023178c4067Alexander Blom } else if (mh == ShadowWrangler.DO_NOTHING) { 1271021315c11e619e0735bea1349e04023178c4067Alexander Blom mh = dropArguments(mh, 0, site.type().parameterList()); 1281021315c11e619e0735bea1349e04023178c4067Alexander Blom } else if (!site.isStatic()) { 1291021315c11e619e0735bea1349e04023178c4067Alexander Blom Class<?> shadowType = mh.type().parameterType(0); 1301021315c11e619e0735bea1349e04023178c4067Alexander Blom mh = filterArguments(mh, 0, GET_SHADOW.asType(methodType(shadowType, site.thisType()))); 1311021315c11e619e0735bea1349e04023178c4067Alexander Blom } 1321021315c11e619e0735bea1349e04023178c4067Alexander Blom 1331021315c11e619e0735bea1349e04023178c4067Alexander Blom try { 1341021315c11e619e0735bea1349e04023178c4067Alexander Blom return bindWithFallback(mh, site, BIND_CALL_SITE); 1351021315c11e619e0735bea1349e04023178c4067Alexander Blom } catch (Throwable t) { 1361021315c11e619e0735bea1349e04023178c4067Alexander Blom // The error that bubbles up is currently not very helpful so we print any error messages 1371021315c11e619e0735bea1349e04023178c4067Alexander Blom // here 1381021315c11e619e0735bea1349e04023178c4067Alexander Blom t.printStackTrace(); 1391021315c11e619e0735bea1349e04023178c4067Alexander Blom System.err.println(site.getCaller()); 1401021315c11e619e0735bea1349e04023178c4067Alexander Blom throw t; 1411021315c11e619e0735bea1349e04023178c4067Alexander Blom } 1421021315c11e619e0735bea1349e04023178c4067Alexander Blom } 1431021315c11e619e0735bea1349e04023178c4067Alexander Blom 1441021315c11e619e0735bea1349e04023178c4067Alexander Blom private static MethodHandle bindWithFallback(MethodHandle mh, RoboCallSite site, MethodHandle fallback) { 1451021315c11e619e0735bea1349e04023178c4067Alexander Blom SwitchPoint switchPoint = getInvalidator(site.getCaller()); 1461021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodType type = site.type(); 1471021315c11e619e0735bea1349e04023178c4067Alexander Blom 1481021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodHandle boundFallback = foldArguments(exactInvoker(type), fallback.bindTo(site)); 1491021315c11e619e0735bea1349e04023178c4067Alexander Blom mh = switchPoint.guardWithTest(mh.asType(type), boundFallback); 1501021315c11e619e0735bea1349e04023178c4067Alexander Blom 1511021315c11e619e0735bea1349e04023178c4067Alexander Blom site.setTarget(mh); 1521021315c11e619e0735bea1349e04023178c4067Alexander Blom return mh; 1531021315c11e619e0735bea1349e04023178c4067Alexander Blom } 1541021315c11e619e0735bea1349e04023178c4067Alexander Blom 1551021315c11e619e0735bea1349e04023178c4067Alexander Blom private static SwitchPoint getInvalidator(Class<?> cl) { 1561021315c11e619e0735bea1349e04023178c4067Alexander Blom return RobolectricInternals.getShadowInvalidator().getSwitchPoint(cl); 1571021315c11e619e0735bea1349e04023178c4067Alexander Blom } 1581021315c11e619e0735bea1349e04023178c4067Alexander Blom 1591021315c11e619e0735bea1349e04023178c4067Alexander Blom private static MethodHandle cleanStackTraces(MethodHandle mh) { 1601021315c11e619e0735bea1349e04023178c4067Alexander Blom MethodType type = EXCEPTION_HANDLER.type().changeReturnType(mh.type().returnType()); 1611021315c11e619e0735bea1349e04023178c4067Alexander Blom return catchException(mh, Throwable.class, EXCEPTION_HANDLER.asType(type)); 1621021315c11e619e0735bea1349e04023178c4067Alexander Blom } 1631021315c11e619e0735bea1349e04023178c4067Alexander Blom} 164