/** * 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)); }
/** * Create a new SmartBinder with from the given types and argument name. * * @param retType the type of the return value to start with * @param name the name of the sole argument * @param type the sole argument's type * @return a new SmartBinder */ public static SmartBinder from(Class<?> retType, String name, Class<?> type) { return from(Signature.returning(retType).appendArg(name, type)); }
/** * Create a new SmartBinder from the given types and argument name, * using the given Lookup for any handle lookups. * * @param lookup the Lookup to use for handle lookups * @param retType the type of the return value to start with * @param name the name of the sole arguments * @param type the sole argument's type * @return a new SmartBinder */ public static SmartBinder from(Lookup lookup, Class<?> retType, String name, Class<?> type) { return from(lookup, Signature.returning(retType).appendArg(name, type)); }
/** * Create a new SmartBinder from the given types and argument names, * using the given Lookup for any handle lookups. * * @param lookup the Lookup to use for handle lookups * @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(Lookup lookup, Class<?> retType, String[] names, Class<?>... types) { return from(lookup, Signature.returning(retType).appendArgs(names, types)); }
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; }
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); } }
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(); }
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); } }
@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(); }
@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); }
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(); }
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); } }