package org.jruby.runtime.invokedynamic;
import com.headius.invokebinder.Binder;
import com.headius.invokebinder.Signature;
import com.headius.invokebinder.SmartBinder;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.CallConfiguration;
import org.jruby.internal.runtime.methods.Framing;
import org.jruby.internal.runtime.methods.Scoping;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ThreadContext;
import java.lang.invoke.MethodHandle;
import static java.lang.invoke.MethodHandles.foldArguments;
import static java.lang.invoke.MethodHandles.lookup;
import static org.jruby.runtime.Helpers.arrayOf;
public class InvocationLinker {
public static MethodHandle wrapWithFraming(Signature signature, CallConfiguration callConfig, RubyModule implClass, String name, MethodHandle nativeTarget, StaticScope scope) {
MethodHandle framePre = getFramePre(signature, callConfig, implClass, name, scope);
if (framePre != null) {
MethodHandle framePost = getFramePost(signature, callConfig);
boolean heapScoped = callConfig.scoping() != Scoping.None;
boolean framed = callConfig.framing() != Framing.None;
nativeTarget = Binder
.from(nativeTarget.type())
.tryFinally(framePost)
.invoke(nativeTarget);
nativeTarget = foldArguments(nativeTarget, framePre);
nativeTarget = Binder
.from(nativeTarget.type())
.fold(Binder
.from(nativeTarget.type().changeReturnType(void.class))
.permute(0)
.invoke(CALL_THREAD_POLL_HANDLE))
.invoke(nativeTarget);
}
return nativeTarget;
}
private static final MethodHandle CALL_THREAD_POLL_HANDLE =
Binder
.from(void.class, ThreadContext.class)
.invokeStaticQuiet(lookup(), ThreadContext.class, "callThreadPoll");
public static MethodHandle wrapWithFrameOnly(Signature signature, RubyModule implClass, String name, MethodHandle nativeTarget) {
MethodHandle framePre = getFrameOnlyPre(signature, CallConfiguration.FrameFullScopeNone, implClass, name);
MethodHandle framePost = getFramePost(signature, CallConfiguration.FrameFullScopeNone);
nativeTarget = Binder
.from(nativeTarget.type())
.tryFinally(framePost)
.invoke(nativeTarget);
nativeTarget = foldArguments(nativeTarget, framePre);
nativeTarget = Binder
.from(nativeTarget.type())
.fold(Binder
.from(nativeTarget.type().changeReturnType(void.class))
.permute(0)
.invoke(CALL_THREAD_POLL_HANDLE))
.invoke(nativeTarget);
return nativeTarget;
}
public static MethodHandle getFramePre(Signature signature, CallConfiguration callConfig, RubyModule implClass, String name, StaticScope scope) {
Signature inbound = signature.asFold(void.class);
SmartBinder binder = SmartBinder
.from(inbound);
switch (callConfig) {
case FrameFullScopeFull:
return binder
.permute("context", "self", "block")
.insert(1, arrayOf("selfClass", "name"), arrayOf(RubyModule.class, String.class), implClass, name)
.insert(5, arrayOf("scope"), arrayOf(StaticScope.class), scope)
.invokeVirtualQuiet(lookup(), "preMethodFrameAndScope")
.handle();
case FrameFullScopeDummy:
return binder
.permute("context", "self", "block")
.insert(1, arrayOf("selfClass", "name"), arrayOf(RubyModule.class, String.class), implClass, name)
.insert(5, arrayOf("scope"), arrayOf(StaticScope.class), scope)
.invokeVirtualQuiet(lookup(), "preMethodFrameAndDummyScope")
.handle();
case FrameFullScopeNone:
return binder
.permute("context", "self", "block")
.insert(1, arrayOf("selfClass", "name"), arrayOf(RubyModule.class, String.class), implClass, name)
.invokeVirtualQuiet(lookup(), "preMethodFrameOnly")
.handle();
case FrameNoneScopeDummy:
return binder
.permute("context")
.insert(1, arrayOf("selfClass", "scope"), arrayOf(RubyModule.class, StaticScope.class), implClass, scope)
.invokeVirtualQuiet(lookup(), "preMethodNoFrameAndDummyScope")
.handle();
case FrameNoneScopeFull:
return getFrameOnlyPre(signature, callConfig, implClass, name);
}
return null;
}
public static MethodHandle getFrameOnlyPre(Signature signature, CallConfiguration callConfig, RubyModule implClass, String name) {
Signature inbound = signature.asFold(void.class);
SmartBinder binder = SmartBinder
.from(inbound);
switch (callConfig) {
case FrameFullScopeNone:
return binder
.permute("context", "self", "block")
.insert(1, arrayOf("selfClass", "name"), arrayOf(RubyModule.class, String.class), implClass, name)
.invokeVirtualQuiet(lookup(), "preMethodFrameOnly")
.handle();
default:
throw new RuntimeException("invalid input: " + callConfig);
}
}
public static MethodHandle getFramePost(Signature signature, CallConfiguration callConfig) {
Signature inbound = signature.asFold(void.class);
SmartBinder binder = SmartBinder
.from(inbound)
.permute("context");
switch (callConfig) {
case FrameFullScopeFull:
return binder
.invokeVirtualQuiet(lookup(), "postMethodFrameAndScope")
.handle();
case FrameFullScopeDummy:
return binder
.invokeVirtualQuiet(lookup(), "postMethodFrameAndScope")
.handle();
case FrameFullScopeNone:
return binder
.invokeVirtualQuiet(lookup(), "postMethodFrameOnly")
.handle();
case FrameNoneScopeFull:
return binder
.invokeVirtualQuiet(lookup(), "postMethodScopeOnly")
.handle();
case FrameNoneScopeDummy:
return binder
.invokeVirtualQuiet(lookup(), "postMethodScopeOnly")
.handle();
}
return null;
}
}