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