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