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