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