1 /* 2 * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.reflect; 27 28 import jdk.internal.org.objectweb.asm.ClassWriter; 29 import jdk.internal.org.objectweb.asm.Label; 30 import jdk.internal.org.objectweb.asm.MethodVisitor; 31 import jdk.internal.org.objectweb.asm.Opcodes; 32 import sun.security.action.GetBooleanAction; 33 34 import java.io.IOException; 35 import java.lang.invoke.MethodType; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.HashMap; 41 import java.util.LinkedHashMap; 42 import java.util.LinkedList; 43 import java.util.List; 44 import java.util.ListIterator; 45 import java.util.Map; 46 47 import static jdk.internal.org.objectweb.asm.Opcodes.*; 48 49 /** 50 * ProxyGenerator contains the code to generate a dynamic proxy class 51 * for the java.lang.reflect.Proxy API. 52 * <p> 53 * The external interface to ProxyGenerator is the static 54 * "generateProxyClass" method. 55 */ 56 final class ProxyGenerator extends ClassWriter { 57 58 private static final String JL_CLASS = "java/lang/Class"; 59 private static final String JL_OBJECT = "java/lang/Object"; 60 private static final String JL_THROWABLE = "java/lang/Throwable"; 61 private static final String JL_CLASS_NOT_FOUND_EX = "java/lang/ClassNotFoundException"; 62 private static final String JL_NO_CLASS_DEF_FOUND_ERROR = "java/lang/NoClassDefFoundError"; 63 private static final String JL_NO_SUCH_METHOD_EX = "java/lang/NoSuchMethodException"; 64 private static final String JL_NO_SUCH_METHOD_ERROR = "java/lang/NoSuchMethodError"; 65 66 private static final String JLR_INVOCATION_HANDLER = "java/lang/reflect/InvocationHandler"; 67 private static final String JLR_PROXY = "java/lang/reflect/Proxy"; 68 private static final String JLR_UNDECLARED_THROWABLE_EX = "java/lang/reflect/UndeclaredThrowableException"; 69 70 private static final String LJL_CLASS = "Ljava/lang/Class;"; 71 private static final String LJLR_METHOD = "Ljava/lang/reflect/Method;"; 72 private static final String LJLR_INVOCATION_HANDLER = "Ljava/lang/reflect/InvocationHandler;"; 73 74 private static final String MJLR_INVOCATIONHANDLER = "(Ljava/lang/reflect/InvocationHandler;)V"; 75 76 private static final String NAME_CTOR = "<init>"; 77 private static final String NAME_CLINIT = "<clinit>"; 78 79 private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; 80 81 /** 82 * name of field for storing a proxy instance's invocation handler 83 */ 84 private static final String handlerFieldName = "h"; 85 86 /** 87 * debugging flag for saving generated class files 88 */ 89 private static final boolean saveGeneratedFiles = 90 java.security.AccessController.doPrivileged( 91 new GetBooleanAction( 92 "jdk.proxy.ProxyGenerator.saveGeneratedFiles")); 93 94 /* Preloaded ProxyMethod objects for methods in java.lang.Object */ 95 private final static ProxyMethod hashCodeMethod; 96 private final static ProxyMethod equalsMethod; 97 private final static ProxyMethod toStringMethod; 98 99 static { 100 try { 101 hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), "m0"); 102 equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), "m1"); 103 toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), "m2"); 104 } catch (NoSuchMethodException e) { 105 throw new NoSuchMethodError(e.getMessage()); 106 } 107 } 108 109 /** 110 * Class loader 111 */ 112 private final ClassLoader loader; 113 114 /** 115 * Name of proxy class 116 */ 117 private final String className; 118 119 /** 120 * Proxy interfaces 121 */ 122 private final List<Class<?>> interfaces; 123 124 /** 125 * Proxy class access flags 126 */ 127 private final int accessFlags; 128 129 /** 130 * Maps method signature string to list of ProxyMethod objects for 131 * proxy methods with that signature. 132 * Kept in insertion order to make it easier to compare old and new. 133 */ 134 private final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>(); 135 136 /** 137 * Ordinal of next ProxyMethod object added to proxyMethods. 138 * Indexes are reserved for hashcode(0), equals(1), toString(2). 139 */ 140 private int proxyMethodCount = 3; 141 142 /** 143 * Construct a ProxyGenerator to generate a proxy class with the 144 * specified name and for the given interfaces. 145 * <p> 146 * A ProxyGenerator object contains the state for the ongoing 147 * generation of a particular proxy class. 148 */ 149 private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces, 150 int accessFlags) { 151 super(ClassWriter.COMPUTE_FRAMES); 152 this.loader = loader; 153 this.className = className; 154 this.interfaces = interfaces; 155 this.accessFlags = accessFlags; 156 } 157 158 /** 159 * Generate a proxy class given a name and a list of proxy interfaces. 160 * 161 * @param name the class name of the proxy class 162 * @param interfaces proxy interfaces 163 * @param accessFlags access flags of the proxy class 164 */ 165 static byte[] generateProxyClass(ClassLoader loader, 166 final String name, 167 List<Class<?>> interfaces, 168 int accessFlags) { 169 ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); 170 final byte[] classFile = gen.generateClassFile(); 171 172 if (saveGeneratedFiles) { 173 java.security.AccessController.doPrivileged( 174 new java.security.PrivilegedAction<Void>() { 175 public Void run() { 176 try { 177 int i = name.lastIndexOf('.'); 178 Path path; 179 if (i > 0) { 180 Path dir = Path.of(dotToSlash(name.substring(0, i))); 181 Files.createDirectories(dir); 182 path = dir.resolve(name.substring(i + 1) + ".class"); 183 } else { 184 path = Path.of(name + ".class"); 185 } 186 Files.write(path, classFile); 187 return null; 188 } catch (IOException e) { 189 throw new InternalError( 190 "I/O exception saving generated file: " + e); 191 } 192 } 193 }); 194 } 195 196 return classFile; 197 } 198 199 /** 200 * Return an array of the type names from an array of Classes. 201 * 202 * @param classes an array of classes or interfaces 203 * @return the array of class names; or null if there are no classes 204 */ 205 private static String[] typeNames(List<Class<?>> classes) { 206 if (classes == null || classes.size() == 0) 207 return null; 208 int size = classes.size(); 209 String[] ifaces = new String[size]; 210 for (int i = 0; i < size; i++) 211 ifaces[i] = dotToSlash(classes.get(i).getName()); 212 return ifaces; 213 } 214 215 /** 216 * For a given set of proxy methods with the same signature, check 217 * that their return types are compatible according to the Proxy 218 * specification. 219 * 220 * Specifically, if there is more than one such method, then all 221 * of the return types must be reference types, and there must be 222 * one return type that is assignable to each of the rest of them. 223 */ 224 private static void checkReturnTypes(List<ProxyMethod> methods) { 225 /* 226 * If there is only one method with a given signature, there 227 * cannot be a conflict. This is the only case in which a 228 * primitive (or void) return type is allowed. 229 */ 230 if (methods.size() < 2) { 231 return; 232 } 233 234 /* 235 * List of return types that are not yet known to be 236 * assignable from ("covered" by) any of the others. 237 */ 238 LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<>(); 239 240 nextNewReturnType: 241 for (ProxyMethod pm : methods) { 242 Class<?> newReturnType = pm.returnType; 243 if (newReturnType.isPrimitive()) { 244 throw new IllegalArgumentException( 245 "methods with same signature " + 246 pm.shortSignature + 247 " but incompatible return types: " + 248 newReturnType.getName() + " and others"); 249 } 250 boolean added = false; 251 252 /* 253 * Compare the new return type to the existing uncovered 254 * return types. 255 */ 256 ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator(); 257 while (liter.hasNext()) { 258 Class<?> uncoveredReturnType = liter.next(); 259 260 /* 261 * If an existing uncovered return type is assignable 262 * to this new one, then we can forget the new one. 263 */ 264 if (newReturnType.isAssignableFrom(uncoveredReturnType)) { 265 assert !added; 266 continue nextNewReturnType; 267 } 268 269 /* 270 * If the new return type is assignable to an existing 271 * uncovered one, then should replace the existing one 272 * with the new one (or just forget the existing one, 273 * if the new one has already be put in the list). 274 */ 275 if (uncoveredReturnType.isAssignableFrom(newReturnType)) { 276 // (we can assume that each return type is unique) 277 if (!added) { 278 liter.set(newReturnType); 279 added = true; 280 } else { 281 liter.remove(); 282 } 283 } 284 } 285 286 /* 287 * If we got through the list of existing uncovered return 288 * types without an assignability relationship, then add 289 * the new return type to the list of uncovered ones. 290 */ 291 if (!added) { 292 uncoveredReturnTypes.add(newReturnType); 293 } 294 } 295 296 /* 297 * We shouldn't end up with more than one return type that is 298 * not assignable from any of the others. 299 */ 300 if (uncoveredReturnTypes.size() > 1) { 301 ProxyMethod pm = methods.get(0); 302 throw new IllegalArgumentException( 303 "methods with same signature " + 304 pm.shortSignature + 305 " but incompatible return types: " + uncoveredReturnTypes); 306 } 307 } 308 309 /** 310 * Given the exceptions declared in the throws clause of a proxy method, 311 * compute the exceptions that need to be caught from the invocation 312 * handler's invoke method and rethrown intact in the method's 313 * implementation before catching other Throwables and wrapping them 314 * in UndeclaredThrowableExceptions. 315 * 316 * The exceptions to be caught are returned in a List object. Each 317 * exception in the returned list is guaranteed to not be a subclass of 318 * any of the other exceptions in the list, so the catch blocks for 319 * these exceptions may be generated in any order relative to each other. 320 * 321 * Error and RuntimeException are each always contained by the returned 322 * list (if none of their superclasses are contained), since those 323 * unchecked exceptions should always be rethrown intact, and thus their 324 * subclasses will never appear in the returned list. 325 * 326 * The returned List will be empty if java.lang.Throwable is in the 327 * given list of declared exceptions, indicating that no exceptions 328 * need to be caught. 329 */ 330 private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) { 331 List<Class<?>> uniqueList = new ArrayList<>(); 332 // unique exceptions to catch 333 334 uniqueList.add(Error.class); // always catch/rethrow these 335 uniqueList.add(RuntimeException.class); 336 337 nextException: 338 for (Class<?> ex : exceptions) { 339 if (ex.isAssignableFrom(Throwable.class)) { 340 /* 341 * If Throwable is declared to be thrown by the proxy method, 342 * then no catch blocks are necessary, because the invoke 343 * can, at most, throw Throwable anyway. 344 */ 345 uniqueList.clear(); 346 break; 347 } else if (!Throwable.class.isAssignableFrom(ex)) { 348 /* 349 * Ignore types that cannot be thrown by the invoke method. 350 */ 351 continue; 352 } 353 /* 354 * Compare this exception against the current list of 355 * exceptions that need to be caught: 356 */ 357 for (int j = 0; j < uniqueList.size(); ) { 358 Class<?> ex2 = uniqueList.get(j); 359 if (ex2.isAssignableFrom(ex)) { 360 /* 361 * if a superclass of this exception is already on 362 * the list to catch, then ignore this one and continue; 363 */ 364 continue nextException; 365 } else if (ex.isAssignableFrom(ex2)) { 366 /* 367 * if a subclass of this exception is on the list 368 * to catch, then remove it; 369 */ 370 uniqueList.remove(j); 371 } else { 372 j++; // else continue comparing. 373 } 374 } 375 // This exception is unique (so far): add it to the list to catch. 376 uniqueList.add(ex); 377 } 378 return uniqueList; 379 } 380 381 /** 382 * Convert a fully qualified class name that uses '.' as the package 383 * separator, the external representation used by the Java language 384 * and APIs, to a fully qualified class name that uses '/' as the 385 * package separator, the representation used in the class file 386 * format (see JVMS section 4.2). 387 */ 388 private static String dotToSlash(String name) { 389 return name.replace('.', '/'); 390 } 391 392 /** 393 * Return the number of abstract "words", or consecutive local variable 394 * indexes, required to contain a value of the given type. See JVMS 395 * section 3.6.1. 396 * <p> 397 * Note that the original version of the JVMS contained a definition of 398 * this abstract notion of a "word" in section 3.4, but that definition 399 * was removed for the second edition. 400 */ 401 private static int getWordsPerType(Class<?> type) { 402 if (type == long.class || type == double.class) { 403 return 2; 404 } else { 405 return 1; 406 } 407 } 408 409 /** 410 * Add to the given list all of the types in the "from" array that 411 * are not already contained in the list and are assignable to at 412 * least one of the types in the "with" array. 413 * <p> 414 * This method is useful for computing the greatest common set of 415 * declared exceptions from duplicate methods inherited from 416 * different interfaces. 417 */ 418 private static void collectCompatibleTypes(Class<?>[] from, 419 Class<?>[] with, 420 List<Class<?>> list) { 421 for (Class<?> fc : from) { 422 if (!list.contains(fc)) { 423 for (Class<?> wc : with) { 424 if (wc.isAssignableFrom(fc)) { 425 list.add(fc); 426 break; 427 } 428 } 429 } 430 } 431 } 432 433 /** 434 * Returns the {@link ClassLoader} to be used by the default implementation of {@link 435 * #getCommonSuperClass(String, String)}, that of this {@link ClassWriter}'s runtime type by 436 * default. 437 * 438 * @return ClassLoader 439 */ 440 protected ClassLoader getClassLoader() { 441 return loader; 442 } 443 444 /** 445 * Generate a class file for the proxy class. This method drives the 446 * class file generation process. 447 */ 448 private byte[] generateClassFile() { 449 visit(V15, accessFlags, dotToSlash(className), null, 450 JLR_PROXY, typeNames(interfaces)); 451 452 /* 453 * Add proxy methods for the hashCode, equals, 454 * and toString methods of java.lang.Object. This is done before 455 * the methods from the proxy interfaces so that the methods from 456 * java.lang.Object take precedence over duplicate methods in the 457 * proxy interfaces. 458 */ 459 addProxyMethod(hashCodeMethod); 460 addProxyMethod(equalsMethod); 461 addProxyMethod(toStringMethod); 462 463 /* 464 * Accumulate all of the methods from the proxy interfaces. 465 */ 466 for (Class<?> intf : interfaces) { 467 for (Method m : intf.getMethods()) { 468 if (!Modifier.isStatic(m.getModifiers())) { 469 addProxyMethod(m, intf); 470 } 471 } 472 } 473 474 /* 475 * For each set of proxy methods with the same signature, 476 * verify that the methods' return types are compatible. 477 */ 478 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 479 checkReturnTypes(sigmethods); 480 } 481 482 generateConstructor(); 483 484 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 485 for (ProxyMethod pm : sigmethods) { 486 // add static field for the Method object 487 visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName, 488 LJLR_METHOD, null, null); 489 490 // Generate code for proxy method 491 pm.generateMethod(this, className); 492 } 493 } 494 495 generateStaticInitializer(); 496 497 return toByteArray(); 498 } 499 500 /** 501 * Add another method to be proxied, either by creating a new 502 * ProxyMethod object or augmenting an old one for a duplicate 503 * method. 504 * 505 * "fromClass" indicates the proxy interface that the method was 506 * found through, which may be different from (a subinterface of) 507 * the method's "declaring class". Note that the first Method 508 * object passed for a given name and descriptor identifies the 509 * Method object (and thus the declaring class) that will be 510 * passed to the invocation handler's "invoke" method for a given 511 * set of duplicate methods. 512 */ 513 private void addProxyMethod(Method m, Class<?> fromClass) { 514 Class<?> returnType = m.getReturnType(); 515 Class<?>[] exceptionTypes = m.getExceptionTypes(); 516 517 String sig = m.toShortSignature(); 518 List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig, 519 (f) -> new ArrayList<>(3)); 520 for (ProxyMethod pm : sigmethods) { 521 if (returnType == pm.returnType) { 522 /* 523 * Found a match: reduce exception types to the 524 * greatest set of exceptions that can be thrown 525 * compatibly with the throws clauses of both 526 * overridden methods. 527 */ 528 List<Class<?>> legalExceptions = new ArrayList<>(); 529 collectCompatibleTypes( 530 exceptionTypes, pm.exceptionTypes, legalExceptions); 531 collectCompatibleTypes( 532 pm.exceptionTypes, exceptionTypes, legalExceptions); 533 pm.exceptionTypes = legalExceptions.toArray(EMPTY_CLASS_ARRAY); 534 return; 535 } 536 } 537 sigmethods.add(new ProxyMethod(m, sig, m.getParameterTypes(), returnType, 538 exceptionTypes, fromClass, 539 "m" + proxyMethodCount++)); 540 } 541 542 /** 543 * Add an existing ProxyMethod (hashcode, equals, toString). 544 * 545 * @param pm an existing ProxyMethod 546 */ 547 private void addProxyMethod(ProxyMethod pm) { 548 String sig = pm.shortSignature; 549 List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig, 550 (f) -> new ArrayList<>(3)); 551 sigmethods.add(pm); 552 } 553 554 /** 555 * Generate the constructor method for the proxy class. 556 */ 557 private void generateConstructor() { 558 MethodVisitor ctor = visitMethod(Modifier.PUBLIC, NAME_CTOR, 559 MJLR_INVOCATIONHANDLER, null, null); 560 ctor.visitParameter(null, 0); 561 ctor.visitCode(); 562 ctor.visitVarInsn(ALOAD, 0); 563 ctor.visitVarInsn(ALOAD, 1); 564 ctor.visitMethodInsn(INVOKESPECIAL, JLR_PROXY, NAME_CTOR, 565 MJLR_INVOCATIONHANDLER, false); 566 ctor.visitInsn(RETURN); 567 568 // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored 569 ctor.visitMaxs(-1, -1); 570 ctor.visitEnd(); 571 } 572 573 /** 574 * Generate the static initializer method for the proxy class. 575 */ 576 private void generateStaticInitializer() { 577 578 MethodVisitor mv = visitMethod(Modifier.STATIC, NAME_CLINIT, 579 "()V", null, null); 580 mv.visitCode(); 581 Label L_startBlock = new Label(); 582 Label L_endBlock = new Label(); 583 Label L_NoMethodHandler = new Label(); 584 Label L_NoClassHandler = new Label(); 585 586 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_NoMethodHandler, 587 JL_NO_SUCH_METHOD_EX); 588 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_NoClassHandler, 589 JL_CLASS_NOT_FOUND_EX); 590 591 mv.visitLabel(L_startBlock); 592 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 593 for (ProxyMethod pm : sigmethods) { 594 pm.codeFieldInitialization(mv, className); 595 } 596 } 597 mv.visitInsn(RETURN); 598 mv.visitLabel(L_endBlock); 599 // Generate exception handler 600 601 mv.visitLabel(L_NoMethodHandler); 602 mv.visitVarInsn(ASTORE, 1); 603 mv.visitTypeInsn(Opcodes.NEW, JL_NO_SUCH_METHOD_ERROR); 604 mv.visitInsn(DUP); 605 mv.visitVarInsn(ALOAD, 1); 606 mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE, 607 "getMessage", "()Ljava/lang/String;", false); 608 mv.visitMethodInsn(INVOKESPECIAL, JL_NO_SUCH_METHOD_ERROR, 609 "<init>", "(Ljava/lang/String;)V", false); 610 mv.visitInsn(ATHROW); 611 612 mv.visitLabel(L_NoClassHandler); 613 mv.visitVarInsn(ASTORE, 1); 614 mv.visitTypeInsn(Opcodes.NEW, JL_NO_CLASS_DEF_FOUND_ERROR); 615 mv.visitInsn(DUP); 616 mv.visitVarInsn(ALOAD, 1); 617 mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE, 618 "getMessage", "()Ljava/lang/String;", false); 619 mv.visitMethodInsn(INVOKESPECIAL, JL_NO_CLASS_DEF_FOUND_ERROR, 620 "<init>", "(Ljava/lang/String;)V", false); 621 mv.visitInsn(ATHROW); 622 623 // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored 624 mv.visitMaxs(-1, -1); 625 mv.visitEnd(); 626 } 627 628 /** 629 * A ProxyMethod object represents a proxy method in the proxy class 630 * being generated: a method whose implementation will encode and 631 * dispatch invocations to the proxy instance's invocation handler. 632 */ 633 private static class ProxyMethod { 634 635 private final Method method; 636 private final String shortSignature; 637 private final Class<?> fromClass; 638 private final Class<?>[] parameterTypes; 639 private final Class<?> returnType; 640 private final String methodFieldName; 641 private Class<?>[] exceptionTypes; 642 643 private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes, 644 Class<?> returnType, Class<?>[] exceptionTypes, 645 Class<?> fromClass, String methodFieldName) { 646 this.method = method; 647 this.shortSignature = sig; 648 this.parameterTypes = parameterTypes; 649 this.returnType = returnType; 650 this.exceptionTypes = exceptionTypes; 651 this.fromClass = fromClass; 652 this.methodFieldName = methodFieldName; 653 } 654 655 /** 656 * Create a new specific ProxyMethod with a specific field name 657 * 658 * @param method The method for which to create a proxy 659 * @param methodFieldName the fieldName to generate 660 */ 661 private ProxyMethod(Method method, String methodFieldName) { 662 this(method, method.toShortSignature(), 663 method.getParameterTypes(), method.getReturnType(), 664 method.getExceptionTypes(), method.getDeclaringClass(), methodFieldName); 665 } 666 667 /** 668 * Generate this method, including the code and exception table entry. 669 */ 670 private void generateMethod(ClassWriter cw, String className) { 671 MethodType mt = MethodType.methodType(returnType, parameterTypes); 672 String desc = mt.toMethodDescriptorString(); 673 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_FINAL, 674 method.getName(), desc, null, 675 typeNames(Arrays.asList(exceptionTypes))); 676 677 int[] parameterSlot = new int[parameterTypes.length]; 678 int nextSlot = 1; 679 for (int i = 0; i < parameterSlot.length; i++) { 680 parameterSlot[i] = nextSlot; 681 nextSlot += getWordsPerType(parameterTypes[i]); 682 } 683 684 mv.visitCode(); 685 Label L_startBlock = new Label(); 686 Label L_endBlock = new Label(); 687 Label L_RuntimeHandler = new Label(); 688 Label L_ThrowableHandler = new Label(); 689 690 List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes); 691 if (catchList.size() > 0) { 692 for (Class<?> ex : catchList) { 693 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler, 694 dotToSlash(ex.getName())); 695 } 696 697 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler, 698 JL_THROWABLE); 699 } 700 mv.visitLabel(L_startBlock); 701 702 mv.visitVarInsn(ALOAD, 0); 703 mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName, 704 LJLR_INVOCATION_HANDLER); 705 mv.visitVarInsn(ALOAD, 0); 706 mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName, 707 LJLR_METHOD); 708 709 if (parameterTypes.length > 0) { 710 // Create an array and fill with the parameters converting primitives to wrappers 711 emitIconstInsn(mv, parameterTypes.length); 712 mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT); 713 for (int i = 0; i < parameterTypes.length; i++) { 714 mv.visitInsn(DUP); 715 emitIconstInsn(mv, i); 716 codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]); 717 mv.visitInsn(Opcodes.AASTORE); 718 } 719 } else { 720 mv.visitInsn(Opcodes.ACONST_NULL); 721 } 722 723 mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER, 724 "invoke", 725 "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + 726 "[Ljava/lang/Object;)Ljava/lang/Object;", true); 727 728 if (returnType == void.class) { 729 mv.visitInsn(POP); 730 mv.visitInsn(RETURN); 731 } else { 732 codeUnwrapReturnValue(mv, returnType); 733 } 734 735 mv.visitLabel(L_endBlock); 736 737 // Generate exception handler 738 mv.visitLabel(L_RuntimeHandler); 739 mv.visitInsn(ATHROW); // just rethrow the exception 740 741 mv.visitLabel(L_ThrowableHandler); 742 mv.visitVarInsn(ASTORE, 1); 743 mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX); 744 mv.visitInsn(DUP); 745 mv.visitVarInsn(ALOAD, 1); 746 mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX, 747 "<init>", "(Ljava/lang/Throwable;)V", false); 748 mv.visitInsn(ATHROW); 749 // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored 750 mv.visitMaxs(-1, -1); 751 mv.visitEnd(); 752 } 753 754 /** 755 * Generate code for wrapping an argument of the given type 756 * whose value can be found at the specified local variable 757 * index, in order for it to be passed (as an Object) to the 758 * invocation handler's "invoke" method. 759 */ 760 private void codeWrapArgument(MethodVisitor mv, Class<?> type, int slot) { 761 if (type.isPrimitive()) { 762 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); 763 764 if (type == int.class || 765 type == boolean.class || 766 type == byte.class || 767 type == char.class || 768 type == short.class) { 769 mv.visitVarInsn(ILOAD, slot); 770 } else if (type == long.class) { 771 mv.visitVarInsn(LLOAD, slot); 772 } else if (type == float.class) { 773 mv.visitVarInsn(FLOAD, slot); 774 } else if (type == double.class) { 775 mv.visitVarInsn(DLOAD, slot); 776 } else { 777 throw new AssertionError(); 778 } 779 mv.visitMethodInsn(INVOKESTATIC, prim.wrapperClassName, "valueOf", 780 prim.wrapperValueOfDesc, false); 781 } else { 782 mv.visitVarInsn(ALOAD, slot); 783 } 784 } 785 786 /** 787 * Generate code for unwrapping a return value of the given 788 * type from the invocation handler's "invoke" method (as type 789 * Object) to its correct type. 790 */ 791 private void codeUnwrapReturnValue(MethodVisitor mv, Class<?> type) { 792 if (type.isPrimitive()) { 793 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); 794 795 mv.visitTypeInsn(CHECKCAST, prim.wrapperClassName); 796 mv.visitMethodInsn(INVOKEVIRTUAL, 797 prim.wrapperClassName, 798 prim.unwrapMethodName, prim.unwrapMethodDesc, false); 799 800 if (type == int.class || 801 type == boolean.class || 802 type == byte.class || 803 type == char.class || 804 type == short.class) { 805 mv.visitInsn(IRETURN); 806 } else if (type == long.class) { 807 mv.visitInsn(LRETURN); 808 } else if (type == float.class) { 809 mv.visitInsn(FRETURN); 810 } else if (type == double.class) { 811 mv.visitInsn(DRETURN); 812 } else { 813 throw new AssertionError(); 814 } 815 } else { 816 String internalName = dotToSlash(type.getName()); 817 if (type.isInlineClass() && !type.isIndirectType()) { 818 internalName = 'Q' + internalName + ";"; 819 } 820 mv.visitTypeInsn(CHECKCAST, internalName); 821 mv.visitInsn(ARETURN); 822 } 823 } 824 825 /** 826 * Generate code for initializing the static field that stores 827 * the Method object for this proxy method. 828 */ 829 private void codeFieldInitialization(MethodVisitor mv, String className) { 830 codeClassForName(mv, fromClass); 831 832 mv.visitLdcInsn(method.getName()); 833 834 emitIconstInsn(mv, parameterTypes.length); 835 836 mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS); 837 838 // Construct an array with the parameter types mapping primitives to Wrapper types 839 for (int i = 0; i < parameterTypes.length; i++) { 840 mv.visitInsn(DUP); 841 emitIconstInsn(mv, i); 842 843 if (parameterTypes[i].isPrimitive()) { 844 PrimitiveTypeInfo prim = 845 PrimitiveTypeInfo.get(parameterTypes[i]); 846 mv.visitFieldInsn(GETSTATIC, 847 prim.wrapperClassName, "TYPE", LJL_CLASS); 848 } else { 849 codeClassForName(mv, parameterTypes[i]); 850 } 851 mv.visitInsn(Opcodes.AASTORE); 852 } 853 // lookup the method 854 mv.visitMethodInsn(INVOKEVIRTUAL, 855 JL_CLASS, 856 "getMethod", 857 "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", 858 false); 859 860 mv.visitFieldInsn(PUTSTATIC, 861 dotToSlash(className), 862 methodFieldName, LJLR_METHOD); 863 } 864 865 /* 866 * =============== Code Generation Utility Methods =============== 867 */ 868 869 /** 870 * Generate code to invoke the Class.forName with the name of the given 871 * class to get its Class object at runtime. And also generate code 872 * to invoke Class.asPrimaryType if the class is regular value type. 873 * 874 * The code is written to the supplied stream. Note that the code generated 875 * by this method may caused the checked ClassNotFoundException to be thrown. 876 */ 877 private void codeClassForName(MethodVisitor mv, Class<?> cl) { 878 mv.visitLdcInsn(cl.getName()); 879 mv.visitMethodInsn(INVOKESTATIC, 880 JL_CLASS, 881 "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false); 882 if (cl.isInlineClass() && cl == cl.asPrimaryType()) { 883 mv.visitMethodInsn(INVOKEVIRTUAL, 884 JL_CLASS, 885 "asPrimaryType", "()Ljava/lang/Class;", false); 886 } 887 } 888 889 /** 890 * Visit a bytecode for a constant. 891 * 892 * @param mv The MethodVisitor 893 * @param cst The constant value 894 */ 895 private void emitIconstInsn(MethodVisitor mv, final int cst) { 896 if (cst >= -1 && cst <= 5) { 897 mv.visitInsn(Opcodes.ICONST_0 + cst); 898 } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { 899 mv.visitIntInsn(Opcodes.BIPUSH, cst); 900 } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { 901 mv.visitIntInsn(Opcodes.SIPUSH, cst); 902 } else { 903 mv.visitLdcInsn(cst); 904 } 905 } 906 907 @Override 908 public String toString() { 909 return method.toShortString(); 910 } 911 } 912 913 /** 914 * A PrimitiveTypeInfo object contains assorted information about 915 * a primitive type in its public fields. The struct for a particular 916 * primitive type can be obtained using the static "get" method. 917 */ 918 private static class PrimitiveTypeInfo { 919 920 private static Map<Class<?>, PrimitiveTypeInfo> table = new HashMap<>(); 921 922 static { 923 add(byte.class, Byte.class); 924 add(char.class, Character.class); 925 add(double.class, Double.class); 926 add(float.class, Float.class); 927 add(int.class, Integer.class); 928 add(long.class, Long.class); 929 add(short.class, Short.class); 930 add(boolean.class, Boolean.class); 931 } 932 933 /** 934 * name of corresponding wrapper class 935 */ 936 private String wrapperClassName; 937 /** 938 * method descriptor for wrapper class "valueOf" factory method 939 */ 940 private String wrapperValueOfDesc; 941 /** 942 * name of wrapper class method for retrieving primitive value 943 */ 944 private String unwrapMethodName; 945 /** 946 * descriptor of same method 947 */ 948 private String unwrapMethodDesc; 949 950 private PrimitiveTypeInfo(Class<?> primitiveClass, Class<?> wrapperClass) { 951 assert primitiveClass.isPrimitive(); 952 953 /** 954 * "base type" used in various descriptors (see JVMS section 4.3.2) 955 */ 956 String baseTypeString = 957 Array.newInstance(primitiveClass, 0) 958 .getClass().getName().substring(1); 959 wrapperClassName = dotToSlash(wrapperClass.getName()); 960 wrapperValueOfDesc = 961 "(" + baseTypeString + ")L" + wrapperClassName + ";"; 962 unwrapMethodName = primitiveClass.getName() + "Value"; 963 unwrapMethodDesc = "()" + baseTypeString; 964 } 965 966 private static void add(Class<?> primitiveClass, Class<?> wrapperClass) { 967 table.put(primitiveClass, 968 new PrimitiveTypeInfo(primitiveClass, wrapperClass)); 969 } 970 971 public static PrimitiveTypeInfo get(Class<?> cl) { 972 return table.get(cl); 973 } 974 } 975 }