public static CallSite ivar(Lookup lookup, String name, MethodType type) throws Throwable { String[] names = name.split(":"); String operation = names[0]; String varName = names[1]; VariableSite site = new VariableSite(type, varName, "noname", 0); MethodHandle handle; handle = lookup.findStatic( Bootstrap.class, operation, type.insertParameterTypes(0, VariableSite.class)); handle = handle.bindTo(site); site.setTarget(handle.asType(site.type())); return site; }
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); }
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); }