Skip to content

Commit

Permalink
Start propagating callInfo through call path
Browse files Browse the repository at this point in the history
This is an attempt to start passing callInfo (flags for keyword
arguments calls) through the call stack to bring it closer to the
target method body. This will allow us to perform appropriate
keyword munging on the receiver side without having to store the
callInfo in ThreadContext and clear it when done. We can also
introduce new encodings for kwargs, such as directly in the args
array rather than pre-packaging in a Hash at the point of call.
  • Loading branch information
headius committed Nov 8, 2023
1 parent 11d1ba7 commit 0234742
Show file tree
Hide file tree
Showing 22 changed files with 494 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.RubySymbol;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
Expand Down Expand Up @@ -158,6 +159,45 @@ public void setIsBuiltin(boolean isBuiltin) {
}
}

/**
* A call path specialized for passing keyword arguments. Keywords can be passed
* as a Hash or as a series of values, with the exact configuration being passed
* in callInfo. The default version sets callInfo using thread-local storage.
*
* @param context The thread context for the currently executing thread
* @param self The 'self' or 'receiver' object to use for this call
* @param clazz The Ruby class against which this method is binding
* @param name The incoming name used to invoke this method
* @param callInfo The call info flags for passing keyword configuration
* @param args The arguments and keywords for the method
* @return The result of the call
*/
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz,
String name, int callInfo, IRubyObject... args) {
IRRuntimeHelpers.setCallInfo(context, callInfo);
return call(context, self, clazz, name, args);
}

/**
* A call path specialized for passing keyword arguments. Keywords can be passed
* as a Hash or as a series of values, with the exact configuration being passed
* in callInfo. The default version sets callInfo using thread-local storage.
*
* @param context The thread context for the currently executing thread
* @param self The 'self' or 'receiver' object to use for this call
* @param clazz The Ruby class against which this method is binding
* @param name The incoming name used to invoke this method
* @param callInfo The call info flags for passing keyword configuration
* @param block The block passed to this invocation
* @param args The arguments and keywords for the method
* @return The result of the call
*/
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz,
String name, int callInfo, Block block, IRubyObject... args) {
IRRuntimeHelpers.setCallInfo(context, callInfo);
return call(context, self, clazz, name, args, block);
}

/**
* The minimum 'call' method required for a dynamic method handle.
* Subclasses must implement this method, but may implement the other
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/ir/instructions/CallBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,13 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
IRubyObject[] values = prepareArguments(context, self, currScope, dynamicScope, temp);
Block preparedBlock = prepareBlock(context, self, currScope, dynamicScope, temp);

IRRuntimeHelpers.setCallInfo(context, getFlags());
int callInfo = getFlags();

if (hasLiteralClosure()) {
return callSite.callIter(context, self, object, values, preparedBlock);
return callSite.callIter(context, self, object, callInfo, preparedBlock, values);
}

return callSite.call(context, self, object, values, preparedBlock);
return callSite.call(context, self, object, callInfo, preparedBlock, values);
}

public IRubyObject[] prepareArguments(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope dynamicScope, Object[] temp) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
IRubyObject[] args = prepareArguments(context, self, currScope, currDynScope, temp);
Block block = prepareBlock(context, self, currScope, currDynScope, temp);

IRRuntimeHelpers.setCallInfo(context, getFlags());
int callInfo = getFlags();

if (isLiteralBlock) {
return IRRuntimeHelpers.unresolvedSuperIter(context, self, args, block);
return IRRuntimeHelpers.unresolvedSuperIter(context, self, callInfo, args, block);
} else {
return IRRuntimeHelpers.unresolvedSuper(context, self, args, block);
return IRRuntimeHelpers.unresolvedSuper(context, self, callInfo, args, block);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
Block block = prepareBlock(context, self, currScope, currDynScope, temp);
RubyModule definingModule = ((RubyModule) getDefiningModule().retrieve(context, self, currScope, currDynScope, temp));

IRRuntimeHelpers.setCallInfo(context, getFlags());
int callInfo = getFlags();

if (isLiteralBlock) {
return IRRuntimeHelpers.instanceSuperIter(context, self, getId(), definingModule, args, block);
return IRRuntimeHelpers.instanceSuperIter(context, self, getId(), definingModule, callInfo, args, block);
} else {
return IRRuntimeHelpers.instanceSuper(context, self, getId(), definingModule, args, block);
return IRRuntimeHelpers.instanceSuper(context, self, getId(), definingModule, callInfo, args, block);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
IRubyObject[] args = prepareArguments(context, self, currScope, currDynScope, temp);
Block block = prepareBlock(context, self, currScope, currDynScope, temp);

IRRuntimeHelpers.setCallInfo(context, getFlags());
int flags = getFlags();

if (isLiteralBlock) {
return IRRuntimeHelpers.unresolvedSuperIter(context, self, args, block);
return IRRuntimeHelpers.unresolvedSuperIter(context, self, flags, args, block);
} else {
return IRRuntimeHelpers.unresolvedSuper(context, self, args, block);
return IRRuntimeHelpers.unresolvedSuper(context, self, flags, args, block);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
IRubyObject[] args = prepareArguments(context, self, currScope, currDynScope, temp);
Block block = prepareBlock(context, self, currScope, currDynScope, temp);

IRRuntimeHelpers.setCallInfo(context, getFlags());

return IRRuntimeHelpers.zSuper(context, self, args, block);
return IRRuntimeHelpers.zSuper(context, self, getFlags(), args, block);
}

@Override
Expand Down
124 changes: 123 additions & 1 deletion core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -1292,11 +1292,21 @@ public static IRubyObject instanceSuperSplatArgs(ThreadContext context, IRubyObj
return instanceSuper(context, self, methodName, definingModule, splatArguments(args, splatMap), block);
}

@JIT // for JVM6
public static IRubyObject instanceSuperSplatArgs(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block, boolean[] splatMap) {
return instanceSuper(context, self, methodName, definingModule, callInfo, splatArguments(args, splatMap), block);
}

@JIT // for JVM6
public static IRubyObject instanceSuperIterSplatArgs(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, IRubyObject[] args, Block block, boolean[] splatMap) {
return instanceSuperIter(context, self, methodName, definingModule, splatArguments(args, splatMap), block);
}

@JIT // for JVM6
public static IRubyObject instanceSuperIterSplatArgs(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block, boolean[] splatMap) {
return instanceSuperIter(context, self, methodName, definingModule, callInfo, splatArguments(args, splatMap), block);
}

@Interp
public static IRubyObject instanceSuper(ThreadContext context, IRubyObject self, String id, RubyModule definingModule, IRubyObject[] args, Block block) {
CacheEntry entry = getSuperMethodEntry(id, definingModule);
Expand All @@ -1319,6 +1329,28 @@ public static IRubyObject instanceSuper(ThreadContext context, IRubyObject self,
return method.call(context, self, entry.sourceModule, id, args, block);
}

@Interp
public static IRubyObject instanceSuper(ThreadContext context, IRubyObject self, String id, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block) {
CacheEntry entry = getSuperMethodEntry(id, definingModule);
DynamicMethod method = entry.method;

if (method instanceof InstanceMethodInvoker && self instanceof JavaProxy) {
return javaProxySuper(
context,
(JavaProxy) self,
id,
(RubyClass) definingModule,
args,
(InstanceMethodInvoker) method);
}

if (method.isUndefined()) {
return Helpers.callMethodMissing(context, self, method.getVisibility(), id, CallType.SUPER, callInfo, args, block);
}

return method.call(context, self, entry.sourceModule, id, callInfo, block, args);
}

/**
* Perform a super invocation against a Java proxy, using proxy logic to locate and invoke the appropriate shim
* method.
Expand Down Expand Up @@ -1364,21 +1396,40 @@ public static IRubyObject instanceSuperIter(ThreadContext context, IRubyObject s
}
}

@Interp
public static IRubyObject instanceSuperIter(ThreadContext context, IRubyObject self, String id, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block) {
try {
return instanceSuper(context, self, id, definingModule, callInfo, args, block);
} finally {
block.escape();
}
}

private static CacheEntry getSuperMethodEntry(String id, RubyModule definingModule) {
RubyClass superClass = definingModule.getMethodLocation().getSuperClass();
return superClass != null ? superClass.searchWithCache(id) : CacheEntry.NULL_CACHE;
}

@JIT // for JVM6
@JIT
public static IRubyObject classSuperSplatArgs(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, IRubyObject[] args, Block block, boolean[] splatMap) {
return classSuper(context, self, methodName, definingModule, splatArguments(args, splatMap), block);
}

@JIT
public static IRubyObject classSuperSplatArgs(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block, boolean[] splatMap) {
return classSuper(context, self, methodName, definingModule, callInfo, splatArguments(args, splatMap), block);
}

@JIT // for JVM6
public static IRubyObject classSuperIterSplatArgs(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, IRubyObject[] args, Block block, boolean[] splatMap) {
return classSuperIter(context, self, methodName, definingModule, splatArguments(args, splatMap), block);
}

@JIT // for JVM6
public static IRubyObject classSuperIterSplatArgs(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block, boolean[] splatMap) {
return classSuperIter(context, self, methodName, definingModule, callInfo, splatArguments(args, splatMap), block);
}

@Interp
public static IRubyObject classSuper(ThreadContext context, IRubyObject self, String id, RubyModule definingModule, IRubyObject[] args, Block block) {
CacheEntry entry = getSuperMethodEntry(id, definingModule.getMetaClass());
Expand All @@ -1391,6 +1442,18 @@ public static IRubyObject classSuper(ThreadContext context, IRubyObject self, St
return method.call(context, self, entry.sourceModule, id, args, block);
}

@Interp
public static IRubyObject classSuper(ThreadContext context, IRubyObject self, String id, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block) {
CacheEntry entry = getSuperMethodEntry(id, definingModule.getMetaClass());
DynamicMethod method = entry.method;

if (method.isUndefined()) {
return Helpers.callMethodMissing(context, self, method.getVisibility(), id, CallType.SUPER, callInfo, args, block);
}

return method.call(context, self, entry.sourceModule, id, callInfo, block, args);
}

@Interp
public static IRubyObject classSuperIter(ThreadContext context, IRubyObject self, String id, RubyModule definingModule, IRubyObject[] args, Block block) {
try {
Expand All @@ -1400,16 +1463,35 @@ public static IRubyObject classSuperIter(ThreadContext context, IRubyObject self
}
}

@Interp
public static IRubyObject classSuperIter(ThreadContext context, IRubyObject self, String id, RubyModule definingModule, int callInfo, IRubyObject[] args, Block block) {
try {
return classSuper(context, self, id, definingModule, callInfo, args, block);
} finally {
block.escape();
}
}

@JIT
public static IRubyObject unresolvedSuperSplatArgs(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block, boolean[] splatMap) {
return unresolvedSuper(context, self, splatArguments(args, splatMap), block);
}

@JIT
public static IRubyObject unresolvedSuperSplatArgs(ThreadContext context, IRubyObject self, int callInfo, IRubyObject[] args, Block block, boolean[] splatMap) {
return unresolvedSuper(context, self, callInfo, splatArguments(args, splatMap), block);
}

@JIT
public static IRubyObject unresolvedSuperIterSplatArgs(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block, boolean[] splatMap) {
return unresolvedSuperIter(context, self, splatArguments(args, splatMap), block);
}

@JIT
public static IRubyObject unresolvedSuperIterSplatArgs(ThreadContext context, IRubyObject self, int callInfo, IRubyObject[] args, Block block, boolean[] splatMap) {
return unresolvedSuperIter(context, self, callInfo, splatArguments(args, splatMap), block);
}

@Interp
public static IRubyObject unresolvedSuper(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
// We have to rely on the frame stack to find the implementation class
Expand All @@ -1431,6 +1513,27 @@ public static IRubyObject unresolvedSuper(ThreadContext context, IRubyObject sel
return rVal;
}

@Interp
public static IRubyObject unresolvedSuper(ThreadContext context, IRubyObject self, int callInfo, IRubyObject[] args, Block block) {
// We have to rely on the frame stack to find the implementation class
RubyModule klazz = context.getFrameKlazz();
String methodName = context.getFrameName();

Helpers.checkSuperDisabledOrOutOfMethod(context, klazz, methodName);

RubyClass superClass = searchNormalSuperclass(klazz);
CacheEntry entry = superClass != null ? superClass.searchWithCache(methodName) : CacheEntry.NULL_CACHE;

IRubyObject rVal;
if (entry.method.isUndefined()) {
rVal = Helpers.callMethodMissing(context, self, entry.method.getVisibility(), methodName, CallType.SUPER, callInfo, args, block);
} else {
rVal = entry.method.call(context, self, entry.sourceModule, methodName, callInfo, block, args);
}

return rVal;
}

@Interp
public static IRubyObject unresolvedSuperIter(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
try {
Expand All @@ -1440,6 +1543,15 @@ public static IRubyObject unresolvedSuperIter(ThreadContext context, IRubyObject
}
}

@Interp
public static IRubyObject unresolvedSuperIter(ThreadContext context, IRubyObject self, int callInfo, IRubyObject[] args, Block block) {
try {
return unresolvedSuper(context, self, callInfo, args, block);
} finally {
block.escape();
}
}

// MRI: vm_search_normal_superclass
private static RubyClass searchNormalSuperclass(RubyModule klazz) {
// Unwrap refinements, since super should always dispatch back to the refined class
Expand All @@ -1456,11 +1568,21 @@ public static IRubyObject zSuperSplatArgs(ThreadContext context, IRubyObject sel
return unresolvedSuper(context, self, splatArguments(args, splatMap), block);
}

public static IRubyObject zSuperSplatArgs(ThreadContext context, IRubyObject self, int callInfo, IRubyObject[] args, Block block, boolean[] splatMap) {
if (block == null || !block.isGiven()) block = context.getFrameBlock();
return unresolvedSuper(context, self, callInfo, splatArguments(args, splatMap), block);
}

public static IRubyObject zSuper(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
if (block == null || !block.isGiven()) block = context.getFrameBlock();
return unresolvedSuper(context, self, args, block);
}

public static IRubyObject zSuper(ThreadContext context, IRubyObject self, int callInfo, IRubyObject[] args, Block block) {
if (block == null || !block.isGiven()) block = context.getFrameBlock();
return unresolvedSuper(context, self, callInfo, args, block);
}

public static IRubyObject[] splatArguments(IRubyObject[] args, boolean[] splatMap) {
if (splatMap != null && splatMap.length > 0) {
int count = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ protected RubyClass getSuperClass(RubyClass definingModule) {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
IRRuntimeHelpers.setCallInfo(context, flags);
return IRRuntimeHelpers.classSuperSplatArgs(context, self, superName, definingModule, args, block, splatMap);
return IRRuntimeHelpers.classSuperSplatArgs(context, self, superName, definingModule, flags, args, block, splatMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ protected RubyClass getSuperClass(RubyClass definingModule) {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
IRRuntimeHelpers.setCallInfo(context, flags);
return IRRuntimeHelpers.classSuperIterSplatArgs(context, self, superName, definingModule, args, block, splatMap);
return IRRuntimeHelpers.classSuperIterSplatArgs(context, self, superName, definingModule, flags, args, block, splatMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ protected RubyClass getSuperClass(RubyClass definingModule) {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
IRRuntimeHelpers.setCallInfo(context, flags);
return IRRuntimeHelpers.instanceSuperSplatArgs(context, self, superName, definingModule, args, block, splatMap);
return IRRuntimeHelpers.instanceSuperSplatArgs(context, self, superName, definingModule, flags, args, block, splatMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ protected RubyClass getSuperClass(RubyClass definingModule) {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
IRRuntimeHelpers.setCallInfo(context, flags);
return IRRuntimeHelpers.instanceSuperIterSplatArgs(context, self, superName, definingModule, args, block, splatMap);
return IRRuntimeHelpers.instanceSuperIterSplatArgs(context, self, superName, definingModule, flags, args, block, splatMap);
}
}

0 comments on commit 0234742

Please sign in to comment.