private static MethodHandle dynamicCallTarget(Signature from, Signature to) { return SmartBinder .from(from) .fold("selfClass", from.asFold(RubyClass.class).permuteWith(PGC, "context", "self")) .permute(to) .cast(to) .invokeVirtualQuiet(lookup(), "call") .handle(); }
/** * Prepare a binder for this call site's target, forcing varargs if specified * * @param varargs whether to only call an arg-boxed variable arity path * @return the prepared binder */ public Binder prepareBinder(boolean varargs) { SmartBinder binder = SmartBinder.from(signature); if (varargs || arity > 3) { // we know we want to call varargs path always, so prepare args[] here if (arity == -1) { // do nothing, already have IRubyObject[] in args } else if (arity == 0) { binder = binder.insert(argOffset, "args", IRubyObject.NULL_ARRAY); } else { binder = binder .collect("args", "arg[0-9]+"); } } // add block if needed if (signature.lastArgType() != Block.class) { binder = binder.append("block", Block.NULL_BLOCK); } // bind to site binder = binder.insert(0, "site", this); return binder.binder(); }
targetBinder = SmartBinder.from(baseSignature); targetBinder = targetBinder.exclude("class", "name"); targetBinder = targetBinder.exclude("block"); targetBinder = targetBinder.exclude("context"); } else { targetBinder = targetBinder.exclude("context", "block"); if (hasContext) { if (hasBlock) { targetBinder = targetBinder.permute("self", "context", "arg*", "block"); } else { targetBinder = targetBinder.permute("self", "context", "arg*"); targetBinder = targetBinder.permute("self", "arg*", "block"); } else { targetBinder = targetBinder.permute("self", "arg*");
/** * Permute all parameters except the names given. Blacklisting to #permute's * whitelisting. * * @param excludeNames parameter patterns to exclude * @return a new SmartBinder with the exclude applied */ public SmartBinder exclude(String... excludeNames) { return permute(signature().exclude(excludeNames)); }
targetBinder = SmartBinder.from(baseSignature); targetBinder = targetBinder.exclude("block"); targetBinder = targetBinder.exclude("context"); } else { targetBinder = targetBinder.exclude("context", "block"); .filterReturn(returnFilter); } else if (castReturn) { targetBinder = targetBinder .castReturn(desc.returnClass); targetBinder = targetBinder.permute("self", "context", "arg*", "block"); } else { targetBinder = targetBinder.permute("self", "context", "arg*"); targetBinder = targetBinder.permute("self", "arg*", "block"); } else { targetBinder = targetBinder.permute("self", "arg*"); .filterReturn(returnFilter); } else if (castReturn) { targetBinder = targetBinder .castReturn(desc.returnClass); .castArg("self", desc.getDeclaringClass()); .invokeStaticQuiet(LOOKUP, desc.getDeclaringClass(), javaMethodName);
static MethodHandle buildGenericHandle(InvokeSite site, DynamicMethod method, RubyClass dispatchClass) { SmartBinder binder; binder = SmartBinder.from(site.signature) .permute("context", "self", "arg.*", "block") .insert(2, new String[]{"rubyClass", "name"}, new Class[]{RubyModule.class, String.class}, dispatchClass, site.name()) .insert(0, "method", DynamicMethod.class, method); if (site.arity > 3) { binder = binder.collect("args", "arg.*"); } if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) { LOG.info(site.name() + "\tbound indirectly " + method + ", " + Bootstrap.logMethod(method)); } return binder.invokeVirtualQuiet(LOOKUP, "call").handle(); }
binder = SmartBinder.from(lookup(), site.signature); } else { binder = SmartBinder.from(lookup(), site.signature); } else if (site.arity == 0) { binder = SmartBinder.from(lookup(), site.signature) .insert(2, "args", IRubyObject.NULL_ARRAY); } else { binder = SmartBinder.from(lookup(), site.signature) .collect("args", "arg.*"); binder = binder.drop("context"); binder = binder.append("block", Block.NULL_BLOCK); } else if (!nc.hasBlock() && blockGiven) { binder = binder.drop("block"); .permute("context", "self", "arg.*", "block") // filter caller .cast(nc.getNativeReturn(), nc.getNativeSignature()) .invokeStaticQuiet(LOOKUP, nc.getNativeTarget(), nc.getNativeName()) .handle(); } else { mh = binder .permute("self", "context", "arg.*", "block") // filter caller, move self .castArg("self", nc.getNativeTarget()) .castVirtual(nc.getNativeReturn(), nc.getNativeTarget(), nc.getNativeSignature()) .invokeVirtualQuiet(LOOKUP, nc.getNativeName())
.from(signature.asFold(boolean.class)) .permute("self") .insert(1, "selfJavaType", self.getClass()) .cast(boolean.class, Object.class, Class.class) .invoke(TEST_CLASS); .from(signature.changeReturn(boolean.class)) .permute("self") .insert(0, "selfClass", RubyClass.class, testClass) .invokeStaticQuiet(Bootstrap.LOOKUP, Bootstrap.class, "testType");
.from(VARIABLE_ARITY_SIGNATURE.prependArg("script", scriptClass)) .invokeStaticQuiet(LOOKUP, scriptClass, javaName) .bindTo(scriptObject); } else { .from(SPECIFIC_ARITY_SIGNATURES[specificArity].prependArg("script", scriptClass)) .invokeStaticQuiet(LOOKUP, scriptClass, javaName) .bindTo(scriptObject); if (specificArity >= 0) { SmartHandle arityCheck = SmartBinder .from(ARITY_CHECK_FOLD) .append(new String[]{"min", "max"}, new Class[]{int.class, int.class}, specificArity, specificArity) .cast(ARITY_CHECK_SIGNATURE) .invokeStaticQuiet(LOOKUP, Arity.class, "checkArgumentCount"); .from(VARIABLE_ARITY_SIGNATURE) .foldVoid(arityCheck) .permute("script", "context", "self", "block", "args") .spread("arg", specificArity) .permute("script", "context", "self", "arg*", "block") .invoke(directCall); } else { variableCall = directCall;
binder = SmartBinder.from(site.signature) .permute("context", "self", "arg.*", "block"); } else { mh = (MethodHandle)compiledIRMethod.getHandle(); binder = binder.insert(2, "args", IRubyObject.NULL_ARRAY); } else { mh = (MethodHandle) compiledIRMethod.getHandle(); binder = binder.collect("args", "arg.*"); binder = binder.append("block", Block.class, Block.NULL_BLOCK); .insert(1, "scope", StaticScope.class, compiledIRMethod.getStaticScope()) .append("class", RubyModule.class, compiledIRMethod.getImplementationClass()) .append("frameName", String.class, site.name()); mh = binder.invoke(mh).handle();
public static MethodHandle getFramePost(Signature signature, CallConfiguration callConfig) { Signature inbound = signature.asFold(void.class); SmartBinder binder = SmartBinder .from(inbound) .permute("context"); .invokeVirtualQuiet(lookup(), "postMethodFrameAndScope") .handle(); .invokeVirtualQuiet(lookup(), "postMethodFrameAndScope") .handle(); .invokeVirtualQuiet(lookup(), "postMethodFrameOnly") .handle(); .invokeVirtualQuiet(lookup(), "postMethodScopeOnly") .handle(); .invokeVirtualQuiet(lookup(), "postMethodScopeOnly") .handle();
MethodHandle buildNewInstanceHandle(DynamicMethod method, IRubyObject self) { MethodHandle mh = null; if (method == self.getRuntime().getBaseNewMethod()) { RubyClass recvClass = (RubyClass) self; // Bind a second site as a dynamic invoker to guard against changes in new object's type CallSite initSite = SelfInvokeSite.bootstrap(LOOKUP, "callFunctional:initialize", type(), literalClosure ? 1 : 0, file, line); MethodHandle initHandle = initSite.dynamicInvoker(); MethodHandle allocFilter = Binder.from(IRubyObject.class, IRubyObject.class) .cast(IRubyObject.class, RubyClass.class) .insert(0, new Class[] {ObjectAllocator.class, Ruby.class}, recvClass.getAllocator(), self.getRuntime()) .invokeVirtualQuiet(LOOKUP, "allocate"); mh = SmartBinder.from(LOOKUP, signature) .filter("self", allocFilter) .fold("dummy", initHandle) .permute("self") .identity() .handle(); if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) { LOG.info(name() + "\tbound as new instance creation " + Bootstrap.logMethod(method)); } } return mh; }
SmartHandle rewriteHandle = SmartBinder.from(lookup(), site.signature().insertArg(0, "throwable", Throwable.class)) .permute("throwable") .append("runtime", method.getImplementationClass().getRuntime()) .invokeStaticQuiet(lookup(), Helpers.class, "rewriteStackTraceAndThrow");
targetBinder = targetBinder.filterReturn(MethodHandles.constant(IRubyObject.class, runtime.getNil())); } else { targetBinder = targetBinder .castReturn(returnClass); .castArg("self", declaringClass); SmartHandle smartTarget = targetBinder.invoke(method); if (frame) { smartTarget = SmartHandle .from(smartTarget.signature(), InvocationLinker.wrapWithFrameOnly(binder.baseSignature(), implementationClass, rubyName, smartTarget.handle()));
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) throws Throwable { RubyClass selfClass = pollAndGetClass(context, self); MethodHandle mh; CacheEntry entry = selfClass.searchWithCache(methodName); DynamicMethod method = entry.method; if (method.isBuiltin() && selfClass == context.runtime.getHash()) { // fast path since we know we're working with a normal hash and have a pre-frozen string mh = SmartBinder.from(signature) .permute("self", "context", "arg0") .cast(IRubyObject.class, RubyHash.class, ThreadContext.class, IRubyObject.class) .invokeVirtual(MethodHandles.publicLookup(), "op_aref") .handle(); SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData(); updateInvocationTarget(mh, self, selfClass, method, switchPoint); return ((RubyHash) self).op_aref(context, args[0]); } else { // slow path follows normal invoke logic with a strDup for the key SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData(); // strdup for this call args[0] = ((RubyString) args[0]).strDup(context.runtime); if (methodMissing(entry, caller)) { return callMethodMissing(entry, callType, context, self, selfClass, methodName, args, block); } mh = getHandle(self, selfClass, method); // strdup for future calls mh = MethodHandles.filterArguments(mh, 3, STRDUP_FILTER); updateInvocationTarget(mh, self, selfClass, entry.method, switchPoint); return method.call(context, self, selfClass, methodName, args, block); } }
private static MethodHandle getBlockEscape(Signature signature) { Signature voidSignature = signature.changeReturn(void.class); MethodHandle escape = BLOCK_ESCAPES.get(voidSignature); if (escape == null) { escape = SmartBinder.from(voidSignature) .permute("block") .invoke(ESCAPE_BLOCK) .handle(); BLOCK_ESCAPES.put(voidSignature, escape); } return escape; }
@Override public Binder prepareBinder() { // collect dregexp args into an array String[] argNames = new String[type().parameterCount()]; Class[] argTypes = new Class[argNames.length]; argNames[0] = "context"; argTypes[0] = ThreadContext.class; for (int i = 1; i < argNames.length; i++) { argNames[i] = "part" + i; argTypes[i] = RubyString.class; } // "once" deregexp must be handled on the call side return SmartBinder .from(RubyRegexp.class, argNames, argTypes) .collect("parts", "part.*") .binder(); }
public static RubyString string(MutableCallSite site, ByteList value, int cr, ThreadContext context) throws Throwable { MethodHandle handle = SmartBinder .from(STRING_SIGNATURE) .invoke(NEW_STRING_SHARED_HANDLE.apply("byteList", value)) .handle(); site.setTarget(handle); return RubyString.newStringShared(context.runtime, value, cr); }
/** * Create a new SmartBinder from the given types and argument names. * * @param retType the type of the return value to start with * @param names the names of arguments * @param types the argument types * @return a new SmartBinder */ public static SmartBinder from(Class<?> retType, String[] names, Class<?>... types) { return from(Signature.returning(retType).appendArgs(names, types)); }