Skip to content
Prev Previous commit
Next Next commit
Basic fallback path for simple keywords only
This wires up methods with only simple keywords, using the fallback
logic of boxing those arguments into a Hash and reinvoking the
existing call sites.
  • Loading branch information
headius committed Feb 6, 2026
commit c2f972e4868e416094e47e4e44d8ca00f8a26581
61 changes: 58 additions & 3 deletions core/src/main/java/org/jruby/ir/targets/indy/MetaCallSite.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,30 @@
import com.headius.invokebinder.Signature;
import com.headius.invokebinder.SmartBinder;
import jnr.ffi.annotations.Meta;
import org.jruby.Ruby;
import org.jruby.RubyHash;
import org.jruby.RubySymbol;
import org.jruby.api.Create;
import org.jruby.ir.targets.simple.NormalInvokeSite;
import org.jruby.runtime.CallArgument;
import org.jruby.runtime.CallType;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.StringSupport;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.util.Arrays;
import java.util.List;

import static org.jruby.util.CodegenUtils.p;
import static org.jruby.util.CodegenUtils.params;
import static org.jruby.util.CodegenUtils.sig;

/**
Expand Down Expand Up @@ -58,9 +66,56 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, Metho
return new MetaCallSite(lookup, name, type, CallArgument.decodeMulti(callArguments), flags, file, line);
}

public IRubyObject invoke(Object[] arguments) {
System.out.println(Arrays.toString(callArguments));
public IRubyObject invoke(Object[] arguments) throws Throwable {
var binder = Binder.from(type());

return null;
int rubyArgsStart = 0;
ThreadContext context = null;
if (callArguments[rubyArgsStart].type() == CallArgument.Type.CONTEXT) {
context = (ThreadContext) arguments[rubyArgsStart];
rubyArgsStart++;
}
if (callArguments[rubyArgsStart].type() == CallArgument.Type.RECEIVER) rubyArgsStart++;

int rubyKwargsStart = rubyArgsStart;
int rubyKwargsCount = callArguments.length - rubyKwargsStart;
int closureInt = 0;
CallArgument lastArgument = callArguments[callArguments.length - 1];
if (lastArgument.type() == CallArgument.Type.BLOCK) {
closureInt = 1;
rubyKwargsCount--;
}
if (lastArgument.type() == CallArgument.Type.BLOCK_PASS) {
closureInt = 0;
rubyKwargsCount--;
}

Ruby runtime = context.runtime;
RubySymbol[] keys = Arrays.stream(callArguments).filter(a -> a.type() == CallArgument.Type.KEYWORD).map(a -> runtime.newSymbol(a.identifier().id())).toArray(RubySymbol[]::new);
var hashBuilder = Binder
.from(IRubyObject.class, params(IRubyObject.class, rubyKwargsCount))
.collect(0, IRubyObject[].class)
.prepend(runtime, keys)
.invokeStaticQuiet(MetaCallSite.class, "buildHash");

binder = binder.collect(rubyKwargsStart, rubyKwargsCount, IRubyObject.class, hashBuilder);

CallSite specificSite = name.startsWith("invoke") ?
NormalInvokeSite.bootstrap(lookup, name, binder.type(), closureInt, flags, file, line) :
SelfInvokeSite.bootstrap(lookup, name, binder.type(), closureInt, flags, file, line);

var target = binder.invoke(specificSite.dynamicInvoker());

setTarget(target);

return (IRubyObject) target.invokeWithArguments(arguments);
}

public static IRubyObject buildHash(Ruby runtime, RubySymbol[] keys, IRubyObject[] values) {
RubyHash hash = RubyHash.newSmallHash(runtime);
for (int i = 0; i < values.length; i++) {
hash.fastASetSmall(keys[i], values[i]);
}
return hash;
}
}