1 /* 2 * Copyright (c) 1999, 2025, 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 java.io.IOException; 29 import java.lang.classfile.*; 30 import java.lang.classfile.attribute.ExceptionsAttribute; 31 import java.lang.classfile.constantpool.*; 32 import java.lang.constant.ClassDesc; 33 import java.lang.constant.MethodTypeDesc; 34 import java.nio.file.Files; 35 import java.nio.file.Path; 36 import java.util.ArrayList; 37 import java.util.LinkedHashMap; 38 import java.util.List; 39 import java.util.ListIterator; 40 import java.util.Map; 41 import java.util.Objects; 42 43 import jdk.internal.constant.ClassOrInterfaceDescImpl; 44 import jdk.internal.constant.ConstantUtils; 45 import jdk.internal.constant.MethodTypeDescImpl; 46 47 import static java.lang.classfile.ClassFile.*; 48 import java.lang.classfile.attribute.StackMapFrameInfo; 49 import java.lang.classfile.attribute.StackMapTableAttribute; 50 51 import static java.lang.constant.ConstantDescs.*; 52 import static jdk.internal.constant.ConstantUtils.*; 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 { 62 63 private static final ClassFile CF_CONTEXT = 64 ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS); 65 66 private static final ClassDesc 67 CD_ClassLoader = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/ClassLoader;"), 68 CD_Class_array = CD_Class.arrayType(), 69 CD_ClassNotFoundException = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/ClassNotFoundException;"), 70 CD_NoClassDefFoundError = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/NoClassDefFoundError;"), 71 CD_IllegalAccessException = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/IllegalAccessException;"), 72 CD_InvocationHandler = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/reflect/InvocationHandler;"), 73 CD_Method = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/reflect/Method;"), 74 CD_NoSuchMethodError = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/NoSuchMethodError;"), 75 CD_NoSuchMethodException = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/NoSuchMethodException;"), 76 CD_Object_array = ConstantUtils.CD_Object_array, 77 CD_Proxy = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/reflect/Proxy;"), 78 CD_UndeclaredThrowableException = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/reflect/UndeclaredThrowableException;"); 79 80 private static final MethodTypeDesc 81 MTD_boolean = MethodTypeDescImpl.ofValidated(CD_boolean), 82 MTD_void_InvocationHandler = MethodTypeDescImpl.ofValidated(CD_void, CD_InvocationHandler), 83 MTD_void_String = MethodTypeDescImpl.ofValidated(CD_void, CD_String), 84 MTD_void_Throwable = MethodTypeDescImpl.ofValidated(CD_void, CD_Throwable), 85 MTD_Class = MethodTypeDescImpl.ofValidated(CD_Class), 86 MTD_Class_String_boolean_ClassLoader = MethodTypeDescImpl.ofValidated(CD_Class, CD_String, CD_boolean, CD_ClassLoader), 87 MTD_ClassLoader = MethodTypeDescImpl.ofValidated(CD_ClassLoader), 88 MTD_Method_String_Class_array = MethodTypeDescImpl.ofValidated(CD_Method, CD_String, CD_Class_array), 89 MTD_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup), 90 MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup), 91 MTD_Object_Object_Method_ObjectArray = MethodTypeDescImpl.ofValidated(CD_Object, CD_Object, CD_Method, CD_Object_array), 92 MTD_String = MethodTypeDescImpl.ofValidated(CD_String); 93 94 private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup"; 95 96 private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; 97 98 /** 99 * name of field for storing a proxy instance's invocation handler 100 */ 101 private static final String NAME_HANDLER_FIELD = "h"; 102 103 /** 104 * debugging flag for saving generated class files 105 */ 106 private static final boolean SAVE_GENERATED_FILES = 107 Boolean.getBoolean("jdk.proxy.ProxyGenerator.saveGeneratedFiles"); 108 109 110 /* Preloaded ProxyMethod objects for methods in java.lang.Object */ 111 private static final Method OBJECT_HASH_CODE_METHOD; 112 private static final Method OBJECT_EQUALS_METHOD; 113 private static final Method OBJECT_TO_STRING_METHOD; 114 115 private static final String OBJECT_HASH_CODE_SIG; 116 private static final String OBJECT_EQUALS_SIG; 117 private static final String OBJECT_TO_STRING_SIG; 118 119 static { 120 try { 121 OBJECT_HASH_CODE_METHOD = Object.class.getMethod("hashCode"); 122 OBJECT_HASH_CODE_SIG = OBJECT_HASH_CODE_METHOD.toShortSignature(); 123 OBJECT_EQUALS_METHOD = Object.class.getMethod("equals", Object.class); 124 OBJECT_EQUALS_SIG = OBJECT_EQUALS_METHOD.toShortSignature(); 125 OBJECT_TO_STRING_METHOD = Object.class.getMethod("toString"); 126 OBJECT_TO_STRING_SIG = OBJECT_TO_STRING_METHOD.toShortSignature(); 127 } catch (NoSuchMethodException e) { 128 throw new NoSuchMethodError(e.getMessage()); 129 } 130 } 131 132 private final ConstantPoolBuilder cp; 133 private final List<StackMapFrameInfo.VerificationTypeInfo> classLoaderLocal, throwableStack; 134 private final NameAndTypeEntry exInit; 135 private final ClassEntry objectCE, proxyCE, uteCE, classCE; 136 private final FieldRefEntry handlerField; 137 private final InterfaceMethodRefEntry invocationHandlerInvoke; 138 private final MethodRefEntry uteInit, classGetMethod, classForName, throwableGetMessage; 139 140 141 /** 142 * ClassEntry for this proxy class 143 */ 144 private final ClassEntry thisClassCE; 145 146 /** 147 * Proxy interfaces 148 */ 149 private final List<Class<?>> interfaces; 150 151 /** 152 * Proxy class access flags 153 */ 154 private final int accessFlags; 155 156 /** 157 * Maps method signature string to list of ProxyMethod objects for 158 * proxy methods with that signature. 159 * Kept in insertion order to make it easier to compare old and new. 160 */ 161 private final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>(); 162 163 /** 164 * Ordinal of next ProxyMethod object added to proxyMethods. 165 * Indexes are reserved for hashcode(0), equals(1), toString(2). 166 */ 167 private int proxyMethodCount = 3; 168 169 /** 170 * Construct a ProxyGenerator to generate a proxy class with the 171 * specified name and for the given interfaces. 172 * <p> 173 * A ProxyGenerator object contains the state for the ongoing 174 * generation of a particular proxy class. 175 */ 176 private ProxyGenerator(String className, List<Class<?>> interfaces, 177 int accessFlags) { 178 this.cp = ConstantPoolBuilder.of(); 179 this.thisClassCE = cp.classEntry(ConstantUtils.binaryNameToDesc(className)); 180 this.interfaces = interfaces; 181 this.accessFlags = accessFlags; 182 var throwable = cp.classEntry(CD_Throwable); 183 this.classLoaderLocal = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(cp.classEntry(CD_ClassLoader))); 184 this.throwableStack = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(throwable)); 185 this.exInit = cp.nameAndTypeEntry(INIT_NAME, MTD_void_String); 186 this.objectCE = cp.classEntry(CD_Object); 187 this.proxyCE = cp.classEntry(CD_Proxy); 188 this.classCE = cp.classEntry(CD_Class); 189 this.handlerField = cp.fieldRefEntry(proxyCE, cp.nameAndTypeEntry(NAME_HANDLER_FIELD, CD_InvocationHandler)); 190 this.invocationHandlerInvoke = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray); 191 this.uteCE = cp.classEntry(CD_UndeclaredThrowableException); 192 this.uteInit = cp.methodRefEntry(uteCE, cp.nameAndTypeEntry(INIT_NAME, MTD_void_Throwable)); 193 this.classGetMethod = cp.methodRefEntry(classCE, cp.nameAndTypeEntry("getMethod", MTD_Method_String_Class_array)); 194 this.classForName = cp.methodRefEntry(classCE, cp.nameAndTypeEntry("forName", MTD_Class_String_boolean_ClassLoader)); 195 this.throwableGetMessage = cp.methodRefEntry(throwable, cp.nameAndTypeEntry("getMessage", MTD_String)); 196 } 197 198 /** 199 * Generate a proxy class given a name and a list of proxy interfaces. 200 * 201 * @param name the class name of the proxy class 202 * @param interfaces proxy interfaces 203 * @param accessFlags access flags of the proxy class 204 */ 205 static byte[] generateProxyClass(ClassLoader loader, 206 final String name, 207 List<Class<?>> interfaces, 208 int accessFlags) { 209 Objects.requireNonNull(interfaces); 210 ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); 211 final byte[] classFile = gen.generateClassFile(); 212 213 if (SAVE_GENERATED_FILES) { 214 try { 215 int i = name.lastIndexOf('.'); 216 Path path; 217 if (i > 0) { 218 Path dir = Path.of(name.substring(0, i).replace('.', '/')); 219 Files.createDirectories(dir); 220 path = dir.resolve(name.substring(i + 1) + ".class"); 221 } else { 222 path = Path.of(name + ".class"); 223 } 224 Files.write(path, classFile); 225 return null; 226 } catch (IOException e) { 227 throw new InternalError("I/O exception saving generated file: " + e); 228 } 229 } 230 231 return classFile; 232 } 233 234 /** 235 * {@return the entries of the given type} 236 * @param types the {@code Class} objects, not primitive types nor array types 237 */ 238 private static List<ClassEntry> toClassEntries(ConstantPoolBuilder cp, List<Class<?>> types) { 239 var ces = new ArrayList<ClassEntry>(types.size()); 240 for (var t : types) 241 ces.add(cp.classEntry(ConstantUtils.binaryNameToDesc(t.getName()))); 242 return ces; 243 } 244 245 /** 246 * For a given set of proxy methods with the same signature, check 247 * that their return types are compatible according to the Proxy 248 * specification. 249 * 250 * Specifically, if there is more than one such method, then all 251 * of the return types must be reference types, and there must be 252 * one return type that is assignable to each of the rest of them. 253 */ 254 private static void checkReturnTypes(List<ProxyMethod> methods) { 255 /* 256 * If there is only one method with a given signature, there 257 * cannot be a conflict. This is the only case in which a 258 * primitive (or void) return type is allowed. 259 */ 260 if (methods.size() < 2) { 261 return; 262 } 263 264 /* 265 * List of return types that are not yet known to be 266 * assignable from ("covered" by) any of the others. 267 */ 268 List<Class<?>> uncoveredReturnTypes = new ArrayList<>(1); 269 270 nextNewReturnType: 271 for (ProxyMethod pm : methods) { 272 Class<?> newReturnType = pm.returnType; 273 if (newReturnType.isPrimitive()) { 274 throw new IllegalArgumentException( 275 "methods with same signature " + 276 pm.shortSignature + 277 " but incompatible return types: " + 278 newReturnType.getName() + " and others"); 279 } 280 boolean added = false; 281 282 /* 283 * Compare the new return type to the existing uncovered 284 * return types. 285 */ 286 ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator(); 287 while (liter.hasNext()) { 288 Class<?> uncoveredReturnType = liter.next(); 289 290 /* 291 * If an existing uncovered return type is assignable 292 * to this new one, then we can forget the new one. 293 */ 294 if (newReturnType.isAssignableFrom(uncoveredReturnType)) { 295 assert !added; 296 continue nextNewReturnType; 297 } 298 299 /* 300 * If the new return type is assignable to an existing 301 * uncovered one, then should replace the existing one 302 * with the new one (or just forget the existing one, 303 * if the new one has already be put in the list). 304 */ 305 if (uncoveredReturnType.isAssignableFrom(newReturnType)) { 306 // (we can assume that each return type is unique) 307 if (!added) { 308 liter.set(newReturnType); 309 added = true; 310 } else { 311 liter.remove(); 312 } 313 } 314 } 315 316 /* 317 * If we got through the list of existing uncovered return 318 * types without an assignability relationship, then add 319 * the new return type to the list of uncovered ones. 320 */ 321 if (!added) { 322 uncoveredReturnTypes.add(newReturnType); 323 } 324 } 325 326 /* 327 * We shouldn't end up with more than one return type that is 328 * not assignable from any of the others. 329 */ 330 if (uncoveredReturnTypes.size() > 1) { 331 ProxyMethod pm = methods.getFirst(); 332 throw new IllegalArgumentException( 333 "methods with same signature " + 334 pm.shortSignature + 335 " but incompatible return types: " + uncoveredReturnTypes); 336 } 337 } 338 339 /** 340 * Given the exceptions declared in the throws clause of a proxy method, 341 * compute the exceptions that need to be caught from the invocation 342 * handler's invoke method and rethrown intact in the method's 343 * implementation before catching other Throwables and wrapping them 344 * in UndeclaredThrowableExceptions. 345 * 346 * The exceptions to be caught are returned in a List object. Each 347 * exception in the returned list is guaranteed to not be a subclass of 348 * any of the other exceptions in the list, so the catch blocks for 349 * these exceptions may be generated in any order relative to each other. 350 * 351 * Error and RuntimeException are each always contained by the returned 352 * list (if none of their superclasses are contained), since those 353 * unchecked exceptions should always be rethrown intact, and thus their 354 * subclasses will never appear in the returned list. 355 * 356 * The returned List will be empty if java.lang.Throwable is in the 357 * given list of declared exceptions, indicating that no exceptions 358 * need to be caught. 359 */ 360 private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) { 361 List<Class<?>> uniqueList = new ArrayList<>(); 362 // unique exceptions to catch 363 364 uniqueList.add(Error.class); // always catch/rethrow these 365 uniqueList.add(RuntimeException.class); 366 367 nextException: 368 for (Class<?> ex : exceptions) { 369 if (ex.isAssignableFrom(Throwable.class)) { 370 /* 371 * If Throwable is declared to be thrown by the proxy method, 372 * then no catch blocks are necessary, because the invoke 373 * can, at most, throw Throwable anyway. 374 */ 375 uniqueList.clear(); 376 break; 377 } else if (!Throwable.class.isAssignableFrom(ex)) { 378 /* 379 * Ignore types that cannot be thrown by the invoke method. 380 */ 381 continue; 382 } 383 /* 384 * Compare this exception against the current list of 385 * exceptions that need to be caught: 386 */ 387 for (int j = 0; j < uniqueList.size(); ) { 388 Class<?> ex2 = uniqueList.get(j); 389 if (ex2.isAssignableFrom(ex)) { 390 /* 391 * if a superclass of this exception is already on 392 * the list to catch, then ignore this one and continue; 393 */ 394 continue nextException; 395 } else if (ex.isAssignableFrom(ex2)) { 396 /* 397 * if a subclass of this exception is on the list 398 * to catch, then remove it; 399 */ 400 uniqueList.remove(j); 401 } else { 402 j++; // else continue comparing. 403 } 404 } 405 // This exception is unique (so far): add it to the list to catch. 406 uniqueList.add(ex); 407 } 408 return uniqueList; 409 } 410 411 /** 412 * Add to the given list all of the types in the "from" array that 413 * are not already contained in the list and are assignable to at 414 * least one of the types in the "with" array. 415 * <p> 416 * This method is useful for computing the greatest common set of 417 * declared exceptions from duplicate methods inherited from 418 * different interfaces. 419 */ 420 private static void collectCompatibleTypes(Class<?>[] from, 421 Class<?>[] with, 422 List<Class<?>> list) { 423 for (Class<?> fc : from) { 424 if (!list.contains(fc)) { 425 for (Class<?> wc : with) { 426 if (wc.isAssignableFrom(fc)) { 427 list.add(fc); 428 break; 429 } 430 } 431 } 432 } 433 } 434 435 /** 436 * Generate a class file for the proxy class. This method drives the 437 * class file generation process. 438 * 439 * If a proxy interface references any value classes, the value classes 440 * are listed in the loadable descriptors attribute of the interface class. The 441 * classes that are referenced by the proxy interface have already 442 * been loaded before the proxy class. Hence the proxy class is 443 * generated with no loadable descriptors attributes as it essentially has no effect. 444 */ 445 private byte[] generateClassFile() { 446 /* 447 * Add proxy methods for the hashCode, equals, 448 * and toString methods of java.lang.Object. This is done before 449 * the methods from the proxy interfaces so that the methods from 450 * java.lang.Object take precedence over duplicate methods in the 451 * proxy interfaces. 452 */ 453 addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, OBJECT_HASH_CODE_SIG, "m0")); 454 addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, OBJECT_EQUALS_SIG, "m1")); 455 addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, OBJECT_TO_STRING_SIG, "m2")); 456 457 /* 458 * Accumulate all of the methods from the proxy interfaces. 459 */ 460 for (Class<?> intf : interfaces) { 461 for (Method m : intf.getMethods()) { 462 if (!Modifier.isStatic(m.getModifiers())) { 463 addProxyMethod(m, intf); 464 } 465 } 466 } 467 468 /* 469 * For each set of proxy methods with the same signature, 470 * verify that the methods' return types are compatible. 471 */ 472 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 473 checkReturnTypes(sigmethods); 474 } 475 476 return CF_CONTEXT.build(thisClassCE, cp, clb -> { 477 clb.withSuperclass(proxyCE); 478 clb.withFlags(accessFlags); 479 clb.withInterfaces(toClassEntries(cp, interfaces)); 480 generateConstructor(clb); 481 482 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 483 for (ProxyMethod pm : sigmethods) { 484 // add static field for the Method object 485 clb.withField(pm.methodFieldName, CD_Method, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 486 487 // Generate code for proxy method 488 pm.generateMethod(clb); 489 } 490 } 491 492 generateStaticInitializer(clb); 493 generateLookupAccessor(clb); 494 }); 495 } 496 497 /** 498 * Add another method to be proxied, either by creating a new 499 * ProxyMethod object or augmenting an old one for a duplicate 500 * method. 501 * 502 * "fromClass" indicates the proxy interface that the method was 503 * found through, which may be different from (a subinterface of) 504 * the method's "declaring class". Note that the first Method 505 * object passed for a given name and descriptor identifies the 506 * Method object (and thus the declaring class) that will be 507 * passed to the invocation handler's "invoke" method for a given 508 * set of duplicate methods. 509 */ 510 private void addProxyMethod(Method m, Class<?> fromClass) { 511 Class<?> returnType = m.getReturnType(); 512 Class<?>[] exceptionTypes = m.getSharedExceptionTypes(); 513 514 String sig = m.toShortSignature(); 515 List<ProxyMethod> sigmethods = proxyMethodsFor(sig); 516 for (ProxyMethod pm : sigmethods) { 517 if (returnType == pm.returnType) { 518 /* 519 * Found a match: reduce exception types to the 520 * greatest set of exceptions that can be thrown 521 * compatibly with the throws clauses of both 522 * overridden methods. 523 */ 524 List<Class<?>> legalExceptions = new ArrayList<>(); 525 collectCompatibleTypes( 526 exceptionTypes, pm.exceptionTypes, legalExceptions); 527 collectCompatibleTypes( 528 pm.exceptionTypes, exceptionTypes, legalExceptions); 529 pm.exceptionTypes = legalExceptions.toArray(EMPTY_CLASS_ARRAY); 530 return; 531 } 532 } 533 sigmethods.add(new ProxyMethod(m, sig, returnType, 534 exceptionTypes, fromClass, "m" + proxyMethodCount++)); 535 } 536 537 private List<ProxyMethod> proxyMethodsFor(String sig) { 538 return proxyMethods.computeIfAbsent(sig, _ -> new ArrayList<>(3)); 539 } 540 541 /** 542 * Add an existing ProxyMethod (hashcode, equals, toString). 543 * 544 * @param pm an existing ProxyMethod 545 */ 546 private void addProxyMethod(ProxyMethod pm) { 547 proxyMethodsFor(pm.shortSignature).add(pm); 548 } 549 550 /** 551 * Generate the constructor method for the proxy class. 552 */ 553 private void generateConstructor(ClassBuilder clb) { 554 clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob 555 .aload(0) 556 .aload(1) 557 .invokespecial(cp.methodRefEntry(proxyCE, 558 cp.nameAndTypeEntry(INIT_NAME, MTD_void_InvocationHandler))) 559 .return_()); 560 } 561 562 /** 563 * Generate the class initializer. 564 */ 565 private void generateStaticInitializer(ClassBuilder clb) { 566 clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> { 567 // Put ClassLoader at local variable index 0, used by 568 // Class.forName(String, boolean, ClassLoader) calls 569 cob.ldc(thisClassCE) 570 .invokevirtual(cp.methodRefEntry(classCE, 571 cp.nameAndTypeEntry("getClassLoader", MTD_ClassLoader))) 572 .astore(0); 573 var ts = cob.newBoundLabel(); 574 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 575 for (ProxyMethod pm : sigmethods) { 576 pm.codeFieldInitialization(cob); 577 } 578 } 579 cob.return_(); 580 var c1 = cob.newBoundLabel(); 581 var nsmError = cp.classEntry(CD_NoSuchMethodError); 582 cob.exceptionCatch(ts, c1, c1, CD_NoSuchMethodException) 583 .new_(nsmError) 584 .dup_x1() 585 .swap() 586 .invokevirtual(throwableGetMessage) 587 .invokespecial(cp.methodRefEntry(nsmError, exInit)) 588 .athrow(); 589 var c2 = cob.newBoundLabel(); 590 var ncdfError = cp.classEntry(CD_NoClassDefFoundError); 591 cob.exceptionCatch(ts, c1, c2, CD_ClassNotFoundException) 592 .new_(ncdfError) 593 .dup_x1() 594 .swap() 595 .invokevirtual(throwableGetMessage) 596 .invokespecial(cp.methodRefEntry(ncdfError, exInit)) 597 .athrow(); 598 cob.with(StackMapTableAttribute.of(List.of( 599 StackMapFrameInfo.of(c1, classLoaderLocal, throwableStack), 600 StackMapFrameInfo.of(c2, classLoaderLocal, throwableStack)))); 601 602 }); 603 } 604 605 /** 606 * Generate the static lookup accessor method that returns the Lookup 607 * on this proxy class if the caller's lookup class is java.lang.reflect.Proxy; 608 * otherwise, IllegalAccessException is thrown 609 */ 610 private void generateLookupAccessor(ClassBuilder clb) { 611 clb.withMethod(NAME_LOOKUP_ACCESSOR, 612 MTD_MethodHandles$Lookup_MethodHandles$Lookup, 613 ACC_PRIVATE | ACC_STATIC, 614 mb -> mb.with(ExceptionsAttribute.of(List.of(mb.constantPool().classEntry(CD_IllegalAccessException)))) 615 .withCode(cob -> { 616 Label failLabel = cob.newLabel(); 617 ClassEntry mhl = cp.classEntry(CD_MethodHandles_Lookup); 618 ClassEntry iae = cp.classEntry(CD_IllegalAccessException); 619 cob.aload(0) 620 .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("lookupClass", MTD_Class))) 621 .ldc(proxyCE) 622 .if_acmpne(failLabel) 623 .aload(0) 624 .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("hasFullPrivilegeAccess", MTD_boolean))) 625 .ifeq(failLabel) 626 .invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup) 627 .areturn() 628 .labelBinding(failLabel) 629 .new_(iae) 630 .dup() 631 .aload(0) 632 .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("toString", MTD_String))) 633 .invokespecial(cp.methodRefEntry(iae, exInit)) 634 .athrow() 635 .with(StackMapTableAttribute.of(List.of( 636 StackMapFrameInfo.of(failLabel, 637 List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(mhl)), 638 List.of())))); 639 })); 640 } 641 642 /** 643 * A ProxyMethod object represents a proxy method in the proxy class 644 * being generated: a method whose implementation will encode and 645 * dispatch invocations to the proxy instance's invocation handler. 646 */ 647 private class ProxyMethod { 648 649 private final Method method; 650 private final String shortSignature; 651 private final Class<?> fromClass; 652 private final Class<?> returnType; 653 private final String methodFieldName; 654 private Class<?>[] exceptionTypes; 655 private final FieldRefEntry methodField; 656 657 private ProxyMethod(Method method, String sig, 658 Class<?> returnType, Class<?>[] exceptionTypes, 659 Class<?> fromClass, String methodFieldName) { 660 this.method = method; 661 this.shortSignature = sig; 662 this.returnType = returnType; 663 this.exceptionTypes = exceptionTypes; 664 this.fromClass = fromClass; 665 this.methodFieldName = methodFieldName; 666 this.methodField = cp.fieldRefEntry(thisClassCE, 667 cp.nameAndTypeEntry(methodFieldName, CD_Method)); 668 } 669 670 private Class<?>[] parameterTypes() { 671 return method.getSharedParameterTypes(); 672 } 673 674 /** 675 * Create a new specific ProxyMethod with a specific field name 676 * 677 * @param method The method for which to create a proxy 678 */ 679 private ProxyMethod(Method method, String sig, String methodFieldName) { 680 this(method, sig, method.getReturnType(), 681 method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName); 682 } 683 684 /** 685 * Generate this method, including the code and exception table entry. 686 */ 687 private void generateMethod(ClassBuilder clb) { 688 var desc = methodTypeDesc(returnType, parameterTypes()); 689 int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL 690 : ACC_PUBLIC | ACC_FINAL; 691 clb.withMethod(method.getName(), desc, accessFlags, mb -> 692 mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes)))) 693 .withCode(cob -> { 694 var catchList = computeUniqueCatchList(exceptionTypes); 695 cob.aload(cob.receiverSlot()) 696 .getfield(handlerField) 697 .aload(cob.receiverSlot()) 698 .getstatic(methodField); 699 Class<?>[] parameterTypes = parameterTypes(); 700 if (parameterTypes.length > 0) { 701 // Create an array and fill with the parameters converting primitives to wrappers 702 cob.loadConstant(parameterTypes.length) 703 .anewarray(objectCE); 704 for (int i = 0; i < parameterTypes.length; i++) { 705 cob.dup() 706 .loadConstant(i); 707 codeWrapArgument(cob, parameterTypes[i], cob.parameterSlot(i)); 708 cob.aastore(); 709 } 710 } else { 711 cob.aconst_null(); 712 } 713 714 cob.invokeinterface(invocationHandlerInvoke); 715 716 if (returnType == void.class) { 717 cob.pop() 718 .return_(); 719 } else { 720 codeUnwrapReturnValue(cob, returnType); 721 } 722 if (!catchList.isEmpty()) { 723 var c1 = cob.newBoundLabel(); 724 for (var exc : catchList) { 725 cob.exceptionCatch(cob.startLabel(), c1, c1, referenceClassDesc(exc)); 726 } 727 cob.athrow(); // just rethrow the exception 728 var c2 = cob.newBoundLabel(); 729 cob.exceptionCatchAll(cob.startLabel(), c1, c2) 730 .new_(uteCE) 731 .dup_x1() 732 .swap() 733 .invokespecial(uteInit) 734 .athrow() 735 .with(StackMapTableAttribute.of(List.of( 736 StackMapFrameInfo.of(c1, List.of(), throwableStack), 737 StackMapFrameInfo.of(c2, List.of(), throwableStack)))); 738 } 739 })); 740 } 741 742 /** 743 * Generate code for wrapping an argument of the given type 744 * whose value can be found at the specified local variable 745 * index, in order for it to be passed (as an Object) to the 746 * invocation handler's "invoke" method. 747 */ 748 private void codeWrapArgument(CodeBuilder cob, Class<?> type, int slot) { 749 if (type.isPrimitive()) { 750 cob.loadLocal(TypeKind.from(type).asLoadable(), slot); 751 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); 752 cob.invokestatic(prim.wrapperMethodRef(cp)); 753 } else { 754 cob.aload(slot); 755 } 756 } 757 758 /** 759 * Generate code for unwrapping a return value of the given 760 * type from the invocation handler's "invoke" method (as type 761 * Object) to its correct type. 762 */ 763 private void codeUnwrapReturnValue(CodeBuilder cob, Class<?> type) { 764 if (type.isPrimitive()) { 765 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); 766 767 cob.checkcast(prim.wrapperClass) 768 .invokevirtual(prim.unwrapMethodRef(cp)) 769 .return_(TypeKind.from(type).asLoadable()); 770 } else { 771 cob.checkcast(referenceClassDesc(type)) 772 .areturn(); 773 } 774 } 775 776 /** 777 * Generate code for initializing the static field that stores 778 * the Method object for this proxy method. A class loader is 779 * anticipated at local variable index 0. 780 */ 781 private void codeFieldInitialization(CodeBuilder cob) { 782 var cp = cob.constantPool(); 783 codeClassForName(cob, fromClass); 784 785 Class<?>[] parameterTypes = parameterTypes(); 786 cob.ldc(method.getName()) 787 .loadConstant(parameterTypes.length) 788 .anewarray(classCE); 789 790 // Construct an array with the parameter types mapping primitives to Wrapper types 791 for (int i = 0; i < parameterTypes.length; i++) { 792 cob.dup() 793 .loadConstant(i); 794 if (parameterTypes[i].isPrimitive()) { 795 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]); 796 cob.getstatic(prim.typeFieldRef(cp)); 797 } else { 798 codeClassForName(cob, parameterTypes[i]); 799 } 800 cob.aastore(); 801 } 802 // lookup the method 803 cob.invokevirtual(classGetMethod) 804 .putstatic(methodField); 805 } 806 807 /* 808 * =============== Code Generation Utility Methods =============== 809 */ 810 811 /** 812 * Generate code to invoke the Class.forName with the name of the given 813 * class to get its Class object at runtime. The code is written to 814 * the supplied stream. Note that the code generated by this method 815 * may cause the checked ClassNotFoundException to be thrown. A class 816 * loader is anticipated at local variable index 0. 817 */ 818 private void codeClassForName(CodeBuilder cob, Class<?> cl) { 819 if (cl == Object.class) { 820 cob.ldc(objectCE); 821 } else { 822 cob.ldc(cl.getName()) 823 .iconst_0() // false 824 .aload(0)// classLoader 825 .invokestatic(classForName); 826 } 827 } 828 829 @Override 830 public String toString() { 831 return method.toShortString(); 832 } 833 } 834 835 /** 836 * A PrimitiveTypeInfo object contains bytecode-related information about 837 * a primitive type in its instance fields. The struct for a particular 838 * primitive type can be obtained using the static "get" method. 839 */ 840 private enum PrimitiveTypeInfo { 841 BYTE(byte.class, CD_byte, CD_Byte), 842 CHAR(char.class, CD_char, CD_Character), 843 DOUBLE(double.class, CD_double, CD_Double), 844 FLOAT(float.class, CD_float, CD_Float), 845 INT(int.class, CD_int, CD_Integer), 846 LONG(long.class, CD_long, CD_Long), 847 SHORT(short.class, CD_short, CD_Short), 848 BOOLEAN(boolean.class, CD_boolean, CD_Boolean); 849 850 /** 851 * wrapper class 852 */ 853 private final ClassDesc wrapperClass; 854 /** 855 * wrapper factory method type 856 */ 857 private final MethodTypeDesc wrapperMethodType; 858 /** 859 * wrapper class method name for retrieving primitive value 860 */ 861 private final String unwrapMethodName; 862 /** 863 * wrapper class method type for retrieving primitive value 864 */ 865 private final MethodTypeDesc unwrapMethodType; 866 867 PrimitiveTypeInfo(Class<?> primitiveClass, ClassDesc baseType, ClassDesc wrapperClass) { 868 assert baseType.isPrimitive(); 869 this.wrapperClass = wrapperClass; 870 this.wrapperMethodType = MethodTypeDescImpl.ofValidated(wrapperClass, baseType); 871 this.unwrapMethodName = primitiveClass.getName() + "Value"; 872 this.unwrapMethodType = MethodTypeDescImpl.ofValidated(baseType); 873 } 874 875 public static PrimitiveTypeInfo get(Class<?> cl) { 876 // Uses if chain for speed: 8284880 877 if (cl == int.class) return INT; 878 if (cl == long.class) return LONG; 879 if (cl == boolean.class) return BOOLEAN; 880 if (cl == short.class) return SHORT; 881 if (cl == byte.class) return BYTE; 882 if (cl == char.class) return CHAR; 883 if (cl == float.class) return FLOAT; 884 if (cl == double.class) return DOUBLE; 885 throw new AssertionError(cl); 886 } 887 888 public MethodRefEntry wrapperMethodRef(ConstantPoolBuilder cp) { 889 return cp.methodRefEntry(wrapperClass, "valueOf", wrapperMethodType); 890 } 891 892 public MethodRefEntry unwrapMethodRef(ConstantPoolBuilder cp) { 893 return cp.methodRefEntry(wrapperClass, unwrapMethodName, unwrapMethodType); 894 } 895 896 public FieldRefEntry typeFieldRef(ConstantPoolBuilder cp) { 897 return cp.fieldRefEntry(wrapperClass, "TYPE", CD_Class); 898 } 899 } 900 }