/** * Terminate this binder by invoking the given target handle. The signature * of this binder is not compared to the signature of the given * SmartHandle. * * @param target the handle to invoke * @return a new SmartHandle with this binder's starting signature, bound * through to the given handle */ public SmartHandle invoke(SmartHandle target) { return new SmartHandle(start, binder.invoke(target.handle())); }
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; }
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; }
/** * Pass all arguments to the given function and drop any result. * * @param function a function which will receive all arguments and have its * return value inserted into the call chain * @return a new SmartBinder with the fold applied */ public SmartBinder foldVoid(SmartHandle function) { if (Arrays.equals(signature().argNames(), function.signature().argNames())) { return foldVoid(function.handle()); } else { return foldVoid(signature().asFold(void.class).permuteWith(function).handle()); } }
public static MethodHandle getFrameOnlyPre(Signature signature, CallConfiguration callConfig, RubyModule implClass, String name) { Signature inbound = signature.asFold(void.class); SmartBinder binder = SmartBinder .from(inbound); switch (callConfig) { case FrameFullScopeNone: // before logic return binder .permute("context", "self", "block") .insert(1, arrayOf("selfClass", "name"), arrayOf(RubyModule.class, String.class), implClass, name) .invokeVirtualQuiet(lookup(), "preMethodFrameOnly") .handle(); default: throw new RuntimeException("invalid input: " + callConfig); } }
/** * Produce a new SmartHandle by permuting this Signature's arguments to the * Signature of a target SmartHandle. The new SmartHandle's signature will * match this one, permuting those arguments and invoking the target handle. * * @param target the SmartHandle to use as a permutation target * @return a new SmartHandle that permutes this Signature's args into a call * to the target SmartHandle. * @see Signature#permuteWith(java.lang.invoke.MethodHandle, java.lang.String[]) */ public SmartHandle permuteWith(SmartHandle target) { String[] argNames = target.signature().argNames(); return new SmartHandle(this, permuteWith(target.handle(), argNames)); }
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); }
public static MethodHandle getFrameOnlyPre(Signature signature, CallConfiguration callConfig, RubyModule implClass, String name) { Signature inbound = signature.asFold(void.class); SmartBinder binder = SmartBinder .from(inbound); switch (callConfig) { case FrameFullScopeNone: // before logic return binder .permute("context", "self", "block") .insert(1, arrayOf("selfClass", "name"), arrayOf(RubyModule.class, String.class), implClass, name) .invokeVirtualQuiet(lookup(), "preMethodFrameOnly") .handle(); default: throw new RuntimeException("invalid input: " + callConfig); } }
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(); }
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); }
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(); }
/** * Pass all arguments to the given function and insert the resulting value * as newName into the argument list. * * @param newName the name of the new first argument where the fold * function's result will be passed * @param function a function which will receive all arguments and have its * return value inserted into the call chain * @return a new SmartBinder with the fold applied */ public SmartBinder fold(String newName, SmartHandle function) { if (Arrays.equals(signature().argNames(), function.signature().argNames())) { return fold(newName, function.handle()); } else { return fold(newName, signature().changeReturn(function.signature().type().returnType()).permuteWith(function).handle()); } }
/** * Use the given filter function to transform the return value at this * point in the binder. The filter will be inserted into the handle, and * return values will pass through it before continuing. * * The filter's argument must match the expected return value downstream * from this point in the binder, and the return value must match the * return value at this point in the binder. * * @param filter the function to use to transform the return value at this point * @return a new SmartBinder with the filter applied */ public SmartBinder filterReturn(SmartHandle filter) { return new SmartBinder(this, signature().changeReturn(filter.signature().type().returnType()), binder.filterReturn(filter.handle())); }
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(); }
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(); }
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; }
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; }
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); } }
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); } }