• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.stock;
18 
19 import com.android.dx.Code;
20 import com.android.dx.Comparison;
21 import com.android.dx.DexMaker;
22 import com.android.dx.FieldId;
23 import com.android.dx.Label;
24 import com.android.dx.Local;
25 import com.android.dx.MethodId;
26 import com.android.dx.TypeId;
27 
28 import java.io.File;
29 import java.io.IOException;
30 import java.lang.reflect.Constructor;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.InvocationHandler;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 import java.lang.reflect.Modifier;
36 import java.lang.reflect.UndeclaredThrowableException;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.Comparator;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.concurrent.CopyOnWriteArraySet;
45 
46 import static java.lang.reflect.Modifier.PRIVATE;
47 import static java.lang.reflect.Modifier.PUBLIC;
48 import static java.lang.reflect.Modifier.STATIC;
49 
50 /**
51  * Creates dynamic proxies of concrete classes.
52  * <p>
53  * This is similar to the {@code java.lang.reflect.Proxy} class, but works for classes instead of
54  * interfaces.
55  * <h3>Example</h3>
56  * The following example demonstrates the creation of a dynamic proxy for {@code java.util.Random}
57  * which will always return 4 when asked for integers, and which logs method calls to every method.
58  * <pre>
59  * InvocationHandler handler = new InvocationHandler() {
60  *     &#64;Override
61  *     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
62  *         if (method.getName().equals("nextInt")) {
63  *             // Chosen by fair dice roll, guaranteed to be random.
64  *             return 4;
65  *         }
66  *         Object result = ProxyBuilder.callSuper(proxy, method, args);
67  *         System.out.println("Method: " + method.getName() + " args: "
68  *                 + Arrays.toString(args) + " result: " + result);
69  *         return result;
70  *     }
71  * };
72  * Random debugRandom = ProxyBuilder.forClass(Random.class)
73  *         .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE))
74  *         .handler(handler)
75  *         .build();
76  * assertEquals(4, debugRandom.nextInt());
77  * debugRandom.setSeed(0);
78  * assertTrue(debugRandom.nextBoolean());
79  * </pre>
80  * <h3>Usage</h3>
81  * Call {@link #forClass(Class)} for the Class you wish to proxy. Call
82  * {@link #handler(InvocationHandler)} passing in an {@link InvocationHandler}, and then call
83  * {@link #build()}. The returned instance will be a dynamically generated subclass where all method
84  * calls will be delegated to the invocation handler, except as noted below.
85  * <p>
86  * The static method {@link #callSuper(Object, Method, Object...)} allows you to access the original
87  * super method for a given proxy. This allows the invocation handler to selectively override some
88  * methods but not others.
89  * <p>
90  * By default, the {@link #build()} method will call the no-arg constructor belonging to the class
91  * being proxied. If you wish to call a different constructor, you must provide arguments for both
92  * {@link #constructorArgTypes(Class[])} and {@link #constructorArgValues(Object[])}.
93  * <p>
94  * This process works only for classes with public and protected level of visibility.
95  * <p>
96  * You may proxy abstract classes.  You may not proxy final classes.
97  * <p>
98  * Only non-private, non-final, non-static methods will be dispatched to the invocation handler.
99  * Private, static or final methods will always call through to the superclass as normal.
100  * <p>
101  * The {@link #finalize()} method on {@code Object} will not be proxied.
102  * <p>
103  * You must provide a dex cache directory via the {@link #dexCache(File)} method. You should take
104  * care not to make this a world-writable directory, so that third parties cannot inject code into
105  * your application.  A suitable parameter for these output directories would be something like
106  * this:
107  * <pre>{@code
108  *     getApplicationContext().getDir("dx", Context.MODE_PRIVATE);
109  * }</pre>
110  * <p>
111  * If the base class to be proxied leaks the {@code this} pointer in the constructor (bad practice),
112  * that is to say calls a non-private non-final method from the constructor, the invocation handler
113  * will not be invoked.  As a simple concrete example, when proxying Random we discover that it
114  * internally calls setSeed during the constructor.  The proxy will not intercept this call during
115  * proxy construction, but will intercept as normal afterwards.  This behaviour may be subject to
116  * change in future releases.
117  * <p>
118  * This class is <b>not thread safe</b>.
119  */
120 public final class ProxyBuilder<T> {
121     // Version of ProxyBuilder. It should be updated if the implementation
122     // of the generated proxy class changes.
123     public static final int VERSION = 1;
124 
125     private static final String FIELD_NAME_HANDLER = "$__handler";
126     private static final String FIELD_NAME_METHODS = "$__methodArray";
127 
128     /**
129      * A cache of all proxy classes ever generated. At the time of writing,
130      * Android's runtime doesn't support class unloading so there's little
131      * value in using weak references.
132      */
133     private static final Map<Class<?>, Class<?>> generatedProxyClasses
134             = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());
135 
136     private final Class<T> baseClass;
137     private ClassLoader parentClassLoader = ProxyBuilder.class.getClassLoader();
138     private InvocationHandler handler;
139     private File dexCache;
140     private Class<?>[] constructorArgTypes = new Class[0];
141     private Object[] constructorArgValues = new Object[0];
142     private Set<Class<?>> interfaces = new HashSet<>();
143 
ProxyBuilder(Class<T> clazz)144     private ProxyBuilder(Class<T> clazz) {
145         baseClass = clazz;
146     }
147 
forClass(Class<T> clazz)148     public static <T> ProxyBuilder<T> forClass(Class<T> clazz) {
149         return new ProxyBuilder<T>(clazz);
150     }
151 
152     /**
153      * Specifies the parent ClassLoader to use when creating the proxy.
154      *
155      * <p>If null, {@code ProxyBuilder.class.getClassLoader()} will be used.
156      */
parentClassLoader(ClassLoader parent)157     public ProxyBuilder<T> parentClassLoader(ClassLoader parent) {
158         parentClassLoader = parent;
159         return this;
160     }
161 
handler(InvocationHandler handler)162     public ProxyBuilder<T> handler(InvocationHandler handler) {
163         this.handler = handler;
164         return this;
165     }
166 
167     /**
168      * Sets the directory where executable code is stored. See {@link
169      * DexMaker#generateAndLoad DexMaker.generateAndLoad()} for guidance on
170      * choosing a secure location for the dex cache.
171      */
dexCache(File dexCacheParent)172     public ProxyBuilder<T> dexCache(File dexCacheParent) {
173         dexCache = new File(dexCacheParent, "v" + Integer.toString(VERSION));
174         dexCache.mkdir();
175         return this;
176     }
177 
implementing(Class<?>.... interfaces)178     public ProxyBuilder<T> implementing(Class<?>... interfaces) {
179         for (Class<?> i : interfaces) {
180             if (!i.isInterface()) {
181                 throw new IllegalArgumentException("Not an interface: " + i.getName());
182             }
183             this.interfaces.add(i);
184         }
185         return this;
186     }
187 
constructorArgValues(Object... constructorArgValues)188     public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) {
189         this.constructorArgValues = constructorArgValues;
190         return this;
191     }
192 
constructorArgTypes(Class<?>.... constructorArgTypes)193     public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) {
194         this.constructorArgTypes = constructorArgTypes;
195         return this;
196     }
197 
198     /**
199      * Create a new instance of the class to proxy.
200      *
201      * @throws UnsupportedOperationException if the class we are trying to create a proxy for is
202      *     not accessible.
203      * @throws IOException if an exception occurred writing to the {@code dexCache} directory.
204      * @throws UndeclaredThrowableException if the constructor for the base class to proxy throws
205      *     a declared exception during construction.
206      * @throws IllegalArgumentException if the handler is null, if the constructor argument types
207      *     do not match the constructor argument values, or if no such constructor exists.
208      */
build()209     public T build() throws IOException {
210         check(handler != null, "handler == null");
211         check(constructorArgTypes.length == constructorArgValues.length,
212                 "constructorArgValues.length != constructorArgTypes.length");
213         Class<? extends T> proxyClass = buildProxyClass();
214         Constructor<? extends T> constructor;
215         try {
216             constructor = proxyClass.getConstructor(constructorArgTypes);
217         } catch (NoSuchMethodException e) {
218             throw new IllegalArgumentException("No constructor for " + baseClass.getName()
219                     + " with parameter types " + Arrays.toString(constructorArgTypes));
220         }
221         T result;
222         try {
223             result = constructor.newInstance(constructorArgValues);
224         } catch (InstantiationException e) {
225             // Should not be thrown, generated class is not abstract.
226             throw new AssertionError(e);
227         } catch (IllegalAccessException e) {
228             // Should not be thrown, the generated constructor is accessible.
229             throw new AssertionError(e);
230         } catch (InvocationTargetException e) {
231             // Thrown when the base class constructor throws an exception.
232             throw launderCause(e);
233         }
234         setInvocationHandler(result, handler);
235         return result;
236     }
237 
238     // TODO: test coverage for this
239 
240     /**
241      * Generate a proxy class. Note that new instances of this class will not automatically have an
242      * an invocation handler, even if {@link #handler(InvocationHandler)} was called. The handler
243      * must be set on each instance after it is created, using
244      * {@link #setInvocationHandler(Object, InvocationHandler)}.
245      */
buildProxyClass()246     public Class<? extends T> buildProxyClass() throws IOException {
247         // try the cache to see if we've generated this one before
248         // we only populate the map with matching types
249         @SuppressWarnings("unchecked")
250         Class<? extends T> proxyClass = (Class) generatedProxyClasses.get(baseClass);
251         if (proxyClass != null) {
252             boolean shareClassLoader = Boolean.parseBoolean(System.getProperty(
253                         "dexmaker.share_classloader", "false"));
254             boolean validClassLoader;
255             if (shareClassLoader) {
256                 ClassLoader parent = parentClassLoader != null ? parentClassLoader
257                         : baseClass.getClassLoader();
258                 validClassLoader = proxyClass.getClassLoader() == parent;
259             } else {
260                 validClassLoader = proxyClass.getClassLoader().getParent() == parentClassLoader;
261             }
262             if (validClassLoader && interfaces.equals(asSet(proxyClass.getInterfaces()))) {
263                 return proxyClass; // cache hit!
264             }
265         }
266 
267         // the cache missed; generate the class
268         DexMaker dexMaker = new DexMaker();
269         String generatedName = getMethodNameForProxyOf(baseClass);
270         TypeId<? extends T> generatedType = TypeId.get("L" + generatedName + ";");
271         TypeId<T> superType = TypeId.get(baseClass);
272         generateConstructorsAndFields(dexMaker, generatedType, superType, baseClass);
273         Method[] methodsToProxy = getMethodsToProxyRecursive();
274         generateCodeForAllMethods(dexMaker, generatedType, methodsToProxy, superType);
275         dexMaker.declare(generatedType, generatedName + ".generated", PUBLIC, superType, getInterfacesAsTypeIds());
276         ClassLoader classLoader = dexMaker.generateAndLoad(baseClass.getClassLoader(),
277                 parentClassLoader, dexCache);
278         try {
279             proxyClass = loadClass(classLoader, generatedName);
280         } catch (IllegalAccessError e) {
281             // Thrown when the base class is not accessible.
282             throw new UnsupportedOperationException(
283                     "cannot proxy inaccessible class " + baseClass, e);
284         } catch (ClassNotFoundException e) {
285             // Should not be thrown, we're sure to have generated this class.
286             throw new AssertionError(e);
287         }
288         setMethodsStaticField(proxyClass, methodsToProxy);
289         generatedProxyClasses.put(baseClass, proxyClass);
290         return proxyClass;
291     }
292 
293     // The type cast is safe: the generated type will extend the base class type.
294     @SuppressWarnings("unchecked")
loadClass(ClassLoader classLoader, String generatedName)295     private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName)
296             throws ClassNotFoundException {
297         return (Class<? extends T>) classLoader.loadClass(generatedName);
298     }
299 
launderCause(InvocationTargetException e)300     private static RuntimeException launderCause(InvocationTargetException e) {
301         Throwable cause = e.getCause();
302         // Errors should be thrown as they are.
303         if (cause instanceof Error) {
304             throw (Error) cause;
305         }
306         // RuntimeException can be thrown as-is.
307         if (cause instanceof RuntimeException) {
308             throw (RuntimeException) cause;
309         }
310         // Declared exceptions will have to be wrapped.
311         throw new UndeclaredThrowableException(cause);
312     }
313 
setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy)314     private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy) {
315         try {
316             Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS);
317             methodArrayField.setAccessible(true);
318             methodArrayField.set(null, methodsToProxy);
319         } catch (NoSuchFieldException e) {
320             // Should not be thrown, generated proxy class has been generated with this field.
321             throw new AssertionError(e);
322         } catch (IllegalAccessException e) {
323             // Should not be thrown, we just set the field to accessible.
324             throw new AssertionError(e);
325         }
326     }
327 
328     /**
329      * Returns the proxy's {@link InvocationHandler}.
330      *
331      * @throws IllegalArgumentException if the object supplied is not a proxy created by this class.
332      */
getInvocationHandler(Object instance)333     public static InvocationHandler getInvocationHandler(Object instance) {
334         try {
335             Field field = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
336             field.setAccessible(true);
337             return (InvocationHandler) field.get(instance);
338         } catch (NoSuchFieldException e) {
339             throw new IllegalArgumentException("Not a valid proxy instance", e);
340         } catch (IllegalAccessException e) {
341             // Should not be thrown, we just set the field to accessible.
342             throw new AssertionError(e);
343         }
344     }
345 
346     /**
347      * Sets the proxy's {@link InvocationHandler}.
348      * <p>
349      * If you create a proxy with {@link #build()}, the proxy will already have a handler set,
350      * provided that you configured one with {@link #handler(InvocationHandler)}.
351      * <p>
352      * If you generate a proxy class with {@link #buildProxyClass()}, instances of the proxy class
353      * will not automatically have a handler set, and it is necessary to use this method with each
354      * instance.
355      *
356      * @throws IllegalArgumentException if the object supplied is not a proxy created by this class.
357      */
setInvocationHandler(Object instance, InvocationHandler handler)358     public static void setInvocationHandler(Object instance, InvocationHandler handler) {
359         try {
360             Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
361             handlerField.setAccessible(true);
362             handlerField.set(instance, handler);
363         } catch (NoSuchFieldException e) {
364             throw new IllegalArgumentException("Not a valid proxy instance", e);
365         } catch (IllegalAccessException e) {
366             // Should not be thrown, we just set the field to accessible.
367             throw new AssertionError(e);
368         }
369     }
370 
371     // TODO: test coverage for isProxyClass
372 
373     /**
374      * Returns true if {@code c} is a proxy class created by this builder.
375      */
isProxyClass(Class<?> c)376     public static boolean isProxyClass(Class<?> c) {
377         // TODO: use a marker interface instead?
378         try {
379             c.getDeclaredField(FIELD_NAME_HANDLER);
380             return true;
381         } catch (NoSuchFieldException e) {
382             return false;
383         }
384     }
385 
generateCodeForAllMethods(DexMaker dexMaker, TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType)386     private static <T, G extends T> void generateCodeForAllMethods(DexMaker dexMaker,
387             TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType) {
388         TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class);
389         TypeId<Method[]> methodArrayType = TypeId.get(Method[].class);
390         FieldId<G, InvocationHandler> handlerField =
391                 generatedType.getField(handlerType, FIELD_NAME_HANDLER);
392         FieldId<G, Method[]> allMethods =
393                 generatedType.getField(methodArrayType, FIELD_NAME_METHODS);
394         TypeId<Method> methodType = TypeId.get(Method.class);
395         TypeId<Object[]> objectArrayType = TypeId.get(Object[].class);
396         MethodId<InvocationHandler, Object> methodInvoke = handlerType.getMethod(TypeId.OBJECT,
397                 "invoke", TypeId.OBJECT, methodType, objectArrayType);
398         for (int m = 0; m < methodsToProxy.length; ++m) {
399             /*
400              * If the 5th method on the superclass Example that can be overridden were to look like
401              * this:
402              *
403              *     public int doSomething(Bar param0, int param1) {
404              *         ...
405              *     }
406              *
407              * Then the following code will generate a method on the proxy that looks something
408              * like this:
409              *
410              *     public int doSomething(Bar param0, int param1) {
411              *         int methodIndex = 4;
412              *         Method[] allMethods = Example_Proxy.$__methodArray;
413              *         Method thisMethod = allMethods[methodIndex];
414              *         int argsLength = 2;
415              *         Object[] args = new Object[argsLength];
416              *         InvocationHandler localHandler = this.$__handler;
417              *         // for-loop begins
418              *         int p = 0;
419              *         Bar parameter0 = param0;
420              *         args[p] = parameter0;
421              *         p = 1;
422              *         int parameter1 = param1;
423              *         Integer boxed1 = Integer.valueOf(parameter1);
424              *         args[p] = boxed1;
425              *         // for-loop ends
426              *         Object result = localHandler.invoke(this, thisMethod, args);
427              *         Integer castResult = (Integer) result;
428              *         int unboxedResult = castResult.intValue();
429              *         return unboxedResult;
430              *     }
431              *
432              * Or, in more idiomatic Java:
433              *
434              *     public int doSomething(Bar param0, int param1) {
435              *         if ($__handler == null) {
436              *             return super.doSomething(param0, param1);
437              *         }
438              *         return __handler.invoke(this, __methodArray[4],
439              *                 new Object[] { param0, Integer.valueOf(param1) });
440              *     }
441              */
442             Method method = methodsToProxy[m];
443             String name = method.getName();
444             Class<?>[] argClasses = method.getParameterTypes();
445             TypeId<?>[] argTypes = new TypeId<?>[argClasses.length];
446             for (int i = 0; i < argTypes.length; ++i) {
447                 argTypes[i] = TypeId.get(argClasses[i]);
448             }
449             Class<?> returnType = method.getReturnType();
450             TypeId<?> resultType = TypeId.get(returnType);
451             MethodId<T, ?> superMethod = superclassType.getMethod(resultType, name, argTypes);
452             MethodId<?, ?> methodId = generatedType.getMethod(resultType, name, argTypes);
453             Code code = dexMaker.declare(methodId, PUBLIC);
454             Local<G> localThis = code.getThis(generatedType);
455             Local<InvocationHandler> localHandler = code.newLocal(handlerType);
456             Local<Object> invokeResult = code.newLocal(TypeId.OBJECT);
457             Local<Integer> intValue = code.newLocal(TypeId.INT);
458             Local<Object[]> args = code.newLocal(objectArrayType);
459             Local<Integer> argsLength = code.newLocal(TypeId.INT);
460             Local<Object> temp = code.newLocal(TypeId.OBJECT);
461             Local<?> resultHolder = code.newLocal(resultType);
462             Local<Method[]> methodArray = code.newLocal(methodArrayType);
463             Local<Method> thisMethod = code.newLocal(methodType);
464             Local<Integer> methodIndex = code.newLocal(TypeId.INT);
465             Class<?> aBoxedClass = PRIMITIVE_TO_BOXED.get(returnType);
466             Local<?> aBoxedResult = null;
467             if (aBoxedClass != null) {
468                 aBoxedResult = code.newLocal(TypeId.get(aBoxedClass));
469             }
470             Local<?>[] superArgs2 = new Local<?>[argClasses.length];
471             Local<?> superResult2 = code.newLocal(resultType);
472             Local<InvocationHandler> nullHandler = code.newLocal(handlerType);
473 
474             code.loadConstant(methodIndex, m);
475             code.sget(allMethods, methodArray);
476             code.aget(thisMethod, methodArray, methodIndex);
477             code.loadConstant(argsLength, argTypes.length);
478             code.newArray(args, argsLength);
479             code.iget(handlerField, localHandler, localThis);
480 
481             // if (proxy == null)
482             code.loadConstant(nullHandler, null);
483             Label handlerNullCase = new Label();
484             code.compare(Comparison.EQ, handlerNullCase, nullHandler, localHandler);
485 
486             // This code is what we execute when we have a valid proxy: delegate to invocation
487             // handler.
488             for (int p = 0; p < argTypes.length; ++p) {
489                 code.loadConstant(intValue, p);
490                 Local<?> parameter = code.getParameter(p, argTypes[p]);
491                 Local<?> unboxedIfNecessary = boxIfRequired(code, parameter, temp);
492                 code.aput(args, intValue, unboxedIfNecessary);
493             }
494             code.invokeInterface(methodInvoke, invokeResult, localHandler,
495                     localThis, thisMethod, args);
496             generateCodeForReturnStatement(code, returnType, invokeResult, resultHolder,
497                     aBoxedResult);
498 
499             // This code is executed if proxy is null: call the original super method.
500             // This is required to handle the case of construction of an object which leaks the
501             // "this" pointer.
502             code.mark(handlerNullCase);
503             for (int i = 0; i < superArgs2.length; ++i) {
504                 superArgs2[i] = code.getParameter(i, argTypes[i]);
505             }
506             if (void.class.equals(returnType)) {
507                 code.invokeSuper(superMethod, null, localThis, superArgs2);
508                 code.returnVoid();
509             } else {
510                 invokeSuper(superMethod, code, localThis, superArgs2, superResult2);
511                 code.returnValue(superResult2);
512             }
513 
514             /*
515              * And to allow calling the original super method, the following is also generated:
516              *
517              *     public String super$doSomething$java_lang_String(Bar param0, int param1) {
518              *          int result = super.doSomething(param0, param1);
519              *          return result;
520              *     }
521              */
522             // TODO: don't include a super_ method if the target is abstract!
523             MethodId<G, ?> callsSuperMethod = generatedType.getMethod(
524                     resultType, superMethodName(method), argTypes);
525             Code superCode = dexMaker.declare(callsSuperMethod, PUBLIC);
526             Local<G> superThis = superCode.getThis(generatedType);
527             Local<?>[] superArgs = new Local<?>[argClasses.length];
528             for (int i = 0; i < superArgs.length; ++i) {
529                 superArgs[i] = superCode.getParameter(i, argTypes[i]);
530             }
531             if (void.class.equals(returnType)) {
532                 superCode.invokeSuper(superMethod, null, superThis, superArgs);
533                 superCode.returnVoid();
534             } else {
535                 Local<?> superResult = superCode.newLocal(resultType);
536                 invokeSuper(superMethod, superCode, superThis, superArgs, superResult);
537                 superCode.returnValue(superResult);
538             }
539         }
540     }
541 
542     @SuppressWarnings({"unchecked", "rawtypes"})
invokeSuper(MethodId superMethod, Code superCode, Local superThis, Local[] superArgs, Local superResult)543     private static void invokeSuper(MethodId superMethod, Code superCode,
544             Local superThis, Local[] superArgs, Local superResult) {
545         superCode.invokeSuper(superMethod, superResult, superThis, superArgs);
546     }
547 
boxIfRequired(Code code, Local<?> parameter, Local<Object> temp)548     private static Local<?> boxIfRequired(Code code, Local<?> parameter, Local<Object> temp) {
549         MethodId<?, ?> unboxMethod = PRIMITIVE_TYPE_TO_UNBOX_METHOD.get(parameter.getType());
550         if (unboxMethod == null) {
551             return parameter;
552         }
553         code.invokeStatic(unboxMethod, temp, parameter);
554         return temp;
555     }
556 
callSuper(Object proxy, Method method, Object... args)557     public static Object callSuper(Object proxy, Method method, Object... args) throws Throwable {
558         try {
559             return proxy.getClass()
560                     .getMethod(superMethodName(method), method.getParameterTypes())
561                     .invoke(proxy, args);
562         } catch (InvocationTargetException e) {
563             throw e.getCause();
564         }
565     }
566 
567     /**
568      * The super method must include the return type, otherwise its ambiguous
569      * for methods with covariant return types.
570      */
superMethodName(Method method)571     private static String superMethodName(Method method) {
572         String returnType = method.getReturnType().getName();
573         return "super$" + method.getName() + "$"
574                 + returnType.replace('.', '_').replace('[', '_').replace(';', '_');
575     }
576 
check(boolean condition, String message)577     private static void check(boolean condition, String message) {
578         if (!condition) {
579             throw new IllegalArgumentException(message);
580         }
581     }
582 
generateConstructorsAndFields(DexMaker dexMaker, TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass)583     private static <T, G extends T> void generateConstructorsAndFields(DexMaker dexMaker,
584             TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass) {
585         TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class);
586         TypeId<Method[]> methodArrayType = TypeId.get(Method[].class);
587         FieldId<G, InvocationHandler> handlerField = generatedType.getField(
588                 handlerType, FIELD_NAME_HANDLER);
589         dexMaker.declare(handlerField, PRIVATE, null);
590         FieldId<G, Method[]> allMethods = generatedType.getField(
591                 methodArrayType, FIELD_NAME_METHODS);
592         dexMaker.declare(allMethods, PRIVATE | STATIC, null);
593         for (Constructor<T> constructor : getConstructorsToOverwrite(superClass)) {
594             if (constructor.getModifiers() == Modifier.FINAL) {
595                 continue;
596             }
597             TypeId<?>[] types = classArrayToTypeArray(constructor.getParameterTypes());
598             MethodId<?, ?> method = generatedType.getConstructor(types);
599             Code constructorCode = dexMaker.declare(method, PUBLIC);
600             Local<G> thisRef = constructorCode.getThis(generatedType);
601             Local<?>[] params = new Local[types.length];
602             for (int i = 0; i < params.length; ++i) {
603                 params[i] = constructorCode.getParameter(i, types[i]);
604             }
605             MethodId<T, ?> superConstructor = superType.getConstructor(types);
606             constructorCode.invokeDirect(superConstructor, null, thisRef, params);
607             constructorCode.returnVoid();
608         }
609     }
610 
611     // The type parameter on Constructor is the class in which the constructor is declared.
612     // The getDeclaredConstructors() method gets constructors declared only in the given class,
613     // hence this cast is safe.
614     @SuppressWarnings("unchecked")
getConstructorsToOverwrite(Class<T> clazz)615     private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) {
616         return (Constructor<T>[]) clazz.getDeclaredConstructors();
617     }
618 
getInterfacesAsTypeIds()619     private TypeId<?>[] getInterfacesAsTypeIds() {
620         TypeId<?>[] result = new TypeId<?>[interfaces.size()];
621         int i = 0;
622         for (Class<?> implemented : interfaces) {
623             result[i++] = TypeId.get(implemented);
624         }
625         return result;
626     }
627 
628     /**
629      * Gets all {@link Method} objects we can proxy in the hierarchy of the
630      * supplied class.
631      */
getMethodsToProxyRecursive()632     private Method[] getMethodsToProxyRecursive() {
633         Set<MethodSetEntry> methodsToProxy = new HashSet<>();
634         Set<MethodSetEntry> seenFinalMethods = new HashSet<>();
635         // Traverse the class hierarchy to ensure that all concrete methods (which could be marked
636         // as final) are visited before any abstract methods from interfaces.
637         for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
638             getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
639         }
640         // Now traverse the interface hierarchy, starting with the ones implemented by the class,
641         // followed by any extra interfaces.
642         for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
643             for (Class<?> i : c.getInterfaces()) {
644                 getMethodsToProxy(methodsToProxy, seenFinalMethods, i);
645             }
646         }
647         for (Class<?> c : interfaces) {
648             getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
649         }
650 
651         Method[] results = new Method[methodsToProxy.size()];
652         int i = 0;
653         for (MethodSetEntry entry : methodsToProxy) {
654             results[i++] = entry.originalMethod;
655         }
656 
657         // Sort the results array so that they are returned by this method
658         // in a deterministic fashion.
659         Arrays.sort(results, new Comparator<Method>() {
660             @Override
661             public int compare(Method method1, Method method2) {
662                 return method1.toString().compareTo(method2.toString());
663             }
664         });
665 
666         return results;
667     }
668 
getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods, Class<?> c)669     private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods,
670             Class<?> c) {
671         boolean shareClassLoader = Boolean.parseBoolean(System.getProperty(
672                "dexmaker.share_classloader", "false"));
673         for (Method method : c.getDeclaredMethods()) {
674             if ((method.getModifiers() & Modifier.FINAL) != 0) {
675                 // Skip final methods, we can't override them. We
676                 // also need to remember them, in case the same
677                 // method exists in a parent class.
678                 MethodSetEntry entry = new MethodSetEntry(method);
679                 seenFinalMethods.add(entry);
680                 // We may have seen this method already, from an interface
681                 // implemented by a child class. We need to remove it here.
682                 sink.remove(entry);
683                 continue;
684             }
685             if ((method.getModifiers() & STATIC) != 0) {
686                 // Skip static methods, overriding them has no effect.
687                 continue;
688             }
689             if (!Modifier.isPublic(method.getModifiers())
690 					&& !Modifier.isProtected(method.getModifiers())
691                     && (!shareClassLoader || Modifier.isPrivate(method.getModifiers()))) {
692                 // Skip private methods, since they are invoked through direct
693                 // invocation (as opposed to virtual). Therefore, it would not
694                 // be possible to intercept any private method defined inside
695                 // the proxy class except through reflection.
696 
697                 // Skip package-private methods as well (for non-shared class
698                 // loaders). The proxy class does
699                 // not actually inherit package-private methods from the parent
700                 // class because it is not a member of the parent's package.
701                 // This is even true if the two classes have the same package
702                 // name, as they use different class loaders.
703                 continue;
704             }
705             if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) {
706                 // Skip finalize method, it's likely important that it execute as normal.
707                 continue;
708             }
709             MethodSetEntry entry = new MethodSetEntry(method);
710             if (seenFinalMethods.contains(entry)) {
711                 // This method is final in a child class.
712                 // We can't override it.
713                 continue;
714             }
715             sink.add(entry);
716         }
717 
718         // Only visit the interfaces of this class if it is itself an interface. That prevents
719         // visiting interfaces of a class before its super classes.
720         if (c.isInterface()) {
721             for (Class<?> i : c.getInterfaces()) {
722                 getMethodsToProxy(sink, seenFinalMethods, i);
723             }
724         }
725     }
726 
getMethodNameForProxyOf(Class<T> clazz)727     private static <T> String getMethodNameForProxyOf(Class<T> clazz) {
728         return clazz.getName().replace(".", "/") + "_Proxy";
729     }
730 
classArrayToTypeArray(Class<?>[] input)731     private static TypeId<?>[] classArrayToTypeArray(Class<?>[] input) {
732         TypeId<?>[] result = new TypeId[input.length];
733         for (int i = 0; i < input.length; ++i) {
734             result[i] = TypeId.get(input[i]);
735         }
736         return result;
737     }
738 
739     /**
740      * Calculates the correct return statement code for a method.
741      * <p>
742      * A void method will not return anything.  A method that returns a primitive will need to
743      * unbox the boxed result.  Otherwise we will cast the result.
744      */
745     // This one is tricky to fix, I gave up.
746     @SuppressWarnings({ "rawtypes", "unchecked" })
generateCodeForReturnStatement(Code code, Class methodReturnType, Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult)747     private static void generateCodeForReturnStatement(Code code, Class methodReturnType,
748             Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult) {
749         if (PRIMITIVE_TO_UNBOX_METHOD.containsKey(methodReturnType)) {
750             code.cast(aBoxedResult, localForResultOfInvoke);
751             MethodId unboxingMethodFor = getUnboxMethodForPrimitive(methodReturnType);
752             code.invokeVirtual(unboxingMethodFor, localOfMethodReturnType, aBoxedResult);
753             code.returnValue(localOfMethodReturnType);
754         } else if (void.class.equals(methodReturnType)) {
755             code.returnVoid();
756         } else {
757             code.cast(localOfMethodReturnType, localForResultOfInvoke);
758             code.returnValue(localOfMethodReturnType);
759         }
760     }
761 
asSet(T... array)762     private static <T> Set<T> asSet(T... array) {
763         return new CopyOnWriteArraySet<>(Arrays.asList(array));
764     }
765 
getUnboxMethodForPrimitive(Class<?> methodReturnType)766     private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType) {
767         return PRIMITIVE_TO_UNBOX_METHOD.get(methodReturnType);
768     }
769 
770     private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED;
771     static {
772         PRIMITIVE_TO_BOXED = new HashMap<>();
PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class)773         PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class);
PRIMITIVE_TO_BOXED.put(int.class, Integer.class)774         PRIMITIVE_TO_BOXED.put(int.class, Integer.class);
PRIMITIVE_TO_BOXED.put(byte.class, Byte.class)775         PRIMITIVE_TO_BOXED.put(byte.class, Byte.class);
PRIMITIVE_TO_BOXED.put(long.class, Long.class)776         PRIMITIVE_TO_BOXED.put(long.class, Long.class);
PRIMITIVE_TO_BOXED.put(short.class, Short.class)777         PRIMITIVE_TO_BOXED.put(short.class, Short.class);
PRIMITIVE_TO_BOXED.put(float.class, Float.class)778         PRIMITIVE_TO_BOXED.put(float.class, Float.class);
PRIMITIVE_TO_BOXED.put(double.class, Double.class)779         PRIMITIVE_TO_BOXED.put(double.class, Double.class);
PRIMITIVE_TO_BOXED.put(char.class, Character.class)780         PRIMITIVE_TO_BOXED.put(char.class, Character.class);
781     }
782 
783     private static final Map<TypeId<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD;
784     static {
785         PRIMITIVE_TYPE_TO_UNBOX_METHOD = new HashMap<>();
786         for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_TO_BOXED.entrySet()) {
787             TypeId<?> primitiveType = TypeId.get(entry.getKey());
788             TypeId<?> boxedType = TypeId.get(entry.getValue());
789             MethodId<?, ?> valueOfMethod = boxedType.getMethod(boxedType, "valueOf", primitiveType);
PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod)790             PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod);
791         }
792     }
793 
794     /**
795      * Map from primitive type to method used to unbox a boxed version of the primitive.
796      * <p>
797      * This is required for methods whose return type is primitive, since the
798      * {@link InvocationHandler} will return us a boxed result, and we'll need to convert it back to
799      * primitive value.
800      */
801     private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD;
802     static {
803         Map<Class<?>, MethodId<?, ?>> map = new HashMap<>();
map.put(boolean.class, TypeId.get(Boolean.class).getMethod(TypeId.BOOLEAN, "booleanValue"))804         map.put(boolean.class, TypeId.get(Boolean.class).getMethod(TypeId.BOOLEAN, "booleanValue"));
map.put(int.class, TypeId.get(Integer.class).getMethod(TypeId.INT, "intValue"))805         map.put(int.class, TypeId.get(Integer.class).getMethod(TypeId.INT, "intValue"));
map.put(byte.class, TypeId.get(Byte.class).getMethod(TypeId.BYTE, "byteValue"))806         map.put(byte.class, TypeId.get(Byte.class).getMethod(TypeId.BYTE, "byteValue"));
map.put(long.class, TypeId.get(Long.class).getMethod(TypeId.LONG, "longValue"))807         map.put(long.class, TypeId.get(Long.class).getMethod(TypeId.LONG, "longValue"));
map.put(short.class, TypeId.get(Short.class).getMethod(TypeId.SHORT, "shortValue"))808         map.put(short.class, TypeId.get(Short.class).getMethod(TypeId.SHORT, "shortValue"));
map.put(float.class, TypeId.get(Float.class).getMethod(TypeId.FLOAT, "floatValue"))809         map.put(float.class, TypeId.get(Float.class).getMethod(TypeId.FLOAT, "floatValue"));
map.put(double.class, TypeId.get(Double.class).getMethod(TypeId.DOUBLE, "doubleValue"))810         map.put(double.class, TypeId.get(Double.class).getMethod(TypeId.DOUBLE, "doubleValue"));
map.put(char.class, TypeId.get(Character.class).getMethod(TypeId.CHAR, "charValue"))811         map.put(char.class, TypeId.get(Character.class).getMethod(TypeId.CHAR, "charValue"));
812         PRIMITIVE_TO_UNBOX_METHOD = map;
813     }
814 
815     /**
816      * Wrapper class to let us disambiguate {@link Method} objects.
817      * <p>
818      * The purpose of this class is to override the {@link #equals(Object)} and {@link #hashCode()}
819      * methods so we can use a {@link Set} to remove duplicate methods that are overrides of one
820      * another. For these purposes, we consider two methods to be equal if they have the same
821      * name, return type, and parameter types.
822      */
823     private static class MethodSetEntry {
824         private final String name;
825         private final Class<?>[] paramTypes;
826         private final Class<?> returnType;
827         private final Method originalMethod;
828 
MethodSetEntry(Method method)829         public MethodSetEntry(Method method) {
830             originalMethod = method;
831             name = method.getName();
832             paramTypes = method.getParameterTypes();
833             returnType = method.getReturnType();
834         }
835 
836         @Override
equals(Object o)837         public boolean equals(Object o) {
838             if (o instanceof MethodSetEntry) {
839                 MethodSetEntry other = (MethodSetEntry) o;
840                 return name.equals(other.name)
841                         && returnType.equals(other.returnType)
842                         && Arrays.equals(paramTypes, other.paramTypes);
843             }
844             return false;
845         }
846 
847         @Override
hashCode()848         public int hashCode() {
849             int result = 17;
850             result += 31 * result + name.hashCode();
851             result += 31 * result + returnType.hashCode();
852             result += 31 * result + Arrays.hashCode(paramTypes);
853             return result;
854         }
855     }
856 }
857