static MethodHandle buildJittedHandle(InvokeSite site, DynamicMethod method, boolean blockGiven) { MethodHandle mh = null; SmartBinder binder; CompiledIRMethod compiledIRMethod = null; if (method instanceof CompiledIRMethod) { compiledIRMethod = (CompiledIRMethod) method; } else if (method instanceof InterpretedIRMethod) { DynamicMethod actualMethod = ((InterpretedIRMethod) method).getActualMethod(); if (actualMethod instanceof CompiledIRMethod) { compiledIRMethod = (CompiledIRMethod) actualMethod; } } if (compiledIRMethod != null) { // attempt IR direct binding // TODO: this will have to expand when we start specializing arities binder = SmartBinder.from(site.signature).permute("context", "self", "arg.*", "block"); if (site.arity == -1) { // already [], nothing to do mh = (MethodHandle) compiledIRMethod.getHandle(); } else if (site.arity == 0) { MethodHandle specific; if ((specific = compiledIRMethod.getHandleFor(site.arity)) != null) { mh = specific; } else { mh = (MethodHandle) compiledIRMethod.getHandle(); binder = binder.insert(2, "args", IRubyObject.NULL_ARRAY); } } else { MethodHandle specific; if ((specific = compiledIRMethod.getHandleFor(site.arity)) != null) { mh = specific; } else { mh = (MethodHandle) compiledIRMethod.getHandle(); binder = binder.collect("args", "arg.*"); } } if (!blockGiven) { binder = binder.append("block", Block.class, Block.NULL_BLOCK); } binder = binder .insert(1, "scope", StaticScope.class, compiledIRMethod.getStaticScope()) .append("class", RubyModule.class, compiledIRMethod.getImplementationClass()); mh = binder.invoke(mh).handle(); } return mh; }
public Binder prepareBinder() { SmartBinder binder = SmartBinder.from(signature); // prepare arg[] 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(); }
/** * Update the given call site using the new target, wrapping with appropriate guard and * argument-juggling logic. Return a handle suitable for invoking with the site's original method * type. */ MethodHandle updateInvocationTarget( MethodHandle target, IRubyObject self, RubyModule testClass, CacheEntry entry, SwitchPoint switchPoint) { if (target == null || clearCount > Options.INVOKEDYNAMIC_MAXFAIL.load() || (!hasSeenType(testClass.id) && seenTypesCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load())) { setTarget(target = prepareBinder().invokeVirtualQuiet(lookup(), "fail")); } else { MethodHandle fallback; MethodHandle gwt; // if we've cached no types, and the site is bound and we haven't seen this new type... if (seenTypesCount() > 0 && getTarget() != null && !hasSeenType(testClass.id)) { // stack it up into a PIC if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info(methodName + "\tadded to PIC " + logMethod(entry.method)); fallback = getTarget(); } else { // wipe out site with this new type and method String bind = boundOnce ? "rebind" : "bind"; if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( methodName + "\ttriggered site #" + siteID + " " + bind); // + " (" + file() + ":" + line() + ")"); fallback = this.fallback; clearTypes(); } addType(testClass.id); SmartHandle test; SmartBinder selfTest = SmartBinder.from(signature.asFold(boolean.class)).permute("self"); if (self instanceof RubySymbol || self instanceof RubyFixnum || self instanceof RubyFloat || self instanceof RubyNil || self instanceof RubyBoolean.True || self instanceof RubyBoolean.False) { test = selfTest .insert(1, "selfJavaType", self.getClass()) .cast(boolean.class, Object.class, Class.class) .invoke(TEST_CLASS); } else { test = SmartBinder.from(signature.changeReturn(boolean.class)) .permute("self") .insert(0, "selfClass", RubyClass.class, testClass) .invokeStaticQuiet(Bootstrap.LOOKUP, Bootstrap.class, "testType"); } gwt = MethodHandles.guardWithTest(test.handle(), target, fallback); // wrap in switchpoint for mutation invalidation gwt = switchPoint.guardWithTest(gwt, fallback); setTarget(gwt); } return target; }