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