public static IRubyObject ivarGet(VariableSite site, IRubyObject self) throws Throwable { RubyClass realClass = self.getMetaClass().getRealClass(); VariableAccessor accessor = realClass.getVariableAccessorForRead(site.name()); // produce nil if the variable has not been initialize MethodHandle nullToNil = findStatic( Helpers.class, "nullToNil", methodType(IRubyObject.class, IRubyObject.class, IRubyObject.class)); nullToNil = insertArguments(nullToNil, 1, self.getRuntime().getNil()); nullToNil = explicitCastArguments(nullToNil, methodType(IRubyObject.class, Object.class)); // get variable value and filter with nullToNil MethodHandle getValue; boolean direct = false; if (accessor instanceof FieldVariableAccessor) { direct = true; int offset = ((FieldVariableAccessor) accessor).getOffset(); Class cls = REIFIED_OBJECT_CLASSES[offset]; getValue = lookup().findGetter(cls, "var" + offset, Object.class); getValue = explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class)); } else { getValue = findStatic( VariableAccessor.class, "getVariable", methodType(Object.class, RubyBasicObject.class, int.class)); getValue = explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class, int.class)); getValue = insertArguments(getValue, 1, accessor.getIndex()); } getValue = filterReturnValue(getValue, nullToNil); // prepare fallback MethodHandle fallback = null; if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tqet on type " + self.getMetaClass().id + " failed (polymorphic)" + extractSourceInfo(site)); fallback = findStatic( Bootstrap.class, "ivarGetFail", methodType(IRubyObject.class, VariableSite.class, IRubyObject.class)); fallback = fallback.bindTo(site); site.setTarget(fallback); return (IRubyObject) fallback.invokeWithArguments(self); } else { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) { if (direct) { LOG.info( site.name() + "\tget field on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } else { LOG.info( site.name() + "\tget on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } } fallback = site.getTarget(); site.incrementChainCount(); } // prepare test MethodHandle test = findStatic( InvocationLinker.class, "testRealClass", methodType(boolean.class, int.class, IRubyObject.class)); test = insertArguments(test, 0, accessor.getClassId()); getValue = guardWithTest(test, getValue, fallback); if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tget on class " + self.getMetaClass().id + " bound directly" + extractSourceInfo(site)); site.setTarget(getValue); return (IRubyObject) getValue.invokeExact(self); }
public static void ivarSet(VariableSite site, IRubyObject self, IRubyObject value) throws Throwable { RubyClass realClass = self.getMetaClass().getRealClass(); VariableAccessor accessor = realClass.getVariableAccessorForWrite(site.name()); // set variable value and fold by returning value MethodHandle setValue; boolean direct = false; if (accessor instanceof FieldVariableAccessor) { direct = true; int offset = ((FieldVariableAccessor) accessor).getOffset(); Class cls = REIFIED_OBJECT_CLASSES[offset]; setValue = findStatic(cls, "setVariableChecked", methodType(void.class, cls, Object.class)); setValue = explicitCastArguments( setValue, methodType(void.class, IRubyObject.class, IRubyObject.class)); } else { setValue = findStatic( accessor.getClass(), "setVariableChecked", methodType( void.class, RubyBasicObject.class, RubyClass.class, int.class, Object.class)); setValue = explicitCastArguments( setValue, methodType( void.class, IRubyObject.class, RubyClass.class, int.class, IRubyObject.class)); setValue = insertArguments(setValue, 1, realClass, accessor.getIndex()); } // prepare fallback MethodHandle fallback = null; if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tset on type " + self.getMetaClass().id + " failed (polymorphic)" + extractSourceInfo(site)); fallback = findStatic( Bootstrap.class, "ivarSetFail", methodType(void.class, VariableSite.class, IRubyObject.class, IRubyObject.class)); fallback = fallback.bindTo(site); site.setTarget(fallback); fallback.invokeExact(self, value); } else { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) { if (direct) { LOG.info( site.name() + "\tset field on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } else { LOG.info( site.name() + "\tset on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } } fallback = site.getTarget(); site.incrementChainCount(); } // prepare test MethodHandle test = findStatic( InvocationLinker.class, "testRealClass", methodType(boolean.class, int.class, IRubyObject.class)); test = insertArguments(test, 0, accessor.getClassId()); test = dropArguments(test, 1, IRubyObject.class); setValue = guardWithTest(test, setValue, fallback); if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tset on class " + self.getMetaClass().id + " bound directly" + extractSourceInfo(site)); site.setTarget(setValue); setValue.invokeExact(self, value); }
/** * 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; }