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