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 * @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