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