Home | History | Annotate | Download | only in stock
      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 
    149     private ProxyBuilder(Class<T> clazz) {
    150         baseClass = clazz;
    151     }
    152 
    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      */
    162     public ProxyBuilder<T> parentClassLoader(ClassLoader parent) {
    163         parentClassLoader = parent;
    164         return this;
    165     }
    166 
    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      */
    177     public ProxyBuilder<T> dexCache(File dexCacheParent) {
    178         dexCache = new File(dexCacheParent, "v" + Integer.toString(VERSION));
    179         dexCache.mkdir();
    180         return this;
    181     }
    182 
    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 
    196     public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) {
    197         this.constructorArgValues = constructorArgValues;
    198         return this;
    199     }
    200 
    201     public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) {
    202         this.constructorArgTypes = constructorArgTypes;
    203         return this;
    204     }
    205 
    206     public ProxyBuilder<T> onlyMethods(Method[] methods) {
    207         this.methods = methods;
    208         return this;
    209     }
    210 
    211     public ProxyBuilder<T> withSharedClassLoader() {
    212         this.sharedClassLoader = true;
    213         return this;
    214     }
    215 
    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      */
    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      */
    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")
    356     private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName)
    357             throws ClassNotFoundException {
    358         return (Class<? extends T>) classLoader.loadClass(generatedName);
    359     }
    360 
    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 
    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      */
    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      */
    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      */
    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      */
    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 
    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"})
    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 
    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 
    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      */
    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 
    680     private static void check(boolean condition, String message) {
    681         if (!condition) {
    682             throw new IllegalArgumentException(message);
    683         }
    684     }
    685 
    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")
    718     private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) {
    719         return (Constructor<T>[]) clazz.getDeclaredConstructors();
    720     }
    721 
    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      */
    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 
    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 
    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 
    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" })
    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 
    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<>();
    862         PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class);
    863         PRIMITIVE_TO_BOXED.put(int.class, Integer.class);
    864         PRIMITIVE_TO_BOXED.put(byte.class, Byte.class);
    865         PRIMITIVE_TO_BOXED.put(long.class, Long.class);
    866         PRIMITIVE_TO_BOXED.put(short.class, Short.class);
    867         PRIMITIVE_TO_BOXED.put(float.class, Float.class);
    868         PRIMITIVE_TO_BOXED.put(double.class, Double.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);
    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<>();
    893         map.put(boolean.class, TypeId.get(Boolean.class).getMethod(TypeId.BOOLEAN, "booleanValue"));
    894         map.put(int.class, TypeId.get(Integer.class).getMethod(TypeId.INT, "intValue"));
    895         map.put(byte.class, TypeId.get(Byte.class).getMethod(TypeId.BYTE, "byteValue"));
    896         map.put(long.class, TypeId.get(Long.class).getMethod(TypeId.LONG, "longValue"));
    897         map.put(short.class, TypeId.get(Short.class).getMethod(TypeId.SHORT, "shortValue"));
    898         map.put(float.class, TypeId.get(Float.class).getMethod(TypeId.FLOAT, "floatValue"));
    899         map.put(double.class, TypeId.get(Double.class).getMethod(TypeId.DOUBLE, "doubleValue"));
    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 
    918         public MethodSetEntry(Method method) {
    919             originalMethod = method;
    920             name = method.getName();
    921             paramTypes = method.getParameterTypes();
    922             returnType = method.getReturnType();
    923         }
    924 
    925         @Override
    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
    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
    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
    981         public int hashCode() {
    982             return clazz.hashCode() + interfaces.hashCode() + requestedClassloader.hashCode()
    983                     + (sharedClassLoader ? 1 : 0);
    984         }
    985 
    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