1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx.stock; 18 19 import com.android.dx.Code; 20 import com.android.dx.Comparison; 21 import com.android.dx.DexMaker; 22 import com.android.dx.FieldId; 23 import com.android.dx.Label; 24 import com.android.dx.Local; 25 import com.android.dx.MethodId; 26 import com.android.dx.TypeId; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.lang.reflect.Constructor; 31 import java.lang.reflect.Field; 32 import java.lang.reflect.InvocationHandler; 33 import java.lang.reflect.InvocationTargetException; 34 import java.lang.reflect.Method; 35 import java.lang.reflect.Modifier; 36 import java.lang.reflect.UndeclaredThrowableException; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.Comparator; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.Set; 46 47 import static java.lang.reflect.Modifier.ABSTRACT; 48 import static java.lang.reflect.Modifier.PRIVATE; 49 import static java.lang.reflect.Modifier.PUBLIC; 50 import static java.lang.reflect.Modifier.STATIC; 51 52 /** 53 * Creates dynamic proxies of concrete classes. 54 * <p> 55 * This is similar to the {@code java.lang.reflect.Proxy} class, but works for classes instead of 56 * interfaces. 57 * <h3>Example</h3> 58 * The following example demonstrates the creation of a dynamic proxy for {@code java.util.Random} 59 * which will always return 4 when asked for integers, and which logs method calls to every method. 60 * <pre> 61 * InvocationHandler handler = new InvocationHandler() { 62 * @Override 63 * public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 64 * if (method.getName().equals("nextInt")) { 65 * // Chosen by fair dice roll, guaranteed to be random. 66 * return 4; 67 * } 68 * Object result = ProxyBuilder.callSuper(proxy, method, args); 69 * System.out.println("Method: " + method.getName() + " args: " 70 * + Arrays.toString(args) + " result: " + result); 71 * return result; 72 * } 73 * }; 74 * Random debugRandom = ProxyBuilder.forClass(Random.class) 75 * .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE)) 76 * .handler(handler) 77 * .build(); 78 * assertEquals(4, debugRandom.nextInt()); 79 * debugRandom.setSeed(0); 80 * assertTrue(debugRandom.nextBoolean()); 81 * </pre> 82 * <h3>Usage</h3> 83 * Call {@link #forClass(Class)} for the Class you wish to proxy. Call 84 * {@link #handler(InvocationHandler)} passing in an {@link InvocationHandler}, and then call 85 * {@link #build()}. The returned instance will be a dynamically generated subclass where all method 86 * calls will be delegated to the invocation handler, except as noted below. 87 * <p> 88 * The static method {@link #callSuper(Object, Method, Object...)} allows you to access the original 89 * super method for a given proxy. This allows the invocation handler to selectively override some 90 * methods but not others. 91 * <p> 92 * By default, the {@link #build()} method will call the no-arg constructor belonging to the class 93 * being proxied. If you wish to call a different constructor, you must provide arguments for both 94 * {@link #constructorArgTypes(Class[])} and {@link #constructorArgValues(Object[])}. 95 * <p> 96 * This process works only for classes with public and protected level of visibility. 97 * <p> 98 * You may proxy abstract classes. You may not proxy final classes. 99 * <p> 100 * Only non-private, non-final, non-static methods will be dispatched to the invocation handler. 101 * Private, static or final methods will always call through to the superclass as normal. 102 * <p> 103 * The {@link #finalize()} method on {@code Object} will not be proxied. 104 * <p> 105 * You must provide a dex cache directory via the {@link #dexCache(File)} method. You should take 106 * care not to make this a world-writable directory, so that third parties cannot inject code into 107 * your application. A suitable parameter for these output directories would be something like 108 * this: 109 * <pre>{@code 110 * getApplicationContext().getDir("dx", Context.MODE_PRIVATE); 111 * }</pre> 112 * <p> 113 * If the base class to be proxied leaks the {@code this} pointer in the constructor (bad practice), 114 * that is to say calls a non-private non-final method from the constructor, the invocation handler 115 * will not be invoked. As a simple concrete example, when proxying Random we discover that it 116 * internally calls setSeed during the constructor. The proxy will not intercept this call during 117 * proxy construction, but will intercept as normal afterwards. This behaviour may be subject to 118 * change in future releases. 119 * <p> 120 * This class is <b>not thread safe</b>. 121 */ 122 public final class ProxyBuilder<T> { 123 // Version of ProxyBuilder. It should be updated if the implementation 124 // of the generated proxy class changes. 125 public static final int VERSION = 1; 126 127 private static final String FIELD_NAME_HANDLER = "$__handler"; 128 private static final String FIELD_NAME_METHODS = "$__methodArray"; 129 130 /** 131 * A cache of all proxy classes ever generated. At the time of writing, 132 * Android's runtime doesn't support class unloading so there's little 133 * value in using weak references. 134 */ 135 private static final Map<ProxiedClass<?>, Class<?>> generatedProxyClasses 136 = Collections.synchronizedMap(new HashMap<ProxiedClass<?>, Class<?>>()); 137 138 private final Class<T> baseClass; 139 private ClassLoader parentClassLoader = ProxyBuilder.class.getClassLoader(); 140 private InvocationHandler handler; 141 private File dexCache; 142 private Class<?>[] constructorArgTypes = new Class[0]; 143 private Object[] constructorArgValues = new Object[0]; 144 private List<Class<?>> interfaces = new ArrayList<>(); 145 private Method[] methods; 146 private boolean sharedClassLoader; 147 private boolean markTrusted; 148 ProxyBuilder(Class<T> clazz)149 private ProxyBuilder(Class<T> clazz) { 150 baseClass = clazz; 151 } 152 forClass(Class<T> clazz)153 public static <T> ProxyBuilder<T> forClass(Class<T> clazz) { 154 return new ProxyBuilder<T>(clazz); 155 } 156 157 /** 158 * Specifies the parent ClassLoader to use when creating the proxy. 159 * 160 * <p>If null, {@code ProxyBuilder.class.getClassLoader()} will be used. 161 */ parentClassLoader(ClassLoader parent)162 public ProxyBuilder<T> parentClassLoader(ClassLoader parent) { 163 parentClassLoader = parent; 164 return this; 165 } 166 handler(InvocationHandler handler)167 public ProxyBuilder<T> handler(InvocationHandler handler) { 168 this.handler = handler; 169 return this; 170 } 171 172 /** 173 * Sets the directory where executable code is stored. See {@link 174 * DexMaker#generateAndLoad DexMaker.generateAndLoad()} for guidance on 175 * choosing a secure location for the dex cache. 176 */ dexCache(File dexCacheParent)177 public ProxyBuilder<T> dexCache(File dexCacheParent) { 178 dexCache = new File(dexCacheParent, "v" + Integer.toString(VERSION)); 179 dexCache.mkdir(); 180 return this; 181 } 182 implementing(Class<?>.... interfaces)183 public ProxyBuilder<T> implementing(Class<?>... interfaces) { 184 List<Class<?>> list = this.interfaces; 185 for (Class<?> i : interfaces) { 186 if (!i.isInterface()) { 187 throw new IllegalArgumentException("Not an interface: " + i.getName()); 188 } 189 if (!list.contains(i)) { 190 list.add(i); 191 } 192 } 193 return this; 194 } 195 constructorArgValues(Object... constructorArgValues)196 public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) { 197 this.constructorArgValues = constructorArgValues; 198 return this; 199 } 200 constructorArgTypes(Class<?>.... constructorArgTypes)201 public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) { 202 this.constructorArgTypes = constructorArgTypes; 203 return this; 204 } 205 onlyMethods(Method[] methods)206 public ProxyBuilder<T> onlyMethods(Method[] methods) { 207 this.methods = methods; 208 return this; 209 } 210 withSharedClassLoader()211 public ProxyBuilder<T> withSharedClassLoader() { 212 this.sharedClassLoader = true; 213 return this; 214 } 215 markTrusted()216 public ProxyBuilder<T> markTrusted() { 217 this.markTrusted = true; 218 return this; 219 } 220 221 /** 222 * Create a new instance of the class to proxy. 223 * 224 * @throws UnsupportedOperationException if the class we are trying to create a proxy for is 225 * not accessible. 226 * @throws IOException if an exception occurred writing to the {@code dexCache} directory. 227 * @throws UndeclaredThrowableException if the constructor for the base class to proxy throws 228 * a declared exception during construction. 229 * @throws IllegalArgumentException if the handler is null, if the constructor argument types 230 * do not match the constructor argument values, or if no such constructor exists. 231 */ build()232 public T build() throws IOException { 233 check(handler != null, "handler == null"); 234 check(constructorArgTypes.length == constructorArgValues.length, 235 "constructorArgValues.length != constructorArgTypes.length"); 236 Class<? extends T> proxyClass = buildProxyClass(); 237 Constructor<? extends T> constructor; 238 try { 239 constructor = proxyClass.getConstructor(constructorArgTypes); 240 } catch (NoSuchMethodException e) { 241 throw new IllegalArgumentException("No constructor for " + baseClass.getName() 242 + " with parameter types " + Arrays.toString(constructorArgTypes)); 243 } 244 T result; 245 try { 246 result = constructor.newInstance(constructorArgValues); 247 } catch (InstantiationException e) { 248 // Should not be thrown, generated class is not abstract. 249 throw new AssertionError(e); 250 } catch (IllegalAccessException e) { 251 // Should not be thrown, the generated constructor is accessible. 252 throw new AssertionError(e); 253 } catch (InvocationTargetException e) { 254 // Thrown when the base class constructor throws an exception. 255 throw launderCause(e); 256 } 257 setInvocationHandler(result, handler); 258 return result; 259 } 260 261 // TODO: test coverage for this 262 263 /** 264 * Generate a proxy class. Note that new instances of this class will not automatically have an 265 * an invocation handler, even if {@link #handler(InvocationHandler)} was called. The handler 266 * must be set on each instance after it is created, using 267 * {@link #setInvocationHandler(Object, InvocationHandler)}. 268 */ buildProxyClass()269 public Class<? extends T> buildProxyClass() throws IOException { 270 ClassLoader requestedClassloader; 271 if (sharedClassLoader) { 272 requestedClassloader = baseClass.getClassLoader(); 273 } else { 274 requestedClassloader = parentClassLoader; 275 } 276 277 // try the cache to see if we've generated this one before 278 // we only populate the map with matching types 279 ProxiedClass<T> cacheKey = 280 new ProxiedClass<>(baseClass, interfaces, requestedClassloader, sharedClassLoader); 281 @SuppressWarnings("unchecked") 282 Class<? extends T> proxyClass = (Class) generatedProxyClasses.get(cacheKey); 283 if (proxyClass != null) { 284 return proxyClass; // cache hit! 285 } 286 287 // the cache missed; generate the class 288 DexMaker dexMaker = new DexMaker(); 289 String generatedName = getMethodNameForProxyOf(baseClass, interfaces); 290 TypeId<? extends T> generatedType = TypeId.get("L" + generatedName + ";"); 291 TypeId<T> superType = TypeId.get(baseClass); 292 generateConstructorsAndFields(dexMaker, generatedType, superType, baseClass); 293 294 Method[] methodsToProxy; 295 if (methods == null) { 296 methodsToProxy = getMethodsToProxyRecursive(); 297 } else { 298 methodsToProxy = methods; 299 } 300 301 // Sort the results array so that they are in a deterministic fashion. 302 // 303 // We use the same parameters to sort as used in {@link MethodId#hashCode}. This is needed 304 // as e.g. making a method "public" instead of "protected" should not change the id's of the 305 // methods. If the id's would change the classes loaded from the cache would be incorrect. 306 Arrays.sort(methodsToProxy, new Comparator<Method>() { 307 @Override 308 public int compare(Method method1, Method method2) { 309 String m1Signature = method1.getDeclaringClass() + method1.getName() + Arrays.toString(method1.getParameterTypes()) + method1.getReturnType(); 310 String m2Signature = method2.getDeclaringClass() + method2.getName() + Arrays.toString(method2.getParameterTypes()) + method2.getReturnType(); 311 312 return m1Signature.compareTo(m2Signature); 313 } 314 }); 315 316 generateCodeForAllMethods(dexMaker, generatedType, methodsToProxy, superType); 317 dexMaker.declare(generatedType, generatedName + ".generated", PUBLIC, superType, getInterfacesAsTypeIds()); 318 if (sharedClassLoader) { 319 dexMaker.setSharedClassLoader(requestedClassloader); 320 } 321 if (markTrusted) { 322 // The proxied class might have blacklisted methods. Blacklisting methods (and fields) 323 // is a new feature of Android P: 324 // 325 // https://android-developers.googleblog.com/2018/02/ 326 // improving-stability-by-reducing-usage.html 327 // 328 // The newly generated class might not be allowed to call methods of the proxied class 329 // if it is not trusted. As it is not clear which classes have blacklisted methods, mark 330 // all generated classes as trusted. 331 dexMaker.markAsTrusted(); 332 } 333 ClassLoader classLoader; 334 if (sharedClassLoader) { 335 classLoader = dexMaker.generateAndLoad(null, dexCache); 336 } else { 337 classLoader = dexMaker.generateAndLoad(parentClassLoader, dexCache); 338 } 339 try { 340 proxyClass = loadClass(classLoader, generatedName); 341 } catch (IllegalAccessError e) { 342 // Thrown when the base class is not accessible. 343 throw new UnsupportedOperationException( 344 "cannot proxy inaccessible class " + baseClass, e); 345 } catch (ClassNotFoundException e) { 346 // Should not be thrown, we're sure to have generated this class. 347 throw new AssertionError(e); 348 } 349 setMethodsStaticField(proxyClass, methodsToProxy); 350 generatedProxyClasses.put(cacheKey, proxyClass); 351 return proxyClass; 352 } 353 354 // The type cast is safe: the generated type will extend the base class type. 355 @SuppressWarnings("unchecked") loadClass(ClassLoader classLoader, String generatedName)356 private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName) 357 throws ClassNotFoundException { 358 return (Class<? extends T>) classLoader.loadClass(generatedName); 359 } 360 launderCause(InvocationTargetException e)361 private static RuntimeException launderCause(InvocationTargetException e) { 362 Throwable cause = e.getCause(); 363 // Errors should be thrown as they are. 364 if (cause instanceof Error) { 365 throw (Error) cause; 366 } 367 // RuntimeException can be thrown as-is. 368 if (cause instanceof RuntimeException) { 369 throw (RuntimeException) cause; 370 } 371 // Declared exceptions will have to be wrapped. 372 throw new UndeclaredThrowableException(cause); 373 } 374 setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy)375 private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy) { 376 try { 377 Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS); 378 methodArrayField.setAccessible(true); 379 methodArrayField.set(null, methodsToProxy); 380 } catch (NoSuchFieldException e) { 381 // Should not be thrown, generated proxy class has been generated with this field. 382 throw new AssertionError(e); 383 } catch (IllegalAccessException e) { 384 // Should not be thrown, we just set the field to accessible. 385 throw new AssertionError(e); 386 } 387 } 388 389 /** 390 * Returns the proxy's {@link InvocationHandler}. 391 * 392 * @throws IllegalArgumentException if the object supplied is not a proxy created by this class. 393 */ getInvocationHandler(Object instance)394 public static InvocationHandler getInvocationHandler(Object instance) { 395 try { 396 Field field = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); 397 field.setAccessible(true); 398 return (InvocationHandler) field.get(instance); 399 } catch (NoSuchFieldException e) { 400 throw new IllegalArgumentException("Not a valid proxy instance", e); 401 } catch (IllegalAccessException e) { 402 // Should not be thrown, we just set the field to accessible. 403 throw new AssertionError(e); 404 } 405 } 406 407 /** 408 * Sets the proxy's {@link InvocationHandler}. 409 * <p> 410 * If you create a proxy with {@link #build()}, the proxy will already have a handler set, 411 * provided that you configured one with {@link #handler(InvocationHandler)}. 412 * <p> 413 * If you generate a proxy class with {@link #buildProxyClass()}, instances of the proxy class 414 * will not automatically have a handler set, and it is necessary to use this method with each 415 * instance. 416 * 417 * @throws IllegalArgumentException if the object supplied is not a proxy created by this class. 418 */ setInvocationHandler(Object instance, InvocationHandler handler)419 public static void setInvocationHandler(Object instance, InvocationHandler handler) { 420 try { 421 Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); 422 handlerField.setAccessible(true); 423 handlerField.set(instance, handler); 424 } catch (NoSuchFieldException e) { 425 throw new IllegalArgumentException("Not a valid proxy instance", e); 426 } catch (IllegalAccessException e) { 427 // Should not be thrown, we just set the field to accessible. 428 throw new AssertionError(e); 429 } 430 } 431 432 // TODO: test coverage for isProxyClass 433 434 /** 435 * Returns true if {@code c} is a proxy class created by this builder. 436 */ isProxyClass(Class<?> c)437 public static boolean isProxyClass(Class<?> c) { 438 // TODO: use a marker interface instead? 439 try { 440 c.getDeclaredField(FIELD_NAME_HANDLER); 441 return true; 442 } catch (NoSuchFieldException e) { 443 return false; 444 } 445 } 446 447 /** 448 * Add 449 * 450 * <pre> 451 * abstractMethodErrorMessage = method + " cannot be called"; 452 * abstractMethodError = new AbstractMethodError(abstractMethodErrorMessage); 453 * throw abstractMethodError; 454 * </pre> 455 * 456 * to the {@code code}. 457 * 458 * @param code The code to add to 459 * @param method The method that is abstract 460 * @param abstractMethodErrorMessage The {@link Local} to store the error message 461 * @param abstractMethodError The {@link Local} to store the error object 462 */ throwAbstractMethodError(Code code, Method method, Local<String> abstractMethodErrorMessage, Local<AbstractMethodError> abstractMethodError)463 private static void throwAbstractMethodError(Code code, Method method, 464 Local<String> abstractMethodErrorMessage, 465 Local<AbstractMethodError> abstractMethodError) { 466 TypeId<AbstractMethodError> abstractMethodErrorClass = TypeId.get(AbstractMethodError.class); 467 468 MethodId<AbstractMethodError, Void> abstractMethodErrorConstructor = 469 abstractMethodErrorClass.getConstructor(TypeId.STRING); 470 code.loadConstant(abstractMethodErrorMessage, "'" + method + "' cannot be called"); 471 code.newInstance(abstractMethodError, abstractMethodErrorConstructor, 472 abstractMethodErrorMessage); 473 474 code.throwValue(abstractMethodError); 475 } 476 generateCodeForAllMethods(DexMaker dexMaker, TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType)477 private static <T, G extends T> void generateCodeForAllMethods(DexMaker dexMaker, 478 TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType) { 479 TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class); 480 TypeId<Method[]> methodArrayType = TypeId.get(Method[].class); 481 FieldId<G, InvocationHandler> handlerField = 482 generatedType.getField(handlerType, FIELD_NAME_HANDLER); 483 FieldId<G, Method[]> allMethods = 484 generatedType.getField(methodArrayType, FIELD_NAME_METHODS); 485 TypeId<Method> methodType = TypeId.get(Method.class); 486 TypeId<Object[]> objectArrayType = TypeId.get(Object[].class); 487 MethodId<InvocationHandler, Object> methodInvoke = handlerType.getMethod(TypeId.OBJECT, 488 "invoke", TypeId.OBJECT, methodType, objectArrayType); 489 for (int m = 0; m < methodsToProxy.length; ++m) { 490 /* 491 * If the 5th method on the superclass Example that can be overridden were to look like 492 * this: 493 * 494 * public int doSomething(Bar param0, int param1) { 495 * ... 496 * } 497 * 498 * Then the following dex byte code will generate a method on the proxy that looks 499 * something like this (in idiomatic Java): 500 * 501 * // if doSomething is not abstract 502 * public int doSomething(Bar param0, int param1) { 503 * if ($__handler == null) { 504 * return super.doSomething(param0, param1); 505 * } 506 * return __handler.invoke(this, __methodArray[4], 507 * new Object[] { param0, Integer.valueOf(param1) }); 508 * } 509 * 510 * // if doSomething is abstract 511 * public int doSomething(Bar param0, int param1) { 512 * if ($__handler == null) { 513 * throw new AbstractMethodError("'doSomething' cannot be called"); 514 * } 515 * return __handler.invoke(this, __methodArray[4], 516 * new Object[] { param0, Integer.valueOf(param1) }); 517 * } 518 */ 519 Method method = methodsToProxy[m]; 520 String name = method.getName(); 521 Class<?>[] argClasses = method.getParameterTypes(); 522 TypeId<?>[] argTypes = new TypeId<?>[argClasses.length]; 523 for (int i = 0; i < argTypes.length; ++i) { 524 argTypes[i] = TypeId.get(argClasses[i]); 525 } 526 Class<?> returnType = method.getReturnType(); 527 TypeId<?> resultType = TypeId.get(returnType); 528 MethodId<?, ?> methodId = generatedType.getMethod(resultType, name, argTypes); 529 TypeId<AbstractMethodError> abstractMethodErrorClass = 530 TypeId.get(AbstractMethodError.class); 531 Code code = dexMaker.declare(methodId, PUBLIC); 532 Local<G> localThis = code.getThis(generatedType); 533 Local<InvocationHandler> localHandler = code.newLocal(handlerType); 534 Local<Object> invokeResult = code.newLocal(TypeId.OBJECT); 535 Local<Integer> intValue = code.newLocal(TypeId.INT); 536 Local<Object[]> args = code.newLocal(objectArrayType); 537 Local<Integer> argsLength = code.newLocal(TypeId.INT); 538 Local<Object> temp = code.newLocal(TypeId.OBJECT); 539 Local<?> resultHolder = code.newLocal(resultType); 540 Local<Method[]> methodArray = code.newLocal(methodArrayType); 541 Local<Method> thisMethod = code.newLocal(methodType); 542 Local<Integer> methodIndex = code.newLocal(TypeId.INT); 543 Class<?> aBoxedClass = PRIMITIVE_TO_BOXED.get(returnType); 544 Local<?> aBoxedResult = null; 545 if (aBoxedClass != null) { 546 aBoxedResult = code.newLocal(TypeId.get(aBoxedClass)); 547 } 548 Local<InvocationHandler> nullHandler = code.newLocal(handlerType); 549 550 Local<?>[] superArgs2 = null; 551 Local<?> superResult2 = null; 552 MethodId<T, ?> superMethod = null; 553 Local<String> abstractMethodErrorMessage = null; 554 Local<AbstractMethodError> abstractMethodError = null; 555 if ((method.getModifiers() & ABSTRACT) == 0) { 556 superArgs2 = new Local<?>[argClasses.length]; 557 superResult2 = code.newLocal(resultType); 558 superMethod = superclassType.getMethod(resultType, name, argTypes); 559 } else { 560 abstractMethodErrorMessage = code.newLocal(TypeId.STRING); 561 abstractMethodError = code.newLocal(abstractMethodErrorClass); 562 } 563 564 code.loadConstant(methodIndex, m); 565 code.sget(allMethods, methodArray); 566 code.aget(thisMethod, methodArray, methodIndex); 567 code.loadConstant(argsLength, argTypes.length); 568 code.newArray(args, argsLength); 569 code.iget(handlerField, localHandler, localThis); 570 571 // if (proxy == null) 572 code.loadConstant(nullHandler, null); 573 Label handlerNullCase = new Label(); 574 code.compare(Comparison.EQ, handlerNullCase, nullHandler, localHandler); 575 576 // This code is what we execute when we have a valid proxy: delegate to invocation 577 // handler. 578 for (int p = 0; p < argTypes.length; ++p) { 579 code.loadConstant(intValue, p); 580 Local<?> parameter = code.getParameter(p, argTypes[p]); 581 Local<?> unboxedIfNecessary = boxIfRequired(code, parameter, temp); 582 code.aput(args, intValue, unboxedIfNecessary); 583 } 584 code.invokeInterface(methodInvoke, invokeResult, localHandler, 585 localThis, thisMethod, args); 586 generateCodeForReturnStatement(code, returnType, invokeResult, resultHolder, 587 aBoxedResult); 588 589 // This code is executed if proxy is null: call the original super method. 590 // This is required to handle the case of construction of an object which leaks the 591 // "this" pointer. 592 code.mark(handlerNullCase); 593 594 if ((method.getModifiers() & ABSTRACT) == 0) { 595 for (int i = 0; i < superArgs2.length; ++i) { 596 superArgs2[i] = code.getParameter(i, argTypes[i]); 597 } 598 if (void.class.equals(returnType)) { 599 code.invokeSuper(superMethod, null, localThis, superArgs2); 600 code.returnVoid(); 601 } else { 602 invokeSuper(superMethod, code, localThis, superArgs2, superResult2); 603 code.returnValue(superResult2); 604 } 605 } else { 606 throwAbstractMethodError(code, method, abstractMethodErrorMessage, 607 abstractMethodError); 608 } 609 610 /* 611 * And to allow calling the original super method, the following is also generated: 612 * 613 * public String super$doSomething$java_lang_String(Bar param0, int param1) { 614 * int result = super.doSomething(param0, param1); 615 * return result; 616 * } 617 */ 618 MethodId<G, ?> callsSuperMethod = generatedType.getMethod( 619 resultType, superMethodName(method), argTypes); 620 Code superCode = dexMaker.declare(callsSuperMethod, PUBLIC); 621 if ((method.getModifiers() & ABSTRACT) == 0) { 622 Local<G> superThis = superCode.getThis(generatedType); 623 Local<?>[] superArgs = new Local<?>[argClasses.length]; 624 for (int i = 0; i < superArgs.length; ++i) { 625 superArgs[i] = superCode.getParameter(i, argTypes[i]); 626 } 627 if (void.class.equals(returnType)) { 628 superCode.invokeSuper(superMethod, null, superThis, superArgs); 629 superCode.returnVoid(); 630 } else { 631 Local<?> superResult = superCode.newLocal(resultType); 632 invokeSuper(superMethod, superCode, superThis, superArgs, superResult); 633 superCode.returnValue(superResult); 634 } 635 } else { 636 Local<String> superAbstractMethodErrorMessage = superCode.newLocal(TypeId.STRING); 637 Local<AbstractMethodError> superAbstractMethodError = superCode.newLocal 638 (abstractMethodErrorClass); 639 throwAbstractMethodError(superCode, method, superAbstractMethodErrorMessage, 640 superAbstractMethodError); 641 } 642 } 643 } 644 645 @SuppressWarnings({"unchecked", "rawtypes"}) invokeSuper(MethodId superMethod, Code superCode, Local superThis, Local[] superArgs, Local superResult)646 private static void invokeSuper(MethodId superMethod, Code superCode, 647 Local superThis, Local[] superArgs, Local superResult) { 648 superCode.invokeSuper(superMethod, superResult, superThis, superArgs); 649 } 650 boxIfRequired(Code code, Local<?> parameter, Local<Object> temp)651 private static Local<?> boxIfRequired(Code code, Local<?> parameter, Local<Object> temp) { 652 MethodId<?, ?> unboxMethod = PRIMITIVE_TYPE_TO_UNBOX_METHOD.get(parameter.getType()); 653 if (unboxMethod == null) { 654 return parameter; 655 } 656 code.invokeStatic(unboxMethod, temp, parameter); 657 return temp; 658 } 659 callSuper(Object proxy, Method method, Object... args)660 public static Object callSuper(Object proxy, Method method, Object... args) throws Throwable { 661 try { 662 return proxy.getClass() 663 .getMethod(superMethodName(method), method.getParameterTypes()) 664 .invoke(proxy, args); 665 } catch (InvocationTargetException e) { 666 throw e.getCause(); 667 } 668 } 669 670 /** 671 * The super method must include the return type, otherwise its ambiguous 672 * for methods with covariant return types. 673 */ superMethodName(Method method)674 private static String superMethodName(Method method) { 675 String returnType = method.getReturnType().getName(); 676 return "super$" + method.getName() + "$" 677 + returnType.replace('.', '_').replace('[', '_').replace(';', '_'); 678 } 679 check(boolean condition, String message)680 private static void check(boolean condition, String message) { 681 if (!condition) { 682 throw new IllegalArgumentException(message); 683 } 684 } 685 generateConstructorsAndFields(DexMaker dexMaker, TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass)686 private static <T, G extends T> void generateConstructorsAndFields(DexMaker dexMaker, 687 TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass) { 688 TypeId<InvocationHandler> handlerType = TypeId.get(InvocationHandler.class); 689 TypeId<Method[]> methodArrayType = TypeId.get(Method[].class); 690 FieldId<G, InvocationHandler> handlerField = generatedType.getField( 691 handlerType, FIELD_NAME_HANDLER); 692 dexMaker.declare(handlerField, PRIVATE, null); 693 FieldId<G, Method[]> allMethods = generatedType.getField( 694 methodArrayType, FIELD_NAME_METHODS); 695 dexMaker.declare(allMethods, PRIVATE | STATIC, null); 696 for (Constructor<T> constructor : getConstructorsToOverwrite(superClass)) { 697 if (constructor.getModifiers() == Modifier.FINAL) { 698 continue; 699 } 700 TypeId<?>[] types = classArrayToTypeArray(constructor.getParameterTypes()); 701 MethodId<?, ?> method = generatedType.getConstructor(types); 702 Code constructorCode = dexMaker.declare(method, PUBLIC); 703 Local<G> thisRef = constructorCode.getThis(generatedType); 704 Local<?>[] params = new Local[types.length]; 705 for (int i = 0; i < params.length; ++i) { 706 params[i] = constructorCode.getParameter(i, types[i]); 707 } 708 MethodId<T, ?> superConstructor = superType.getConstructor(types); 709 constructorCode.invokeDirect(superConstructor, null, thisRef, params); 710 constructorCode.returnVoid(); 711 } 712 } 713 714 // The type parameter on Constructor is the class in which the constructor is declared. 715 // The getDeclaredConstructors() method gets constructors declared only in the given class, 716 // hence this cast is safe. 717 @SuppressWarnings("unchecked") getConstructorsToOverwrite(Class<T> clazz)718 private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) { 719 return (Constructor<T>[]) clazz.getDeclaredConstructors(); 720 } 721 getInterfacesAsTypeIds()722 private TypeId<?>[] getInterfacesAsTypeIds() { 723 TypeId<?>[] result = new TypeId<?>[interfaces.size()]; 724 int i = 0; 725 for (Class<?> implemented : interfaces) { 726 result[i++] = TypeId.get(implemented); 727 } 728 return result; 729 } 730 731 /** 732 * Gets all {@link Method} objects we can proxy in the hierarchy of the 733 * supplied class. 734 */ getMethodsToProxyRecursive()735 private Method[] getMethodsToProxyRecursive() { 736 Set<MethodSetEntry> methodsToProxy = new HashSet<>(); 737 Set<MethodSetEntry> seenFinalMethods = new HashSet<>(); 738 // Traverse the class hierarchy to ensure that all concrete methods (which could be marked 739 // as final) are visited before any abstract methods from interfaces. 740 for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) { 741 getMethodsToProxy(methodsToProxy, seenFinalMethods, c); 742 } 743 // Now traverse the interface hierarchy, starting with the ones implemented by the class, 744 // followed by any extra interfaces. 745 for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) { 746 for (Class<?> i : c.getInterfaces()) { 747 getMethodsToProxy(methodsToProxy, seenFinalMethods, i); 748 } 749 } 750 for (Class<?> c : interfaces) { 751 getMethodsToProxy(methodsToProxy, seenFinalMethods, c); 752 } 753 754 Method[] results = new Method[methodsToProxy.size()]; 755 int i = 0; 756 for (MethodSetEntry entry : methodsToProxy) { 757 results[i++] = entry.originalMethod; 758 } 759 760 return results; 761 } 762 getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods, Class<?> c)763 private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods, 764 Class<?> c) { 765 for (Method method : c.getDeclaredMethods()) { 766 if ((method.getModifiers() & Modifier.FINAL) != 0) { 767 // Skip final methods, we can't override them. We 768 // also need to remember them, in case the same 769 // method exists in a parent class. 770 MethodSetEntry entry = new MethodSetEntry(method); 771 seenFinalMethods.add(entry); 772 // We may have seen this method already, from an interface 773 // implemented by a child class. We need to remove it here. 774 sink.remove(entry); 775 continue; 776 } 777 if ((method.getModifiers() & STATIC) != 0) { 778 // Skip static methods, overriding them has no effect. 779 continue; 780 } 781 if (!Modifier.isPublic(method.getModifiers()) 782 && !Modifier.isProtected(method.getModifiers()) 783 && (!sharedClassLoader || Modifier.isPrivate(method.getModifiers()))) { 784 // Skip private methods, since they are invoked through direct 785 // invocation (as opposed to virtual). Therefore, it would not 786 // be possible to intercept any private method defined inside 787 // the proxy class except through reflection. 788 789 // Skip package-private methods as well (for non-shared class 790 // loaders). The proxy class does 791 // not actually inherit package-private methods from the parent 792 // class because it is not a member of the parent's package. 793 // This is even true if the two classes have the same package 794 // name, as they use different class loaders. 795 continue; 796 } 797 if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) { 798 // Skip finalize method, it's likely important that it execute as normal. 799 continue; 800 } 801 MethodSetEntry entry = new MethodSetEntry(method); 802 if (seenFinalMethods.contains(entry)) { 803 // This method is final in a child class. 804 // We can't override it. 805 continue; 806 } 807 sink.add(entry); 808 } 809 810 // Only visit the interfaces of this class if it is itself an interface. That prevents 811 // visiting interfaces of a class before its super classes. 812 if (c.isInterface()) { 813 for (Class<?> i : c.getInterfaces()) { 814 getMethodsToProxy(sink, seenFinalMethods, i); 815 } 816 } 817 } 818 getMethodNameForProxyOf(Class<T> clazz, List<Class<?>> interfaces)819 private static <T> String getMethodNameForProxyOf(Class<T> clazz, List<Class<?>> interfaces) { 820 String interfacesHash = Integer.toHexString(interfaces.hashCode()); 821 return clazz.getName().replace(".", "/") + "_" + interfacesHash + "_Proxy"; 822 } 823 classArrayToTypeArray(Class<?>[] input)824 private static TypeId<?>[] classArrayToTypeArray(Class<?>[] input) { 825 TypeId<?>[] result = new TypeId[input.length]; 826 for (int i = 0; i < input.length; ++i) { 827 result[i] = TypeId.get(input[i]); 828 } 829 return result; 830 } 831 832 /** 833 * Calculates the correct return statement code for a method. 834 * <p> 835 * A void method will not return anything. A method that returns a primitive will need to 836 * unbox the boxed result. Otherwise we will cast the result. 837 */ 838 // This one is tricky to fix, I gave up. 839 @SuppressWarnings({ "rawtypes", "unchecked" }) generateCodeForReturnStatement(Code code, Class methodReturnType, Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult)840 private static void generateCodeForReturnStatement(Code code, Class methodReturnType, 841 Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult) { 842 if (PRIMITIVE_TO_UNBOX_METHOD.containsKey(methodReturnType)) { 843 code.cast(aBoxedResult, localForResultOfInvoke); 844 MethodId unboxingMethodFor = getUnboxMethodForPrimitive(methodReturnType); 845 code.invokeVirtual(unboxingMethodFor, localOfMethodReturnType, aBoxedResult); 846 code.returnValue(localOfMethodReturnType); 847 } else if (void.class.equals(methodReturnType)) { 848 code.returnVoid(); 849 } else { 850 code.cast(localOfMethodReturnType, localForResultOfInvoke); 851 code.returnValue(localOfMethodReturnType); 852 } 853 } 854 getUnboxMethodForPrimitive(Class<?> methodReturnType)855 private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType) { 856 return PRIMITIVE_TO_UNBOX_METHOD.get(methodReturnType); 857 } 858 859 private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED; 860 static { 861 PRIMITIVE_TO_BOXED = new HashMap<>(); PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class)862 PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class); PRIMITIVE_TO_BOXED.put(int.class, Integer.class)863 PRIMITIVE_TO_BOXED.put(int.class, Integer.class); PRIMITIVE_TO_BOXED.put(byte.class, Byte.class)864 PRIMITIVE_TO_BOXED.put(byte.class, Byte.class); PRIMITIVE_TO_BOXED.put(long.class, Long.class)865 PRIMITIVE_TO_BOXED.put(long.class, Long.class); PRIMITIVE_TO_BOXED.put(short.class, Short.class)866 PRIMITIVE_TO_BOXED.put(short.class, Short.class); PRIMITIVE_TO_BOXED.put(float.class, Float.class)867 PRIMITIVE_TO_BOXED.put(float.class, Float.class); PRIMITIVE_TO_BOXED.put(double.class, Double.class)868 PRIMITIVE_TO_BOXED.put(double.class, Double.class); PRIMITIVE_TO_BOXED.put(char.class, Character.class)869 PRIMITIVE_TO_BOXED.put(char.class, Character.class); 870 } 871 872 private static final Map<TypeId<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD; 873 static { 874 PRIMITIVE_TYPE_TO_UNBOX_METHOD = new HashMap<>(); 875 for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_TO_BOXED.entrySet()) { 876 TypeId<?> primitiveType = TypeId.get(entry.getKey()); 877 TypeId<?> boxedType = TypeId.get(entry.getValue()); 878 MethodId<?, ?> valueOfMethod = boxedType.getMethod(boxedType, "valueOf", primitiveType); PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod)879 PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod); 880 } 881 } 882 883 /** 884 * Map from primitive type to method used to unbox a boxed version of the primitive. 885 * <p> 886 * This is required for methods whose return type is primitive, since the 887 * {@link InvocationHandler} will return us a boxed result, and we'll need to convert it back to 888 * primitive value. 889 */ 890 private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD; 891 static { 892 Map<Class<?>, MethodId<?, ?>> map = new HashMap<>(); map.put(boolean.class, TypeId.get(Boolean.class).getMethod(TypeId.BOOLEAN, "booleanValue"))893 map.put(boolean.class, TypeId.get(Boolean.class).getMethod(TypeId.BOOLEAN, "booleanValue")); map.put(int.class, TypeId.get(Integer.class).getMethod(TypeId.INT, "intValue"))894 map.put(int.class, TypeId.get(Integer.class).getMethod(TypeId.INT, "intValue")); map.put(byte.class, TypeId.get(Byte.class).getMethod(TypeId.BYTE, "byteValue"))895 map.put(byte.class, TypeId.get(Byte.class).getMethod(TypeId.BYTE, "byteValue")); map.put(long.class, TypeId.get(Long.class).getMethod(TypeId.LONG, "longValue"))896 map.put(long.class, TypeId.get(Long.class).getMethod(TypeId.LONG, "longValue")); map.put(short.class, TypeId.get(Short.class).getMethod(TypeId.SHORT, "shortValue"))897 map.put(short.class, TypeId.get(Short.class).getMethod(TypeId.SHORT, "shortValue")); map.put(float.class, TypeId.get(Float.class).getMethod(TypeId.FLOAT, "floatValue"))898 map.put(float.class, TypeId.get(Float.class).getMethod(TypeId.FLOAT, "floatValue")); map.put(double.class, TypeId.get(Double.class).getMethod(TypeId.DOUBLE, "doubleValue"))899 map.put(double.class, TypeId.get(Double.class).getMethod(TypeId.DOUBLE, "doubleValue")); map.put(char.class, TypeId.get(Character.class).getMethod(TypeId.CHAR, "charValue"))900 map.put(char.class, TypeId.get(Character.class).getMethod(TypeId.CHAR, "charValue")); 901 PRIMITIVE_TO_UNBOX_METHOD = map; 902 } 903 904 /** 905 * Wrapper class to let us disambiguate {@link Method} objects. 906 * <p> 907 * The purpose of this class is to override the {@link #equals(Object)} and {@link #hashCode()} 908 * methods so we can use a {@link Set} to remove duplicate methods that are overrides of one 909 * another. For these purposes, we consider two methods to be equal if they have the same 910 * name, return type, and parameter types. 911 */ 912 public static class MethodSetEntry { 913 public final String name; 914 public final Class<?>[] paramTypes; 915 public final Class<?> returnType; 916 public final Method originalMethod; 917 MethodSetEntry(Method method)918 public MethodSetEntry(Method method) { 919 originalMethod = method; 920 name = method.getName(); 921 paramTypes = method.getParameterTypes(); 922 returnType = method.getReturnType(); 923 } 924 925 @Override equals(Object o)926 public boolean equals(Object o) { 927 if (o instanceof MethodSetEntry) { 928 MethodSetEntry other = (MethodSetEntry) o; 929 return name.equals(other.name) 930 && returnType.equals(other.returnType) 931 && Arrays.equals(paramTypes, other.paramTypes); 932 } 933 return false; 934 } 935 936 @Override hashCode()937 public int hashCode() { 938 int result = 17; 939 result += 31 * result + name.hashCode(); 940 result += 31 * result + returnType.hashCode(); 941 result += 31 * result + Arrays.hashCode(paramTypes); 942 return result; 943 } 944 } 945 946 /** 947 * A class that was already proxied. 948 */ 949 private static class ProxiedClass<U> { 950 final Class<U> clazz; 951 952 final List<Class<?>> interfaces; 953 954 /** 955 * Class loader requested when the proxy class was generated. This might not be the 956 * class loader of {@code clazz} as not all class loaders can be shared. 957 * 958 * @see DexMaker#generateClassLoader(File, File, ClassLoader) 959 */ 960 final ClassLoader requestedClassloader; 961 962 final boolean sharedClassLoader; 963 964 @Override equals(Object other)965 public boolean equals(Object other) { 966 if (this == other) { 967 return true; 968 } 969 if (other == null || getClass() != other.getClass()) { 970 return false; 971 } 972 973 ProxiedClass<?> that = (ProxiedClass<?>) other; 974 return clazz == that.clazz 975 && interfaces.equals(that.interfaces) 976 && requestedClassloader == that.requestedClassloader 977 && sharedClassLoader == that.sharedClassLoader; 978 } 979 980 @Override hashCode()981 public int hashCode() { 982 return clazz.hashCode() + interfaces.hashCode() + requestedClassloader.hashCode() 983 + (sharedClassLoader ? 1 : 0); 984 } 985 ProxiedClass(Class<U> clazz, List<Class<?>> interfaces, ClassLoader requestedClassloader, boolean sharedClassLoader)986 private ProxiedClass(Class<U> clazz, List<Class<?>> interfaces, 987 ClassLoader requestedClassloader, boolean sharedClassLoader) { 988 this.clazz = clazz; 989 this.interfaces = new ArrayList<>(interfaces); 990 this.requestedClassloader = requestedClassloader; 991 this.sharedClassLoader = sharedClassLoader; 992 } 993 } 994 } 995