ProxyBuilder.java revision 1977585657cb304a9e1ffa8a2320fa8053a7383c
1c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant/*
2c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant * Copyright (C) 2011 The Android Open Source Project
3c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant *
4c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant * Licensed under the Apache License, Version 2.0 (the "License");
5b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant * you may not use this file except in compliance with the License.
6b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant * You may obtain a copy of the License at
7c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant *
8c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant *      http://www.apache.org/licenses/LICENSE-2.0
9c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant *
10c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant * Unless required by applicable law or agreed to in writing, software
11c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant * distributed under the License is distributed on an "AS IS" BASIS,
12c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant * See the License for the specific language governing permissions and
14c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant * limitations under the License.
15c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant */
16c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant
17c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnantpackage com.google.dexmaker.stock;
18c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnant
19c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnantimport com.google.dexmaker.Code;
20c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnantimport com.google.dexmaker.Comparison;
21c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnantimport com.google.dexmaker.DexGenerator;
22c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnantimport com.google.dexmaker.FieldId;
23c52f43e72dfcea03037729649da84c23b3beb04aHoward Hinnantimport com.google.dexmaker.Label;
24import com.google.dexmaker.Local;
25import com.google.dexmaker.MethodId;
26import com.google.dexmaker.Type;
27import java.io.File;
28import java.io.IOException;
29import java.lang.reflect.Constructor;
30import java.lang.reflect.Field;
31import java.lang.reflect.InvocationHandler;
32import java.lang.reflect.InvocationTargetException;
33import java.lang.reflect.Method;
34import java.lang.reflect.Modifier;
35import static java.lang.reflect.Modifier.PRIVATE;
36import static java.lang.reflect.Modifier.PUBLIC;
37import static java.lang.reflect.Modifier.STATIC;
38import java.lang.reflect.UndeclaredThrowableException;
39import java.util.Arrays;
40import java.util.HashMap;
41import java.util.HashSet;
42import java.util.Map;
43import java.util.Set;
44
45/**
46 * Creates dynamic proxies of concrete classes.
47 * <p>
48 * This is similar to the {@code java.lang.reflect.Proxy} class, but works for classes instead of
49 * interfaces.
50 * <h3>Example</h3>
51 * The following example demonstrates the creation of a dynamic proxy for {@code java.util.Random}
52 * which will always return 4 when asked for integers, and which logs method calls to every method.
53 * <pre>
54 * InvocationHandler handler = new InvocationHandler() {
55 *     &#64;Override
56 *     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
57 *         if (method.getName().equals("nextInt")) {
58 *             // Chosen by fair dice roll, guaranteed to be random.
59 *             return 4;
60 *         }
61 *         Object result = ProxyBuilder.callSuper(proxy, method, args);
62 *         System.out.println("Method: " + method.getName() + " args: "
63 *                 + Arrays.toString(args) + " result: " + result);
64 *         return result;
65 *     }
66 * };
67 * Random debugRandom = ProxyBuilder.forClass(Random.class)
68 *         .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE))
69 *         .handler(handler)
70 *         .build();
71 * assertEquals(4, debugRandom.nextInt());
72 * debugRandom.setSeed(0);
73 * assertTrue(debugRandom.nextBoolean());
74 * </pre>
75 * <h3>Usage</h3>
76 * Call {@link #forClass(Class)} for the Class you wish to proxy. Call
77 * {@link #handler(InvocationHandler)} passing in an {@link InvocationHandler}, and then call
78 * {@link #build()}. The returned instance will be a dynamically generated subclass where all method
79 * calls will be delegated to the invocation handler, except as noted below.
80 * <p>
81 * The static method {@link #callSuper(Object, Method, Object...)} allows you to access the original
82 * super method for a given proxy. This allows the invocation handler to selectively override some
83 * methods but not others.
84 * <p>
85 * By default, the {@link #build()} method will call the no-arg constructor belonging to the class
86 * being proxied. If you wish to call a different constructor, you must provide arguments for both
87 * {@link #constructorArgTypes(Class[])} and {@link #constructorArgValues(Object[])}.
88 * <p>
89 * This process works only for classes with public and protected level of visibility.
90 * <p>
91 * You may proxy abstract classes.  You may not proxy final classes.
92 * <p>
93 * Only non-private, non-final, non-static methods will be dispatched to the invocation handler.
94 * Private, static or final methods will always call through to the superclass as normal.
95 * <p>
96 * The {@link #finalize()} method on {@code Object} will not be proxied.
97 * <p>
98 * You must provide a dex cache directory via the {@link #dexCache(File)} method. You should take
99 * care not to make this a world-writable directory, so that third parties cannot inject code into
100 * your application.  A suitable parameter for these output directories would be something like
101 * this:
102 * <pre>{@code
103 *     getApplicationContext().getDir("dx", Context.MODE_PRIVATE);
104 * }</pre>
105 * <p>
106 * If the base class to be proxied leaks the {@code this} pointer in the constructor (bad practice),
107 * that is to say calls a non-private non-final method from the constructor, the invocation handler
108 * will not be invoked.  As a simple concrete example, when proxying Random we discover that it
109 * inernally calls setSeed during the constructor.  The proxy will not intercept this call during
110 * proxy construction, but will intercept as normal afterwards.  This behaviour may be subject to
111 * change in future releases.
112 * <p>
113 * This class is <b>not thread safe</b>.
114 */
115public final class ProxyBuilder<T> {
116    private static final String FIELD_NAME_HANDLER = "$__handler";
117    private static final String FIELD_NAME_METHODS = "$__methodArray";
118
119    private final Class<T> baseClass;
120    private ClassLoader parentClassLoader = ProxyBuilder.class.getClassLoader();
121    private InvocationHandler handler;
122    private File dexCache;
123    private Class<?>[] constructorArgTypes = new Class[0];
124    private Object[] constructorArgValues = new Object[0];
125
126    private ProxyBuilder(Class<T> clazz) {
127        baseClass = clazz;
128    }
129
130    public static <T> ProxyBuilder<T> forClass(Class<T> clazz) {
131        return new ProxyBuilder<T>(clazz);
132    }
133
134    /**
135     * Specifies the parent ClassLoader to use when creating the proxy.
136     *
137     * <p>If null, {@code ProxyBuilder.class.getClassLoader()} will be used.
138     */
139    public ProxyBuilder<T> parentClassLoader(ClassLoader parent) {
140        parentClassLoader = parent;
141        return this;
142    }
143
144    public ProxyBuilder<T> handler(InvocationHandler handler) {
145        this.handler = handler;
146        return this;
147    }
148
149    public ProxyBuilder<T> dexCache(File dexCache) {
150        this.dexCache = dexCache;
151        return this;
152    }
153
154    public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) {
155        this.constructorArgValues = constructorArgValues;
156        return this;
157    }
158
159    public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) {
160        this.constructorArgTypes = constructorArgTypes;
161        return this;
162    }
163
164    /**
165     * Create a new instance of the class to proxy.
166     *
167     * @throws UnsupportedOperationException if the class we are trying to create a proxy for is
168     *     not accessible.
169     * @throws IOException if an exception occurred writing to the {@code dexCache} directory.
170     * @throws UndeclaredThrowableException if the constructor for the base class to proxy throws
171     *     a declared exception during construction.
172     * @throws IllegalArgumentException if the handler is null, if the constructor argument types
173     *     do not match the constructor argument values, or if no such constructor exists.
174     */
175    public T build() throws IOException {
176        check(handler != null, "handler == null");
177        check(constructorArgTypes.length == constructorArgValues.length,
178                "constructorArgValues.length != constructorArgTypes.length");
179        DexGenerator generator = new DexGenerator();
180        String generatedName = getMethodNameForProxyOf(baseClass);
181        Type<? extends T> generatedType = Type.get("L" + generatedName + ";");
182        Type<T> superType = Type.get(baseClass);
183        generateConstructorsAndFields(generator, generatedType, superType, baseClass);
184        Method[] methodsToProxy = getMethodsToProxy(baseClass);
185        generateCodeForAllMethods(generator, generatedType, methodsToProxy, superType);
186        generator.declare(generatedType, generatedName + ".generated", PUBLIC, superType);
187        ClassLoader classLoader = generator.load(parentClassLoader, dexCache, dexCache);
188        Class<? extends T> proxyClass;
189        try {
190            proxyClass = loadClass(classLoader, generatedName);
191        } catch (IllegalAccessError e) {
192            // Thrown when the base class is not accessible.
193            throw new UnsupportedOperationException("cannot proxy inaccessible classes", e);
194        } catch (ClassNotFoundException e) {
195            // Should not be thrown, we're sure to have generated this class.
196            throw new AssertionError(e);
197        }
198        setMethodsStaticField(proxyClass, methodsToProxy);
199        Constructor<? extends T> constructor;
200        try {
201            constructor = proxyClass.getConstructor(constructorArgTypes);
202        } catch (NoSuchMethodException e) {
203            // Thrown when the ctor to be called does not exist.
204            throw new IllegalArgumentException("could not find matching constructor", e);
205        }
206        T result;
207        try {
208            result = constructor.newInstance(constructorArgValues);
209        } catch (InstantiationException e) {
210            // Should not be thrown, generated class is not abstract.
211            throw new AssertionError(e);
212        } catch (IllegalAccessException e) {
213            // Should not be thrown, the generated constructor is accessible.
214            throw new AssertionError(e);
215        } catch (InvocationTargetException e) {
216            // Thrown when the base class ctor throws an exception.
217            throw launderCause(e);
218        }
219        setHandlerInstanceField(result, handler);
220        return result;
221    }
222
223    // The type cast is safe: the generated type will extend the base class type.
224    @SuppressWarnings("unchecked")
225    private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName)
226            throws ClassNotFoundException {
227        return (Class<? extends T>) classLoader.loadClass(generatedName);
228    }
229
230    private static RuntimeException launderCause(InvocationTargetException e) {
231        Throwable cause = e.getCause();
232        // Errors should be thrown as they are.
233        if (cause instanceof Error) {
234            throw (Error) cause;
235        }
236        // RuntimeException can be thrown as-is.
237        if (cause instanceof RuntimeException) {
238            throw (RuntimeException) cause;
239        }
240        // Declared exceptions will have to be wrapped.
241        throw new UndeclaredThrowableException(cause);
242    }
243
244    private static void setHandlerInstanceField(Object instance, InvocationHandler handler) {
245        try {
246            Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
247            handlerField.setAccessible(true);
248            handlerField.set(instance, handler);
249        } catch (NoSuchFieldException e) {
250            // Should not be thrown, generated proxy class has been generated with this field.
251            throw new AssertionError(e);
252        } catch (IllegalAccessException e) {
253            // Should not be thrown, we just set the field to accessible.
254            throw new AssertionError(e);
255        }
256    }
257
258    private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy) {
259        try {
260            Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS);
261            methodArrayField.setAccessible(true);
262            methodArrayField.set(null, methodsToProxy);
263        } catch (NoSuchFieldException e) {
264            // Should not be thrown, generated proxy class has been generated with this field.
265            throw new AssertionError(e);
266        } catch (IllegalAccessException e) {
267            // Should not be thrown, we just set the field to accessible.
268            throw new AssertionError(e);
269        }
270    }
271
272    /**
273     * Returns the proxy's {@link InvocationHandler}.
274     *
275     * @throws IllegalArgumentException if the object supplied is not a proxy created by this class.
276     */
277    public static InvocationHandler getInvocationHandler(Object instance) {
278        try {
279            Field field = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER);
280            field.setAccessible(true);
281            return (InvocationHandler) field.get(instance);
282        } catch (NoSuchFieldException e) {
283            throw new IllegalArgumentException("Not a valid proxy instance", e);
284        } catch (IllegalAccessException e) {
285            // Should not be thrown, we just set the field to accessible.
286            throw new AssertionError(e);
287        }
288    }
289
290    private static <T, G extends T> void generateCodeForAllMethods(DexGenerator generator,
291            Type<G> generatedType, Method[] methodsToProxy, Type<T> superclassType) {
292        Type<InvocationHandler> handlerType = Type.get(InvocationHandler.class);
293        Type<Method[]> methodArrayType = Type.get(Method[].class);
294        FieldId<G, InvocationHandler> handlerField =
295                generatedType.getField(handlerType, FIELD_NAME_HANDLER);
296        FieldId<G, Method[]> allMethods =
297                generatedType.getField(methodArrayType, FIELD_NAME_METHODS);
298        Type<Method> methodType = Type.get(Method.class);
299        Type<Object[]> objectArrayType = Type.get(Object[].class);
300        MethodId<InvocationHandler, Object> methodInvoke = handlerType.getMethod(Type.OBJECT,
301                "invoke", Type.OBJECT, methodType, objectArrayType);
302        for (int m = 0; m < methodsToProxy.length; ++m) {
303            /*
304             * If the 5th method on the superclass Example that can be overridden were to look like
305             * this:
306             *
307             *     public int doSomething(Bar param0, int param1) {
308             *         ...
309             *     }
310             *
311             * Then the following code will generate a method on the proxy that looks something
312             * like this:
313             *
314             *     public int doSomething(Bar param0, int param1) {
315             *         int methodIndex = 4;
316             *         Method[] allMethods = Example_Proxy.$__methodArray;
317             *         Method thisMethod = allMethods[methodIndex];
318             *         int argsLength = 2;
319             *         Object[] args = new Object[argsLength];
320             *         InvocationHandler localHandler = this.$__handler;
321             *         // for-loop begins
322             *         int p = 0;
323             *         Bar parameter0 = param0;
324             *         args[p] = parameter0;
325             *         p = 1;
326             *         int parameter1 = param1;
327             *         Integer boxed1 = Integer.valueOf(parameter1);
328             *         args[p] = boxed1;
329             *         // for-loop ends
330             *         Object result = localHandler.invoke(this, thisMethod, args);
331             *         Integer castResult = (Integer) result;
332             *         int unboxedResult = castResult.intValue();
333             *         return unboxedResult;
334             *     }
335             *
336             * Or, in more idiomatic Java:
337             *
338             *     public int doSomething(Bar param0, int param1) {
339             *         if ($__handler == null) {
340             *             return super.doSomething(param0, param1);
341             *         }
342             *         return __handler.invoke(this, __methodArray[4],
343             *                 new Object[] { param0, Integer.valueOf(param1) });
344             *     }
345             */
346            Method method = methodsToProxy[m];
347            String name = method.getName();
348            Class<?>[] argClasses = method.getParameterTypes();
349            Type<?>[] argTypes = new Type<?>[argClasses.length];
350            for (int i = 0; i < argTypes.length; ++i) {
351                argTypes[i] = Type.get(argClasses[i]);
352            }
353            Class<?> returnType = method.getReturnType();
354            Type<?> resultType = Type.get(returnType);
355            MethodId<T, ?> superMethod = superclassType.getMethod(resultType, name, argTypes);
356            MethodId<?, ?> methodId = generatedType.getMethod(resultType, name, argTypes);
357            Code code = generator.declare(methodId, PUBLIC);
358            Local<G> localThis = code.getThis(generatedType);
359            Local<InvocationHandler> localHandler = code.newLocal(handlerType);
360            Local<Object> invokeResult = code.newLocal(Type.OBJECT);
361            Local<Integer> intValue = code.newLocal(Type.INT);
362            Local<Object[]> args = code.newLocal(objectArrayType);
363            Local<Integer> argsLength = code.newLocal(Type.INT);
364            Local<Object> temp = code.newLocal(Type.OBJECT);
365            Local<?> resultHolder = code.newLocal(resultType);
366            Local<Method[]> methodArray = code.newLocal(methodArrayType);
367            Local<Method> thisMethod = code.newLocal(methodType);
368            Local<Integer> methodIndex = code.newLocal(Type.INT);
369            Class<?> aBoxedClass = PRIMITIVE_TO_BOXED.get(returnType);
370            Local<?> aBoxedResult = null;
371            if (aBoxedClass != null) {
372                aBoxedResult = code.newLocal(Type.get(aBoxedClass));
373            }
374            Local<?>[] superArgs2 = new Local<?>[argClasses.length];
375            Local<?> superResult2 = code.newLocal(resultType);
376            Local<InvocationHandler> nullHandler = code.newLocal(handlerType);
377
378            code.loadConstant(methodIndex, m);
379            code.sget(allMethods, methodArray);
380            code.aget(methodArray, methodIndex, thisMethod);
381            code.loadConstant(argsLength, argTypes.length);
382            code.newArray(argsLength, args);
383            code.iget(handlerField, localThis, localHandler);
384
385            // if (proxy == null)
386            code.loadConstant(nullHandler, null);
387            Label handlerNullCase = code.newLabel();
388            code.compare(Comparison.EQ, nullHandler, localHandler, handlerNullCase);
389
390            // This code is what we execute when we have a valid proxy: delegate to invocation
391            // handler.
392            for (int p = 0; p < argTypes.length; ++p) {
393                code.loadConstant(intValue, p);
394                Local<?> parameter = code.getParameter(p, argTypes[p]);
395                Local<?> unboxedIfNecessary = boxIfRequired(code, parameter, temp);
396                code.aput(args, intValue, unboxedIfNecessary);
397            }
398            code.invokeInterface(methodInvoke, invokeResult, localHandler,
399                    localThis, thisMethod, args);
400            generateCodeForReturnStatement(code, returnType, invokeResult, resultHolder,
401                    aBoxedResult);
402
403            // This code is executed if proxy is null: call the original super method.
404            // This is required to handle the case of construction of an object which leaks the
405            // "this" pointer.
406            code.mark(handlerNullCase);
407            for (int i = 0; i < superArgs2.length; ++i) {
408                superArgs2[i] = code.getParameter(i, argTypes[i]);
409            }
410            if (void.class.equals(returnType)) {
411                code.invokeSuper(superMethod, null, localThis, superArgs2);
412                code.returnVoid();
413            } else {
414                invokeSuper(superMethod, code, localThis, superArgs2, superResult2);
415                code.returnValue(superResult2);
416            }
417
418            /*
419             * And to allow calling the original super method, the following is also generated:
420             *
421             *     public int super_doSomething(Bar param0, int param1) {
422             *          int result = super.doSomething(param0, param1);
423             *          return result;
424             *     }
425             */
426            String superName = "super_" + name;
427            MethodId<G, ?> callsSuperMethod = generatedType.getMethod(
428                    resultType, superName, argTypes);
429            Code superCode = generator.declare(callsSuperMethod, PUBLIC);
430            Local<G> superThis = superCode.getThis(generatedType);
431            Local<?>[] superArgs = new Local<?>[argClasses.length];
432            for (int i = 0; i < superArgs.length; ++i) {
433                superArgs[i] = superCode.getParameter(i, argTypes[i]);
434            }
435            if (void.class.equals(returnType)) {
436                superCode.invokeSuper(superMethod, null, superThis, superArgs);
437                superCode.returnVoid();
438            } else {
439                Local<?> superResult = superCode.newLocal(resultType);
440                invokeSuper(superMethod, superCode, superThis, superArgs, superResult);
441                superCode.returnValue(superResult);
442            }
443        }
444    }
445
446    @SuppressWarnings({"unchecked", "rawtypes"})
447    private static void invokeSuper(MethodId superMethod, Code superCode,
448            Local superThis, Local[] superArgs, Local superResult) {
449        superCode.invokeSuper(superMethod, superResult, superThis, superArgs);
450    }
451
452    private static Local<?> boxIfRequired(Code code, Local<?> parameter, Local<Object> temp) {
453        MethodId<?, ?> unboxMethod = PRIMITIVE_TYPE_TO_UNBOX_METHOD.get(parameter.getType());
454        if (unboxMethod == null) {
455            return parameter;
456        }
457        code.invokeStatic(unboxMethod, temp, parameter);
458        return temp;
459    }
460
461    public static Object callSuper(Object proxy, Method method, Object... args)
462            throws SecurityException, IllegalAccessException,
463            InvocationTargetException, NoSuchMethodException {
464        return proxy.getClass()
465                .getMethod("super_" + method.getName(), method.getParameterTypes())
466                .invoke(proxy, args);
467    }
468
469    private static void check(boolean condition, String message) {
470        if (!condition) {
471            throw new IllegalArgumentException(message);
472        }
473    }
474
475    private static <T, G extends T> void generateConstructorsAndFields(DexGenerator generator,
476            Type<G> generatedType, Type<T> superType, Class<T> superClass) {
477        Type<InvocationHandler> handlerType = Type.get(InvocationHandler.class);
478        Type<Method[]> methodArrayType = Type.get(Method[].class);
479        FieldId<G, InvocationHandler> handlerField = generatedType.getField(
480                handlerType, FIELD_NAME_HANDLER);
481        generator.declare(handlerField, PRIVATE, null);
482        FieldId<G, Method[]> allMethods = generatedType.getField(
483                methodArrayType, FIELD_NAME_METHODS);
484        generator.declare(allMethods, PRIVATE | STATIC, null);
485        for (Constructor<T> constructor : getConstructorsToOverwrite(superClass)) {
486            if (constructor.getModifiers() == Modifier.FINAL) {
487                continue;
488            }
489            Type<?>[] types = classArrayToTypeArray(constructor.getParameterTypes());
490            MethodId<?, ?> method = generatedType.getConstructor(types);
491            Code constructorCode = generator.declareConstructor(method, PUBLIC);
492            Local<G> thisRef = constructorCode.getThis(generatedType);
493            Local<?>[] params = new Local[types.length];
494            for (int i = 0; i < params.length; ++i) {
495                params[i] = constructorCode.getParameter(i, types[i]);
496            }
497            MethodId<T, ?> superConstructor = superType.getConstructor(types);
498            constructorCode.invokeDirect(superConstructor, null, thisRef, params);
499            constructorCode.returnVoid();
500        }
501    }
502
503    // The type parameter on Constructor is the class in which the constructor is declared.
504    // The getDeclaredConstructors() method gets constructors declared only in the given class,
505    // hence this cast is safe.
506    @SuppressWarnings("unchecked")
507    private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) {
508        return (Constructor<T>[]) clazz.getDeclaredConstructors();
509    }
510
511    /**
512     * Gets all {@link Method} objects we can proxy in the hierarchy of the supplied class.
513     */
514    private static <T> Method[] getMethodsToProxy(Class<T> clazz) {
515        Set<MethodSetEntry> methodsToProxy = new HashSet<MethodSetEntry>();
516        for (Class<?> current = clazz; current != null; current = current.getSuperclass()) {
517            for (Method method : current.getDeclaredMethods()) {
518                if ((method.getModifiers() & Modifier.FINAL) != 0) {
519                    // Skip final methods, we can't override them.
520                    continue;
521                }
522                if ((method.getModifiers() & STATIC) != 0) {
523                    // Skip static methods, overriding them has no effect.
524                    continue;
525                }
526                if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) {
527                    // Skip finalize method, it's likely important that it execute as normal.
528                    continue;
529                }
530                methodsToProxy.add(new MethodSetEntry(method));
531            }
532        }
533        Method[] results = new Method[methodsToProxy.size()];
534        int i = 0;
535        for (MethodSetEntry entry : methodsToProxy) {
536            results[i++] = entry.originalMethod;
537        }
538        return results;
539    }
540
541    private static <T> String getMethodNameForProxyOf(Class<T> clazz) {
542        return clazz.getSimpleName() + "_Proxy";
543    }
544
545    private static Type<?>[] classArrayToTypeArray(Class<?>[] input) {
546        Type<?>[] result = new Type[input.length];
547        for (int i = 0; i < input.length; ++i) {
548            result[i] = Type.get(input[i]);
549        }
550        return result;
551    }
552
553    /**
554     * Calculates the correct return statement code for a method.
555     * <p>
556     * A void method will not return anything.  A method that returns a primitive will need to
557     * unbox the boxed result.  Otherwise we will cast the result.
558     */
559    // This one is tricky to fix, I gave up.
560    @SuppressWarnings({ "rawtypes", "unchecked" })
561    private static void generateCodeForReturnStatement(Code code, Class methodReturnType,
562            Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult) {
563        if (PRIMITIVE_TO_UNBOX_METHOD.containsKey(methodReturnType)) {
564            code.typeCast(localForResultOfInvoke, aBoxedResult);
565            MethodId unboxingMethodFor = getUnboxMethodForPrimitive(methodReturnType);
566            code.invokeVirtual(unboxingMethodFor, localOfMethodReturnType, aBoxedResult);
567            code.returnValue(localOfMethodReturnType);
568        } else if (void.class.equals(methodReturnType)) {
569            code.returnVoid();
570        } else {
571            code.typeCast(localForResultOfInvoke, localOfMethodReturnType);
572            code.returnValue(localOfMethodReturnType);
573        }
574    }
575
576    private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType) {
577        return PRIMITIVE_TO_UNBOX_METHOD.get(methodReturnType);
578    }
579
580    private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED;
581    static {
582        PRIMITIVE_TO_BOXED = new HashMap<Class<?>, Class<?>>();
583        PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class);
584        PRIMITIVE_TO_BOXED.put(int.class, Integer.class);
585        PRIMITIVE_TO_BOXED.put(byte.class, Byte.class);
586        PRIMITIVE_TO_BOXED.put(long.class, Long.class);
587        PRIMITIVE_TO_BOXED.put(short.class, Short.class);
588        PRIMITIVE_TO_BOXED.put(float.class, Float.class);
589        PRIMITIVE_TO_BOXED.put(double.class, Double.class);
590        PRIMITIVE_TO_BOXED.put(char.class, Character.class);
591    }
592
593    private static final Map<Type<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD;
594    static {
595        PRIMITIVE_TYPE_TO_UNBOX_METHOD = new HashMap<Type<?>, MethodId<?, ?>>();
596        for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_TO_BOXED.entrySet()) {
597            Type<?> primitiveType = Type.get(entry.getKey());
598            Type<?> boxedType = Type.get(entry.getValue());
599            MethodId<?, ?> valueOfMethod = boxedType.getMethod(boxedType, "valueOf", primitiveType);
600            PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod);
601        }
602    }
603
604    /**
605     * Map from primitive type to method used to unbox a boxed version of the primitive.
606     * <p>
607     * This is required for methods whose return type is primitive, since the
608     * {@link InvocationHandler} will return us a boxed result, and we'll need to convert it back to
609     * primitive value.
610     */
611    private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD;
612    static {
613        Map<Class<?>, MethodId<?, ?>> map = new HashMap<Class<?>, MethodId<?, ?>>();
614        map.put(boolean.class, Type.get(Boolean.class).getMethod(Type.BOOLEAN, "booleanValue"));
615        map.put(int.class, Type.get(Integer.class).getMethod(Type.INT, "intValue"));
616        map.put(byte.class, Type.get(Byte.class).getMethod(Type.BYTE, "byteValue"));
617        map.put(long.class, Type.get(Long.class).getMethod(Type.LONG, "longValue"));
618        map.put(short.class, Type.get(Short.class).getMethod(Type.SHORT, "shortValue"));
619        map.put(float.class, Type.get(Float.class).getMethod(Type.FLOAT, "floatValue"));
620        map.put(double.class, Type.get(Double.class).getMethod(Type.DOUBLE, "doubleValue"));
621        map.put(char.class, Type.get(Character.class).getMethod(Type.CHAR, "charValue"));
622        PRIMITIVE_TO_UNBOX_METHOD = map;
623    }
624
625    /**
626     * Wrapper class to let us disambiguate {@link Method} objects.
627     * <p>
628     * The purpose of this class is to override the {@link #equals(Object)} and {@link #hashCode()}
629     * methods so we can use a {@link Set} to remove duplicate methods that are overrides of one
630     * another. For these purposes, we consider two methods to be equal if they have the same
631     * name, return type, and parameter types.
632     */
633    private static class MethodSetEntry {
634        private final String name;
635        private final Class<?>[] paramTypes;
636        private final Class<?> returnType;
637        private final Method originalMethod;
638
639        public MethodSetEntry(Method method) {
640            originalMethod = method;
641            name = method.getName();
642            paramTypes = method.getParameterTypes();
643            returnType = method.getReturnType();
644        }
645
646        @Override
647        public boolean equals(Object o) {
648            if (o instanceof MethodSetEntry) {
649                MethodSetEntry other = (MethodSetEntry) o;
650                return name.equals(other.name)
651                        && returnType.equals(other.returnType)
652                        && Arrays.equals(paramTypes, other.paramTypes);
653            }
654            return false;
655        }
656
657        @Override
658        public int hashCode() {
659            int result = 17;
660            result += 31 * result + name.hashCode();
661            result += 31 * result + returnType.hashCode();
662            result += 31 * result + Arrays.hashCode(paramTypes);
663            return result;
664        }
665    }
666}
667