* 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 * 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 (refKind == REF_invokeVirtual) { if (defc == MethodHandle.class) { return Invokers.methodHandleInvokeLinkerMethod( name, fixMethodType(callerClass, type), appendixResult); } else if (defc == VarHandle.class) { return varHandleOperationLinkerMethod( name, fixMethodType(callerClass, type), appendixResult); } } } catch (Error e) { // Pass through an Error, including say StackOverflowError or // OutOfMemoryError throw e; } catch (Throwable ex) { // Wrap anything else in LinkageError 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.fromDescriptor((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; } } /** * Obtain the method to link to the VarHandle operation. * This method is located here and not in Invokers to avoid * intializing that and other classes early on in VM bootup. */ private static MemberName varHandleOperationLinkerMethod(String name, MethodType mtype, Object[] appendixResult) { // Get the signature method type final MethodType sigType = mtype.basicType(); // Get the access kind from the method name VarHandle.AccessMode ak; try { ak = VarHandle.AccessMode.valueFromMethodName(name); } catch (IllegalArgumentException e) { throw MethodHandleStatics.newInternalError(e); } // Create the appendix descriptor constant VarHandle.AccessDescriptor ad = new VarHandle.AccessDescriptor(mtype, ak.at.ordinal(), ak.ordinal()); appendixResult[0] = ad; if (MethodHandleStatics.VAR_HANDLE_GUARDS) { // If not polymorphic in the return type, such as the compareAndSet // methods that return boolean Class> guardReturnType = sigType.returnType(); if (ak.at.isMonomorphicInReturnType) { if (ak.at.returnType != mtype.returnType()) { // The caller contains a different return type than that // defined by the method throw newNoSuchMethodErrorOnVarHandle(name, mtype); } // Adjust the return type of the signature method type guardReturnType = ak.at.returnType; } // Get the guard method type for linking final Class>[] guardParams = new Class>[sigType.parameterCount() + 2]; // VarHandle at start guardParams[0] = VarHandle.class; for (int i = 0; i < sigType.parameterCount(); i++) { guardParams[i + 1] = sigType.parameterType(i); } // Access descriptor at end guardParams[guardParams.length - 1] = VarHandle.AccessDescriptor.class; MethodType guardType = MethodType.makeImpl(guardReturnType, guardParams, true); MemberName linker = new MemberName( VarHandleGuards.class, getVarHandleGuardMethodName(guardType), guardType, REF_invokeStatic); linker = MemberName.getFactory().resolveOrNull(REF_invokeStatic, linker, VarHandleGuards.class); if (linker != null) { return linker; } // Fall back to lambda form linkage if guard method is not available // TODO Optionally log fallback ? } return Invokers.varHandleInvokeLinkerMethod(ak, mtype); } static String getVarHandleGuardMethodName(MethodType guardType) { String prefix = "guard_"; StringBuilder sb = new StringBuilder(prefix.length() + guardType.parameterCount()); sb.append(prefix); for (int i = 1; i < guardType.parameterCount() - 1; i++) { Class> pt = guardType.parameterType(i); sb.append(getCharType(pt)); } sb.append('_').append(getCharType(guardType.returnType())); return sb.toString(); } static char getCharType(Class> pt) { return Wrapper.forBasicType(pt).basicTypeChar(); } static NoSuchMethodError newNoSuchMethodErrorOnVarHandle(String name, MethodType mtype) { return new NoSuchMethodError("VarHandle." + name + mtype); } /** * 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 (ReflectiveOperationException ex) {
throw mapLookupExceptionToError(ex);
}
}
/**
* Map a reflective exception to a linkage error.
*/
static LinkageError mapLookupExceptionToError(ReflectiveOperationException ex) {
LinkageError err;
if (ex instanceof IllegalAccessException) {
Throwable cause = ex.getCause();
if (cause instanceof AbstractMethodError) {
return (AbstractMethodError) cause;
} else {
err = new IllegalAccessError(ex.getMessage());
}
} else if (ex instanceof NoSuchMethodException) {
err = new NoSuchMethodError(ex.getMessage());
} else if (ex instanceof NoSuchFieldException) {
err = new NoSuchFieldError(ex.getMessage());
} else {
err = new IncompatibleClassChangeError();
}
return 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