/** * Sole factory method to find or create an interned method type. * * @param rtype desired return type * @param ptypes desired parameter types * @param trusted whether the ptypes can be used without cloning * @return the unique method type of the desired structure */ /*trusted*/ static MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) { if (ptypes.length == 0) { ptypes = NO_PTYPES; trusted = true; } MethodType mt1 = new MethodType(rtype, ptypes); MethodType mt0; synchronized (internTable) { mt0 = internTable.get(mt1); if (mt0 != null) return mt0; } if (!trusted) // defensively copy the array passed in by the user mt1 = new MethodType(rtype, ptypes.clone()); // promote the object to the Real Thing, and reprobe MethodTypeForm form = MethodTypeForm.findForm(mt1); mt1.form = form; if (form.erasedType == mt1) { // This is a principal (erased) type; show it to the JVM. MethodHandleNatives.init(mt1); } synchronized (internTable) { mt0 = internTable.get(mt1); if (mt0 != null) return mt0; internTable.put(mt1, mt1); } return mt1; }
private TestCase(Class<T> rtype, Function<Object, T> cast, ThrowMode throwMode, Throwable thrown) throws NoSuchMethodException, IllegalAccessException { this.cast = cast; filter = MethodHandles.lookup() .findVirtual(Function.class, "apply", MethodType.methodType(Object.class, Object.class)) .bindTo(cast); this.rtype = rtype; this.throwMode = throwMode; this.throwableClass = thrown.getClass(); switch (throwMode) { case NOTHING: this.thrown = null; break; case ADAPTER: case UNCAUGHT: this.thrown = new Error("do not catch this"); break; default: this.thrown = thrown; } MethodHandle throwOrReturn = THROW_OR_RETURN; if (throwMode == ThrowMode.ADAPTER) { MethodHandle fakeIdentity = FAKE_IDENTITY.bindTo(this); for (int i = 0; i < 10; ++i) { throwOrReturn = MethodHandles.filterReturnValue(throwOrReturn, fakeIdentity); } } thrower = throwOrReturn.asType(MethodType.genericMethodType(2)); }
static MethodHandle getResultConversionHandle(NativeType nativeType, Class from, Class to) { try { switch (nativeType) { case FLOAT: return MethodHandles.explicitCastArguments( LOOKUP.findStatic( Float.class, "intBitsToFloat", MethodType.methodType(float.class, int.class)), MethodType.methodType(to, from)); case DOUBLE: return LOOKUP .findStatic( Double.class, "longBitsToDouble", MethodType.methodType(double.class, long.class)) .asType(MethodType.methodType(to, from)); case VOID: return null; default: return getIntegerConversionHandle(nativeType, from, to); } } catch (NoSuchMethodException | IllegalAccessException e) { throw new IllegalArgumentException(e); } }
static MethodHandle getIntegerConversionHandle(NativeType nativeType, Class from, Class to) throws NoSuchMethodException, IllegalAccessException { switch (nativeType) { case SCHAR: case UCHAR: case SSHORT: case USHORT: case SINT: case UINT: case SLONG: case ULONG: case SLONG_LONG: case ULONG_LONG: case POINTER: if (nativeType.size() <= 4) { Class nativeIntType = long.class == to ? long.class : int.class; String conversionHelper = (nativeType.isUnsigned() ? "u" : "s") + Integer.toString(nativeType.size() * 8); MethodHandle mh = LOOKUP.findStatic( AsmRuntime.class, conversionHelper, MethodType.methodType(nativeIntType, nativeIntType)); return MethodHandles.explicitCastArguments(mh, MethodType.methodType(to, from)); } return null; default: return null; } }
/** Return the declared type of this member, which must be a method or constructor. */ public MethodType getMethodType() { if (type == null) { expandFromVM(); if (type == null) return null; } if (!isInvocable()) throw newIllegalArgumentException("not invocable, no method type"); if (type instanceof MethodType) { return (MethodType) type; } if (type instanceof String) { String sig = (String) type; MethodType res = MethodType.fromMethodDescriptorString(sig, getClassLoader()); this.type = res; return res; } if (type instanceof Object[]) { Object[] typeInfo = (Object[]) type; Class<?>[] ptypes = (Class<?>[]) typeInfo[1]; Class<?> rtype = (Class<?>) typeInfo[0]; MethodType res = MethodType.methodType(rtype, ptypes); this.type = res; return res; } throw new InternalError("bad method type " + type); }
static MethodHandle getParameterConversionHandle(NativeType nativeType, Class from, Class to) { try { switch (nativeType) { case FLOAT: return LOOKUP .findStatic( Float.class, "floatToRawIntBits", MethodType.methodType(int.class, float.class)) .asType(MethodType.methodType(to, from)); case DOUBLE: return LOOKUP .findStatic( Double.class, "doubleToRawLongBits", MethodType.methodType(long.class, double.class)) .asType(MethodType.methodType(to, from)); default: return getIntegerConversionHandle(nativeType, from, to); } } catch (NoSuchMethodException | IllegalAccessException e) { throw new IllegalArgumentException(e); } }
private MethodHandle getMethodHandle(Method method) { MethodHandle methodHandle; try { methodHandle = lookup().unreflect(method); } catch (IllegalAccessException e) { throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, e); } if (!isStatic(method.getModifiers())) { // Re-arrange the parameters, so that the "this" parameter is after the meta parameters int[] permutedIndices = new int[methodHandle.type().parameterCount()]; permutedIndices[0] = dependencies.size(); MethodType newType = methodHandle .type() .changeParameterType(dependencies.size(), methodHandle.type().parameterType(0)); for (int i = 0; i < dependencies.size(); i++) { permutedIndices[i + 1] = i; newType = newType.changeParameterType(i, methodHandle.type().parameterType(i + 1)); } for (int i = dependencies.size() + 1; i < permutedIndices.length; i++) { permutedIndices[i] = i; } methodHandle = permuteArguments(methodHandle, newType, permutedIndices); } return methodHandle; }
// Factory methods: static DirectMethodHandle make(byte refKind, Class<?> receiver, MemberName member) { MethodType mtype = member.getMethodOrFieldType(); if (!member.isStatic()) { if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor()) throw new InternalError(member.toString()); mtype = mtype.insertParameterTypes(0, receiver); } if (!member.isField()) { if (refKind == REF_invokeSpecial) { member = member.asSpecial(); LambdaForm lform = preparedLambdaForm(member); return new Special(mtype, lform, member); } else { LambdaForm lform = preparedLambdaForm(member); return new DirectMethodHandle(mtype, lform, member); } } else { LambdaForm lform = preparedFieldLambdaForm(member); if (member.isStatic()) { long offset = MethodHandleNatives.staticFieldOffset(member); Object base = MethodHandleNatives.staticFieldBase(member); return new StaticAccessor(mtype, lform, member, base, offset); } else { long offset = MethodHandleNatives.objectFieldOffset(member); assert (offset == (int) offset); return new Accessor(mtype, lform, member, (int) offset); } } }
public CatchExceptionTest( TestCase testCase, final boolean isVararg, final int argsCount, final int catchDrops) { this.testCase = testCase; this.dropped = catchDrops; if (Helper.IS_VERBOSE) { System.out.printf( "CatchException::CatchException(%s, isVararg=%b " + "argsCount=%d catchDrops=%d)%n", testCase, isVararg, argsCount, catchDrops); } MethodHandle thrower = testCase.thrower; int throwerLen = thrower.type().parameterCount(); List<Class<?>> classes; int extra = Math.max(0, argsCount - throwerLen); classes = getThrowerParams(isVararg, extra); this.argsCount = throwerLen + classes.size(); thrower = Helper.addTrailingArgs(thrower, this.argsCount, classes); if (isVararg && argsCount > throwerLen) { MethodType mt = thrower.type(); Class<?> lastParam = mt.parameterType(mt.parameterCount() - 1); thrower = thrower.asVarargsCollector(lastParam); } this.thrower = thrower; this.dropped = Math.min(this.argsCount, catchDrops); catcher = testCase.getCatcher(getCatcherParams()); nargs = Math.max(2, this.argsCount); }
private boolean returnConversionNeeded(MethodType callerType, MethodHandle target) { Class<?> needType = callerType.returnType(); if (needType == erasedCallerType.returnType()) return false; // no conversions possible, since must be primitive or Object Class<?> haveType = target.type().returnType(); if (VerifyType.isNullConversion(haveType, needType) && !needType.isInterface()) return false; return true; }
/** * Compute and cache information for this adapter, so that it can call out to targets of the * erasure-family of the given erased type. */ /*non-public*/ InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException { assert (erasedCallerType.equals(erasedCallerType.erase())); this.erasedCallerType = erasedCallerType; this.initialInvoker = makeInitialInvoker(); assert initialInvoker .type() .equals(erasedCallerType.insertParameterTypes(0, MethodType.class, MethodHandle.class)) : initialInvoker.type(); }
/*non-public*/ boolean isConvertibleTo(MethodType newType) { if (!canConvert(returnType(), newType.returnType())) return false; int argc = parameterCount(); if (argc != newType.parameterCount()) return false; for (int i = 0; i < argc; i++) { if (!canConvert(newType.parameterType(i), parameterType(i))) return false; } return true; }
private MethodHandle addReturnConversion(MethodHandle finisher, Class<?> type) { // FIXME: This is slow because it creates a closure node on every call that requires a return // cast. MethodType finisherType = finisher.type(); MethodHandle caster = ValueConversions.identity(type); caster = caster.asType(caster.type().changeParameterType(0, finisherType.returnType())); finisher = MethodHandles.filterReturnValue(finisher, caster); return finisher.asType(finisherType); }
private static MethodType unwrapWithNoPrims(MethodType wt) { assert (!wt.hasPrimitives()); MethodType uwt = wt.wrapAlt; if (uwt == null) { // fill in lazily uwt = MethodTypeForm.canonicalize(wt, MethodTypeForm.UNWRAP, MethodTypeForm.UNWRAP); if (uwt == null) uwt = wt; // type has no wrappers or prims at all wt.wrapAlt = uwt; } return uwt; }
private static MethodType wrapWithPrims(MethodType pt) { assert (pt.hasPrimitives()); MethodType wt = pt.wrapAlt; if (wt == null) { // fill in lazily wt = MethodTypeForm.canonicalize(pt, MethodTypeForm.WRAP, MethodTypeForm.WRAP); assert (wt != null); pt.wrapAlt = wt; } return wt; }
public static void main(String[] args) throws Throwable { Object x, y; String s; int i; MethodType mt; MethodHandle mh; MethodHandles.Lookup lookup = MethodHandles.lookup(); // mt is (char,char)String mt = MethodType.methodType(String.class, char.class, char.class); mh = lookup.findVirtual(String.class, "replace", mt); s = (String) mh.invokeExact("daddy", 'd', 'n'); System.out.println("Result: " + s); // invokeExact(Ljava/lang/String;CC)Ljava/lang/String; // weakly typed invocation (using MHs.invoke) s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); System.out.println("Result: " + s); // mt is (Object[])List mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert (mh.isVarargsCollector()); x = mh.invoke("one", "two"); // ArrayList System.out.println("x: " + x + " type: " + x.getClass().getCanonicalName()); // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; // assertEquals(x, java.util.Arrays.asList("one","two")); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); mh = mh.asType(mt); x = mh.invokeExact((Object) 1, (Object) 2, (Object) 3); // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; // 1,2,3 System.out.println("x: " + x + " type: " + x.getClass().getCanonicalName()); // assertEquals(x, java.util.Arrays.asList(1,2,3)); // mt is ()int mt = MethodType.methodType(int.class); mh = lookup.findVirtual(java.util.List.class, "size", mt); i = (int) mh.invokeExact(java.util.Arrays.asList(1, 2, 3)); // invokeExact(Ljava/util/List;)I assert (i == 3); mt = MethodType.methodType(void.class, String.class); mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); mh.invokeExact(System.out, "Hello, world."); // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V }
/** * Return a method handle to invoke on the callerType, target, and remaining arguments. The method * handle must finish the call. This is the first look at the caller type and target. */ private MethodHandle dispatch(MethodType callerType, MethodHandle target) { MethodType targetType = target.type(); if (USE_AS_TYPE_PATH || target.isVarargsCollector()) { MethodHandle newTarget = target.asType(callerType); targetType = callerType; Invokers invokers = targetType.invokers(); MethodHandle invoker = invokers.erasedInvokerWithDrops; if (invoker == null) { invokers.erasedInvokerWithDrops = invoker = dropDispatchArguments(invokers.erasedInvoker()); } return invoker.bindTo(newTarget); } throw new RuntimeException("NYI"); }
public static int add5(int a) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType type = MethodType.methodType(int.class, int.class, int.class); MethodHandle mhAdd = lookup.findStatic(FunctionProgramming.class, "add", type); MethodHandle mh = curry(mhAdd, 5); return (int) mh.invoke(a); }
private MethodHandle dispatcher(String dispatchName) throws ReflectiveOperationException { return lookup() .bind( this, dispatchName, MethodType.methodType(MethodHandle.class, MethodType.class, MethodHandle.class)); }
@Override public MethodHandle methodHandle(MethodType inputType) throws Exception { return MethodHandles.lookup() .findVirtual( ShadowFilter.class, "filter", MethodType.methodType(JSObject.class, Object.class)) .bindTo(this); }
/** @author chengfu */ public class ToUpperCaseGenerator { private static final MethodHandle BSM = new MethodHandle( MH_INVOKESTATIC, ToUpperCase.class.getName().replace('.', '/'), "bootstrap", MethodType.methodType( CallSite.class, Lookup.class, String.class, MethodType.class, String.class) .toMethodDescriptorString()); /** @param args the command line arguments */ public static void main(String[] args) throws IOException { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, "ToUpperCaseMain", null, "java/lang/Object", null); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitInvokeDynamicInsn("toUpperCase", "()Ljava/lang/String;", BSM, "Hello"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); cw.visitEnd(); Files.write(Paths.get("build", "classes", "ToUpperCaseMain.class"), cw.toByteArray()); } }
void thinking() { try { MethodType mt = MethodType.methodType(void.class); MethodHandle mh = lookup().findSpecial(GrandFather.class, "thinking", mt, getClass()); mh.invoke(this); } catch (Throwable e) { } }
// warning ! stolen code ahead ! (http://code.google.com/p/jsr292-cookbook) public static CallSite myBSM(Lookup lookup, String name, MethodType type, Class<?> staticType) throws ReflectiveOperationException { MethodHandle target = lookup.findStatic(staticType, name, type); HashMap<String, HashMap<Object, Object>> cacheTable = cacheTables.get(staticType); String selector = name + type.toMethodDescriptorString(); HashMap<Object, Object> cache = cacheTable.get(selector); if (cache == null) { cache = new HashMap<Object, Object>(); cacheTable.put(selector, cache); } MethodHandle identity = MethodHandles.identity(type.returnType()); identity = identity.asType(identity.type().changeParameterType(0, Object.class)); identity = MethodHandles.dropArguments(identity, 1, type.parameterType(0)); MethodHandle update = UPDATE.bindTo(cache); update = update.asType(type.insertParameterTypes(0, type.returnType())); MethodHandle fallback = MethodHandles.foldArguments(update, target); fallback = MethodHandles.dropArguments(fallback, 0, Object.class); MethodHandle combiner = MethodHandles.guardWithTest(NOT_NULL, identity, fallback); MethodHandle cacheQuerier = MAP_GET.bindTo(cache); cacheQuerier = cacheQuerier.asType(MethodType.methodType(Object.class, type.parameterType(0))); MethodHandle memoize = MethodHandles.foldArguments(combiner, cacheQuerier); return new ConstantCallSite(memoize); }
private static MethodHandle virtualHandle( final String name, final Class<?> rtype, final Class<?>... ptypes) { try { return MethodHandles.lookup() .findVirtual(Prototype.class, name, MethodType.methodType(rtype, ptypes)); } catch (final ReflectiveOperationException e) { throw new IllegalStateException(e); } }
/** * Adapts the return type of the method handle with {@code explicitCastArguments} when it is an * unboxing conversion. This will ensure that nulls are unwrapped to false or 0. * * @param target the target method handle * @param newType the desired new type. Note that this method does not adapt the method handle * completely to the new type, it only adapts the return type; this is allowed as per {@link * DynamicLinkerFactory#setAutoConversionStrategy(MethodTypeConversionStrategy)}, which is * what this method is used for. * @return the method handle with adapted return type, if it required an unboxing conversion. */ private static MethodHandle unboxReturnType(final MethodHandle target, final MethodType newType) { final MethodType targetType = target.type(); final Class<?> oldReturnType = targetType.returnType(); final Class<?> newReturnType = newType.returnType(); if (TypeUtilities.isWrapperType(oldReturnType)) { if (newReturnType.isPrimitive()) { // The contract of setAutoConversionStrategy is such that the difference between newType and // targetType // can only be JLS method invocation conversions. assert TypeUtilities.isMethodInvocationConvertible(oldReturnType, newReturnType); return MethodHandles.explicitCastArguments( target, targetType.changeReturnType(newReturnType)); } } else if (oldReturnType == void.class && newReturnType == Object.class) { return MethodHandles.filterReturnValue(target, VOID_TO_OBJECT); } return target; }
/** * Given a method handle and an expected return type, perform return value filtering according to * the optimistic type coercion rules * * @param mh method handle * @param expectedReturnType expected return type * @param programPoint program point * @return filtered method */ public static MethodHandle filterOptimisticReturnValue( final MethodHandle mh, final Class<?> expectedReturnType, final int programPoint) { if (!isValid(programPoint)) { return mh; } final MethodType type = mh.type(); final Class<?> actualReturnType = type.returnType(); if (TypeUtilities.isConvertibleWithoutLoss(actualReturnType, expectedReturnType)) { return mh; } final MethodHandle guard = getOptimisticTypeGuard(expectedReturnType, actualReturnType); return guard == null ? mh : MH.filterReturnValue( mh, MH.insertArguments(guard, guard.type().parameterCount() - 1, programPoint)); }
public static Object call(InterfaceCallSite ics, Object o, Object[] args) throws Throwable { final Class<?> receiverClass = o.getClass(); // drop the 'this' (locals[0]) parameter from the signature // since that is implicit in a virtual method MethodHandle handle = lookup.findVirtual( receiverClass, ics.methodName, ics.methodType.dropParameterTypes(0, 1)); // cache receiver type as long as the CallSite // is not megamorphic if (ics.cacheDepth != -1) { // figure out what kind of an upgrade we're doing if (ics.cacheDepth < MAX_CACHE_DEPTH) { // upgrade, from either uninitialized or mono/polymorphic, // add class to polymorphic tree MethodHandle test = lookup .findVirtual( Class.class, "isInstance", MethodType.methodType(boolean.class, Object.class)) .bindTo(receiverClass); ics.cacheDepth++; // the new receiver type becomes the root // of the cache tree ics.setTarget( MethodHandles.guardWithTest( test, handle.asType(ics.getTarget().type()), ics.getTarget())); } else { // call site has become megamorphic. // don't use a cache tree anymore, set cacheDepth // to indicate megamorphic call site ics.setTarget(ics.rootHandle); ics.cacheDepth = -1; // TODO: since rootHandle points to this method, // every megmorphic invocation pays for // the branch above with ics.cacheDepth != -1 // really another static method needs to be // created that just does the invocation // without the upgrade logic } } // cache is now setup for next invocation, but we still need // to handle this invocation, so spread arguments array // over resolved receiver method and invoke handle = MethodHandles.insertArguments(handle, 0, o) .asSpreader(Object[].class, ics.methodType.parameterCount() - 1); return handle.invoke(args); }
/** * Return the declared type of this member, which must be a field or type. If it is a type member, * that type itself is returned. */ public Class<?> getFieldType() { if (type == null) { expandFromVM(); if (type == null) return null; } if (isInvocable()) throw newIllegalArgumentException("not a field or nested class, no simple type"); if (type instanceof Class<?>) { return (Class<?>) type; } if (type instanceof String) { String sig = (String) type; MethodType mtype = MethodType.fromMethodDescriptorString("()" + sig, getClassLoader()); Class<?> res = mtype.returnType(); this.type = res; return res; } throw new InternalError("bad field type " + type); }
static { try { MethodHandles.Lookup lookup = MethodHandles.lookup(); THROW_OR_RETURN = lookup.findStatic( TestCase.class, "throwOrReturn", MethodType.methodType(Object.class, Object.class, Throwable.class)); CATCHER = lookup.findStatic( TestCase.class, "catcher", MethodType.methodType(Object.class, Object.class)); FAKE_IDENTITY = lookup.findVirtual( TestCase.class, "fakeIdentity", MethodType.methodType(Object.class, Object.class)); } catch (NoSuchMethodException | IllegalAccessException e) { throw new Error(e); } PartialConstructor[] constructors = { create(Object.class, Object.class::cast), create(String.class, Objects::toString), create(int[].class, x -> new int[] {Objects.hashCode(x)}), create(long.class, x -> Objects.hashCode(x) & (-1L >>> 32)), create(void.class, TestCase::noop) }; Throwable[] throwables = { new ClassCastException("testing"), new java.io.IOException("testing"), new LinkageError("testing") }; List<Supplier<TestCase>> list = new ArrayList<>(constructors.length * throwables.length * ThrowMode.values().length); //noinspection unchecked for (PartialConstructor f : constructors) { for (ThrowMode mode : ThrowMode.values()) { for (Throwable t : throwables) { list.add(f.apply(mode, t)); } } } CONSTRUCTORS = Collections.unmodifiableList(list); }
static { Lookup lookup = MethodHandles.lookup(); try { NOT_NULL = lookup.findStatic( SpeedRecurenceWithIndyUsingBigInteger.class, "notNull", MethodType.methodType(boolean.class, Object.class)); MAP_GET = lookup.findVirtual( HashMap.class, "get", MethodType.methodType(Object.class, Object.class)); UPDATE = lookup.findStatic( SpeedRecurenceWithIndyUsingBigInteger.class, "update", MethodType.methodType(Object.class, HashMap.class, Object.class, Object.class)); } catch (ReflectiveOperationException e) { throw (AssertionError) new AssertionError().initCause(e); } }