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