diff options
Diffstat (limited to 'dx/src/com/android/dx/gen/ProxyBuilder.java')
-rw-r--r-- | dx/src/com/android/dx/gen/ProxyBuilder.java | 667 |
1 files changed, 0 insertions, 667 deletions
diff --git a/dx/src/com/android/dx/gen/ProxyBuilder.java b/dx/src/com/android/dx/gen/ProxyBuilder.java deleted file mode 100644 index 852fe666f..000000000 --- a/dx/src/com/android/dx/gen/ProxyBuilder.java +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.dx.gen; - -import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR; -import static com.android.dx.rop.code.AccessFlags.ACC_PRIVATE; -import static com.android.dx.rop.code.AccessFlags.ACC_PUBLIC; -import static com.android.dx.rop.code.AccessFlags.ACC_STATIC; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Creates dynamic proxies of concrete classes. - * <p> - * This is similar to the {@code java.lang.reflect.Proxy} class, but works for classes instead of - * interfaces. - * <h3>Example</h3> - * The following example demonstrates the creation of a dynamic proxy for {@code java.util.Random} - * which will always return 4 when asked for integers, and which logs method calls to every method. - * <pre> - * InvocationHandler handler = new InvocationHandler() { - * @Override - * public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - * if (method.getName().equals("nextInt")) { - * // Chosen by fair dice roll, guaranteed to be random. - * return 4; - * } - * Object result = ProxyBuilder.callSuper(proxy, method, args); - * System.out.println("Method: " + method.getName() + " args: " - * + Arrays.toString(args) + " result: " + result); - * return result; - * } - * }; - * Random debugRandom = ProxyBuilder.forClass(Random.class) - * .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE)) - * .handler(handler) - * .build(); - * assertEquals(4, debugRandom.nextInt()); - * debugRandom.setSeed(0); - * assertTrue(debugRandom.nextBoolean()); - * </pre> - * <h3>Usage</h3> - * Call {@link #forClass(Class)} for the Class you wish to proxy. Call - * {@link #handler(InvocationHandler)} passing in an {@link InvocationHandler}, and then call - * {@link #build()}. The returned instance will be a dynamically generated subclass where all method - * calls will be delegated to the invocation handler, except as noted below. - * <p> - * The static method {@link #callSuper(Object, Method, Object...)} allows you to access the original - * super method for a given proxy. This allows the invocation handler to selectively override some - * methods but not others. - * <p> - * By default, the {@link #build()} method will call the no-arg constructor belonging to the class - * being proxied. If you wish to call a different constructor, you must provide arguments for both - * {@link #constructorArgTypes(Class[])} and {@link #constructorArgValues(Object[])}. - * <p> - * This process works only for classes with public and protected level of visibility. - * <p> - * You may proxy abstract classes. You may not proxy final classes. - * <p> - * Only non-private, non-final, non-static methods will be dispatched to the invocation handler. - * Private, static or final methods will always call through to the superclass as normal. - * <p> - * The {@link #finalize()} method on {@code Object} will not be proxied. - * <p> - * You must provide a dex cache directory via the {@link #dexCache(File)} method. You should take - * care not to make this a world-writable directory, so that third parties cannot inject code into - * your application. A suitable parameter for these output directories would be something like - * this: - * <pre>{@code - * getApplicationContext().getDir("dx", Context.MODE_PRIVATE); - * }</pre> - * <p> - * If the base class to be proxied leaks the {@code this} pointer in the constructor (bad practice), - * that is to say calls a non-private non-final method from the constructor, the invocation handler - * will not be invoked. As a simple concrete example, when proxying Random we discover that it - * inernally calls setSeed during the constructor. The proxy will not intercept this call during - * proxy construction, but will intercept as normal afterwards. This behaviour may be subject to - * change in future releases. - * <p> - * This class is <b>not thread safe</b>. - */ -public final class ProxyBuilder<T> { - private static final String FIELD_NAME_HANDLER = "$__handler"; - private static final String FIELD_NAME_METHODS = "$__methodArray"; - - private final Class<T> baseClass; - private ClassLoader parentClassLoader = ProxyBuilder.class.getClassLoader(); - private InvocationHandler handler; - private File dexCache; - private Class<?>[] constructorArgTypes = new Class[0]; - private Object[] constructorArgValues = new Object[0]; - - private ProxyBuilder(Class<T> clazz) { - baseClass = clazz; - } - - public static <T> ProxyBuilder<T> forClass(Class<T> clazz) { - return new ProxyBuilder<T>(clazz); - } - - /** - * Specifies the parent ClassLoader to use when creating the proxy. - * - * <p>If null, {@code ProxyBuilder.class.getClassLoader()} will be used. - */ - public ProxyBuilder<T> parentClassLoader(ClassLoader parent) { - parentClassLoader = parent; - return this; - } - - public ProxyBuilder<T> handler(InvocationHandler handler) { - this.handler = handler; - return this; - } - - public ProxyBuilder<T> dexCache(File dexCache) { - this.dexCache = dexCache; - return this; - } - - public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) { - this.constructorArgValues = constructorArgValues; - return this; - } - - public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) { - this.constructorArgTypes = constructorArgTypes; - return this; - } - - /** - * Create a new instance of the class to proxy. - * - * @throws UnsupportedOperationException if the class we are trying to create a proxy for is - * not accessible. - * @throws DexCacheException if an exception occurred writing to the {@code dexCache} directory. - * @throws UndeclaredThrowableException if the constructor for the base class to proxy throws - * a declared exception during construction. - * @throws IllegalArgumentException if the handler is null, if the constructor argument types - * do not match the constructor argument values, or if no such constructor exists. - */ - public T build() { - check(handler != null, "handler == null"); - check(constructorArgTypes.length == constructorArgValues.length, - "constructorArgValues.length != constructorArgTypes.length"); - DexGenerator generator = new DexGenerator(); - String generatedName = getMethodNameForProxyOf(baseClass); - Type<? extends T> generatedType = Type.get("L" + generatedName + ";"); - Type<T> superType = Type.get(baseClass); - generateConstructorsAndFields(generator, generatedType, superType, baseClass); - Method[] methodsToProxy = getMethodsToProxy(baseClass); - generateCodeForAllMethods(generator, generatedType, methodsToProxy, superType); - generator.declare(generatedType, generatedName + ".generated", ACC_PUBLIC, superType); - ClassLoader classLoader; - try { - classLoader = generator.load(parentClassLoader, dexCache, dexCache); - } catch (IOException e) { - throw new DexCacheException(e); - } - Class<? extends T> proxyClass; - try { - proxyClass = loadClass(classLoader, generatedName); - } catch (IllegalAccessError e) { - // Thrown when the base class is not accessible. - throw new UnsupportedOperationException("cannot proxy inaccessible classes", e); - } catch (ClassNotFoundException e) { - // Should not be thrown, we're sure to have generated this class. - throw new AssertionError(e); - } - setMethodsStaticField(proxyClass, methodsToProxy); - Constructor<? extends T> constructor; - try { - constructor = proxyClass.getConstructor(constructorArgTypes); - } catch (NoSuchMethodException e) { - // Thrown when the ctor to be called does not exist. - throw new IllegalArgumentException("could not find matching constructor", e); - } - T result; - try { - result = constructor.newInstance(constructorArgValues); - } catch (InstantiationException e) { - // Should not be thrown, generated class is not abstract. - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // Should not be thrown, the generated constructor is accessible. - throw new AssertionError(e); - } catch (InvocationTargetException e) { - // Thrown when the base class ctor throws an exception. - throw launderCause(e); - } - setHandlerInstanceField(result, handler); - return result; - } - - // The type cast is safe: the generated type will extend the base class type. - @SuppressWarnings("unchecked") - private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName) - throws ClassNotFoundException { - return (Class<? extends T>) classLoader.loadClass(generatedName); - } - - private static RuntimeException launderCause(InvocationTargetException e) { - Throwable cause = e.getCause(); - // Errors should be thrown as they are. - if (cause instanceof Error) { - throw (Error) cause; - } - // RuntimeException can be thrown as-is. - if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } - // Declared exceptions will have to be wrapped. - throw new UndeclaredThrowableException(cause); - } - - private static void setHandlerInstanceField(Object instance, InvocationHandler handler) { - try { - Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); - handlerField.setAccessible(true); - handlerField.set(instance, handler); - } catch (NoSuchFieldException e) { - // Should not be thrown, generated proxy class has been generated with this field. - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // Should not be thrown, we just set the field to accessible. - throw new AssertionError(e); - } - } - - private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy) { - try { - Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS); - methodArrayField.setAccessible(true); - methodArrayField.set(null, methodsToProxy); - } catch (NoSuchFieldException e) { - // Should not be thrown, generated proxy class has been generated with this field. - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // Should not be thrown, we just set the field to accessible. - throw new AssertionError(e); - } - } - - /** - * Returns the proxy's {@link InvocationHandler}. - * - * @throws IllegalArgumentException if the object supplied is not a proxy created by this class. - */ - public static InvocationHandler getInvocationHandler(Object instance) { - try { - Field field = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); - field.setAccessible(true); - return (InvocationHandler) field.get(instance); - } catch (NoSuchFieldException e) { - throw new IllegalArgumentException("Not a valid proxy instance", e); - } catch (IllegalAccessException e) { - // Should not be thrown, we just set the field to accessible. - throw new AssertionError(e); - } - } - - private static <T, G extends T> void generateCodeForAllMethods(DexGenerator generator, - Type<G> generatedType, Method[] methodsToProxy, Type<T> superclassType) { - Type<InvocationHandler> handlerType = Type.get(InvocationHandler.class); - Type<Method[]> methodArrayType = Type.get(Method[].class); - FieldId<G, InvocationHandler> handlerField = - generatedType.getField(handlerType, FIELD_NAME_HANDLER); - FieldId<G, Method[]> allMethods = - generatedType.getField(methodArrayType, FIELD_NAME_METHODS); - Type<Method> methodType = Type.get(Method.class); - Type<Object[]> objectArrayType = Type.get(Object[].class); - MethodId<InvocationHandler, Object> methodInvoke = handlerType.getMethod(Type.OBJECT, - "invoke", Type.OBJECT, methodType, objectArrayType); - for (int m = 0; m < methodsToProxy.length; ++m) { - /* - * If the 5th method on the superclass Example that can be overridden were to look like - * this: - * - * public int doSomething(Bar param0, int param1) { - * ... - * } - * - * Then the following code will generate a method on the proxy that looks something - * like this: - * - * public int doSomething(Bar param0, int param1) { - * int methodIndex = 4; - * Method[] allMethods = Example_Proxy.$__methodArray; - * Method thisMethod = allMethods[methodIndex]; - * int argsLength = 2; - * Object[] args = new Object[argsLength]; - * InvocationHandler localHandler = this.$__handler; - * // for-loop begins - * int p = 0; - * Bar parameter0 = param0; - * args[p] = parameter0; - * p = 1; - * int parameter1 = param1; - * Integer boxed1 = Integer.valueOf(parameter1); - * args[p] = boxed1; - * // for-loop ends - * Object result = localHandler.invoke(this, thisMethod, args); - * Integer castResult = (Integer) result; - * int unboxedResult = castResult.intValue(); - * return unboxedResult; - * } - * - * Or, in more idiomatic Java: - * - * public int doSomething(Bar param0, int param1) { - * if ($__handler == null) { - * return super.doSomething(param0, param1); - * } - * return __handler.invoke(this, __methodArray[4], - * new Object[] { param0, Integer.valueOf(param1) }); - * } - */ - Method method = methodsToProxy[m]; - String name = method.getName(); - Class<?>[] argClasses = method.getParameterTypes(); - Type<?>[] argTypes = new Type<?>[argClasses.length]; - for (int i = 0; i < argTypes.length; ++i) { - argTypes[i] = Type.get(argClasses[i]); - } - Class<?> returnType = method.getReturnType(); - Type<?> resultType = Type.get(returnType); - MethodId<T, ?> superMethod = superclassType.getMethod(resultType, name, argTypes); - MethodId<?, ?> methodId = generatedType.getMethod(resultType, name, argTypes); - Code code = generator.declare(methodId, ACC_PUBLIC); - Local<G> localThis = code.getThis(generatedType); - Local<InvocationHandler> localHandler = code.newLocal(handlerType); - Local<Object> invokeResult = code.newLocal(Type.OBJECT); - Local<Integer> intValue = code.newLocal(Type.INT); - Local<Object[]> args = code.newLocal(objectArrayType); - Local<Integer> argsLength = code.newLocal(Type.INT); - Local<Object> temp = code.newLocal(Type.OBJECT); - Local<?> resultHolder = code.newLocal(resultType); - Local<Method[]> methodArray = code.newLocal(methodArrayType); - Local<Method> thisMethod = code.newLocal(methodType); - Local<Integer> methodIndex = code.newLocal(Type.INT); - Class<?> aBoxedClass = PRIMITIVE_TO_BOXED.get(returnType); - Local<?> aBoxedResult = null; - if (aBoxedClass != null) { - aBoxedResult = code.newLocal(Type.get(aBoxedClass)); - } - Local<?>[] superArgs2 = new Local<?>[argClasses.length]; - Local<?> superResult2 = code.newLocal(resultType); - Local<InvocationHandler> nullHandler = code.newLocal(handlerType); - - code.loadConstant(methodIndex, m); - code.sget(allMethods, methodArray); - code.aget(methodArray, methodIndex, thisMethod); - code.loadConstant(argsLength, argTypes.length); - code.newArray(argsLength, args); - code.iget(handlerField, localThis, localHandler); - - // if (proxy == null) - code.loadConstant(nullHandler, null); - Label handlerNullCase = code.newLabel(); - code.compare(Comparison.EQ, nullHandler, localHandler, handlerNullCase); - - // This code is what we execute when we have a valid proxy: delegate to invocation - // handler. - for (int p = 0; p < argTypes.length; ++p) { - code.loadConstant(intValue, p); - Local<?> parameter = code.getParameter(p, argTypes[p]); - Local<?> unboxedIfNecessary = boxIfRequired(generator, code, parameter, temp); - code.aput(args, intValue, unboxedIfNecessary); - } - code.invokeInterface(methodInvoke, invokeResult, localHandler, - localThis, thisMethod, args); - generateCodeForReturnStatement(code, returnType, invokeResult, resultHolder, - aBoxedResult); - - // This code is executed if proxy is null: call the original super method. - // This is required to handle the case of construction of an object which leaks the - // "this" pointer. - code.mark(handlerNullCase); - for (int i = 0; i < superArgs2.length; ++i) { - superArgs2[i] = code.getParameter(i, argTypes[i]); - } - if (void.class.equals(returnType)) { - code.invokeSuper(superMethod, null, localThis, superArgs2); - code.returnVoid(); - } else { - invokeSuper(superMethod, code, localThis, superArgs2, superResult2); - code.returnValue(superResult2); - } - - /* - * And to allow calling the original super method, the following is also generated: - * - * public int super_doSomething(Bar param0, int param1) { - * int result = super.doSomething(param0, param1); - * return result; - * } - */ - String superName = "super_" + name; - MethodId<G, ?> callsSuperMethod = generatedType.getMethod( - resultType, superName, argTypes); - Code superCode = generator.declare(callsSuperMethod, ACC_PUBLIC); - Local<G> superThis = superCode.getThis(generatedType); - Local<?>[] superArgs = new Local<?>[argClasses.length]; - for (int i = 0; i < superArgs.length; ++i) { - superArgs[i] = superCode.getParameter(i, argTypes[i]); - } - if (void.class.equals(returnType)) { - superCode.invokeSuper(superMethod, null, superThis, superArgs); - superCode.returnVoid(); - } else { - Local<?> superResult = superCode.newLocal(resultType); - invokeSuper(superMethod, superCode, superThis, superArgs, superResult); - superCode.returnValue(superResult); - } - } - } - - // This one is tricky to fix, I gave up. - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static <T> void invokeSuper(MethodId superMethod, Code superCode, - Local superThis, Local[] superArgs, Local superResult) { - superCode.invokeSuper(superMethod, superResult, superThis, superArgs); - } - - private static Local<?> boxIfRequired(DexGenerator generator, Code code, Local<?> parameter, - Local<Object> temp) { - MethodId<?, ?> unboxMethod = PRIMITIVE_TYPE_TO_UNBOX_METHOD.get(parameter.getType()); - if (unboxMethod == null) { - return parameter; - } - code.invokeStatic(unboxMethod, temp, parameter); - return temp; - } - - public static Object callSuper(Object proxy, Method method, Object... args) - throws SecurityException, IllegalAccessException, - InvocationTargetException, NoSuchMethodException { - return proxy.getClass() - .getMethod("super_" + method.getName(), method.getParameterTypes()) - .invoke(proxy, args); - } - - private static void check(boolean condition, String message) { - if (!condition) { - throw new IllegalArgumentException(message); - } - } - - private static <T, G extends T> void generateConstructorsAndFields(DexGenerator generator, - Type<G> generatedType, Type<T> superType, Class<T> superClass) { - Type<InvocationHandler> handlerType = Type.get(InvocationHandler.class); - Type<Method[]> methodArrayType = Type.get(Method[].class); - FieldId<G, InvocationHandler> handlerField = generatedType.getField( - handlerType, FIELD_NAME_HANDLER); - generator.declare(handlerField, ACC_PRIVATE, null); - FieldId<G, Method[]> allMethods = generatedType.getField( - methodArrayType, FIELD_NAME_METHODS); - generator.declare(allMethods, ACC_PRIVATE | ACC_STATIC, null); - for (Constructor<T> constructor : getConstructorsToOverwrite(superClass)) { - if (constructor.getModifiers() == Modifier.FINAL) { - continue; - } - Type<?>[] types = classArrayToTypeArray(constructor.getParameterTypes()); - MethodId<?, ?> method = generatedType.getConstructor(types); - Code constructorCode = generator.declare(method, ACC_PUBLIC | ACC_CONSTRUCTOR); - Local<G> thisRef = constructorCode.getThis(generatedType); - Local<?>[] params = new Local[types.length]; - for (int i = 0; i < params.length; ++i) { - params[i] = constructorCode.getParameter(i, types[i]); - } - MethodId<T, ?> superConstructor = superType.getConstructor(types); - constructorCode.invokeDirect(superConstructor, null, thisRef, params); - constructorCode.returnVoid(); - } - } - - // The type parameter on Constructor is the class in which the constructor is declared. - // The getDeclaredConstructors() method gets constructors declared only in the given class, - // hence this cast is safe. - @SuppressWarnings("unchecked") - private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) { - return (Constructor<T>[]) clazz.getDeclaredConstructors(); - } - - /** - * Gets all {@link Method} objects we can proxy in the hierarchy of the supplied class. - */ - private static <T> Method[] getMethodsToProxy(Class<T> clazz) { - Set<MethodSetEntry> methodsToProxy = new HashSet<MethodSetEntry>(); - for (Class<?> current = clazz; current != null; current = current.getSuperclass()) { - for (Method method : current.getDeclaredMethods()) { - if ((method.getModifiers() & Modifier.FINAL) != 0) { - // Skip final methods, we can't override them. - continue; - } - if ((method.getModifiers() & Modifier.STATIC) != 0) { - // Skip static methods, overriding them has no effect. - continue; - } - if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) { - // Skip finalize method, it's likely important that it execute as normal. - continue; - } - methodsToProxy.add(new MethodSetEntry(method)); - } - } - Method[] results = new Method[methodsToProxy.size()]; - int i = 0; - for (MethodSetEntry entry : methodsToProxy) { - results[i++] = entry.originalMethod; - } - return results; - } - - private static <T> String getMethodNameForProxyOf(Class<T> clazz) { - return clazz.getSimpleName() + "_Proxy"; - } - - private static Type<?>[] classArrayToTypeArray(Class<?>[] input) { - Type<?>[] result = new Type[input.length]; - for (int i = 0; i < input.length; ++i) { - result[i] = Type.get(input[i]); - } - return result; - } - - /** - * Calculates the correct return statement code for a method. - * <p> - * A void method will not return anything. A method that returns a primitive will need to - * unbox the boxed result. Otherwise we will cast the result. - */ - // This one is tricky to fix, I gave up. - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static void generateCodeForReturnStatement(Code code, Class methodReturnType, - Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult) { - if (PRIMITIVE_TO_UNBOX_METHOD.containsKey(methodReturnType)) { - code.typeCast(localForResultOfInvoke, aBoxedResult); - MethodId unboxingMethodFor = getUnboxMethodForPrimitive(methodReturnType); - code.invokeVirtual(unboxingMethodFor, localOfMethodReturnType, aBoxedResult); - code.returnValue(localOfMethodReturnType); - } else if (void.class.equals(methodReturnType)) { - code.returnVoid(); - } else { - code.typeCast(localForResultOfInvoke, localOfMethodReturnType); - code.returnValue(localOfMethodReturnType); - } - } - - private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType) { - return PRIMITIVE_TO_UNBOX_METHOD.get(methodReturnType); - } - - private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED; - static { - PRIMITIVE_TO_BOXED = new HashMap<Class<?>, Class<?>>(); - PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class); - PRIMITIVE_TO_BOXED.put(int.class, Integer.class); - PRIMITIVE_TO_BOXED.put(byte.class, Byte.class); - PRIMITIVE_TO_BOXED.put(long.class, Long.class); - PRIMITIVE_TO_BOXED.put(short.class, Short.class); - PRIMITIVE_TO_BOXED.put(float.class, Float.class); - PRIMITIVE_TO_BOXED.put(double.class, Double.class); - PRIMITIVE_TO_BOXED.put(char.class, Character.class); - } - - private static final Map<Type<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD; - static { - PRIMITIVE_TYPE_TO_UNBOX_METHOD = new HashMap<Type<?>, MethodId<?, ?>>(); - for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_TO_BOXED.entrySet()) { - Type<?> primitiveType = Type.get(entry.getKey()); - Type<?> boxedType = Type.get(entry.getValue()); - MethodId<?, ?> valueOfMethod = boxedType.getMethod(boxedType, "valueOf", primitiveType); - PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod); - } - } - - /** - * Map from primitive type to method used to unbox a boxed version of the primitive. - * <p> - * This is required for methods whose return type is primitive, since the - * {@link InvocationHandler} will return us a boxed result, and we'll need to convert it back to - * primitive value. - */ - private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD; - static { - Map<Class<?>, MethodId<?, ?>> map = new HashMap<Class<?>, MethodId<?, ?>>(); - map.put(boolean.class, Type.get(Boolean.class).getMethod(Type.BOOLEAN, "booleanValue")); - map.put(int.class, Type.get(Integer.class).getMethod(Type.INT, "intValue")); - map.put(byte.class, Type.get(Byte.class).getMethod(Type.BYTE, "byteValue")); - map.put(long.class, Type.get(Long.class).getMethod(Type.LONG, "longValue")); - map.put(short.class, Type.get(Short.class).getMethod(Type.SHORT, "shortValue")); - map.put(float.class, Type.get(Float.class).getMethod(Type.FLOAT, "floatValue")); - map.put(double.class, Type.get(Double.class).getMethod(Type.DOUBLE, "doubleValue")); - map.put(char.class, Type.get(Character.class).getMethod(Type.CHAR, "charValue")); - PRIMITIVE_TO_UNBOX_METHOD = map; - } - - /** - * Wrapper class to let us disambiguate {@link Method} objects. - * <p> - * The purpose of this class is to override the {@link #equals(Object)} and {@link #hashCode()} - * methods so we can use a {@link Set} to remove duplicate methods that are overrides of one - * another. For these purposes, we consider two methods to be equal if they have the same - * name, return type, and parameter types. - */ - private static class MethodSetEntry { - private final String name; - private final Class<?>[] paramTypes; - private final Class<?> returnType; - private final Method originalMethod; - - public MethodSetEntry(Method method) { - originalMethod = method; - name = method.getName(); - paramTypes = method.getParameterTypes(); - returnType = method.getReturnType(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof MethodSetEntry) { - MethodSetEntry other = (MethodSetEntry) o; - return name.equals(other.name) - && returnType.equals(other.returnType) - && Arrays.equals(paramTypes, other.paramTypes); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result += 31 * result + name.hashCode(); - result += 31 * result + returnType.hashCode(); - result += 31 * result + Arrays.hashCode(paramTypes); - return result; - } - } -} |