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