• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.internal.bytecode;
2 
3 import static java.lang.invoke.MethodHandles.catchException;
4 import static java.lang.invoke.MethodHandles.constant;
5 import static java.lang.invoke.MethodHandles.dropArguments;
6 import static java.lang.invoke.MethodHandles.exactInvoker;
7 import static java.lang.invoke.MethodHandles.filterArguments;
8 import static java.lang.invoke.MethodHandles.foldArguments;
9 import static java.lang.invoke.MethodHandles.throwException;
10 import static java.lang.invoke.MethodType.methodType;
11 import static org.robolectric.internal.bytecode.MethodCallSite.Kind.REGULAR;
12 import static org.robolectric.internal.bytecode.MethodCallSite.Kind.STATIC;
13 
14 import java.lang.invoke.CallSite;
15 import java.lang.invoke.ConstantCallSite;
16 import java.lang.invoke.MethodHandle;
17 import java.lang.invoke.MethodHandles;
18 import java.lang.invoke.MethodType;
19 import java.lang.invoke.SwitchPoint;
20 import java.lang.invoke.WrongMethodTypeException;
21 import org.robolectric.util.ReflectionHelpers;
22 
23 public class InvokeDynamicSupport {
24   @SuppressWarnings("unused")
25   private static Interceptors INTERCEPTORS;
26 
27   private static final MethodHandle BIND_CALL_SITE;
28   private static final MethodHandle BIND_INIT_CALL_SITE;
29   private static final MethodHandle EXCEPTION_HANDLER;
30   private static final MethodHandle GET_SHADOW;
31 
32   static {
33     try {
34       MethodHandles.Lookup lookup = MethodHandles.lookup();
35 
36       BIND_CALL_SITE = lookup.findStatic(InvokeDynamicSupport.class, "bindCallSite",
37           methodType(MethodHandle.class, MethodCallSite.class));
38       BIND_INIT_CALL_SITE = lookup.findStatic(InvokeDynamicSupport.class, "bindInitCallSite",
39           methodType(MethodHandle.class, RoboCallSite.class));
40       MethodHandle cleanStackTrace = lookup.findStatic(RobolectricInternals.class, "cleanStackTrace",
41           methodType(Throwable.class, Throwable.class));
42       EXCEPTION_HANDLER = filterArguments(throwException(void.class, Throwable.class), 0, cleanStackTrace);
43       GET_SHADOW = lookup.findVirtual(ShadowedObject.class, "$$robo$getData", methodType(Object.class));
44     } catch (NoSuchMethodException | IllegalAccessException e) {
45       throw new AssertionError(e);
46     }
47   }
48 
49   @SuppressWarnings("UnusedDeclaration")
bootstrapInit(MethodHandles.Lookup caller, String name, MethodType type)50   public static CallSite bootstrapInit(MethodHandles.Lookup caller, String name, MethodType type) {
51     RoboCallSite site = new RoboCallSite(type, caller.lookupClass());
52 
53     bindInitCallSite(site);
54 
55     return site;
56   }
57 
58   @SuppressWarnings("UnusedDeclaration")
bootstrap(MethodHandles.Lookup caller, String name, MethodType type, MethodHandle original)59   public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type,
60       MethodHandle original) throws IllegalAccessException {
61     MethodCallSite site = new MethodCallSite(caller.lookupClass(), type, name, original, REGULAR);
62 
63     bindCallSite(site);
64 
65     return site;
66   }
67 
68   @SuppressWarnings("UnusedDeclaration")
bootstrapStatic(MethodHandles.Lookup caller, String name, MethodType type, MethodHandle original)69   public static CallSite bootstrapStatic(MethodHandles.Lookup caller, String name, MethodType type,
70       MethodHandle original) throws IllegalAccessException {
71     MethodCallSite site = new MethodCallSite(caller.lookupClass(), type, name, original, STATIC);
72 
73     bindCallSite(site);
74 
75     return site;
76   }
77 
78   @SuppressWarnings("UnusedDeclaration")
bootstrapIntrinsic(MethodHandles.Lookup caller, String name, MethodType type, String callee)79   public static CallSite bootstrapIntrinsic(MethodHandles.Lookup caller, String name,
80       MethodType type, String callee) throws IllegalAccessException {
81 
82     MethodHandle mh = getMethodHandle(callee, name, type);
83     if (mh == null) {
84       throw new IllegalArgumentException("Could not find intrinsic for " + callee + ":" + name);
85     }
86 
87     return new ConstantCallSite(mh.asType(type));
88   }
89 
90   private static final MethodHandle NOTHING = constant(Void.class, null).asType(methodType(void.class));
91 
getMethodHandle(String className, String methodName, MethodType type)92   private static MethodHandle getMethodHandle(String className, String methodName, MethodType type) {
93     Interceptor interceptor = INTERCEPTORS.findInterceptor(className, methodName);
94     if (interceptor != null) {
95       try {
96         // reload interceptor in sandbox...
97         Class<Interceptor> theClass =
98             (Class<Interceptor>) ReflectionHelpers.loadClass(
99                 RobolectricInternals.getClassLoader(),
100                 interceptor.getClass().getName()).asSubclass(Interceptor.class);
101         return ReflectionHelpers.newInstance(theClass).getMethodHandle(methodName, type);
102       } catch (NoSuchMethodException | IllegalAccessException e) {
103         throw new RuntimeException(e);
104       }
105     }
106 
107     if (type.parameterCount() != 0) {
108       return dropArguments(NOTHING, 0, type.parameterArray());
109     } else {
110       return NOTHING;
111     }
112   }
113 
bindInitCallSite(RoboCallSite site)114   private static MethodHandle bindInitCallSite(RoboCallSite site) {
115     MethodHandle mh = RobolectricInternals.getShadowCreator(site.getTheClass());
116     return bindWithFallback(site, mh, BIND_INIT_CALL_SITE);
117   }
118 
bindCallSite(MethodCallSite site)119   private static MethodHandle bindCallSite(MethodCallSite site) throws IllegalAccessException {
120     MethodHandle mh =
121         RobolectricInternals.findShadowMethodHandle(site.getTheClass(), site.getName(), site.type(),
122             site.isStatic());
123 
124     if (mh == null) {
125       // call original code
126       mh = site.getOriginal();
127     } else if (mh == ShadowWrangler.DO_NOTHING) {
128       // no-op
129       mh = dropArguments(mh, 0, site.type().parameterList());
130     } else if (!site.isStatic()) {
131       // drop arg 0 (this) for static methods
132       Class<?> shadowType = mh.type().parameterType(0);
133       mh = filterArguments(mh, 0, GET_SHADOW.asType(methodType(shadowType, site.thisType())));
134     }
135 
136     try {
137       return bindWithFallback(site, cleanStackTraces(mh), BIND_CALL_SITE);
138     } catch (Throwable t) {
139       // The error that bubbles up is currently not very helpful so we print any error messages
140       // here
141       t.printStackTrace();
142       System.err.println(site.getTheClass());
143       throw t;
144     }
145   }
146 
bindWithFallback(RoboCallSite site, MethodHandle mh, MethodHandle fallback)147   private static MethodHandle bindWithFallback(RoboCallSite site, MethodHandle mh,
148       MethodHandle fallback) {
149     SwitchPoint switchPoint = getInvalidator(site.getTheClass());
150     MethodType type = site.type();
151 
152     MethodHandle boundFallback = foldArguments(exactInvoker(type), fallback.bindTo(site));
153     try {
154       mh = switchPoint.guardWithTest(mh.asType(type), boundFallback);
155     } catch (WrongMethodTypeException e) {
156       if (site instanceof MethodCallSite) {
157         MethodCallSite methodCallSite = (MethodCallSite) site;
158         throw new RuntimeException("failed to bind " + methodCallSite.thisType() + "."
159             + methodCallSite.getName(), e);
160       } else {
161         throw e;
162       }
163     }
164 
165     site.setTarget(mh);
166     return mh;
167   }
168 
getInvalidator(Class<?> cl)169   private static SwitchPoint getInvalidator(Class<?> cl) {
170     return RobolectricInternals.getShadowInvalidator().getSwitchPoint(cl);
171   }
172 
cleanStackTraces(MethodHandle mh)173   private static MethodHandle cleanStackTraces(MethodHandle mh) {
174     MethodType type = EXCEPTION_HANDLER.type().changeReturnType(mh.type().returnType());
175     return catchException(mh, Throwable.class, EXCEPTION_HANDLER.asType(type));
176   }
177 }
178