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