private static void registerCallSitePlugins(InvocationPlugins plugins) { InvocationPlugin plugin = new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { ValueNode callSite = receiver.get(); ValueNode folded = CallSiteTargetNode.tryFold(GraphUtil.originalValue(callSite), b.getMetaAccess(), b.getAssumptions()); if (folded != null) { b.addPush(JavaKind.Object, folded); } else { b.addPush(JavaKind.Object, new CallSiteTargetNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnStamp(b.getAssumptions()), callSite)); } return true; } @Override public boolean inlineOnly() { return true; } }; plugins.register(plugin, ConstantCallSite.class, "getTarget", Receiver.class); plugins.register(plugin, MutableCallSite.class, "getTarget", Receiver.class); plugins.register(plugin, VolatileCallSite.class, "getTarget", Receiver.class); }
public static void testNonBoundCallSite() throws Throwable { mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE)); // mcs.context == null MethodHandle mh = mcs.dynamicInvoker(); execute(1, mh); // mcs.context == cls1 Class<?> cls1 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("NonBound_1"), null); MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE); execute(1, mh1); mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE)); execute(2, mh, mh1); }
public static void testNonBoundCallSite() throws Throwable { mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE)); // mcs.context == null MethodHandle mh = mcs.dynamicInvoker(); execute(1, mh); // mcs.context == cls1 Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("NonBound_1"), null); MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE); execute(1, mh1); mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE)); execute(2, mh, mh1); }
@SuppressWarnings("unused") private static MethodHandle nativeCallSetup(MutableCallSite callsite, String name, ExecutionContext cx) { RuntimeContext context = cx.getRuntimeContext(); MethodHandle target; try { MethodHandle mh = context.getNativeCallResolver().apply(name, callsite.type()); if (mh == null) { throw new IllegalArgumentException(); } target = adaptNativeMethodHandle(mh); target = adaptMethodHandle(name, callsite.type(), target); } catch (IllegalArgumentException e) { target = invalidCallHandle(name, callsite.type()); } callsite.setTarget(target); return target; }
private static void concatSetup(MutableCallSite callsite, MethodType type) { MethodHandle target, test, generic; int numberOfStrings = type.parameterCount() - 1; // CharSequence..., ExecutionContext if (numberOfStrings <= CONCAT_MAX_SPECIALIZATION) { assert numberOfStrings >= CONCAT_MIN_PARAMS; int index = numberOfStrings - CONCAT_MIN_PARAMS; target = concatMH[index]; test = testConcatMH[index]; generic = concatConsMH[index]; } else { final int index = CONCAT_MAX_SPECIALIZATION - CONCAT_MIN_PARAMS + 1; target = concatMH[index].asCollector(CharSequence[].class, numberOfStrings); test = testConcatMH[index].asCollector(CharSequence[].class, numberOfStrings); generic = concatConsMH[index].asCollector(CharSequence[].class, numberOfStrings); } setCallSiteTarget(callsite, target, test, generic); }
private static MethodHandle setCallSiteTarget(MutableCallSite callsite, MethodHandle target, MethodHandle test, MethodHandle generic) { MethodHandle callSiteTarget; if (target != null) { target = target.asType(callsite.type()); if (test != null) { MethodHandle fallback = createFallback(callsite, generic); callSiteTarget = MethodHandles.guardWithTest(test, target, fallback); } else { callSiteTarget = target; } } else { callSiteTarget = target = generic; } callsite.setTarget(callSiteTarget); return target; }
public static CallSite bootstrapValue (Lookup lookup, String name, MethodType type) { name = StringNames.toSourceName(name); // get or create constant method handle: // reuse same constant method handle instead // of creating a new method handle for each call site MethodHandle methodHandle = getOrCreateValueMethodHandle(name); // create and record callsite MutableCallSite callSite = new MutableCallSite(methodHandle); List<MutableCallSite> callSites = valueCallSites.get(name); if (callSites == null) callSites = new ArrayList<>(); callSites.add(callSite); valueCallSites.put(name, callSites); return callSite; }
private MethodHandle createComposableInvoker(final boolean isConstructor) { final MethodHandle handle = getInvokerOrConstructor(isConstructor); // If compiled function is not optimistic, it can't ever change its invoker/constructor, so just return them // directly. if(!canBeDeoptimized()) { return handle; } // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itslef // to the compiled function's changed target whenever the optimistic assumptions are invalidated. final CallSite cs = new MutableCallSite(handle.type()); relinkComposableInvoker(cs, this, isConstructor); return cs.dynamicInvoker(); }
public static void main(String[] args) throws ReflectiveOperationException { // Objects test(new Object()); test("TEST"); test(new VMAnonymousClasses()); test(null); // Class test(String.class); // Arrays test(new boolean[0]); test(new byte[0]); test(new char[0]); test(new short[0]); test(new int[0]); test(new long[0]); test(new float[0]); test(new double[0]); test(new Object[0]); // Multi-dimensional arrays test(new byte[0][0]); test(new Object[0][0]); // MethodHandle-related MethodType mt = MethodType.methodType(void.class, String[].class); MethodHandle mh = MethodHandles.lookup().findStatic(VMAnonymousClasses.class, "main", mt); test(mt); test(mh); test(new ConstantCallSite(mh)); test(new MutableCallSite(MethodType.methodType(void.class))); test(new VolatileCallSite(MethodType.methodType(void.class))); System.out.println("TEST PASSED"); }
public static void testSharedCallSite() throws Throwable { Class<?> cls1 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_1"), null); Class<?> cls2 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_2"), null); MethodHandle[] mhs = new MethodHandle[] { LOOKUP.findStatic(cls1, METHOD_NAME, TYPE), LOOKUP.findStatic(cls2, METHOD_NAME, TYPE) }; mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE)); execute(1, mhs); mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE)); execute(2, mhs); }
private MethodHandle createComposableInvoker(final boolean isConstructor) { final MethodHandle handle = getInvokerOrConstructor(isConstructor); // If compiled function is not optimistic, it can't ever change its invoker/constructor, so just return them // directly. if(!canBeDeoptimized()) { return handle; } // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itself // to the compiled function's changed target whenever the optimistic assumptions are invalidated. final CallSite cs = new MutableCallSite(handle.type()); relinkComposableInvoker(cs, this, isConstructor); return cs.dynamicInvoker(); }
/** * Relinks a call site conforming to the invocation arguments. * * @param callSite the call site itself * @param arguments arguments to the invocation * * @return return the method handle for the invocation * * @throws Exception rethrows any exception thrown by the linkers */ @SuppressWarnings("unused") private MethodHandle relink(final RelinkableCallSite callSite, final int relinkCount, final Object... arguments) throws Exception { final CallSiteDescriptor callSiteDescriptor = callSite.getDescriptor(); final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0; final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold; final LinkRequest linkRequest = new SimpleLinkRequest(callSiteDescriptor, callSiteUnstable, arguments); GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest); // None found - throw an exception if(guardedInvocation == null) { throw new NoSuchDynamicMethodException(callSiteDescriptor.toString()); } // Make sure we transform the invocation before linking it into the call site. This is typically used to match the // return type of the invocation to the call site. guardedInvocation = prelinkTransformer.filter(guardedInvocation, linkRequest, linkerServices); Objects.requireNonNull(guardedInvocation); int newRelinkCount = relinkCount; // Note that the short-circuited "&&" evaluation below ensures we'll increment the relinkCount until // threshold + 1 but not beyond that. Threshold + 1 is treated as a special value to signal that resetAndRelink // has already executed once for the unstable call site; we only want the call site to throw away its current // linkage once, when it transitions to unstable. if(unstableDetectionEnabled && newRelinkCount <= unstableRelinkThreshold && newRelinkCount++ == unstableRelinkThreshold) { callSite.resetAndRelink(guardedInvocation, createRelinkAndInvokeMethod(callSite, newRelinkCount)); } else { callSite.relink(guardedInvocation, createRelinkAndInvokeMethod(callSite, newRelinkCount)); } if(syncOnRelink) { MutableCallSite.syncAll(new MutableCallSite[] { (MutableCallSite)callSite }); } return guardedInvocation.getInvocation(); }
public static void testSharedCallSite() throws Throwable { Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null); Class<?> cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null); MethodHandle[] mhs = new MethodHandle[] { LOOKUP.findStatic(cls1, METHOD_NAME, TYPE), LOOKUP.findStatic(cls2, METHOD_NAME, TYPE) }; mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE)); execute(1, mhs); mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE)); execute(2, mhs); }
/** * Returns the Selector */ public static Selector getSelector(MutableCallSite callSite, Class sender, String methodName, int callID, boolean safeNavigation, boolean thisCall, boolean spreadCall, Object[] arguments) { CALL_TYPES callType = CALL_TYPES_VALUES[callID]; switch (callType) { case INIT: return new InitSelector(callSite, sender, methodName, callType, safeNavigation, thisCall, spreadCall, arguments); case METHOD: return new MethodSelector(callSite, sender, methodName, callType, safeNavigation, thisCall, spreadCall, arguments); case GET: return new PropertySelector(callSite, sender, methodName, callType, safeNavigation, thisCall, spreadCall, arguments); case SET: throw new GroovyBugError("your call tried to do a property set, which is not supported."); case CAST: return new CastSelector(callSite, arguments); default: throw new GroovyBugError("unexpected call type"); } }
public MethodSelector(MutableCallSite callSite, Class sender, String methodName, CALL_TYPES callType, Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object[] arguments) { this.callType = callType; this.targetType = callSite.type(); this.name = methodName; this.originalArguments = arguments; this.args = spread(arguments, spreadCall); this.callSite = callSite; this.sender = sender; this.safeNavigationOrig = safeNavigation; this.safeNavigation = safeNavigation && arguments[0]==null; this.thisCall = thisCall; this.spread = spreadCall; this.cache = !spread; if (LOG_ENABLED) { StringBuilder msg = new StringBuilder("----------------------------------------------------" + "\n\t\tinvocation of method '" + methodName + "'" + "\n\t\tinvocation type: " + callType + "\n\t\tsender: " + sender + "\n\t\ttargetType: " + targetType + "\n\t\tsafe navigation: " + safeNavigation + "\n\t\tthisCall: " + thisCall + "\n\t\tspreadCall: " + spreadCall + "\n\t\twith " + arguments.length + " arguments"); for (int i=0; i<arguments.length; i++) { msg.append("\n\t\t\targument[").append(i).append("] = "); if (arguments[i] == null) { msg.append("null"); } else { msg.append(arguments[i].getClass().getName()).append("@").append(Integer.toHexString(System.identityHashCode(arguments[i]))); } } LOG.info(msg.toString()); } }
/** * backing bootstrap method with all parameters */ private static CallSite realBootstrap(Lookup caller, String name, int callID, MethodType type, boolean safe, boolean thisCall, boolean spreadCall) { // since indy does not give us the runtime types // we produce first a dummy call site, which then changes the target to one, // that does the method selection including the the direct call to the // real method. MutableCallSite mc = new MutableCallSite(type); MethodHandle mh = makeFallBack(mc,caller.lookupClass(),name,callID,type,safe,thisCall,spreadCall); mc.setTarget(mh); return mc; }
/** * Makes a fallback method for an invalidated method selection */ protected static MethodHandle makeFallBack(MutableCallSite mc, Class<?> sender, String name, int callID, MethodType type, boolean safeNavigation, boolean thisCall, boolean spreadCall) { MethodHandle mh = MethodHandles.insertArguments(SELECT_METHOD, 0, mc, sender, name, callID, safeNavigation, thisCall, spreadCall, /*dummy receiver:*/ 1); mh = mh.asCollector(Object[].class, type.parameterCount()). asType(type); return mh; }
/** * Core method for indy method selection using runtime types. */ public static Object selectMethod(MutableCallSite callSite, Class sender, String methodName, int callID, Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object dummyReceiver, Object[] arguments) throws Throwable { Selector selector = Selector.getSelector(callSite, sender, methodName, callID, safeNavigation, thisCall, spreadCall, arguments); selector.setCallSiteTarget(); MethodHandle call = selector.handle.asSpreader(Object[].class, arguments.length); call = call.asType(MethodType.methodType(Object.class,Object[].class)); return call.invokeExact(arguments); }
private CallSite createCallSite(int parameterIndex, String parameterName) { Annotation[] annotations; GuardTarget target = new GuardTarget(guardable, parameterIndex, parameterName); if ( parameterIndex < 0 ) { annotations = guardable.getAnnotations(); } else { Annotation[][] allAnnotations = guardable.getParameterAnnotations(); if ( parameterIndex >= allAnnotations.length ) { return new ConstantCallSite(Indy.nopHandle(target.getValueType())); } annotations = allAnnotations[parameterIndex]; } if ( annotations.length == 0 ) { return new ConstantCallSite(Indy.nopHandle(target.getValueType())); } ArrayList<GuardInstance> guardInstances = new ArrayList<>(annotations.length); for( Annotation annotation : annotations ) { if ( isGuard(annotation) ) { guardInstances.add(new GuardInstance(target, annotation, null)); } } MethodHandle handle = null; for( GuardInstance guardInstance : Lists.reverse(guardInstances)) { handle = GuardDefinition.get(guardInstance.getAnnotation().annotationType()).resolveTestMethod(guardInstance, handle); } if ( handle == null ) { handle = Indy.nopHandle(target.getValueType()); } if ( options.isXMutableCallSites() ) { return new MutableCallSite(handle); } else { return new ConstantCallSite(handle); } }
public static CallSite bootstrap(Lookup lookup, String name, MethodType type) throws ReflectiveOperationException { CallSite callSite = new MutableCallSite(type); MethodHandle send = from(type) .insert(0, lookup) .invokeStatic(lookup, SnacksDispatcher.class, name); callSite.setTarget(send); return callSite; }
private static MethodHandle addSetup(MutableCallSite callsite, Object arg1, Object arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.String) { target = addStringMH; } else if (type == Type.Number) { target = addNumberMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinary(type), addGenericMH); }
private static MethodHandle relCmpSetup(MutableCallSite callsite, RelationalOperator op, Object arg1, Object arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.String) { target = filterReturnValue(relCmpStringMH, op); } else if (type == Type.Number) { target = filterReturnValue(relCmpNumberMH, op); } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinary(type), filterReturnValue(MethodHandles.insertArguments(relCmpGenericMH, 2, op), op)); }
private static MethodHandle eqCmpSetup(MutableCallSite callsite, Object arg1, Object arg2, ExecutionContext cx) { // TODO(BigInt): Implement BigInt specializations. Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.String) { target = eqCmpStringMH; } else if (type == Type.Number) { target = eqCmpNumberMH; } else if (type == Type.Boolean) { target = eqCmpBooleanMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinary(type), eqCmpGenericMH); }
private static MethodHandle strictEqCmpSetup(MutableCallSite callsite, Object arg1, Object arg2) { // TODO(BigInt): Implement BigInt specializations. Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.String) { target = strictEqCmpStringMH; } else if (type == Type.Number) { target = strictEqCmpNumberMH; } else if (type == Type.Boolean) { target = strictEqCmpBooleanMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinary(type), strictEqCmpGenericMH); }
private static MethodHandle subSetup(MutableCallSite callsite, Number arg1, Number arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.Number) { target = subNumberMH; } else if (type == Type.BigInt) { target = subBigIntMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinaryNumber(type), subGenericMH); }
private static MethodHandle expSetup(MutableCallSite callsite, Number arg1, Number arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.Number) { target = expNumberMH; } else if (type == Type.BigInt) { target = expBigIntMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinaryNumber(type), expGenericMH); }
private static MethodHandle mulSetup(MutableCallSite callsite, Number arg1, Number arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.Number) { target = mulNumberMH; } else if (type == Type.BigInt) { target = mulBigIntMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinaryNumber(type), mulGenericMH); }
private static MethodHandle divSetup(MutableCallSite callsite, Number arg1, Number arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.Number) { target = divNumberMH; } else if (type == Type.BigInt) { target = divBigIntMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinaryNumber(type), divGenericMH); }
private static MethodHandle modSetup(MutableCallSite callsite, Number arg1, Number arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.Number) { target = modNumberMH; } else if (type == Type.BigInt) { target = modBigIntMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinaryNumber(type), modGenericMH); }
private static MethodHandle shlSetup(MutableCallSite callsite, Number arg1, Number arg2, ExecutionContext cx) { Type type = getType(arg1, arg2); MethodHandle target; if (type == Type.Number) { target = shlNumberMH; } else if (type == Type.BigInt) { target = shlBigIntMH; } else { target = null; } return setCallSiteTarget(callsite, target, getTestForBinaryNumber(type), shlGenericMH); }