@Override public IRubyObject call( ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) { if (IRRuntimeHelpers.isDebug()) doDebug(); DynamicMethodBox box = this.box; if (box.callCount >= 0) tryJit(context, box); DynamicMethod jittedMethod = box.actualMethod; if (jittedMethod != null) { return jittedMethod.call(context, self, clazz, name, arg0, arg1, arg2, block); } else { return INTERPRET_METHOD( context, ensureInstrsReady(), getImplementationClass().getMethodLocation(), self, name, arg0, arg1, arg2, block); } }
protected Object interpretSuper( ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { // SSS FIXME: We should check in the current module (for instance methods) or the current // module's meta class (for class methods) // // RubyModule currM = context.getCurrentScope().getStaticScope().getModule(); // RubyModule klazz = (isInstanceMethodSuper) ? currM : currM.getMetaClass(); // // The question is how do we know what this 'super' ought to do? // For 'super' that occurs in a method scope, this is easy to figure out. // But, what about 'super' that occurs in block scope? How do we figure that out? RubyModule klazz = context.getFrameKlazz(); // SSS FIXME: Even though we may know the method name in some instances, // we are not making use of it here. String methodName = context.getCurrentFrame().getName(); // methAddr.getName(); checkSuperDisabledOrOutOfMethod(context, klazz, methodName); RubyClass superClass = RuntimeHelpers.findImplementerIfNecessary(self.getMetaClass(), klazz).getSuperClass(); DynamicMethod method = superClass != null ? superClass.searchMethod(methodName) : UndefinedMethod.INSTANCE; Object rVal = method.isUndefined() ? RuntimeHelpers.callMethodMissing( context, self, method.getVisibility(), methodName, CallType.SUPER, args, block) : method.call(context, self, superClass, methodName, args, block); return hasUnusedResult() ? null : rVal; }
@Override public Object interpret( ThreadContext context, DynamicScope currDynScope, IRubyObject self, Object[] temp, Block aBlock) { // FIXME: Receiver is not being used...should we be retrieving it? IRubyObject receiver = (IRubyObject) getReceiver().retrieve(context, self, currDynScope, temp); IRubyObject[] args = prepareArguments(context, self, getCallArgs(), currDynScope, temp); Block block = prepareBlock(context, self, currDynScope, temp); RubyModule klazz = context.getFrameKlazz(); // SSS FIXME: Even though we may know the method name in some instances, // we are not making use of it here. It is cleaner in the sense of not // relying on implicit information whose data flow doesn't show up in the IR. String methodName = context.getCurrentFrame().getName(); // methAddr.getName(); checkSuperDisabledOrOutOfMethod(context, klazz, methodName); RubyClass superClass = RuntimeHelpers.findImplementerIfNecessary(self.getMetaClass(), klazz).getSuperClass(); DynamicMethod method = superClass != null ? superClass.searchMethod(methodName) : UndefinedMethod.INSTANCE; Object rVal = method.isUndefined() ? RuntimeHelpers.callMethodMissing( context, self, method.getVisibility(), methodName, CallType.SUPER, args, block) : method.call(context, self, superClass, methodName, args, block); return hasUnusedResult() ? null : rVal; }
protected static boolean methodMissing( CacheEntry entry, CallType callType, String name, IRubyObject caller) { DynamicMethod method = entry.method; return method.isUndefined() || (callType == CallType.NORMAL && !name.equals("method_missing") && !method.isCallableFrom(caller, callType)); }
public DynamicMethod searchWithCache(RubyClass clazz, int index, String name1) { CacheEntry entry = clazz.searchWithCache(name1); DynamicMethod method = entry.method; if (entry.method == UndefinedMethod.INSTANCE) { return RuntimeHelpers.selectMethodMissing( clazz, method.getVisibility(), name1, CallType.FUNCTIONAL); } methodCache[index] = entry; return method; }
private DynamicMethod cacheAndGet( ThreadContext context, RubyClass selfType, int index, String methodName) { CacheEntry entry = selfType.searchWithCache(methodName); DynamicMethod method = entry.method; if (method.isUndefined()) { return RuntimeHelpers.selectMethodMissing( context, selfType, method.getVisibility(), methodName, CallType.FUNCTIONAL); } methodCache[index] = entry; return method; }
private static IRubyObject callConversionMethod( ThreadContext context, DynamicMethod method, IRubyObject converter, String methodName, IRubyObject value) { if (method.getArity().required() == 2) { return method.call( context, converter, converter.getMetaClass(), methodName, value, context.runtime.getNil()); } else { return method.call(context, converter, converter.getMetaClass(), methodName, value); } }
public IRubyObject invoke( ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) throws Throwable { RubyClass selfClass = pollAndGetClass(context, self); SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData(); CacheEntry entry = selfClass.searchWithCache(methodName); DynamicMethod method = entry.method; if (methodMissing(entry, caller)) { return callMethodMissing(entry, callType, context, self, methodName, args, block); } MethodHandle mh = getHandle(selfClass, this, method); updateInvocationTarget(mh, self, selfClass, entry, switchPoint); return method.call(context, self, selfClass, methodName, args, block); }
MethodHandle getHandle(RubyClass dispatchClass, InvokeSite site, DynamicMethod method) throws Throwable { boolean blockGiven = signature.lastArgType() == Block.class; MethodHandle mh = Bootstrap.buildNativeHandle(site, method, blockGiven); if (mh == null) mh = Bootstrap.buildIndyHandle(site, method, method.getImplementationClass()); if (mh == null) mh = Bootstrap.buildJittedHandle(site, method, blockGiven); if (mh == null) mh = Bootstrap.buildGenericHandle(site, method, dispatchClass); assert mh != null : "we should have a method handle of some sort by now"; return mh; }
private IRubyObject invokeRuby( final DynamicMethod method, final JavaProxyMethod proxyMethod, final RubyClass metaClass, final String name, final Object[] nargs) { final IRubyObject[] newArgs = new IRubyObject[nargs.length]; for (int i = nargs.length; --i >= 0; ) { newArgs[i] = JavaUtil.convertJavaToUsableRubyObject(runtime, nargs[i]); } final int arity = method.getArity().getValue(); if (arity < 0 || arity == newArgs.length) { final ThreadContext context = runtime.getCurrentContext(); return method.call(context, self, metaClass, name, newArgs); } if (proxyMethod.hasSuperImplementation()) { final ThreadContext context = runtime.getCurrentContext(); final RubyClass superClass = metaClass.getSuperClass(); return Helpers.invokeAs(context, superClass, self, name, newArgs, Block.NULL_BLOCK); } throw runtime.newArgumentError(newArgs.length, arity); }
static MethodHandle buildNativeHandle(InvokeSite site, DynamicMethod method, boolean blockGiven) { MethodHandle mh = null; SmartBinder binder = null; if (method.getNativeCall() != null) { int nativeArgCount = getNativeArgCount(method, method.getNativeCall()); DynamicMethod.NativeCall nc = method.getNativeCall(); if (nc.isJava()) { // not supported yet, use DynamicMethod.call } else { if (nativeArgCount >= 0) { // native methods only support arity 3 if (nativeArgCount == site.arity) { // nothing to do binder = SmartBinder.from(lookup(), site.signature); } else { // arity mismatch...leave null and use DynamicMethod.call below } } else { // varargs if (site.arity == -1) { // ok, already passing [] binder = SmartBinder.from(lookup(), site.signature); } else if (site.arity == 0) { // no args, insert dummy binder = SmartBinder.from(lookup(), site.signature) .insert(2, "args", IRubyObject.NULL_ARRAY); } else { // 1 or more args, collect into [] binder = SmartBinder.from(lookup(), site.signature).collect("args", "arg.*"); } } if (binder != null) { // clean up non-arguments, ordering, types if (!nc.hasContext()) { binder = binder.drop("context"); } if (nc.hasBlock() && !blockGiven) { binder = binder.append("block", Block.NULL_BLOCK); } else if (!nc.hasBlock() && blockGiven) { binder = binder.drop("block"); } if (nc.isStatic()) { mh = binder .permute("context", "self", "arg.*", "block") // filter caller .cast(nc.getNativeReturn(), nc.getNativeSignature()) .invokeStaticQuiet(LOOKUP, nc.getNativeTarget(), nc.getNativeName()) .handle(); } else { mh = binder .permute("self", "context", "arg.*", "block") // filter caller, move self .castArg("self", nc.getNativeTarget()) .castVirtual( nc.getNativeReturn(), nc.getNativeTarget(), nc.getNativeSignature()) .invokeVirtualQuiet(LOOKUP, nc.getNativeName()) .handle(); } } } } return mh; }
@Override public boolean methodMissing(CacheEntry entry, IRubyObject caller) { DynamicMethod method = entry.method; return method.isUndefined(); }
@JRubyMethod(name = "new", meta = true) public static final IRubyObject newMappedType( ThreadContext context, IRubyObject klass, IRubyObject converter) { if (!converter.respondsTo("native_type")) { throw context.runtime.newNoMethodError( "converter needs a native_type method", "native_type", converter.getMetaClass()); } DynamicMethod toNativeMethod = converter.getMetaClass().searchMethod("to_native"); if (toNativeMethod.isUndefined()) { throw context.runtime.newNoMethodError( "converter needs a to_native method", "to_native", converter.getMetaClass()); } if (toNativeMethod.getArity().required() < 1 || toNativeMethod.getArity().required() > 2) { throw context.runtime.newArgumentError("to_native should accept one or two arguments"); } DynamicMethod fromNativeMethod = converter.getMetaClass().searchMethod("from_native"); if (fromNativeMethod.isUndefined()) { throw context.runtime.newNoMethodError( "converter needs a from_native method", "from_native", converter.getMetaClass()); } if (fromNativeMethod.getArity().required() < 1 || fromNativeMethod.getArity().required() > 2) { throw context.runtime.newArgumentError("from_native should accept one or two arguments"); } Type nativeType; try { nativeType = (Type) converter.callMethod(context, "native_type"); } catch (ClassCastException ex) { throw context.runtime.newTypeError("native_type did not return instance of FFI::Type"); } boolean isReferenceRequired; if (converter.respondsTo("reference_required?")) { isReferenceRequired = converter.callMethod(context, "reference_required?").isTrue(); } else { switch (nativeType.nativeType) { case BOOL: case CHAR: case UCHAR: case SHORT: case USHORT: case INT: case UINT: case LONG: case ULONG: case LONG_LONG: case ULONG_LONG: case FLOAT: case DOUBLE: isReferenceRequired = false; break; default: isReferenceRequired = true; break; } } return new MappedType( context.runtime, (RubyClass) klass, nativeType, converter, toNativeMethod, fromNativeMethod, isReferenceRequired); }
public static DynamicMethod populateModuleMethod(RubyModule cls, DynamicMethod javaMethod) { DynamicMethod moduleMethod = javaMethod.dup(); moduleMethod.setImplementationClass(cls.getSingletonClass()); moduleMethod.setVisibility(Visibility.PUBLIC); return moduleMethod; }
private static String logMethod(DynamicMethod method) { return "[#" + method.getSerialNumber() + " " + method.getImplementationClass() + "]"; }