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
17b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinpackage com.android.dx.stock;
18b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin
19b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.Code;
20b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.Comparison;
21b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.DexMaker;
22b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.FieldId;
23b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.Label;
24b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.Local;
25b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.MethodId;
26b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dx.TypeId;
27b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin
28579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.File;
29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.IOException;
30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Constructor;
31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Field;
32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.InvocationHandler;
33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.InvocationTargetException;
34579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Method;
35579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.Modifier;
36579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.UndeclaredThrowableException;
37579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Arrays;
382e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilsonimport java.util.Collections;
398d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousefimport java.util.Comparator;
40579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.HashMap;
41579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.HashSet;
42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Map;
43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Set;
441af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilsonimport java.util.concurrent.CopyOnWriteArraySet;
45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
46b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport static java.lang.reflect.Modifier.PRIVATE;
47b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport static java.lang.reflect.Modifier.PUBLIC;
48b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport static java.lang.reflect.Modifier.STATIC;
49b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin
50579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/**
51579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Creates dynamic proxies of concrete classes.
52579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
53579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This is similar to the {@code java.lang.reflect.Proxy} class, but works for classes instead of
54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * interfaces.
55579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <h3>Example</h3>
56579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * The following example demonstrates the creation of a dynamic proxy for {@code java.util.Random}
57579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * which will always return 4 when asked for integers, and which logs method calls to every method.
58579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <pre>
59579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * InvocationHandler handler = new InvocationHandler() {
60579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     &#64;Override
61579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
62579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         if (method.getName().equals("nextInt")) {
63579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *             // Chosen by fair dice roll, guaranteed to be random.
64579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *             return 4;
65579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         }
66579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         Object result = ProxyBuilder.callSuper(proxy, method, args);
67579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         System.out.println("Method: " + method.getName() + " args: "
68579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *                 + Arrays.toString(args) + " result: " + result);
69579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         return result;
70579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     }
71579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * };
72579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Random debugRandom = ProxyBuilder.forClass(Random.class)
73579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE))
74579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         .handler(handler)
75579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *         .build();
76579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * assertEquals(4, debugRandom.nextInt());
77579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * debugRandom.setSeed(0);
78579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * assertTrue(debugRandom.nextBoolean());
79579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * </pre>
80579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <h3>Usage</h3>
81579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Call {@link #forClass(Class)} for the Class you wish to proxy. Call
82579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@link #handler(InvocationHandler)} passing in an {@link InvocationHandler}, and then call
83579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@link #build()}. The returned instance will be a dynamically generated subclass where all method
84579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * calls will be delegated to the invocation handler, except as noted below.
85579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
86579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * The static method {@link #callSuper(Object, Method, Object...)} allows you to access the original
87579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * super method for a given proxy. This allows the invocation handler to selectively override some
88579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * methods but not others.
89579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
90579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * By default, the {@link #build()} method will call the no-arg constructor belonging to the class
91579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * being proxied. If you wish to call a different constructor, you must provide arguments for both
92579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@link #constructorArgTypes(Class[])} and {@link #constructorArgValues(Object[])}.
93579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
94579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This process works only for classes with public and protected level of visibility.
95579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
96579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may proxy abstract classes.  You may not proxy final classes.
97579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
98579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Only non-private, non-final, non-static methods will be dispatched to the invocation handler.
99579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Private, static or final methods will always call through to the superclass as normal.
100579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
101579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * The {@link #finalize()} method on {@code Object} will not be proxied.
102579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
103579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You must provide a dex cache directory via the {@link #dexCache(File)} method. You should take
104579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * care not to make this a world-writable directory, so that third parties cannot inject code into
105579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * your application.  A suitable parameter for these output directories would be something like
106579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * this:
107579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <pre>{@code
108579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *     getApplicationContext().getDir("dx", Context.MODE_PRIVATE);
109579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * }</pre>
110579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
111579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * If the base class to be proxied leaks the {@code this} pointer in the constructor (bad practice),
112579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * that is to say calls a non-private non-final method from the constructor, the invocation handler
113579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * will not be invoked.  As a simple concrete example, when proxying Random we discover that it
114b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin * internally calls setSeed during the constructor.  The proxy will not intercept this call during
115579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * proxy construction, but will intercept as normal afterwards.  This behaviour may be subject to
116579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * change in future releases.
117579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>
118579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This class is <b>not thread safe</b>.
119579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
120579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpublic final class ProxyBuilder<T> {
121054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    // Version of ProxyBuilder. It should be updated if the implementation
122054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    // of the generated proxy class changes.
123054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    public static final int VERSION = 1;
124054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef
125579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final String FIELD_NAME_HANDLER = "$__handler";
126579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final String FIELD_NAME_METHODS = "$__methodArray";
127579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
1282e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson    /**
1292e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     * A cache of all proxy classes ever generated. At the time of writing,
1302e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     * Android's runtime doesn't support class unloading so there's little
1312e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     * value in using weak references.
1322e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson     */
1332e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson    private static final Map<Class<?>, Class<?>> generatedProxyClasses
1342e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());
1352e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson
136579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private final Class<T> baseClass;
137579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private ClassLoader parentClassLoader = ProxyBuilder.class.getClassLoader();
138579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private InvocationHandler handler;
139579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private File dexCache;
140579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private Class<?>[] constructorArgTypes = new Class[0];
141579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private Object[] constructorArgValues = new Object[0];
142b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin    private Set<Class<?>> interfaces = new HashSet<>();
143579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
144579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private ProxyBuilder(Class<T> clazz) {
145579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        baseClass = clazz;
146579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
147579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
148579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public static <T> ProxyBuilder<T> forClass(Class<T> clazz) {
149579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return new ProxyBuilder<T>(clazz);
150579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
151579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
152579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
153579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Specifies the parent ClassLoader to use when creating the proxy.
154579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
155579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>If null, {@code ProxyBuilder.class.getClassLoader()} will be used.
156579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
157579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> parentClassLoader(ClassLoader parent) {
158579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        parentClassLoader = parent;
159579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
160579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
161579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
162579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> handler(InvocationHandler handler) {
163579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.handler = handler;
164579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
165579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
166579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
16773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    /**
16873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * Sets the directory where executable code is stored. See {@link
16973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * DexMaker#generateAndLoad DexMaker.generateAndLoad()} for guidance on
17073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * choosing a secure location for the dex cache.
17173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     */
172054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef    public ProxyBuilder<T> dexCache(File dexCacheParent) {
173054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef        dexCache = new File(dexCacheParent, "v" + Integer.toString(VERSION));
174054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef        dexCache.mkdir();
175579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
176579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
177b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin
1781af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    public ProxyBuilder<T> implementing(Class<?>... interfaces) {
1791af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> i : interfaces) {
1801af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if (!i.isInterface()) {
1811af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                throw new IllegalArgumentException("Not an interface: " + i.getName());
1821af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
1831af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            this.interfaces.add(i);
1841af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
1851af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        return this;
1861af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
187579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
188579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) {
189579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.constructorArgValues = constructorArgValues;
190579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
191579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
192579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
193579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) {
194579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.constructorArgTypes = constructorArgTypes;
195579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return this;
196579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
197579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
198579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
199579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Create a new instance of the class to proxy.
200579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
201579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws UnsupportedOperationException if the class we are trying to create a proxy for is
202579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *     not accessible.
2031977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson     * @throws IOException if an exception occurred writing to the {@code dexCache} directory.
204579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws UndeclaredThrowableException if the constructor for the base class to proxy throws
205579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *     a declared exception during construction.
206579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws IllegalArgumentException if the handler is null, if the constructor argument types
207579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *     do not match the constructor argument values, or if no such constructor exists.
208579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
2091977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    public T build() throws IOException {
210579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        check(handler != null, "handler == null");
211579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        check(constructorArgTypes.length == constructorArgValues.length,
212579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                "constructorArgValues.length != constructorArgTypes.length");
2131af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        Class<? extends T> proxyClass = buildProxyClass();
214579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Constructor<? extends T> constructor;
215579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
216579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            constructor = proxyClass.getConstructor(constructorArgTypes);
217579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NoSuchMethodException e) {
2181af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            throw new IllegalArgumentException("No constructor for " + baseClass.getName()
219679fb66c12a24691a6d7720d79c64c28f5b0532bJesse Wilson                    + " with parameter types " + Arrays.toString(constructorArgTypes));
220579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
221579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        T result;
222579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
223579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result = constructor.newInstance(constructorArgValues);
224579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (InstantiationException e) {
225579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, generated class is not abstract.
226579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
227579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IllegalAccessException e) {
228579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, the generated constructor is accessible.
229579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
230579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (InvocationTargetException e) {
2312e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            // Thrown when the base class constructor throws an exception.
232579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw launderCause(e);
233579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
234a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin        setInvocationHandler(result, handler);
235579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
236579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
237579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
2381af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    // TODO: test coverage for this
239a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin
240a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin    /**
241a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * Generate a proxy class. Note that new instances of this class will not automatically have an
242a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * an invocation handler, even if {@link #handler(InvocationHandler)} was called. The handler
243a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * must be set on each instance after it is created, using
244a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * {@link #setInvocationHandler(Object, InvocationHandler)}.
245a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     */
2461af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    public Class<? extends T> buildProxyClass() throws IOException {
2472e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        // try the cache to see if we've generated this one before
248b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        // we only populate the map with matching types
249b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        @SuppressWarnings("unchecked")
2502e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        Class<? extends T> proxyClass = (Class) generatedProxyClasses.get(baseClass);
251db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin        if (proxyClass != null) {
252db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin            boolean shareClassLoader = Boolean.parseBoolean(System.getProperty(
253db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                        "dexmaker.share_classloader", "false"));
254db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin            boolean validClassLoader;
255db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin            if (shareClassLoader) {
256db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                ClassLoader parent = parentClassLoader != null ? parentClassLoader
257db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                        : baseClass.getClassLoader();
258db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                validClassLoader = proxyClass.getClassLoader() == parent;
259db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin            } else {
260db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                validClassLoader = proxyClass.getClassLoader().getParent() == parentClassLoader;
261db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin            }
262db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin            if (validClassLoader && interfaces.equals(asSet(proxyClass.getInterfaces()))) {
263db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                return proxyClass; // cache hit!
264db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin            }
2652e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        }
2662e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson
2672e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        // the cache missed; generate the class
2682e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        DexMaker dexMaker = new DexMaker();
2692e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        String generatedName = getMethodNameForProxyOf(baseClass);
2702e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        TypeId<? extends T> generatedType = TypeId.get("L" + generatedName + ";");
2712e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        TypeId<T> superType = TypeId.get(baseClass);
2722e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        generateConstructorsAndFields(dexMaker, generatedType, superType, baseClass);
2731af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        Method[] methodsToProxy = getMethodsToProxyRecursive();
2742e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        generateCodeForAllMethods(dexMaker, generatedType, methodsToProxy, superType);
275b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        dexMaker.declare(generatedType, generatedName + ".generated", PUBLIC, superType, getInterfacesAsTypeIds());
276db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin        ClassLoader classLoader = dexMaker.generateAndLoad(baseClass.getClassLoader(),
277db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                parentClassLoader, dexCache);
2782e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        try {
2792e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            proxyClass = loadClass(classLoader, generatedName);
2802e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        } catch (IllegalAccessError e) {
2812e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            // Thrown when the base class is not accessible.
2821af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            throw new UnsupportedOperationException(
2831af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    "cannot proxy inaccessible class " + baseClass, e);
2842e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        } catch (ClassNotFoundException e) {
2852e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            // Should not be thrown, we're sure to have generated this class.
2862e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson            throw new AssertionError(e);
2872e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        }
2882e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        setMethodsStaticField(proxyClass, methodsToProxy);
2892e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        generatedProxyClasses.put(baseClass, proxyClass);
2902e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson        return proxyClass;
2912e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson    }
2922e28a229885e9ba7fec9ef42cbf30fdcf8a0c939Jesse Wilson
293579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // The type cast is safe: the generated type will extend the base class type.
294579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @SuppressWarnings("unchecked")
295579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName)
296579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throws ClassNotFoundException {
297579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return (Class<? extends T>) classLoader.loadClass(generatedName);
298579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
299579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
300579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static RuntimeException launderCause(InvocationTargetException e) {
301579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Throwable cause = e.getCause();
302579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // Errors should be thrown as they are.
303579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (cause instanceof Error) {
304579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw (Error) cause;
305579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
306579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // RuntimeException can be thrown as-is.
307579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (cause instanceof RuntimeException) {
308579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw (RuntimeException) cause;
309579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
310579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // Declared exceptions will have to be wrapped.
311579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        throw new UndeclaredThrowableException(cause);
312579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
313579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
314579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy) {
315579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
316579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS);
317579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            methodArrayField.setAccessible(true);
318579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            methodArrayField.set(null, methodsToProxy);
319579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NoSuchFieldException e) {
320579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, generated proxy class has been generated with this field.
321579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
322579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IllegalAccessException e) {
323579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, we just set the field to accessible.
324579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
325579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
326579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
327579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
328579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
329579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Returns the proxy's {@link InvocationHandler}.
330579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
331579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @throws IllegalArgumentException if the object supplied is not a proxy created by this class.
332579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
333579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public static InvocationHandler getInvocationHandler(Object instance) {
334579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
335579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Field field = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
336579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            field.setAccessible(true);
337579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return (InvocationHandler) field.get(instance);
338579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NoSuchFieldException e) {
339579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new IllegalArgumentException("Not a valid proxy instance", e);
340579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IllegalAccessException e) {
341579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Should not be thrown, we just set the field to accessible.
342579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new AssertionError(e);
34373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        }
34473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    }
34573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
346a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin    /**
347a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * Sets the proxy's {@link InvocationHandler}.
348a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * <p>
349a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * If you create a proxy with {@link #build()}, the proxy will already have a handler set,
350a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * provided that you configured one with {@link #handler(InvocationHandler)}.
351a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * <p>
352a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * If you generate a proxy class with {@link #buildProxyClass()}, instances of the proxy class
353a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * will not automatically have a handler set, and it is necessary to use this method with each
354a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * instance.
355a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     *
356a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     * @throws IllegalArgumentException if the object supplied is not a proxy created by this class.
357a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin     */
358a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin    public static void setInvocationHandler(Object instance, InvocationHandler handler) {
359a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin        try {
360a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
361a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            handlerField.setAccessible(true);
362a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            handlerField.set(instance, handler);
363a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin        } catch (NoSuchFieldException e) {
364a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            throw new IllegalArgumentException("Not a valid proxy instance", e);
365a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin        } catch (IllegalAccessException e) {
366a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            // Should not be thrown, we just set the field to accessible.
367a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            throw new AssertionError(e);
368a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin        }
369a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin    }
370a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin
3711af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    // TODO: test coverage for isProxyClass
372b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin
37373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    /**
37473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     * Returns true if {@code c} is a proxy class created by this builder.
37573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson     */
37673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    public static boolean isProxyClass(Class<?> c) {
37773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        // TODO: use a marker interface instead?
37873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        try {
37973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            c.getDeclaredField(FIELD_NAME_HANDLER);
38073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            return true;
38173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        } catch (NoSuchFieldException e) {
38273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            return false;
383579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
384579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
385579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
386ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson    private static <T, G extends T> void generateCodeForAllMethods(DexMaker dexMaker,
3870e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType) {
3880e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class);
3890e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Method[]> methodArrayType = TypeId.get(Method[].class);
390579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, InvocationHandler> handlerField =
391579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                generatedType.getField(handlerType, FIELD_NAME_HANDLER);
392579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, Method[]> allMethods =
393579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                generatedType.getField(methodArrayType, FIELD_NAME_METHODS);
3940e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Method> methodType = TypeId.get(Method.class);
3950e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Object[]> objectArrayType = TypeId.get(Object[].class);
3960e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        MethodId<InvocationHandler, Object> methodInvoke = handlerType.getMethod(TypeId.OBJECT,
3970e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson                "invoke", TypeId.OBJECT, methodType, objectArrayType);
398579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int m = 0; m < methodsToProxy.length; ++m) {
399579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            /*
400579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * If the 5th method on the superclass Example that can be overridden were to look like
401579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * this:
402579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
403579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     public int doSomething(Bar param0, int param1) {
404579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         ...
405579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
406579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
407579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * Then the following code will generate a method on the proxy that looks something
408579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * like this:
409579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
410579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     public int doSomething(Bar param0, int param1) {
411579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int methodIndex = 4;
412579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Method[] allMethods = Example_Proxy.$__methodArray;
413579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Method thisMethod = allMethods[methodIndex];
414579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int argsLength = 2;
415579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Object[] args = new Object[argsLength];
416579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         InvocationHandler localHandler = this.$__handler;
417579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         // for-loop begins
418579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int p = 0;
419579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Bar parameter0 = param0;
420579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         args[p] = parameter0;
421579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         p = 1;
422579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int parameter1 = param1;
423579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Integer boxed1 = Integer.valueOf(parameter1);
424579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         args[p] = boxed1;
425579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         // for-loop ends
426579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Object result = localHandler.invoke(this, thisMethod, args);
427579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         Integer castResult = (Integer) result;
428579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         int unboxedResult = castResult.intValue();
429579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         return unboxedResult;
430579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
431579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
432579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * Or, in more idiomatic Java:
433579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
434579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     public int doSomething(Bar param0, int param1) {
435579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         if ($__handler == null) {
436579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *             return super.doSomething(param0, param1);
437579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         }
438579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *         return __handler.invoke(this, __methodArray[4],
439579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *                 new Object[] { param0, Integer.valueOf(param1) });
440579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
441579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             */
442579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Method method = methodsToProxy[m];
443579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            String name = method.getName();
444579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Class<?>[] argClasses = method.getParameterTypes();
4450e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?>[] argTypes = new TypeId<?>[argClasses.length];
446579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < argTypes.length; ++i) {
4470e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson                argTypes[i] = TypeId.get(argClasses[i]);
448579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
449579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Class<?> returnType = method.getReturnType();
4500e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?> resultType = TypeId.get(returnType);
451579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<T, ?> superMethod = superclassType.getMethod(resultType, name, argTypes);
452579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<?, ?> methodId = generatedType.getMethod(resultType, name, argTypes);
453ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson            Code code = dexMaker.declare(methodId, PUBLIC);
454579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<G> localThis = code.getThis(generatedType);
455579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<InvocationHandler> localHandler = code.newLocal(handlerType);
4560e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Object> invokeResult = code.newLocal(TypeId.OBJECT);
4570e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Integer> intValue = code.newLocal(TypeId.INT);
458579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<Object[]> args = code.newLocal(objectArrayType);
4590e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Integer> argsLength = code.newLocal(TypeId.INT);
4600e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Object> temp = code.newLocal(TypeId.OBJECT);
461579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?> resultHolder = code.newLocal(resultType);
462579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<Method[]> methodArray = code.newLocal(methodArrayType);
463579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<Method> thisMethod = code.newLocal(methodType);
4640e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            Local<Integer> methodIndex = code.newLocal(TypeId.INT);
465579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Class<?> aBoxedClass = PRIMITIVE_TO_BOXED.get(returnType);
466579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?> aBoxedResult = null;
467579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (aBoxedClass != null) {
4680e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson                aBoxedResult = code.newLocal(TypeId.get(aBoxedClass));
469579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
470579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?>[] superArgs2 = new Local<?>[argClasses.length];
471579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?> superResult2 = code.newLocal(resultType);
472579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<InvocationHandler> nullHandler = code.newLocal(handlerType);
473579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
474579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.loadConstant(methodIndex, m);
475579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.sget(allMethods, methodArray);
4760e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.aget(thisMethod, methodArray, methodIndex);
477579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.loadConstant(argsLength, argTypes.length);
4780e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.newArray(args, argsLength);
4790e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.iget(handlerField, localHandler, localThis);
480579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
481579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // if (proxy == null)
482579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.loadConstant(nullHandler, null);
48323abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson            Label handlerNullCase = new Label();
4840e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            code.compare(Comparison.EQ, handlerNullCase, nullHandler, localHandler);
485579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
486579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // This code is what we execute when we have a valid proxy: delegate to invocation
487579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // handler.
488579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int p = 0; p < argTypes.length; ++p) {
489579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.loadConstant(intValue, p);
490579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                Local<?> parameter = code.getParameter(p, argTypes[p]);
4911977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson                Local<?> unboxedIfNecessary = boxIfRequired(code, parameter, temp);
492579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.aput(args, intValue, unboxedIfNecessary);
493579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
494579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.invokeInterface(methodInvoke, invokeResult, localHandler,
495579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    localThis, thisMethod, args);
496579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            generateCodeForReturnStatement(code, returnType, invokeResult, resultHolder,
497579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    aBoxedResult);
498579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
499579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // This code is executed if proxy is null: call the original super method.
500579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // This is required to handle the case of construction of an object which leaks the
501579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // "this" pointer.
502579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.mark(handlerNullCase);
503579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < superArgs2.length; ++i) {
504579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superArgs2[i] = code.getParameter(i, argTypes[i]);
505579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
506579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (void.class.equals(returnType)) {
507579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.invokeSuper(superMethod, null, localThis, superArgs2);
508579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.returnVoid();
509579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            } else {
510579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                invokeSuper(superMethod, code, localThis, superArgs2, superResult2);
511579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                code.returnValue(superResult2);
512579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
513579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
514579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            /*
515579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * And to allow calling the original super method, the following is also generated:
516579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *
5171af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson             *     public String super$doSomething$java_lang_String(Bar param0, int param1) {
518579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *          int result = super.doSomething(param0, param1);
519579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *          return result;
520579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             *     }
521579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             */
5221af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            // TODO: don't include a super_ method if the target is abstract!
523579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<G, ?> callsSuperMethod = generatedType.getMethod(
5241af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    resultType, superMethodName(method), argTypes);
525ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson            Code superCode = dexMaker.declare(callsSuperMethod, PUBLIC);
526579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<G> superThis = superCode.getThis(generatedType);
527579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?>[] superArgs = new Local<?>[argClasses.length];
528579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < superArgs.length; ++i) {
529579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superArgs[i] = superCode.getParameter(i, argTypes[i]);
530579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
531579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (void.class.equals(returnType)) {
532579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superCode.invokeSuper(superMethod, null, superThis, superArgs);
533579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superCode.returnVoid();
534579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            } else {
535579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                Local<?> superResult = superCode.newLocal(resultType);
536579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                invokeSuper(superMethod, superCode, superThis, superArgs, superResult);
537579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                superCode.returnValue(superResult);
538579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
539579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
540579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
541579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
5421977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    @SuppressWarnings({"unchecked", "rawtypes"})
5431977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    private static void invokeSuper(MethodId superMethod, Code superCode,
544579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local superThis, Local[] superArgs, Local superResult) {
545579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        superCode.invokeSuper(superMethod, superResult, superThis, superArgs);
546579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
547579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
5481977585657cb304a9e1ffa8a2320fa8053a7383cJesse Wilson    private static Local<?> boxIfRequired(Code code, Local<?> parameter, Local<Object> temp) {
549579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        MethodId<?, ?> unboxMethod = PRIMITIVE_TYPE_TO_UNBOX_METHOD.get(parameter.getType());
550579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (unboxMethod == null) {
551579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return parameter;
552579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
553579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        code.invokeStatic(unboxMethod, temp, parameter);
554579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return temp;
555579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
556579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
5571af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    public static Object callSuper(Object proxy, Method method, Object... args) throws Throwable {
5581af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        try {
5591af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            return proxy.getClass()
5601af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    .getMethod(superMethodName(method), method.getParameterTypes())
5611af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                    .invoke(proxy, args);
5621af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        } catch (InvocationTargetException e) {
5631af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            throw e.getCause();
5641af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
5651af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
5661af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
5671af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    /**
5681af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * The super method must include the return type, otherwise its ambiguous
5691af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * for methods with covariant return types.
5701af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     */
5711af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private static String superMethodName(Method method) {
5721af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        String returnType = method.getReturnType().getName();
5731af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        return "super$" + method.getName() + "$"
5741af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                + returnType.replace('.', '_').replace('[', '_').replace(';', '_');
575579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
576579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
577579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static void check(boolean condition, String message) {
578579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (!condition) {
579579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new IllegalArgumentException(message);
580579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
581579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
582579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
583ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson    private static <T, G extends T> void generateConstructorsAndFields(DexMaker dexMaker,
5840e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass) {
5850e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class);
5860e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<Method[]> methodArrayType = TypeId.get(Method[].class);
587579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, InvocationHandler> handlerField = generatedType.getField(
588579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                handlerType, FIELD_NAME_HANDLER);
589ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson        dexMaker.declare(handlerField, PRIVATE, null);
590579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        FieldId<G, Method[]> allMethods = generatedType.getField(
591579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                methodArrayType, FIELD_NAME_METHODS);
592ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilson        dexMaker.declare(allMethods, PRIVATE | STATIC, null);
593579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (Constructor<T> constructor : getConstructorsToOverwrite(superClass)) {
594579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (constructor.getModifiers() == Modifier.FINAL) {
595579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                continue;
596579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
5970e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?>[] types = classArrayToTypeArray(constructor.getParameterTypes());
598579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<?, ?> method = generatedType.getConstructor(types);
599c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson            Code constructorCode = dexMaker.declare(method, PUBLIC);
600579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<G> thisRef = constructorCode.getThis(generatedType);
601579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local<?>[] params = new Local[types.length];
602579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int i = 0; i < params.length; ++i) {
603579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                params[i] = constructorCode.getParameter(i, types[i]);
604579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
605579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<T, ?> superConstructor = superType.getConstructor(types);
606579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            constructorCode.invokeDirect(superConstructor, null, thisRef, params);
607579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            constructorCode.returnVoid();
608579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
609579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
610579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
611579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // The type parameter on Constructor is the class in which the constructor is declared.
612579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // The getDeclaredConstructors() method gets constructors declared only in the given class,
613579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // hence this cast is safe.
614579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @SuppressWarnings("unchecked")
615579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) {
616579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return (Constructor<T>[]) clazz.getDeclaredConstructors();
617579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
618579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
6191af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private TypeId<?>[] getInterfacesAsTypeIds() {
6201af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        TypeId<?>[] result = new TypeId<?>[interfaces.size()];
6211af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        int i = 0;
6221af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> implemented : interfaces) {
6231af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            result[i++] = TypeId.get(implemented);
6241af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
6251af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        return result;
6261af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
6271af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
628579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
6291af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * Gets all {@link Method} objects we can proxy in the hierarchy of the
6301af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson     * supplied class.
631579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
6321af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private Method[] getMethodsToProxyRecursive() {
633b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        Set<MethodSetEntry> methodsToProxy = new HashSet<>();
634b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        Set<MethodSetEntry> seenFinalMethods = new HashSet<>();
635309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        // Traverse the class hierarchy to ensure that all concrete methods (which could be marked
636309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        // as final) are visited before any abstract methods from interfaces.
6371af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
63895689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
6391af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
640309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        // Now traverse the interface hierarchy, starting with the ones implemented by the class,
641309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        // followed by any extra interfaces.
642cb6e5223823f9f9ad04da51c63548659062a4f43Paul Duffin        for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
643cb6e5223823f9f9ad04da51c63548659062a4f43Paul Duffin            for (Class<?> i : c.getInterfaces()) {
644cb6e5223823f9f9ad04da51c63548659062a4f43Paul Duffin                getMethodsToProxy(methodsToProxy, seenFinalMethods, i);
645cb6e5223823f9f9ad04da51c63548659062a4f43Paul Duffin            }
646cb6e5223823f9f9ad04da51c63548659062a4f43Paul Duffin        }
6471af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Class<?> c : interfaces) {
64895689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
649579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
6501af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
651579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        Method[] results = new Method[methodsToProxy.size()];
652579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int i = 0;
653579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (MethodSetEntry entry : methodsToProxy) {
654579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            results[i++] = entry.originalMethod;
655579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
6568d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef
6578d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef        // Sort the results array so that they are returned by this method
6588d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef        // in a deterministic fashion.
6598d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef        Arrays.sort(results, new Comparator<Method>() {
6608d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef            @Override
6618d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef            public int compare(Method method1, Method method2) {
6628d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef                return method1.toString().compareTo(method2.toString());
6638d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef            }
6648d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef        });
6658d53a06d10c6e2b96be6857aede5e4661804611eAndrew Yousef
666579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return results;
667579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
668579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
66995689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy    private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods,
67095689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            Class<?> c) {
671db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin        boolean shareClassLoader = Boolean.parseBoolean(System.getProperty(
672db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin               "dexmaker.share_classloader", "false"));
6731af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        for (Method method : c.getDeclaredMethods()) {
6741af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if ((method.getModifiers() & Modifier.FINAL) != 0) {
67595689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // Skip final methods, we can't override them. We
67695689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // also need to remember them, in case the same
67795689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // method exists in a parent class.
678ab38abdf175554643d40b889d58d283d358218c3Samuel Tan                MethodSetEntry entry = new MethodSetEntry(method);
679ab38abdf175554643d40b889d58d283d358218c3Samuel Tan                seenFinalMethods.add(entry);
680ab38abdf175554643d40b889d58d283d358218c3Samuel Tan                // We may have seen this method already, from an interface
681ab38abdf175554643d40b889d58d283d358218c3Samuel Tan                // implemented by a child class. We need to remove it here.
682ab38abdf175554643d40b889d58d283d358218c3Samuel Tan                sink.remove(entry);
6831af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                continue;
6841af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
6851af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if ((method.getModifiers() & STATIC) != 0) {
6861af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                // Skip static methods, overriding them has no effect.
6871af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                continue;
6881af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
689a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            if (!Modifier.isPublic(method.getModifiers())
690db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin					&& !Modifier.isProtected(method.getModifiers())
691db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                    && (!shareClassLoader || Modifier.isPrivate(method.getModifiers()))) {
692a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // Skip private methods, since they are invoked through direct
693a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // invocation (as opposed to virtual). Therefore, it would not
694a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // be possible to intercept any private method defined inside
695a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // the proxy class except through reflection.
696a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin
697db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                // Skip package-private methods as well (for non-shared class
698db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin                // loaders). The proxy class does
699a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // not actually inherit package-private methods from the parent
700a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // class because it is not a member of the parent's package.
701a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // This is even true if the two classes have the same package
702a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                // name, as they use different class loaders.
703a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin                continue;
704a13e8e98b64e3f5271759dac44967f1c24c76995Paul Duffin            }
7051af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) {
7061af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                // Skip finalize method, it's likely important that it execute as normal.
7071af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson                continue;
7081af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson            }
70995689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            MethodSetEntry entry = new MethodSetEntry(method);
71095689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            if (seenFinalMethods.contains(entry)) {
71195689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // This method is final in a child class.
71295689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                // We can't override it.
71395689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy                continue;
71495689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            }
71595689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            sink.add(entry);
7161af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson        }
717309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin
718309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        // Only visit the interfaces of this class if it is itself an interface. That prevents
719309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        // visiting interfaces of a class before its super classes.
720309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        if (c.isInterface()) {
721309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin            for (Class<?> i : c.getInterfaces()) {
722309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin                getMethodsToProxy(sink, seenFinalMethods, i);
723309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin            }
724309cc66e50aaac713a5c2c72e3ea6b236716b036Paul Duffin        }
7251af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
7261af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
727579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static <T> String getMethodNameForProxyOf(Class<T> clazz) {
728db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin        return clazz.getName().replace(".", "/") + "_Proxy";
729579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
730579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
7310e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson    private static TypeId<?>[] classArrayToTypeArray(Class<?>[] input) {
7320e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        TypeId<?>[] result = new TypeId[input.length];
733579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < input.length; ++i) {
7340e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            result[i] = TypeId.get(input[i]);
735579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
736579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
737579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
738579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
739579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
740579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Calculates the correct return statement code for a method.
741579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>
742579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * A void method will not return anything.  A method that returns a primitive will need to
743579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * unbox the boxed result.  Otherwise we will cast the result.
744579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
745579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    // This one is tricky to fix, I gave up.
746579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @SuppressWarnings({ "rawtypes", "unchecked" })
747579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static void generateCodeForReturnStatement(Code code, Class methodReturnType,
748579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult) {
749579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (PRIMITIVE_TO_UNBOX_METHOD.containsKey(methodReturnType)) {
75097b0be6b3da9df87e9026f880b0b0bffc7242450Jesse Wilson            code.cast(aBoxedResult, localForResultOfInvoke);
751579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId unboxingMethodFor = getUnboxMethodForPrimitive(methodReturnType);
752579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.invokeVirtual(unboxingMethodFor, localOfMethodReturnType, aBoxedResult);
753579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.returnValue(localOfMethodReturnType);
754579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } else if (void.class.equals(methodReturnType)) {
755579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.returnVoid();
756579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } else {
75797b0be6b3da9df87e9026f880b0b0bffc7242450Jesse Wilson            code.cast(localOfMethodReturnType, localForResultOfInvoke);
758579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            code.returnValue(localOfMethodReturnType);
759579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
760579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
761579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
7621af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    private static <T> Set<T> asSet(T... array) {
763b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        return new CopyOnWriteArraySet<>(Arrays.asList(array));
7641af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson    }
7651af1da6af1f59f0bc1f9d048f31279ce5e614c3dJesse Wilson
766579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType) {
767579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return PRIMITIVE_TO_UNBOX_METHOD.get(methodReturnType);
768579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
769579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
770579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED;
771579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    static {
772b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        PRIMITIVE_TO_BOXED = new HashMap<>();
773579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class);
774579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(int.class, Integer.class);
775579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(byte.class, Byte.class);
776579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(long.class, Long.class);
777579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(short.class, Short.class);
778579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(float.class, Float.class);
779579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(double.class, Double.class);
780579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_BOXED.put(char.class, Character.class);
781579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
782579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
7830e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson    private static final Map<TypeId<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD;
784579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    static {
785b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        PRIMITIVE_TYPE_TO_UNBOX_METHOD = new HashMap<>();
786579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_TO_BOXED.entrySet()) {
7870e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?> primitiveType = TypeId.get(entry.getKey());
7880e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson            TypeId<?> boxedType = TypeId.get(entry.getValue());
789579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            MethodId<?, ?> valueOfMethod = boxedType.getMethod(boxedType, "valueOf", primitiveType);
790579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod);
791579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
792579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
793579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
794579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
795579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Map from primitive type to method used to unbox a boxed version of the primitive.
796579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>
797579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * This is required for methods whose return type is primitive, since the
798579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * {@link InvocationHandler} will return us a boxed result, and we'll need to convert it back to
799579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * primitive value.
800579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
801579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD;
802579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    static {
803b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin        Map<Class<?>, MethodId<?, ?>> map = new HashMap<>();
8040e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(boolean.class, TypeId.get(Boolean.class).getMethod(TypeId.BOOLEAN, "booleanValue"));
8050e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(int.class, TypeId.get(Integer.class).getMethod(TypeId.INT, "intValue"));
8060e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(byte.class, TypeId.get(Byte.class).getMethod(TypeId.BYTE, "byteValue"));
8070e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(long.class, TypeId.get(Long.class).getMethod(TypeId.LONG, "longValue"));
8080e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(short.class, TypeId.get(Short.class).getMethod(TypeId.SHORT, "shortValue"));
8090e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(float.class, TypeId.get(Float.class).getMethod(TypeId.FLOAT, "floatValue"));
8100e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(double.class, TypeId.get(Double.class).getMethod(TypeId.DOUBLE, "doubleValue"));
8110e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson        map.put(char.class, TypeId.get(Character.class).getMethod(TypeId.CHAR, "charValue"));
812579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        PRIMITIVE_TO_UNBOX_METHOD = map;
813579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
814579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
815579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
816579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Wrapper class to let us disambiguate {@link Method} objects.
817579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * <p>
818579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * The purpose of this class is to override the {@link #equals(Object)} and {@link #hashCode()}
819579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * methods so we can use a {@link Set} to remove duplicate methods that are overrides of one
820579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * another. For these purposes, we consider two methods to be equal if they have the same
821579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * name, return type, and parameter types.
822579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
823579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static class MethodSetEntry {
824579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final String name;
825579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final Class<?>[] paramTypes;
826579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final Class<?> returnType;
827579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        private final Method originalMethod;
828579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
829579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        public MethodSetEntry(Method method) {
830579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            originalMethod = method;
831579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            name = method.getName();
832579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            paramTypes = method.getParameterTypes();
833579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            returnType = method.getReturnType();
834579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
835579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
836579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        @Override
837579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        public boolean equals(Object o) {
838579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (o instanceof MethodSetEntry) {
839579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                MethodSetEntry other = (MethodSetEntry) o;
840579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                return name.equals(other.name)
841579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        && returnType.equals(other.returnType)
842579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        && Arrays.equals(paramTypes, other.paramTypes);
843579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
844579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return false;
845579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
846579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
847579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        @Override
848579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        public int hashCode() {
849579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int result = 17;
850579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result += 31 * result + name.hashCode();
851579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result += 31 * result + returnType.hashCode();
852579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result += 31 * result + Arrays.hashCode(paramTypes);
853579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return result;
854579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
855579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
856579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson}
857