1579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/*
2579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Copyright (C) 2011 The Android Open Source Project
3579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
4579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * you may not use this file except in compliance with the License.
6579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may obtain a copy of the License at
7579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
8579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
9579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
10579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Unless required by applicable law or agreed to in writing, software
11579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * See the License for the specific language governing permissions and
14579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * limitations under the License.
15579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
16579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
171977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilsonpackage com.google.dexmaker.stock;
18579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
191977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilsonimport com.google.dexmaker.Code;
201977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilsonimport com.google.dexmaker.Comparison;
21ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilsonimport com.google.dexmaker.DexMaker;
221977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilsonimport com.google.dexmaker.FieldId;
231977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilsonimport com.google.dexmaker.Label;
241977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilsonimport com.google.dexmaker.Local;
251977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilsonimport com.google.dexmaker.MethodId;
260e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilsonimport com.google.dexmaker.TypeId;
27579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.File;
28579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.IOException;
29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Constructor;
30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Field;
31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.InvocationHandler;
32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.InvocationTargetException;
33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Method;
34579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Modifier;
3590699b97998f1582a921202fb909f17f9718d177Jesse Wilsonimport static java.lang.reflect.Modifier.PRIVATE;
3690699b97998f1582a921202fb909f17f9718d177Jesse Wilsonimport static java.lang.reflect.Modifier.PUBLIC;
3790699b97998f1582a921202fb909f17f9718d177Jesse Wilsonimport static java.lang.reflect.Modifier.STATIC;
38579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.UndeclaredThrowableException;
39579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Arrays;
402e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilsonimport java.util.Collections;
41fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousefimport java.util.Comparator;
42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.HashMap;
43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.HashSet;
44579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Map;
45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Set;
461af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilsonimport java.util.concurrent.CopyOnWriteArraySet;
47579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
48579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/**
49579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Creates dynamic proxies of concrete classes.
50579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
51579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This is similar to the {@code java.lang.reflect.Proxy} class, but works for classes instead of
52579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * interfaces.
53579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <h3>Example</h3>
54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * The following example demonstrates the creation of a dynamic proxy for {@code java.util.Random}
55579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * which will always return 4 when asked for integers, and which logs method calls to every method.
56579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <pre>
57579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * InvocationHandler handler = new InvocationHandler() {
58579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     &#64;Override
59579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
60579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         if (method.getName().equals("nextInt")) {
61579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *             // Chosen by fair dice roll, guaranteed to be random.
62579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *             return 4;
63579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         }
64579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         Object result = ProxyBuilder.callSuper(proxy, method, args);
65579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         System.out.println("Method: " + method.getName() + " args: "
66579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *                 + Arrays.toString(args) + " result: " + result);
67579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         return result;
68579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     }
69579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * };
70579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Random debugRandom = ProxyBuilder.forClass(Random.class)
71579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE))
72579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         .handler(handler)
73579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         .build();
74579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * assertEquals(4, debugRandom.nextInt());
75579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * debugRandom.setSeed(0);
76579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * assertTrue(debugRandom.nextBoolean());
77579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * </pre>
78579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <h3>Usage</h3>
79579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Call {@link #forClass(Class)} for the Class you wish to proxy. Call
80579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@link #handler(InvocationHandler)} passing in an {@link InvocationHandler}, and then call
81579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@link #build()}. The returned instance will be a dynamically generated subclass where all method
82579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * calls will be delegated to the invocation handler, except as noted below.
83579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
84579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * The static method {@link #callSuper(Object, Method, Object...)} allows you to access the original
85579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * super method for a given proxy. This allows the invocation handler to selectively override some
86579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * methods but not others.
87579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
88579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * By default, the {@link #build()} method will call the no-arg constructor belonging to the class
89579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * being proxied. If you wish to call a different constructor, you must provide arguments for both
90579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@link #constructorArgTypes(Class[])} and {@link #constructorArgValues(Object[])}.
91579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
92579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This process works only for classes with public and protected level of visibility.
93579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
94579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may proxy abstract classes.  You may not proxy final classes.
95579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
96579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Only non-private, non-final, non-static methods will be dispatched to the invocation handler.
97579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Private, static or final methods will always call through to the superclass as normal.
98579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
99579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * The {@link #finalize()} method on {@code Object} will not be proxied.
100579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
101579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You must provide a dex cache directory via the {@link #dexCache(File)} method. You should take
102579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * care not to make this a world-writable directory, so that third parties cannot inject code into
103579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * your application.  A suitable parameter for these output directories would be something like
104579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * this:
105579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <pre>{@code
106579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     getApplicationContext().getDir("dx", Context.MODE_PRIVATE);
107579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * }</pre>
108579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
109579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * If the base class to be proxied leaks the {@code this} pointer in the constructor (bad practice),
110579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * that is to say calls a non-private non-final method from the constructor, the invocation handler
111579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * will not be invoked.  As a simple concrete example, when proxying Random we discover that it
112579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * inernally calls setSeed during the constructor.  The proxy will not intercept this call during
113579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * proxy construction, but will intercept as normal afterwards.  This behaviour may be subject to
114579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * change in future releases.
115579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
116579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This class is <b>not thread safe</b>.
117579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
118579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpublic final class ProxyBuilder<T> {
119054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    // Version of ProxyBuilder. It should be updated if the implementation
120054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    // of the generated proxy class changes.
121054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    public static final int VERSION = 1;
122054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef
123579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final String FIELD_NAME_HANDLER = "$__handler";
124579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final String FIELD_NAME_METHODS = "$__methodArray";
125579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
1262e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson    /**
1272e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     * A cache of all proxy classes ever generated. At the time of writing,
1282e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     * Android's runtime doesn't support class unloading so there's little
1292e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     * value in using weak references.
1302e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     */
1312e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson    private static final Map<Class<?>, Class<?>> generatedProxyClasses
1322e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());
1332e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson
134579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private final Class<T> baseClass;
135579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private ClassLoader parentClassLoader = ProxyBuilder.class.getClassLoader();
136579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private InvocationHandler handler;
137579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private File dexCache;
138579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private Class<?>[] constructorArgTypes = new Class[0];
139579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private Object[] constructorArgValues = new Object[0];
1401af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private Set<Class<?>> interfaces = new HashSet<Class<?>>();
141579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
142579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private ProxyBuilder(Class<T> clazz) {
143579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        baseClass = clazz;
144579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
145579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
146579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public static <T> ProxyBuilder<T> forClass(Class<T> clazz) {
147579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return new ProxyBuilder<T>(clazz);
148579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
149579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
150579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
151579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Specifies the parent ClassLoader to use when creating the proxy.
152579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
153579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>If null, {@code ProxyBuilder.class.getClassLoader()} will be used.
154579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
155579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> parentClassLoader(ClassLoader parent) {
156579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        parentClassLoader = parent;
157579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
158579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
159579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
160579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> handler(InvocationHandler handler) {
161579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.handler = handler;
162579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
163579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
164579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
16573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    /**
16673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * Sets the directory where executable code is stored. See {@link
16773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * DexMaker#generateAndLoad DexMaker.generateAndLoad()} for guidance on
16873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * choosing a secure location for the dex cache.
16973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     */
170054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    public ProxyBuilder<T> dexCache(File dexCacheParent) {
171054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef        dexCache = new File(dexCacheParent, "v" + Integer.toString(VERSION));
172054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef        dexCache.mkdir();
173579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
174579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
1751af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
1761af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    public ProxyBuilder<T> implementing(Class<?>... interfaces) {
1771af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> i : interfaces) {
1781af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if (!i.isInterface()) {
1791af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                throw new IllegalArgumentException("Not an interface: " + i.getName());
1801af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
1811af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            this.interfaces.add(i);
1821af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
1831af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        return this;
1841af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
185579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
186579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) {
187579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.constructorArgValues = constructorArgValues;
188579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
189579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
190579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
191579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) {
192579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.constructorArgTypes = constructorArgTypes;
193579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
194579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
195579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
196579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
197579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Create a new instance of the class to proxy.
198579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
199579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws UnsupportedOperationException if the class we are trying to create a proxy for is
200579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *     not accessible.
2011977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson     * @throws IOException if an exception occurred writing to the {@code dexCache} directory.
202579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws UndeclaredThrowableException if the constructor for the base class to proxy throws
203579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *     a declared exception during construction.
204579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws IllegalArgumentException if the handler is null, if the constructor argument types
205579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *     do not match the constructor argument values, or if no such constructor exists.
206579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
2071977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    public T build() throws IOException {
208579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        check(handler != null, "handler == null");
209579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        check(constructorArgTypes.length == constructorArgValues.length,
210579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                "constructorArgValues.length != constructorArgTypes.length");
2111af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        Class<? extends T> proxyClass = buildProxyClass();
212579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Constructor<? extends T> constructor;
213579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
214579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            constructor = proxyClass.getConstructor(constructorArgTypes);
215579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NoSuchMethodException e) {
2161af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            throw new IllegalArgumentException("No constructor for " + baseClass.getName()
217679fb66c12a24691a6d7720d79c64c28f5b0532bJesse Wilson                    + " with parameter types " + Arrays.toString(constructorArgTypes));
218579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
219579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        T result;
220579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
221579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result = constructor.newInstance(constructorArgValues);
222579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (InstantiationException e) {
223579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, generated class is not abstract.
224579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
225579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IllegalAccessException e) {
226579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, the generated constructor is accessible.
227579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
228579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (InvocationTargetException e) {
2292e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            // Thrown when the base class constructor throws an exception.
230579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw launderCause(e);
231579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
232579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        setHandlerInstanceField(result, handler);
233579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
234579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
235579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
2361af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    // TODO: test coverage for this
2371af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    // TODO: documentation for this
2381af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    public Class<? extends T> buildProxyClass() throws IOException {
2392e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        // try the cache to see if we've generated this one before
2402e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        @SuppressWarnings("unchecked") // we only populate the map with matching types
2412e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        Class<? extends T> proxyClass = (Class) generatedProxyClasses.get(baseClass);
2421af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        if (proxyClass != null
2431af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                && proxyClass.getClassLoader().getParent() == parentClassLoader
2441af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                && interfaces.equals(asSet(proxyClass.getInterfaces()))) {
2452e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            return proxyClass; // cache hit!
2462e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        }
2472e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson
2482e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        // the cache missed; generate the class
2492e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        DexMaker dexMaker = new DexMaker();
2502e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        String generatedName = getMethodNameForProxyOf(baseClass);
2512e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        TypeId<? extends T> generatedType = TypeId.get("L" + generatedName + ";");
2522e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        TypeId<T> superType = TypeId.get(baseClass);
2532e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        generateConstructorsAndFields(dexMaker, generatedType, superType, baseClass);
2541af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        Method[] methodsToProxy = getMethodsToProxyRecursive();
2552e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        generateCodeForAllMethods(dexMaker, generatedType, methodsToProxy, superType);
2561af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        dexMaker.declare(generatedType, generatedName + ".generated", PUBLIC, superType,
2571af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                getInterfacesAsTypeIds());
2582e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        ClassLoader classLoader = dexMaker.generateAndLoad(parentClassLoader, dexCache);
2592e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        try {
2602e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            proxyClass = loadClass(classLoader, generatedName);
2612e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        } catch (IllegalAccessError e) {
2622e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            // Thrown when the base class is not accessible.
2631af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            throw new UnsupportedOperationException(
2641af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    "cannot proxy inaccessible class " + baseClass, e);
2652e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        } catch (ClassNotFoundException e) {
2662e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            // Should not be thrown, we're sure to have generated this class.
2672e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            throw new AssertionError(e);
2682e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        }
2692e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        setMethodsStaticField(proxyClass, methodsToProxy);
2702e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        generatedProxyClasses.put(baseClass, proxyClass);
2712e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        return proxyClass;
2722e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson    }
2732e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson
274579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // The type cast is safe: the generated type will extend the base class type.
275579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @SuppressWarnings("unchecked")
276579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName)
277579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throws ClassNotFoundException {
278579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return (Class<? extends T>) classLoader.loadClass(generatedName);
279579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
280579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
281579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static RuntimeException launderCause(InvocationTargetException e) {
282579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Throwable cause = e.getCause();
283579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // Errors should be thrown as they are.
284579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (cause instanceof Error) {
285579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw (Error) cause;
286579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
287579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // RuntimeException can be thrown as-is.
288579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (cause instanceof RuntimeException) {
289579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw (RuntimeException) cause;
290579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
291579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // Declared exceptions will have to be wrapped.
292579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        throw new UndeclaredThrowableException(cause);
293579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
294579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
295579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static void setHandlerInstanceField(Object instance, InvocationHandler handler) {
296579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
297579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
298579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            handlerField.setAccessible(true);
299579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            handlerField.set(instance, handler);
300579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NoSuchFieldException e) {
301579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, generated proxy class has been generated with this field.
302579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
303579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IllegalAccessException e) {
304579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, we just set the field to accessible.
305579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
306579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
307579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
308579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
309579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy) {
310579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
311579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS);
312579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            methodArrayField.setAccessible(true);
313579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            methodArrayField.set(null, methodsToProxy);
314579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NoSuchFieldException e) {
315579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, generated proxy class has been generated with this field.
316579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
317579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IllegalAccessException e) {
318579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, we just set the field to accessible.
319579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
320579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
321579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
322579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
323579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
324579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Returns the proxy's {@link InvocationHandler}.
325579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
326579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws IllegalArgumentException if the object supplied is not a proxy created by this class.
327579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
328579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public static InvocationHandler getInvocationHandler(Object instance) {
329579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
330579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Field field = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
331579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            field.setAccessible(true);
332579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return (InvocationHandler) field.get(instance);
333579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NoSuchFieldException e) {
334579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new IllegalArgumentException("Not a valid proxy instance", e);
335579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IllegalAccessException e) {
336579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, we just set the field to accessible.
337579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
33873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        }
33973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    }
34073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
3411af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    // TODO: test coverage for isProxyClass
3421af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
34373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    /**
34473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * Returns true if {@code c} is a proxy class created by this builder.
34573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     */
34673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    public static boolean isProxyClass(Class<?> c) {
34773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        // TODO: use a marker interface instead?
34873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        try {
34973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            c.getDeclaredField(FIELD_NAME_HANDLER);
35073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            return true;
35173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        } catch (NoSuchFieldException e) {
35273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            return false;
353579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
354579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
355579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
356ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson    private static <T, G extends T> void generateCodeForAllMethods(DexMaker dexMaker,
3570e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType) {
3580e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class);
3590e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Method[]> methodArrayType = TypeId.get(Method[].class);
360579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, InvocationHandler> handlerField =
361579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                generatedType.getField(handlerType, FIELD_NAME_HANDLER);
362579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, Method[]> allMethods =
363579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                generatedType.getField(methodArrayType, FIELD_NAME_METHODS);
3640e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Method> methodType = TypeId.get(Method.class);
3650e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Object[]> objectArrayType = TypeId.get(Object[].class);
3660e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        MethodId<InvocationHandler, Object> methodInvoke = handlerType.getMethod(TypeId.OBJECT,
3670e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson                "invoke", TypeId.OBJECT, methodType, objectArrayType);
368579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int m = 0; m < methodsToProxy.length; ++m) {
369579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            /*
370579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * If the 5th method on the superclass Example that can be overridden were to look like
371579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * this:
372579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
373579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     public int doSomething(Bar param0, int param1) {
374579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         ...
375579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
376579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
377579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * Then the following code will generate a method on the proxy that looks something
378579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * like this:
379579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
380579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     public int doSomething(Bar param0, int param1) {
381579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int methodIndex = 4;
382579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Method[] allMethods = Example_Proxy.$__methodArray;
383579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Method thisMethod = allMethods[methodIndex];
384579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int argsLength = 2;
385579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Object[] args = new Object[argsLength];
386579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         InvocationHandler localHandler = this.$__handler;
387579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         // for-loop begins
388579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int p = 0;
389579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Bar parameter0 = param0;
390579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         args[p] = parameter0;
391579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         p = 1;
392579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int parameter1 = param1;
393579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Integer boxed1 = Integer.valueOf(parameter1);
394579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         args[p] = boxed1;
395579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         // for-loop ends
396579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Object result = localHandler.invoke(this, thisMethod, args);
397579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Integer castResult = (Integer) result;
398579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int unboxedResult = castResult.intValue();
399579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         return unboxedResult;
400579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
401579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
402579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * Or, in more idiomatic Java:
403579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
404579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     public int doSomething(Bar param0, int param1) {
405579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         if ($__handler == null) {
406579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *             return super.doSomething(param0, param1);
407579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         }
408579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         return __handler.invoke(this, __methodArray[4],
409579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *                 new Object[] { param0, Integer.valueOf(param1) });
410579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
411579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             */
412579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Method method = methodsToProxy[m];
413579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            String name = method.getName();
414579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Class<?>[] argClasses = method.getParameterTypes();
4150e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?>[] argTypes = new TypeId<?>[argClasses.length];
416579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < argTypes.length; ++i) {
4170e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson                argTypes[i] = TypeId.get(argClasses[i]);
418579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
419579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Class<?> returnType = method.getReturnType();
4200e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?> resultType = TypeId.get(returnType);
421579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<T, ?> superMethod = superclassType.getMethod(resultType, name, argTypes);
422579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<?, ?> methodId = generatedType.getMethod(resultType, name, argTypes);
423ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson            Code code = dexMaker.declare(methodId, PUBLIC);
424579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<G> localThis = code.getThis(generatedType);
425579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<InvocationHandler> localHandler = code.newLocal(handlerType);
4260e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Object> invokeResult = code.newLocal(TypeId.OBJECT);
4270e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Integer> intValue = code.newLocal(TypeId.INT);
428579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<Object[]> args = code.newLocal(objectArrayType);
4290e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Integer> argsLength = code.newLocal(TypeId.INT);
4300e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Object> temp = code.newLocal(TypeId.OBJECT);
431579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?> resultHolder = code.newLocal(resultType);
432579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<Method[]> methodArray = code.newLocal(methodArrayType);
433579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<Method> thisMethod = code.newLocal(methodType);
4340e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Integer> methodIndex = code.newLocal(TypeId.INT);
435579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Class<?> aBoxedClass = PRIMITIVE_TO_BOXED.get(returnType);
436579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?> aBoxedResult = null;
437579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (aBoxedClass != null) {
4380e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson                aBoxedResult = code.newLocal(TypeId.get(aBoxedClass));
439579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
440579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?>[] superArgs2 = new Local<?>[argClasses.length];
441579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?> superResult2 = code.newLocal(resultType);
442579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<InvocationHandler> nullHandler = code.newLocal(handlerType);
443579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
444579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.loadConstant(methodIndex, m);
445579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.sget(allMethods, methodArray);
4460e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.aget(thisMethod, methodArray, methodIndex);
447579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.loadConstant(argsLength, argTypes.length);
4480e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.newArray(args, argsLength);
4490e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.iget(handlerField, localHandler, localThis);
450579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
451579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // if (proxy == null)
452579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.loadConstant(nullHandler, null);
45323abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson            Label handlerNullCase = new Label();
4540e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.compare(Comparison.EQ, handlerNullCase, nullHandler, localHandler);
455579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
456579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // This code is what we execute when we have a valid proxy: delegate to invocation
457579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // handler.
458579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int p = 0; p < argTypes.length; ++p) {
459579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.loadConstant(intValue, p);
460579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                Local<?> parameter = code.getParameter(p, argTypes[p]);
4611977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson                Local<?> unboxedIfNecessary = boxIfRequired(code, parameter, temp);
462579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.aput(args, intValue, unboxedIfNecessary);
463579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
464579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.invokeInterface(methodInvoke, invokeResult, localHandler,
465579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    localThis, thisMethod, args);
466579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            generateCodeForReturnStatement(code, returnType, invokeResult, resultHolder,
467579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    aBoxedResult);
468579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
469579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // This code is executed if proxy is null: call the original super method.
470579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // This is required to handle the case of construction of an object which leaks the
471579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // "this" pointer.
472579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.mark(handlerNullCase);
473579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < superArgs2.length; ++i) {
474579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superArgs2[i] = code.getParameter(i, argTypes[i]);
475579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
476579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (void.class.equals(returnType)) {
477579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.invokeSuper(superMethod, null, localThis, superArgs2);
478579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.returnVoid();
479579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            } else {
480579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                invokeSuper(superMethod, code, localThis, superArgs2, superResult2);
481579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.returnValue(superResult2);
482579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
483579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
484579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            /*
485579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * And to allow calling the original super method, the following is also generated:
486579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
4871af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson             *     public String super$doSomething$java_lang_String(Bar param0, int param1) {
488579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *          int result = super.doSomething(param0, param1);
489579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *          return result;
490579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
491579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             */
4921af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            // TODO: don't include a super_ method if the target is abstract!
493579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<G, ?> callsSuperMethod = generatedType.getMethod(
4941af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    resultType, superMethodName(method), argTypes);
495ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson            Code superCode = dexMaker.declare(callsSuperMethod, PUBLIC);
496579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<G> superThis = superCode.getThis(generatedType);
497579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?>[] superArgs = new Local<?>[argClasses.length];
498579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < superArgs.length; ++i) {
499579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superArgs[i] = superCode.getParameter(i, argTypes[i]);
500579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
501579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (void.class.equals(returnType)) {
502579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superCode.invokeSuper(superMethod, null, superThis, superArgs);
503579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superCode.returnVoid();
504579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            } else {
505579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                Local<?> superResult = superCode.newLocal(resultType);
506579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                invokeSuper(superMethod, superCode, superThis, superArgs, superResult);
507579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superCode.returnValue(superResult);
508579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
509579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
510579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
511579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
5121977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    @SuppressWarnings({"unchecked", "rawtypes"})
5131977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    private static void invokeSuper(MethodId superMethod, Code superCode,
514579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local superThis, Local[] superArgs, Local superResult) {
515579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        superCode.invokeSuper(superMethod, superResult, superThis, superArgs);
516579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
517579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
5181977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    private static Local<?> boxIfRequired(Code code, Local<?> parameter, Local<Object> temp) {
519579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        MethodId<?, ?> unboxMethod = PRIMITIVE_TYPE_TO_UNBOX_METHOD.get(parameter.getType());
520579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (unboxMethod == null) {
521579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return parameter;
522579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
523579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        code.invokeStatic(unboxMethod, temp, parameter);
524579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return temp;
525579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
526579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
5271af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    public static Object callSuper(Object proxy, Method method, Object... args) throws Throwable {
5281af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        try {
5291af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            return proxy.getClass()
5301af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    .getMethod(superMethodName(method), method.getParameterTypes())
5311af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    .invoke(proxy, args);
5321af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        } catch (InvocationTargetException e) {
5331af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            throw e.getCause();
5341af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
5351af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
5361af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
5371af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    /**
5381af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * The super method must include the return type, otherwise its ambiguous
5391af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * for methods with covariant return types.
5401af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     */
5411af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private static String superMethodName(Method method) {
5421af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        String returnType = method.getReturnType().getName();
5431af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        return "super$" + method.getName() + "$"
5441af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                + returnType.replace('.', '_').replace('[', '_').replace(';', '_');
545579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
546579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
547579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static void check(boolean condition, String message) {
548579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (!condition) {
549579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new IllegalArgumentException(message);
550579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
551579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
552579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
553ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson    private static <T, G extends T> void generateConstructorsAndFields(DexMaker dexMaker,
5540e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass) {
5550e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class);
5560e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Method[]> methodArrayType = TypeId.get(Method[].class);
557579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, InvocationHandler> handlerField = generatedType.getField(
558579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                handlerType, FIELD_NAME_HANDLER);
559ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson        dexMaker.declare(handlerField, PRIVATE, null);
560579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, Method[]> allMethods = generatedType.getField(
561579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                methodArrayType, FIELD_NAME_METHODS);
562ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson        dexMaker.declare(allMethods, PRIVATE | STATIC, null);
563579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (Constructor<T> constructor : getConstructorsToOverwrite(superClass)) {
564579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (constructor.getModifiers() == Modifier.FINAL) {
565579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                continue;
566579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
5670e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?>[] types = classArrayToTypeArray(constructor.getParameterTypes());
568579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<?, ?> method = generatedType.getConstructor(types);
569c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson            Code constructorCode = dexMaker.declare(method, PUBLIC);
570579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<G> thisRef = constructorCode.getThis(generatedType);
571579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?>[] params = new Local[types.length];
572579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < params.length; ++i) {
573579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                params[i] = constructorCode.getParameter(i, types[i]);
574579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
575579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<T, ?> superConstructor = superType.getConstructor(types);
576579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            constructorCode.invokeDirect(superConstructor, null, thisRef, params);
577579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            constructorCode.returnVoid();
578579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
579579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
580579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
581579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // The type parameter on Constructor is the class in which the constructor is declared.
582579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // The getDeclaredConstructors() method gets constructors declared only in the given class,
583579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // hence this cast is safe.
584579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @SuppressWarnings("unchecked")
585579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) {
586579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return (Constructor<T>[]) clazz.getDeclaredConstructors();
587579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
588579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
5891af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private TypeId<?>[] getInterfacesAsTypeIds() {
5901af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        TypeId<?>[] result = new TypeId<?>[interfaces.size()];
5911af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        int i = 0;
5921af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> implemented : interfaces) {
5931af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            result[i++] = TypeId.get(implemented);
5941af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
5951af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        return result;
5961af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
5971af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
598579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
5991af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * Gets all {@link Method} objects we can proxy in the hierarchy of the
6001af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * supplied class.
601579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
6021af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private Method[] getMethodsToProxyRecursive() {
603579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Set<MethodSetEntry> methodsToProxy = new HashSet<MethodSetEntry>();
60495689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy        Set<MethodSetEntry> seenFinalMethods = new HashSet<MethodSetEntry>();
6051af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
60695689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
6071af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
6081af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> c : interfaces) {
60995689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
610579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
6111af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
612579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Method[] results = new Method[methodsToProxy.size()];
613579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int i = 0;
614579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (MethodSetEntry entry : methodsToProxy) {
615579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            results[i++] = entry.originalMethod;
616579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
617fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef
618fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef        // Sort the results array so that they are returned by this method
619fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef        // in a deterministic fashion.
620fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef        Arrays.sort(results, new Comparator<Method>() {
621fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef            @Override
622fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef            public int compare(Method method1, Method method2) {
623fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef                return method1.toString().compareTo(method2.toString());
624fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef            }
625fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef        });
626fec7276299b950a8831ead9a9756d27e0fc60560Andrew Yousef
627579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return results;
628579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
629579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
63095689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy    private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods,
63195689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            Class<?> c) {
6321af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Method method : c.getDeclaredMethods()) {
6331af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if ((method.getModifiers() & Modifier.FINAL) != 0) {
63495689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // Skip final methods, we can't override them. We
63595689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // also need to remember them, in case the same
63695689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // method exists in a parent class.
63795689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                seenFinalMethods.add(new MethodSetEntry(method));
6381af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                continue;
6391af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
6401af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if ((method.getModifiers() & STATIC) != 0) {
6411af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                // Skip static methods, overriding them has no effect.
6421af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                continue;
6431af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
6441af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) {
6451af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                // Skip finalize method, it's likely important that it execute as normal.
6461af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                continue;
6471af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
64895689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            MethodSetEntry entry = new MethodSetEntry(method);
64995689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            if (seenFinalMethods.contains(entry)) {
65095689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // This method is final in a child class.
65195689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // We can't override it.
65295689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                continue;
65395689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            }
65495689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            sink.add(entry);
6551af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
65695689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy
6571af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> i : c.getInterfaces()) {
65895689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            getMethodsToProxy(sink, seenFinalMethods, i);
6591af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
6601af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
6611af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
662579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static <T> String getMethodNameForProxyOf(Class<T> clazz) {
663579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return clazz.getSimpleName() + "_Proxy";
664579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
665579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
6660e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson    private static TypeId<?>[] classArrayToTypeArray(Class<?>[] input) {
6670e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<?>[] result = new TypeId[input.length];
668579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < input.length; ++i) {
6690e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            result[i] = TypeId.get(input[i]);
670579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
671579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
672579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
673579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
674579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
675579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Calculates the correct return statement code for a method.
676579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>
677579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * A void method will not return anything.  A method that returns a primitive will need to
678579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * unbox the boxed result.  Otherwise we will cast the result.
679579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
680579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // This one is tricky to fix, I gave up.
681579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @SuppressWarnings({ "rawtypes", "unchecked" })
682579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static void generateCodeForReturnStatement(Code code, Class methodReturnType,
683579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult) {
684579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (PRIMITIVE_TO_UNBOX_METHOD.containsKey(methodReturnType)) {
68597b0be6b3da9df87e9026f880b0b0bffc7242450Jesse Wilson            code.cast(aBoxedResult, localForResultOfInvoke);
686579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId unboxingMethodFor = getUnboxMethodForPrimitive(methodReturnType);
687579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.invokeVirtual(unboxingMethodFor, localOfMethodReturnType, aBoxedResult);
688579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.returnValue(localOfMethodReturnType);
689579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } else if (void.class.equals(methodReturnType)) {
690579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.returnVoid();
691579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } else {
69297b0be6b3da9df87e9026f880b0b0bffc7242450Jesse Wilson            code.cast(localOfMethodReturnType, localForResultOfInvoke);
693579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.returnValue(localOfMethodReturnType);
694579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
695579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
696579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
6971af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private static <T> Set<T> asSet(T... array) {
6981af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        return new CopyOnWriteArraySet<T>(Arrays.asList(array));
6991af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
7001af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
701579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType) {
702579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return PRIMITIVE_TO_UNBOX_METHOD.get(methodReturnType);
703579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
704579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
705579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED;
706579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    static {
707579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED = new HashMap<Class<?>, Class<?>>();
708579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class);
709579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(int.class, Integer.class);
710579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(byte.class, Byte.class);
711579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(long.class, Long.class);
712579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(short.class, Short.class);
713579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(float.class, Float.class);
714579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(double.class, Double.class);
715579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(char.class, Character.class);
716579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
717579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
7180e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson    private static final Map<TypeId<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD;
719579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    static {
7200e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        PRIMITIVE_TYPE_TO_UNBOX_METHOD = new HashMap<TypeId<?>, MethodId<?, ?>>();
721579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_TO_BOXED.entrySet()) {
7220e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?> primitiveType = TypeId.get(entry.getKey());
7230e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?> boxedType = TypeId.get(entry.getValue());
724579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<?, ?> valueOfMethod = boxedType.getMethod(boxedType, "valueOf", primitiveType);
725579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod);
726579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
727579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
728579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
729579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
730579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Map from primitive type to method used to unbox a boxed version of the primitive.
731579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>
732579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * This is required for methods whose return type is primitive, since the
733579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * {@link InvocationHandler} will return us a boxed result, and we'll need to convert it back to
734579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * primitive value.
735579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
736579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD;
737579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    static {
738579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Map<Class<?>, MethodId<?, ?>> map = new HashMap<Class<?>, MethodId<?, ?>>();
7390e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(boolean.class, TypeId.get(Boolean.class).getMethod(TypeId.BOOLEAN, "booleanValue"));
7400e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(int.class, TypeId.get(Integer.class).getMethod(TypeId.INT, "intValue"));
7410e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(byte.class, TypeId.get(Byte.class).getMethod(TypeId.BYTE, "byteValue"));
7420e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(long.class, TypeId.get(Long.class).getMethod(TypeId.LONG, "longValue"));
7430e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(short.class, TypeId.get(Short.class).getMethod(TypeId.SHORT, "shortValue"));
7440e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(float.class, TypeId.get(Float.class).getMethod(TypeId.FLOAT, "floatValue"));
7450e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(double.class, TypeId.get(Double.class).getMethod(TypeId.DOUBLE, "doubleValue"));
7460e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(char.class, TypeId.get(Character.class).getMethod(TypeId.CHAR, "charValue"));
747579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_UNBOX_METHOD = map;
748579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
749579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
750579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
751579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Wrapper class to let us disambiguate {@link Method} objects.
752579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>
753579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * The purpose of this class is to override the {@link #equals(Object)} and {@link #hashCode()}
754579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * methods so we can use a {@link Set} to remove duplicate methods that are overrides of one
755579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * another. For these purposes, we consider two methods to be equal if they have the same
756579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * name, return type, and parameter types.
757579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
758579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static class MethodSetEntry {
759579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final String name;
760579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final Class<?>[] paramTypes;
761579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final Class<?> returnType;
762579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final Method originalMethod;
763579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
764579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        public MethodSetEntry(Method method) {
765579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            originalMethod = method;
766579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            name = method.getName();
767579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            paramTypes = method.getParameterTypes();
768579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            returnType = method.getReturnType();
769579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
770579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
771579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        @Override
772579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        public boolean equals(Object o) {
773579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (o instanceof MethodSetEntry) {
774579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                MethodSetEntry other = (MethodSetEntry) o;
775579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                return name.equals(other.name)
776579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        && returnType.equals(other.returnType)
777579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        && Arrays.equals(paramTypes, other.paramTypes);
778579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
779579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return false;
780579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
781579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
782579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        @Override
783579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        public int hashCode() {
784579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int result = 17;
785579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result += 31 * result + name.hashCode();
786579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result += 31 * result + returnType.hashCode();
787579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result += 31 * result + Arrays.hashCode(paramTypes);
788579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return result;
789579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
790579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
791579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson}
792