jaroslav@1646: * The nominal method at such a call site is an instance of jaroslav@1646: * a signature-polymorphic method (see @PolymorphicSignature). jaroslav@1646: * Such method instances are user-visible entities which are jaroslav@1646: * "split" from the generic placeholder method in {@code MethodHandle}. jaroslav@1646: * (Note that the placeholder method is not identical with any of jaroslav@1646: * its instances. If invoked reflectively, is guaranteed to throw an jaroslav@1646: * {@code UnsupportedOperationException}.) jaroslav@1646: * If the signature-polymorphic method instance is ever reified, jaroslav@1646: * it appears as a "copy" of the original placeholder jaroslav@1646: * (a native final member of {@code MethodHandle}) except jaroslav@1646: * that its type descriptor has shape required by the instance, jaroslav@1646: * and the method instance is not varargs. jaroslav@1646: * The method instance is also marked synthetic, since the jaroslav@1646: * method (by definition) does not appear in Java source code. jaroslav@1646: *
jaroslav@1646: * The JVM is allowed to reify this method as instance metadata. jaroslav@1646: * For example, {@code invokeBasic} is always reified. jaroslav@1646: * But the JVM may instead call {@code linkMethod}. jaroslav@1646: * If the result is an * ordered pair of a {@code (method, appendix)}, jaroslav@1646: * the method gets all the arguments (0..N inclusive) jaroslav@1646: * plus the appendix (N+1), and uses the appendix to complete the call. jaroslav@1646: * In this way, one reusable method (called a "linker method") jaroslav@1646: * can perform the function of any number of polymorphic instance jaroslav@1646: * methods. jaroslav@1646: *
jaroslav@1646: * Linker methods are allowed to be weakly typed, with any or jaroslav@1646: * all references rewritten to {@code Object} and any primitives jaroslav@1646: * (except {@code long}/{@code float}/{@code double}) jaroslav@1646: * rewritten to {@code int}. jaroslav@1646: * A linker method is trusted to return a strongly typed result, jaroslav@1646: * according to the specific method type descriptor of the jaroslav@1646: * signature-polymorphic instance it is emulating. jaroslav@1646: * This can involve (as necessary) a dynamic check using jaroslav@1646: * data extracted from the appendix argument. jaroslav@1646: *
jaroslav@1646: * The JVM does not inspect the appendix, other than to pass jaroslav@1646: * it verbatim to the linker method at every call. jaroslav@1646: * This means that the JDK runtime has wide latitude jaroslav@1646: * for choosing the shape of each linker method and its jaroslav@1646: * corresponding appendix. jaroslav@1646: * Linker methods should be generated from {@code LambdaForm}s jaroslav@1646: * so that they do not become visible on stack traces. jaroslav@1646: *
jaroslav@1646: * The {@code linkMethod} call is free to omit the appendix jaroslav@1646: * (returning null) and instead emulate the required function jaroslav@1646: * completely in the linker method. jaroslav@1646: * As a corner case, if N==255, no appendix is possible. jaroslav@1646: * In this case, the method returned must be custom-generated to jaroslav@1646: * to perform any needed type checking. jaroslav@1646: *
jaroslav@1646: * If the JVM does not reify a method at a call site, but instead jaroslav@1646: * calls {@code linkMethod}, the corresponding call represented jaroslav@1646: * in the bytecodes may mention a valid method which is not jaroslav@1646: * representable with a {@code MemberName}. jaroslav@1646: * Therefore, use cases for {@code linkMethod} tend to correspond to jaroslav@1646: * special cases in reflective code such as {@code findVirtual} jaroslav@1646: * or {@code revealDirect}. jaroslav@1646: */ jaroslav@1646: static MemberName linkMethod(Class> callerClass, int refKind, jaroslav@1646: Class> defc, String name, Object type, jaroslav@1646: Object[] appendixResult) { jaroslav@1646: if (!TRACE_METHOD_LINKAGE) jaroslav@1646: return linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult); jaroslav@1646: return linkMethodTracing(callerClass, refKind, defc, name, type, appendixResult); jaroslav@1646: } jaroslav@1646: static MemberName linkMethodImpl(Class> callerClass, int refKind, jaroslav@1646: Class> defc, String name, Object type, jaroslav@1646: Object[] appendixResult) { jaroslav@1646: try { jaroslav@1646: if (defc == MethodHandle.class && refKind == REF_invokeVirtual) { jaroslav@1646: return Invokers.methodHandleInvokeLinkerMethod(name, fixMethodType(callerClass, type), appendixResult); jaroslav@1646: } jaroslav@1646: } catch (Throwable ex) { jaroslav@1646: if (ex instanceof LinkageError) jaroslav@1646: throw (LinkageError) ex; jaroslav@1646: else jaroslav@1646: throw new LinkageError(ex.getMessage(), ex); jaroslav@1646: } jaroslav@1646: throw new LinkageError("no such method "+defc.getName()+"."+name+type); jaroslav@1646: } jaroslav@1646: private static MethodType fixMethodType(Class> callerClass, Object type) { jaroslav@1646: if (type instanceof MethodType) jaroslav@1646: return (MethodType) type; jaroslav@1646: else jaroslav@1646: return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader()); jaroslav@1646: } jaroslav@1646: // Tracing logic: jaroslav@1646: static MemberName linkMethodTracing(Class> callerClass, int refKind, jaroslav@1646: Class> defc, String name, Object type, jaroslav@1646: Object[] appendixResult) { jaroslav@1646: System.out.println("linkMethod "+defc.getName()+"."+ jaroslav@1646: name+type+"/"+Integer.toHexString(refKind)); jaroslav@1646: try { jaroslav@1646: MemberName res = linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult); jaroslav@1646: System.out.println("linkMethod => "+res+" + "+appendixResult[0]); jaroslav@1646: return res; jaroslav@1646: } catch (Throwable ex) { jaroslav@1646: System.out.println("linkMethod => throw "+ex); jaroslav@1646: throw ex; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help. jaroslav@1646: * It will make an up-call to this method. (Do not change the name or signature.) jaroslav@1646: * The type argument is a Class for field requests and a MethodType for non-fields. jaroslav@1646: *
jaroslav@1646: * Recent versions of the JVM may also pass a resolved MemberName for the type. jaroslav@1646: * In that case, the name is ignored and may be null. jaroslav@1646: */ jaroslav@1646: static MethodHandle linkMethodHandleConstant(Class> callerClass, int refKind, jaroslav@1646: Class> defc, String name, Object type) { jaroslav@1646: try { jaroslav@1646: Lookup lookup = IMPL_LOOKUP.in(callerClass); jaroslav@1646: assert(refKindIsValid(refKind)); jaroslav@1646: return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type); jaroslav@1646: } catch (IllegalAccessException ex) { jaroslav@1646: Throwable cause = ex.getCause(); jaroslav@1646: if (cause instanceof AbstractMethodError) { jaroslav@1646: throw (AbstractMethodError) cause; jaroslav@1646: } else { jaroslav@1646: Error err = new IllegalAccessError(ex.getMessage()); jaroslav@1646: throw initCauseFrom(err, ex); jaroslav@1646: } jaroslav@1646: } catch (NoSuchMethodException ex) { jaroslav@1646: Error err = new NoSuchMethodError(ex.getMessage()); jaroslav@1646: throw initCauseFrom(err, ex); jaroslav@1646: } catch (NoSuchFieldException ex) { jaroslav@1646: Error err = new NoSuchFieldError(ex.getMessage()); jaroslav@1646: throw initCauseFrom(err, ex); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: Error err = new IncompatibleClassChangeError(); jaroslav@1646: throw initCauseFrom(err, ex); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Use best possible cause for err.initCause(), substituting the jaroslav@1646: * cause for err itself if the cause has the same (or better) type. jaroslav@1646: */ jaroslav@1646: static private Error initCauseFrom(Error err, Exception ex) { jaroslav@1646: Throwable th = ex.getCause(); jaroslav@1646: if (err.getClass().isInstance(th)) jaroslav@1646: return (Error) th; jaroslav@1646: err.initCause(th == null ? ex : th); jaroslav@1646: return err; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Is this method a caller-sensitive method? jaroslav@1646: * I.e., does it call Reflection.getCallerClass or a similer method jaroslav@1646: * to ask about the identity of its caller? jaroslav@1646: */ jaroslav@1646: static boolean isCallerSensitive(MemberName mem) { jaroslav@1646: if (!mem.isInvocable()) return false; // fields are not caller sensitive jaroslav@1646: jaroslav@1646: return mem.isCallerSensitive() || canBeCalledVirtual(mem); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static boolean canBeCalledVirtual(MemberName mem) { jaroslav@1646: assert(mem.isInvocable()); jaroslav@1646: Class> defc = mem.getDeclaringClass(); jaroslav@1646: switch (mem.getName()) { jaroslav@1646: case "checkMemberAccess": jaroslav@1651: return true; //canBeCalledVirtual(mem, java.lang.SecurityManager.class); jaroslav@1646: case "getContextClassLoader": jaroslav@1646: return canBeCalledVirtual(mem, java.lang.Thread.class); jaroslav@1646: } jaroslav@1646: return false; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static boolean canBeCalledVirtual(MemberName symbolicRef, Class> definingClass) { jaroslav@1646: Class> symbolicRefClass = symbolicRef.getDeclaringClass(); jaroslav@1646: if (symbolicRefClass == definingClass) return true; jaroslav@1646: if (symbolicRef.isStatic() || symbolicRef.isPrivate()) return false; jaroslav@1646: return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef jaroslav@1646: symbolicRefClass.isInterface()); // Mdef implements Msym jaroslav@1646: } jaroslav@1646: }