Skip to content

Commit 44a5bf5

Browse files
committed
[GR-19220] Introduce an arguments descriptor that says keyword arguments are present (#2624)
PullRequest: truffleruby/3227
2 parents 873d88e + 2cd2ad7 commit 44a5bf5

39 files changed

+810
-118
lines changed

spec/truffle/arguments_descriptor_spec.rb

Lines changed: 443 additions & 0 deletions
Large diffs are not rendered by default.

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.truffleruby.language.LexicalScope;
6767
import org.truffleruby.language.RubyBaseNode;
6868
import org.truffleruby.language.SafepointManager;
69+
import org.truffleruby.language.arguments.EmptyArgumentsDescriptor;
6970
import org.truffleruby.language.backtrace.BacktraceFormatter;
7071
import org.truffleruby.language.dispatch.DispatchNode;
7172
import org.truffleruby.language.globals.GlobalVariableStorage;
@@ -322,7 +323,7 @@ protected boolean patch(Env newEnv) {
322323
.getSourceSection()
323324
.getSource();
324325
TranslatorDriver.printParseTranslateExecuteMetric("before-run-delayed-initialization", this, source);
325-
ProcOperations.rootCall((RubyProc) proc);
326+
ProcOperations.rootCall((RubyProc) proc, EmptyArgumentsDescriptor.INSTANCE, RubyBaseNode.EMPTY_ARGUMENTS);
326327
TranslatorDriver.printParseTranslateExecuteMetric("after-run-delayed-initialization", this, source);
327328
}
328329
Metrics.printTime("after-run-delayed-initialization");

src/main/java/org/truffleruby/cext/CExtNodes.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import org.truffleruby.language.RubyNode;
8585
import org.truffleruby.language.RubyRootNode;
8686
import org.truffleruby.language.Visibility;
87+
import org.truffleruby.language.arguments.EmptyArgumentsDescriptor;
8788
import org.truffleruby.language.arguments.RubyArguments;
8889
import org.truffleruby.language.backtrace.Backtrace;
8990
import org.truffleruby.language.constants.GetConstantNode;
@@ -994,7 +995,9 @@ protected Object callSuper(VirtualFrame frame, Object[] args) {
994995
final MethodLookupResult superMethodLookup = ModuleOperations
995996
.lookupSuperMethod(callingMethod, callingMetaclass);
996997
final InternalMethod superMethod = superMethodLookup.getMethod();
997-
return callSuperMethodNode.execute(frame, callingSelf, superMethod, args, nil);
998+
// This C API only passes positional arguments, but maybe it should be influenced by ruby2_keywords hashes?
999+
return callSuperMethodNode.execute(
1000+
frame, callingSelf, superMethod, EmptyArgumentsDescriptor.INSTANCE, args, nil);
9981001
}
9991002

10001003
@TruffleBoundary

src/main/java/org/truffleruby/core/VMPrimitiveNodes.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.truffleruby.builtins.CoreModule;
5050
import org.truffleruby.builtins.Primitive;
5151
import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
52+
import org.truffleruby.core.array.RubyArray;
5253
import org.truffleruby.core.basicobject.BasicObjectNodes.ReferenceEqualNode;
5354
import org.truffleruby.core.cast.NameToJavaStringNode;
5455
import org.truffleruby.core.cast.ToRubyIntegerNode;
@@ -70,6 +71,10 @@
7071
import org.truffleruby.interop.TranslateInteropExceptionNode;
7172
import org.truffleruby.language.RubyDynamicObject;
7273
import org.truffleruby.language.SafepointAction;
74+
import org.truffleruby.language.arguments.ArgumentsDescriptor;
75+
import org.truffleruby.language.arguments.EmptyArgumentsDescriptor;
76+
import org.truffleruby.language.arguments.KeywordArgumentsDescriptor;
77+
import org.truffleruby.language.arguments.RubyArguments;
7378
import org.truffleruby.language.backtrace.Backtrace;
7479
import org.truffleruby.language.control.ExitException;
7580
import org.truffleruby.language.control.RaiseException;
@@ -276,7 +281,7 @@ protected boolean watchSignalString(Object signalString, boolean isRubyDefaultHa
276281
case "IGNORE":
277282
return registerIgnoreHandler(signalName);
278283
default:
279-
throw new UnsupportedOperationException(actionString);
284+
throw CompilerDirectives.shouldNotReachHere(actionString);
280285
}
281286
}
282287

@@ -300,7 +305,7 @@ protected boolean watchSignalProc(Object signalString, boolean isRubyDefaultHand
300305
new SafepointAction("Handling of signal " + signal, rootThread, true, false) {
301306
@Override
302307
public void run(RubyThread rubyThread, Node currentNode) {
303-
ProcOperations.rootCall(action, signal.getNumber());
308+
ProcOperations.rootCall(action, EmptyArgumentsDescriptor.INSTANCE, signal.getNumber());
304309
}
305310
});
306311
}, isRubyDefaultHandler);
@@ -586,6 +591,33 @@ protected int javaVersion() {
586591

587592
}
588593

594+
@Primitive(name = "arguments")
595+
public abstract static class ArgumentsNode extends PrimitiveArrayArgumentsNode {
596+
@Specialization
597+
protected RubyArray arguments(VirtualFrame frame) {
598+
return createArray(RubyArguments.getArguments(frame));
599+
}
600+
}
601+
602+
@Primitive(name = "arguments_descriptor")
603+
public abstract static class ArgumentsDescriptorNode extends PrimitiveArrayArgumentsNode {
604+
@Specialization
605+
protected RubyArray argumentsDescriptor(VirtualFrame frame) {
606+
return descriptorToArray(RubyArguments.getDescriptor(frame));
607+
}
608+
609+
@TruffleBoundary
610+
private RubyArray descriptorToArray(ArgumentsDescriptor descriptor) {
611+
if (descriptor == EmptyArgumentsDescriptor.INSTANCE) {
612+
return createEmptyArray();
613+
} else if (descriptor instanceof KeywordArgumentsDescriptor) {
614+
return createArray(new Object[]{ getLanguage().getSymbol("keywords") });
615+
} else {
616+
throw CompilerDirectives.shouldNotReachHere();
617+
}
618+
}
619+
}
620+
589621
@Primitive(name = "vm_native_argv")
590622
public abstract static class VMNativeArgvNode extends PrimitiveArrayArgumentsNode {
591623
@Specialization

src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,14 @@ protected Object instanceEval(
372372

373373
@Specialization
374374
protected Object instanceEval(
375-
Object receiver, NotProvided string, NotProvided fileName, NotProvided line, RubyProc block,
375+
VirtualFrame frame,
376+
Object receiver,
377+
NotProvided string,
378+
NotProvided fileName,
379+
NotProvided line,
380+
RubyProc block,
376381
@Cached InstanceExecNode instanceExecNode) {
377-
return instanceExecNode.executeInstanceExec(receiver, new Object[]{ receiver }, block);
382+
return instanceExecNode.executeInstanceExec(frame, receiver, new Object[]{ receiver }, block);
378383
}
379384

380385
@Specialization
@@ -433,16 +438,17 @@ public static InstanceExecNode create() {
433438

434439
@Child private CallBlockNode callBlockNode = CallBlockNode.create();
435440

436-
abstract Object executeInstanceExec(Object self, Object[] args, RubyProc block);
441+
abstract Object executeInstanceExec(VirtualFrame frame, Object self, Object[] args, RubyProc block);
437442

438443
@Specialization
439-
protected Object instanceExec(Object receiver, Object[] arguments, RubyProc block) {
444+
protected Object instanceExec(VirtualFrame frame, Object receiver, Object[] arguments, RubyProc block) {
440445
final DeclarationContext declarationContext = new DeclarationContext(
441446
Visibility.PUBLIC,
442447
new SingletonClassOfSelfDefaultDefinee(receiver),
443448
block.declarationContext.getRefinements());
444-
return callBlockNode
445-
.executeCallBlock(declarationContext, block, receiver, block.block, arguments);
449+
var descriptor = RubyArguments.getDescriptor(frame);
450+
return callBlockNode.executeCallBlock(
451+
declarationContext, block, receiver, block.block, descriptor, arguments);
446452
}
447453

448454
@Specialization

src/main/java/org/truffleruby/core/binding/BindingNodes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public static MaterializedFrame newFrame(MaterializedFrame parent, FrameDescript
128128
null,
129129
RubyArguments.getSelf(parent),
130130
RubyArguments.getBlock(parent),
131+
RubyArguments.getDescriptor(parent),
131132
RubyArguments.getArguments(parent)),
132133
descriptor).materialize();
133134
}

src/main/java/org/truffleruby/core/fiber/FiberManager.java

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.truffleruby.core.thread.ThreadManager;
2828
import org.truffleruby.core.thread.ThreadManager.BlockingAction;
2929
import org.truffleruby.language.SafepointAction;
30+
import org.truffleruby.language.arguments.ArgumentsDescriptor;
31+
import org.truffleruby.language.arguments.EmptyArgumentsDescriptor;
3032
import org.truffleruby.language.control.BreakException;
3133
import org.truffleruby.language.control.DynamicReturnException;
3234
import org.truffleruby.language.control.ExitException;
@@ -120,11 +122,12 @@ private void fiberMain(RubyContext context, RubyFiber fiber, RubyProc block, Nod
120122

121123
FiberMessage lastMessage = null;
122124
try {
123-
final Object[] args = handleMessage(fiber, message, currentNode);
125+
var descriptorAndArgs = handleMessage(fiber, message, currentNode);
124126
fiber.status = FiberStatus.RESUMED;
125-
final Object result = ProcOperations.rootCall(block, args);
127+
final Object result = ProcOperations.rootCall(block, descriptorAndArgs.descriptor, descriptorAndArgs.args);
126128

127-
lastMessage = new FiberResumeMessage(FiberOperation.YIELD, fiber, new Object[]{ result });
129+
lastMessage = new FiberResumeMessage(FiberOperation.YIELD, fiber,
130+
EmptyArgumentsDescriptor.INSTANCE, new Object[]{ result });
128131

129132
// Handlers in the same order as in ThreadManager
130133
} catch (KillException | ExitException | RaiseException e) {
@@ -220,13 +223,14 @@ private void assertNotEntered(String reason) {
220223
}
221224

222225
@TruffleBoundary
223-
private Object[] handleMessage(RubyFiber fiber, FiberMessage message, Node currentNode) {
226+
private DescriptorAndArgs handleMessage(RubyFiber fiber, FiberMessage message, Node currentNode) {
224227
// Written as a loop to not grow the stack when processing guest safepoints
225228
while (message instanceof FiberSafepointMessage) {
226229
final FiberSafepointMessage safepointMessage = (FiberSafepointMessage) message;
227230
safepointMessage.action.run(fiber.rubyThread, currentNode);
228231
final RubyFiber sendingFiber = safepointMessage.sendingFiber;
229-
message = resumeAndWait(fiber, sendingFiber, FiberOperation.TRANSFER, SAFEPOINT_ARGS, currentNode);
232+
message = resumeAndWait(fiber, sendingFiber, FiberOperation.TRANSFER,
233+
EmptyArgumentsDescriptor.INSTANCE, SAFEPOINT_ARGS, currentNode);
230234
}
231235

232236
if (message instanceof FiberShutdownMessage) {
@@ -252,21 +256,22 @@ private Object[] handleMessage(RubyFiber fiber, FiberMessage message, Node curre
252256
throw new RaiseException(context, (RubyException) resumeMessage.getArgs()[0]);
253257
}
254258

255-
return resumeMessage.getArgs();
259+
return resumeMessage.getDescriptorAndArgs();
256260
} else {
257261
throw CompilerDirectives.shouldNotReachHere();
258262
}
259263
}
260264

261265
/** Send a resume message to a fiber by posting into its message queue. Doesn't explicitly notify the Java thread
262266
* (although the queue implementation may) and doesn't wait for the message to be received. */
263-
private void resume(RubyFiber fromFiber, RubyFiber fiber, FiberOperation operation, Object... args) {
264-
addToMessageQueue(fiber, new FiberResumeMessage(operation, fromFiber, args));
267+
private void resume(RubyFiber fromFiber, RubyFiber fiber, FiberOperation operation,
268+
ArgumentsDescriptor descriptor, Object... args) {
269+
addToMessageQueue(fiber, new FiberResumeMessage(operation, fromFiber, descriptor, args));
265270
}
266271

267272
@TruffleBoundary
268-
public Object[] transferControlTo(RubyFiber fromFiber, RubyFiber toFiber, FiberOperation operation, Object[] args,
269-
Node currentNode) {
273+
public DescriptorAndArgs transferControlTo(RubyFiber fromFiber, RubyFiber toFiber, FiberOperation operation,
274+
ArgumentsDescriptor descriptor, Object[] args, Node currentNode) {
270275
assert fromFiber.resumingFiber == null;
271276
if (operation == FiberOperation.RESUME) {
272277
fromFiber.resumingFiber = toFiber;
@@ -280,7 +285,7 @@ public Object[] transferControlTo(RubyFiber fromFiber, RubyFiber toFiber, FiberO
280285
if (fromFiber.status == FiberStatus.RESUMED) {
281286
fromFiber.status = FiberStatus.SUSPENDED;
282287
}
283-
final FiberMessage message = resumeAndWait(fromFiber, toFiber, operation, args, currentNode);
288+
final FiberMessage message = resumeAndWait(fromFiber, toFiber, operation, descriptor, args, currentNode);
284289
return handleMessage(fromFiber, message, currentNode);
285290
}
286291

@@ -294,13 +299,13 @@ public Object[] transferControlTo(RubyFiber fromFiber, RubyFiber toFiber, FiberO
294299
* @param fromFiber the current fiber which will soon be suspended
295300
* @param toFiber the fiber we resume or transfer to */
296301
@TruffleBoundary
297-
private FiberMessage resumeAndWait(RubyFiber fromFiber, RubyFiber toFiber, FiberOperation operation, Object[] args,
298-
Node currentNode) {
302+
private FiberMessage resumeAndWait(RubyFiber fromFiber, RubyFiber toFiber, FiberOperation operation,
303+
ArgumentsDescriptor descriptor, Object[] args, Node currentNode) {
299304
final TruffleContext truffleContext = context.getEnv().getContext();
300305
final FiberMessage message = context
301306
.getThreadManager()
302307
.leaveAndEnter(truffleContext, currentNode, () -> {
303-
resume(fromFiber, toFiber, operation, args);
308+
resume(fromFiber, toFiber, operation, descriptor, args);
304309
return waitMessage(fromFiber, currentNode);
305310
});
306311
fromFiber.rubyThread.setCurrentFiber(fromFiber);
@@ -441,18 +446,34 @@ public String getFiberDebugInfo(RubyThread rubyThread) {
441446
}
442447
}
443448

449+
public static final class DescriptorAndArgs {
450+
public final ArgumentsDescriptor descriptor;
451+
public final Object[] args;
452+
453+
public DescriptorAndArgs(ArgumentsDescriptor descriptor, Object[] args) {
454+
this.descriptor = descriptor;
455+
this.args = args;
456+
}
457+
}
458+
444459
public interface FiberMessage {
445460
}
446461

447462
private static class FiberResumeMessage implements FiberMessage {
448463

449464
private final FiberOperation operation;
450465
private final RubyFiber sendingFiber;
466+
private final ArgumentsDescriptor descriptor;
451467
private final Object[] args;
452468

453-
public FiberResumeMessage(FiberOperation operation, RubyFiber sendingFiber, Object[] args) {
469+
public FiberResumeMessage(
470+
FiberOperation operation,
471+
RubyFiber sendingFiber,
472+
ArgumentsDescriptor descriptor,
473+
Object[] args) {
454474
this.operation = operation;
455475
this.sendingFiber = sendingFiber;
476+
this.descriptor = descriptor;
456477
this.args = args;
457478
}
458479

@@ -464,10 +485,17 @@ public RubyFiber getSendingFiber() {
464485
return sendingFiber;
465486
}
466487

488+
public ArgumentsDescriptor getDescriptor() {
489+
return descriptor;
490+
}
491+
467492
public Object[] getArgs() {
468493
return args;
469494
}
470495

496+
public DescriptorAndArgs getDescriptorAndArgs() {
497+
return new DescriptorAndArgs(descriptor, args);
498+
}
471499
}
472500

473501
private static class FiberSafepointMessage implements FiberMessage {

0 commit comments

Comments
 (0)