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