1 /* 2 * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.invoke; 27 28 import java.lang.constant.ClassDesc; 29 import java.lang.constant.MethodTypeDesc; 30 import java.lang.invoke.MethodHandles.Lookup; 31 import java.lang.module.ModuleDescriptor; 32 import java.lang.ref.WeakReference; 33 import java.lang.reflect.Method; 34 import java.lang.reflect.Modifier; 35 import java.lang.reflect.UndeclaredThrowableException; 36 import java.security.AccessController; 37 import java.security.PrivilegedAction; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Objects; 44 import java.util.Set; 45 import java.util.WeakHashMap; 46 import java.util.concurrent.atomic.AtomicInteger; 47 import java.util.stream.Stream; 48 49 import jdk.internal.access.JavaLangReflectAccess; 50 import jdk.internal.access.SharedSecrets; 51 import jdk.internal.classfile.ClassHierarchyResolver; 52 import jdk.internal.classfile.Classfile; 53 import jdk.internal.classfile.CodeBuilder; 54 import jdk.internal.classfile.TypeKind; 55 import jdk.internal.misc.ValhallaFeatures; 56 import jdk.internal.module.Modules; 57 import jdk.internal.reflect.CallerSensitive; 58 import jdk.internal.reflect.Reflection; 59 import jdk.internal.util.ClassFileDumper; 60 import sun.reflect.misc.ReflectUtil; 61 62 import static java.lang.constant.ConstantDescs.*; 63 import static java.lang.invoke.MethodHandleStatics.*; 64 import static java.lang.invoke.MethodType.methodType; 65 import static java.lang.module.ModuleDescriptor.Modifier.SYNTHETIC; 66 import static jdk.internal.classfile.Classfile.*; 67 68 /** 69 * This class consists exclusively of static methods that help adapt 70 * method handles to other JVM types, such as interfaces. 71 * 72 * @since 1.7 73 */ 74 public class MethodHandleProxies { 75 76 private MethodHandleProxies() { } // do not instantiate 77 78 /** 79 * Produces an instance of the given single-method interface which redirects 80 * its calls to the given method handle. 81 * <p> 82 * A single-method interface is an interface which declares a uniquely named method. 83 * When determining the uniquely named method of a single-method interface, 84 * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode}) 85 * are disregarded as are any default (non-abstract) methods. 86 * For example, {@link java.util.Comparator} is a single-method interface, 87 * even though it re-declares the {@code Object.equals} method and also 88 * declares default methods, such as {@code Comparator.reverse}. 89 * <p> 90 * The interface must be public, not {@linkplain Class#isHidden() hidden}, 91 * and not {@linkplain Class#isSealed() sealed}. 92 * No additional access checks are performed. 93 * <p> 94 * The resulting instance of the required type will respond to 95 * invocation of the type's uniquely named method by calling 96 * the given target on the incoming arguments, 97 * and returning or throwing whatever the target 98 * returns or throws. The invocation will be as if by 99 * {@code target.invoke}. 100 * The target's type will be checked before the 101 * instance is created, as if by a call to {@code asType}, 102 * which may result in a {@code WrongMethodTypeException}. 103 * <p> 104 * The uniquely named method is allowed to be multiply declared, 105 * with distinct type descriptors. (E.g., it can be overloaded, 106 * or can possess bridge methods.) All such declarations are 107 * connected directly to the target method handle. 108 * Argument and return types are adjusted by {@code asType} 109 * for each individual declaration. 110 * <p> 111 * The wrapper instance will implement the requested interface 112 * and its super-types, but no other single-method interfaces. 113 * This means that the instance will not unexpectedly 114 * pass an {@code instanceof} test for any unrequested type. 115 * <p style="font-size:smaller;"> 116 * <em>Implementation Note:</em> 117 * Therefore, each instance must implement a unique single-method interface. 118 * Implementations may not bundle together 119 * multiple single-method interfaces onto single implementation classes 120 * in the style of {@link java.desktop/java.awt.AWTEventMulticaster}. 121 * <p> 122 * The method handle may throw an <em>undeclared exception</em>, 123 * which means any checked exception (or other checked throwable) 124 * not declared by the requested type's single abstract method. 125 * If this happens, the throwable will be wrapped in an instance of 126 * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException} 127 * and thrown in that wrapped form. 128 * <p> 129 * Like {@link java.lang.Integer#valueOf Integer.valueOf}, 130 * {@code asInterfaceInstance} is a factory method whose results are defined 131 * by their behavior. 132 * It is not guaranteed to return a new instance for every call. 133 * <p> 134 * Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods} 135 * and other corner cases, the interface may also have several abstract methods 136 * with the same name but having distinct descriptors (types of returns and parameters). 137 * In this case, all the methods are bound in common to the one given target. 138 * The type check and effective {@code asType} conversion is applied to each 139 * method type descriptor, and all abstract methods are bound to the target in common. 140 * Beyond this type check, no further checks are made to determine that the 141 * abstract methods are related in any way. 142 * <p> 143 * Future versions of this API may accept additional types, 144 * such as abstract classes with single abstract methods. 145 * Future versions of this API may also equip wrapper instances 146 * with one or more additional public "marker" interfaces. 147 * <p> 148 * If a security manager is installed, this method is caller sensitive. 149 * During any invocation of the target method handle via the returned wrapper, 150 * the original creator of the wrapper (the caller) will be visible 151 * to context checks requested by the security manager. 152 * 153 * @param <T> the desired type of the wrapper, a single-method interface 154 * @param intfc a class object representing {@code T} 155 * @param target the method handle to invoke from the wrapper 156 * @return a correctly-typed wrapper for the given target 157 * @throws NullPointerException if either argument is null 158 * @throws IllegalArgumentException if the {@code intfc} is not a 159 * valid argument to this method 160 * @throws WrongMethodTypeException if the target cannot 161 * be converted to the type required by the requested interface 162 */ 163 @SuppressWarnings("doclint:reference") // cross-module links 164 @CallerSensitive 165 public static <T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) { 166 if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers())) 167 throw newIllegalArgumentException("not a public interface", intfc.getName()); 168 if (intfc.isSealed()) 169 throw newIllegalArgumentException("a sealed interface", intfc.getName()); 170 if (intfc.isHidden()) 171 throw newIllegalArgumentException("a hidden interface", intfc.getName()); 172 Objects.requireNonNull(target); 173 final MethodHandle mh; 174 @SuppressWarnings("removal") 175 var sm = System.getSecurityManager(); 176 if (sm != null) { 177 final Class<?> caller = Reflection.getCallerClass(); 178 final ClassLoader ccl = caller != null ? caller.getClassLoader() : null; 179 ReflectUtil.checkProxyPackageAccess(ccl, intfc); 180 mh = ccl != null ? bindCaller(target, caller) : target; 181 } else { 182 mh = target; 183 } 184 185 // Define one hidden class for each interface. Create an instance of 186 // the hidden class for a given target method handle which will be 187 // accessed via getfield. Multiple instances may be created for a 188 // hidden class. This approach allows the generated hidden classes 189 // more shareable. 190 // 191 // The implementation class is weakly referenced; a new class is 192 // defined if the last one has been garbage collected. 193 // 194 // An alternative approach is to define one hidden class with the 195 // target method handle as class data and the target method handle 196 // is loaded via ldc/condy. If more than one target method handles 197 // are used, the extra classes will pollute the same type profiles. 198 // In addition, hidden classes without class data is more friendly 199 // for pre-generation (shifting the dynamic class generation from 200 // runtime to an earlier phrase). 201 Class<?> proxyClass = getProxyClass(intfc); // throws IllegalArgumentException 202 Lookup lookup = new Lookup(proxyClass); 203 Object proxy; 204 try { 205 MethodHandle constructor = lookup.findConstructor(proxyClass, 206 MT_void_Lookup_MethodHandle_MethodHandle) 207 .asType(MT_Object_Lookup_MethodHandle_MethodHandle); 208 proxy = constructor.invokeExact(lookup, target, mh); 209 } catch (Throwable ex) { 210 throw uncaughtException(ex); 211 } 212 assert proxy.getClass().getModule().isNamed() : proxy.getClass() + " " + proxy.getClass().getModule(); 213 return intfc.cast(proxy); 214 } 215 216 private record MethodInfo(MethodTypeDesc desc, List<ClassDesc> thrown, String fieldName) {} 217 218 private static final ClassFileDumper DUMPER = ClassFileDumper.getInstance( 219 "jdk.invoke.MethodHandleProxies.dumpClassFiles", "DUMP_MH_PROXY_CLASSFILES"); 220 221 private static final Set<Class<?>> WRAPPER_TYPES = Collections.newSetFromMap(new WeakHashMap<>()); 222 private static final ClassValue<WeakReferenceHolder<Class<?>>> PROXIES = new ClassValue<>() { 223 @Override 224 protected WeakReferenceHolder<Class<?>> computeValue(Class<?> intfc) { 225 return new WeakReferenceHolder<>(newProxyClass(intfc)); 226 } 227 }; 228 229 private static Class<?> newProxyClass(Class<?> intfc) { 230 List<MethodInfo> methods = new ArrayList<>(); 231 Set<Class<?>> referencedTypes = new HashSet<>(); 232 referencedTypes.add(intfc); 233 String uniqueName = null; 234 int count = 0; 235 for (Method m : intfc.getMethods()) { 236 if (!Modifier.isAbstract(m.getModifiers())) 237 continue; 238 239 if (isObjectMethod(m)) 240 continue; 241 242 // ensure it's SAM interface 243 String methodName = m.getName(); 244 if (uniqueName == null) { 245 uniqueName = methodName; 246 } else if (!uniqueName.equals(methodName)) { 247 // too many abstract methods 248 throw newIllegalArgumentException("not a single-method interface", intfc.getName()); 249 } 250 251 // the field name holding the method handle for this method 252 String fieldName = "m" + count++; 253 var mt = methodType(m.getReturnType(), JLRA.getExecutableSharedParameterTypes(m), true); 254 var thrown = JLRA.getExecutableSharedExceptionTypes(m); 255 var exceptionTypeDescs = 256 thrown.length == 0 ? DEFAULT_RETHROWS 257 : Stream.concat(DEFAULT_RETHROWS.stream(), 258 Arrays.stream(thrown).map(MethodHandleProxies::desc)) 259 .distinct().toList(); 260 methods.add(new MethodInfo(desc(mt), exceptionTypeDescs, fieldName)); 261 262 // find the types referenced by this method 263 addElementType(referencedTypes, m.getReturnType()); 264 addElementTypes(referencedTypes, JLRA.getExecutableSharedParameterTypes(m)); 265 addElementTypes(referencedTypes, JLRA.getExecutableSharedExceptionTypes(m)); 266 } 267 268 if (uniqueName == null) 269 throw newIllegalArgumentException("no method in ", intfc.getName()); 270 271 // create a dynamic module for each proxy class, which needs access 272 // to the types referenced by the members of the interface including 273 // the parameter types, return type and exception types 274 var loader = intfc.getClassLoader(); 275 Module targetModule = newDynamicModule(loader, referencedTypes); 276 277 // generate a class file in the package of the dynamic module 278 String packageName = targetModule.getName(); 279 String intfcName = intfc.getName(); 280 int i = intfcName.lastIndexOf('.'); 281 // jdk.MHProxy#.Interface 282 String className = packageName + "." + (i > 0 ? intfcName.substring(i + 1) : intfcName); 283 byte[] template = createTemplate(loader, ClassDesc.of(className), desc(intfc), uniqueName, methods); 284 // define the dynamic module to the class loader of the interface 285 var definer = new Lookup(intfc).makeHiddenClassDefiner(className, template, Set.of(), DUMPER); 286 287 @SuppressWarnings("removal") 288 var sm = System.getSecurityManager(); 289 Lookup lookup; 290 if (sm != null) { 291 @SuppressWarnings("removal") 292 var l = AccessController.doPrivileged((PrivilegedAction<Lookup>) () -> 293 definer.defineClassAsLookup(true)); 294 lookup = l; 295 } else { 296 lookup = definer.defineClassAsLookup(true); 297 } 298 // cache the wrapper type 299 var ret = lookup.lookupClass(); 300 WRAPPER_TYPES.add(ret); 301 return ret; 302 } 303 304 private static final class WeakReferenceHolder<T> { 305 private volatile WeakReference<T> ref; 306 307 WeakReferenceHolder(T value) { 308 set(value); 309 } 310 311 void set(T value) { 312 ref = new WeakReference<>(value); 313 } 314 315 T get() { 316 return ref.get(); 317 } 318 } 319 320 private static Class<?> getProxyClass(Class<?> intfc) { 321 WeakReferenceHolder<Class<?>> r = PROXIES.get(intfc); 322 Class<?> cl = r.get(); 323 if (cl != null) 324 return cl; 325 326 // avoid spinning multiple classes in a race 327 synchronized (r) { 328 cl = r.get(); 329 if (cl != null) 330 return cl; 331 332 // If the referent is cleared, create a new value and update cached weak reference. 333 cl = newProxyClass(intfc); 334 r.set(cl); 335 return cl; 336 } 337 } 338 339 private static final List<ClassDesc> DEFAULT_RETHROWS = List.of(desc(RuntimeException.class), desc(Error.class)); 340 private static final ClassDesc CD_UndeclaredThrowableException = desc(UndeclaredThrowableException.class); 341 private static final ClassDesc CD_IllegalAccessException = desc(IllegalAccessException.class); 342 private static final MethodTypeDesc MTD_void_Throwable = MethodTypeDesc.of(CD_void, CD_Throwable); 343 private static final MethodType MT_void_Lookup_MethodHandle_MethodHandle = 344 methodType(void.class, Lookup.class, MethodHandle.class, MethodHandle.class); 345 private static final MethodType MT_Object_Lookup_MethodHandle_MethodHandle = 346 MT_void_Lookup_MethodHandle_MethodHandle.changeReturnType(Object.class); 347 private static final MethodType MT_MethodHandle_Object = methodType(MethodHandle.class, Object.class); 348 private static final MethodTypeDesc MTD_void_Lookup_MethodHandle_MethodHandle = 349 desc(MT_void_Lookup_MethodHandle_MethodHandle); 350 private static final MethodTypeDesc MTD_void_Lookup = MethodTypeDesc.of(CD_void, CD_MethodHandles_Lookup); 351 private static final MethodTypeDesc MTD_MethodHandle_MethodType = MethodTypeDesc.of(CD_MethodHandle, CD_MethodType); 352 private static final MethodTypeDesc MTD_Class = MethodTypeDesc.of(CD_Class); 353 private static final MethodTypeDesc MTD_int = MethodTypeDesc.of(CD_int); 354 private static final MethodTypeDesc MTD_String = MethodTypeDesc.of(CD_String); 355 private static final MethodTypeDesc MTD_void_String = MethodTypeDesc.of(CD_void, CD_String); 356 private static final String TARGET_NAME = "target"; 357 private static final String TYPE_NAME = "interfaceType"; 358 private static final String ENSURE_ORIGINAL_LOOKUP = "ensureOriginalLookup"; 359 360 /** 361 * Creates an implementation class file for a given interface. One implementation class is 362 * defined for each interface. 363 * 364 * @param ifaceDesc the given interface 365 * @param methodName the name of the single abstract method 366 * @param methods the information for implementation methods 367 * @return the bytes of the implementation classes 368 */ 369 private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, ClassDesc ifaceDesc, 370 String methodName, List<MethodInfo> methods) { 371 return Classfile.of(ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(loader))) 372 .build(proxyDesc, clb -> { 373 clb.withSuperclass(CD_Object); 374 clb.withFlags((ValhallaFeatures.isEnabled() ? ACC_IDENTITY : 0) | ACC_FINAL | ACC_SYNTHETIC); 375 clb.withInterfaceSymbols(ifaceDesc); 376 377 // static and instance fields 378 clb.withField(TYPE_NAME, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); 379 clb.withField(TARGET_NAME, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); 380 for (var mi : methods) { 381 clb.withField(mi.fieldName, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); 382 } 383 384 // <clinit> 385 clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> { 386 cob.constantInstruction(ifaceDesc); 387 cob.putstatic(proxyDesc, TYPE_NAME, CD_Class); 388 cob.return_(); 389 }); 390 391 // <init>(Lookup, MethodHandle target, MethodHandle callerBoundTarget) 392 clb.withMethodBody(INIT_NAME, MTD_void_Lookup_MethodHandle_MethodHandle, 0, cob -> { 393 cob.aload(0); 394 cob.invokespecial(CD_Object, INIT_NAME, MTD_void); 395 396 // call ensureOriginalLookup to verify the given Lookup has access 397 cob.aload(1); 398 cob.invokestatic(proxyDesc, "ensureOriginalLookup", MTD_void_Lookup); 399 400 // this.target = target; 401 cob.aload(0); 402 cob.aload(2); 403 cob.putfield(proxyDesc, TARGET_NAME, CD_MethodHandle); 404 405 // method handles adjusted to the method type of each method 406 for (var mi : methods) { 407 // this.m<i> = callerBoundTarget.asType(xxType); 408 cob.aload(0); 409 cob.aload(3); 410 cob.constantInstruction(mi.desc); 411 cob.invokevirtual(CD_MethodHandle, "asType", MTD_MethodHandle_MethodType); 412 cob.putfield(proxyDesc, mi.fieldName, CD_MethodHandle); 413 } 414 415 // complete 416 cob.return_(); 417 }); 418 419 // private static void ensureOriginalLookup(Lookup) checks if the given Lookup 420 // has ORIGINAL access to this class, i.e. the lookup class is this class; 421 // otherwise, IllegalAccessException is thrown 422 clb.withMethodBody(ENSURE_ORIGINAL_LOOKUP, MTD_void_Lookup, ACC_PRIVATE | ACC_STATIC, cob -> { 423 var failLabel = cob.newLabel(); 424 // check lookupClass 425 cob.aload(0); 426 cob.invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class); 427 cob.constantInstruction(proxyDesc); 428 cob.if_acmpne(failLabel); 429 // check original access 430 cob.aload(0); 431 cob.invokevirtual(CD_MethodHandles_Lookup, "lookupModes", MTD_int); 432 cob.constantInstruction(Lookup.ORIGINAL); 433 cob.iand(); 434 cob.ifeq(failLabel); 435 // success 436 cob.return_(); 437 // throw exception 438 cob.labelBinding(failLabel); 439 cob.new_(CD_IllegalAccessException); 440 cob.dup(); 441 cob.aload(0); // lookup 442 cob.invokevirtual(CD_Object, "toString", MTD_String); 443 cob.invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String); 444 cob.athrow(); 445 }); 446 447 // implementation methods 448 for (MethodInfo mi : methods) { 449 // no need to generate thrown exception attribute 450 clb.withMethodBody(methodName, mi.desc, ACC_PUBLIC, cob -> cob 451 .trying(bcb -> { 452 // return this.handleField.invokeExact(arguments...); 453 bcb.aload(0); 454 bcb.getfield(proxyDesc, mi.fieldName, CD_MethodHandle); 455 for (int j = 0; j < mi.desc.parameterCount(); j++) { 456 bcb.loadInstruction(TypeKind.from(mi.desc.parameterType(j)), 457 bcb.parameterSlot(j)); 458 } 459 bcb.invokevirtual(CD_MethodHandle, "invokeExact", mi.desc); 460 bcb.returnInstruction(TypeKind.from(mi.desc.returnType())); 461 }, ctb -> ctb 462 // catch (Error | RuntimeException | Declared ex) { throw ex; } 463 .catchingMulti(mi.thrown, CodeBuilder::athrow) 464 // catch (Throwable ex) { throw new UndeclaredThrowableException(ex); } 465 .catchingAll(cb -> cb 466 .new_(CD_UndeclaredThrowableException) 467 .dup_x1() 468 .swap() 469 .invokespecial(CD_UndeclaredThrowableException, 470 INIT_NAME, MTD_void_Throwable) 471 .athrow() 472 ) 473 )); 474 } 475 }); 476 } 477 478 private static MethodHandle bindCaller(MethodHandle target, Class<?> hostClass) { 479 return MethodHandleImpl.bindCaller(target, hostClass).withVarargs(target.isVarargsCollector()); 480 } 481 482 /** 483 * Determines if the given object was produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. 484 * @param x any reference 485 * @return true if the reference is not null and points to an object produced by {@code asInterfaceInstance} 486 */ 487 public static boolean isWrapperInstance(Object x) { 488 return x != null && WRAPPER_TYPES.contains(x.getClass()); 489 } 490 491 /** 492 * Produces or recovers a target method handle which is behaviorally 493 * equivalent to the unique method of this wrapper instance. 494 * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. 495 * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. 496 * @param x any reference 497 * @return a method handle implementing the unique method 498 * @throws IllegalArgumentException if the reference x is not to a wrapper instance 499 */ 500 public static MethodHandle wrapperInstanceTarget(Object x) { 501 if (!isWrapperInstance(x)) 502 throw new IllegalArgumentException("not a wrapper instance: " + x); 503 504 try { 505 Class<?> type = x.getClass(); 506 MethodHandle getter = new Lookup(type).findGetter(type, TARGET_NAME, MethodHandle.class) 507 .asType(MT_MethodHandle_Object); 508 return (MethodHandle) getter.invokeExact(x); 509 } catch (Throwable ex) { 510 throw uncaughtException(ex); 511 } 512 } 513 514 /** 515 * Recovers the unique single-method interface type for which this wrapper instance was created. 516 * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. 517 * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. 518 * @param x any reference 519 * @return the single-method interface type for which the wrapper was created 520 * @throws IllegalArgumentException if the reference x is not to a wrapper instance 521 */ 522 public static Class<?> wrapperInstanceType(Object x) { 523 if (!isWrapperInstance(x)) 524 throw new IllegalArgumentException("not a wrapper instance: " + x); 525 526 try { 527 Class<?> type = x.getClass(); 528 MethodHandle originalTypeField = new Lookup(type).findStaticGetter(type, TYPE_NAME, Class.class); 529 return (Class<?>) originalTypeField.invokeExact(); 530 } catch (Throwable e) { 531 throw uncaughtException(e); 532 } 533 } 534 535 private static ClassDesc desc(Class<?> cl) { 536 return cl.describeConstable().orElseThrow(() -> newInternalError("Cannot convert class " 537 + cl.getName() + " to a constant")); 538 } 539 540 private static MethodTypeDesc desc(MethodType mt) { 541 return mt.describeConstable().orElseThrow(() -> newInternalError("Cannot convert method type " 542 + mt + " to a constant")); 543 } 544 545 private static final JavaLangReflectAccess JLRA = SharedSecrets.getJavaLangReflectAccess(); 546 private static final AtomicInteger counter = new AtomicInteger(); 547 548 private static String nextModuleName() { 549 return "jdk.MHProxy" + counter.incrementAndGet(); 550 } 551 552 /** 553 * Create a dynamic module defined to the given class loader and has 554 * access to the given types. 555 * <p> 556 * The dynamic module contains only one single package named the same as 557 * the name of the dynamic module. It's not exported or open. 558 */ 559 private static Module newDynamicModule(ClassLoader ld, Set<Class<?>> types) { 560 Objects.requireNonNull(types); 561 562 // create a dynamic module and setup module access 563 String mn = nextModuleName(); 564 ModuleDescriptor descriptor = ModuleDescriptor.newModule(mn, Set.of(SYNTHETIC)) 565 .packages(Set.of(mn)) 566 .build(); 567 568 Module dynModule = Modules.defineModule(ld, descriptor, null); 569 Module javaBase = Object.class.getModule(); 570 571 Modules.addReads(dynModule, javaBase); 572 Modules.addOpens(dynModule, mn, javaBase); 573 574 for (Class<?> c : types) { 575 ensureAccess(dynModule, c); 576 } 577 return dynModule; 578 } 579 580 private static boolean isObjectMethod(Method m) { 581 return switch (m.getName()) { 582 case "toString" -> m.getReturnType() == String.class 583 && m.getParameterCount() == 0; 584 case "hashCode" -> m.getReturnType() == int.class 585 && m.getParameterCount() == 0; 586 case "equals" -> m.getReturnType() == boolean.class 587 && m.getParameterCount() == 1 588 && JLRA.getExecutableSharedParameterTypes(m)[0] == Object.class; 589 default -> false; 590 }; 591 } 592 593 /* 594 * Ensure the given module can access the given class. 595 */ 596 private static void ensureAccess(Module target, Class<?> c) { 597 Module m = c.getModule(); 598 // add read edge and qualified export for the target module to access 599 if (!target.canRead(m)) { 600 Modules.addReads(target, m); 601 } 602 String pn = c.getPackageName(); 603 if (!m.isExported(pn, target)) { 604 Modules.addExports(m, pn, target); 605 } 606 } 607 608 private static void addElementTypes(Set<Class<?>> types, Class<?>... classes) { 609 for (var cls : classes) { 610 addElementType(types, cls); 611 } 612 } 613 614 private static void addElementType(Set<Class<?>> types, Class<?> cls) { 615 Class<?> e = cls; 616 while (e.isArray()) { 617 e = e.getComponentType(); 618 } 619 620 if (!e.isPrimitive()) { 621 types.add(e); 622 } 623 } 624 }