package org.jruby.ext.ffi.jffi;
import com.kenai.jffi.CallContext;
import com.kenai.jffi.Function;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.CacheableMethod;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.cli.Options;
public class DefaultMethod extends DynamicMethod implements CacheableMethod {
public static final boolean DEBUG = Options.FFI_COMPILE_DUMP.load() || Options.COMPILE_DUMP.load();
public static final boolean REIFY = Options.FFI_COMPILE_REIFY.load();
protected final Signature signature;
private final NativeInvoker defaultInvoker;
private volatile NativeInvoker compiledInvoker;
private JITHandle jitHandle;
protected final Arity arity;
protected final Function function;
public DefaultMethod(RubyModule implementationClass, Function function,
Signature signature, NativeInvoker defaultInvoker) {
super(implementationClass, Visibility.PUBLIC, defaultInvoker.getName());
this.arity = Arity.fixed(signature.getParameterCount());
this.function = function;
this.defaultInvoker = defaultInvoker;
this.signature = signature;
}
@Override
public final DynamicMethod dup() {
return this;
}
@Override
public final Arity getArity() {
return arity;
}
@Override
public final boolean isNative() {
return true;
}
public DynamicMethod getMethodForCaching() {
return compiledInvoker != null ? compiledInvoker : this;
}
Signature getSignature() {
return signature;
}
CallContext getCallContext() {
return function.getCallContext();
}
long getFunctionAddress() {
return function.getFunctionAddress();
}
protected final NativeInvoker getNativeInvoker() {
return compiledInvoker != null ? compiledInvoker : tryCompilation();
}
private synchronized JITHandle getJITHandle() {
if (jitHandle == null) {
jitHandle = JITCompiler.getInstance().getHandle(signature, REIFY);
}
return jitHandle;
}
private synchronized NativeInvoker tryCompilation() {
if (compiledInvoker != null) {
return compiledInvoker;
}
NativeInvoker invoker = getJITHandle().compile(getImplementationClass(), function, signature, getName());
if (invoker != null) {
compiledInvoker = invoker;
getImplementationClass().invalidateCacheDescendants();
return compiledInvoker;
}
if (getJITHandle().compilationFailed()) {
compiledInvoker = defaultInvoker;
getImplementationClass().invalidateCacheDescendants();
}
return defaultInvoker;
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject self,
RubyModule clazz, String name, IRubyObject[] args) {
return getNativeInvoker().call(context, self, clazz, name, args);
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject self,
RubyModule clazz, String name, IRubyObject[] args, Block block) {
return getNativeInvoker().call(context, self, clazz, name, args, block);
}
public final NativeInvoker forceCompilation() {
NativeInvoker invoker = null;
while (!getJITHandle().compilationFailed() && (invoker = getJITHandle().compile(getImplementationClass(), function, signature, getName())) == null)
;
return invoker;
}
}