Skip to content

Commit 7cd5751

Browse files
committed
Translate nodes (method call) - fix keyword arguments handling
1 parent a1fdc0c commit 7cd5751

File tree

4 files changed

+45
-7
lines changed

4 files changed

+45
-7
lines changed

spec/tags/truffle/parsing/parsing_tags.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ fails:Parsing a Method call (super / outside a method body without explicit argu
7676
fails:Parsing a Method call (Arguments/with a &-deconstruction to a block argument (&bar)) case is parsed correctly
7777
fails:Parsing a Method call (Arguments/with block literal argument) case is parsed correctly
7878
fails:Parsing a Method call (Arguments/with double splat operator (**kw)) case is parsed correctly
79-
fails:Parsing a Method call (Arguments/with keyword arguments) case is parsed correctly
8079
fails:Parsing a Method call (Arguments/with positional argument and splat operator (a, *args)) case is parsed correctly
8180
fails:Parsing a Method call (Arguments/with splat operator (*args)) case is parsed correctly
8281
fails:Parsing a Method call (Arguments/with splat operator and positional arguments (*args, a)) case is parsed correctly

spec/truffle/parsing/fixtures/method_calls/arguments/with_keyword_arguments.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ subject: "Method call"
22
description: "Arguments/with keyword arguments"
33
notes: |
44
Keyword arguments are represented with PackedHashStoreLibrary$SmallHashLiteralNode node.
5-
The only difference between keyword arguments and a Hash literal argument is a descriptor attribute:
6-
`descriptor = org.truffleruby.language.arguments.KeywordArgumentsDescriptor@...`
7-
5+
The only difference between keyword arguments and a Hash literal argument is a `descriptor` attribute:
6+
descriptor = KeywordArgumentsDescriptor(keywords = [bar])
87
focused_on_node: "org.truffleruby.language.dispatch.RubyCallNode"
98
ruby: |
109
foo(bar: :baz)

src/main/java/org/truffleruby/language/arguments/ReadUserKeywordsHashNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public RubyHash execute(VirtualFrame frame) {
2626
if (keywordArgumentsProfile.profile(descriptor instanceof KeywordArgumentsDescriptor)) {
2727
final RubyHash keywords = (RubyHash) RubyArguments.getLastArgument(frame);
2828
assert !keywords.empty();
29-
assert assertHashMatchesDescriptor(keywords, (KeywordArgumentsDescriptor) descriptor);
29+
assert assertHashMatchesDescriptor(keywords, (KeywordArgumentsDescriptor) descriptor); // TODO: seems incorrect as far as descriptor contains keyword names from the arguments, not declared in a called method
3030
return keywords;
3131
} else {
3232
return null;

src/main/java/org/truffleruby/parser/YARPTranslator.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.truffleruby.language.RubyRootNode;
5050
import org.truffleruby.language.RubyTopLevelRootNode;
5151
import org.truffleruby.language.SourceIndexLength;
52+
import org.truffleruby.language.arguments.ArgumentsDescriptor;
5253
import org.truffleruby.language.arguments.EmptyArgumentsDescriptor;
5354
import org.truffleruby.language.arguments.ProfileArgumentNodeGen;
5455
import org.truffleruby.language.arguments.ReadSelfNode;
@@ -249,10 +250,12 @@ public RubyNode visitArrayPatternNode(Nodes.ArrayPatternNode node) {
249250
return defaultVisit(node);
250251
}
251252

253+
// handled in #visitHashNode
252254
public RubyNode visitAssocNode(Nodes.AssocNode node) {
253255
return defaultVisit(node);
254256
}
255257

258+
// handled in #visitHashNode
256259
public RubyNode visitAssocSplatNode(Nodes.AssocSplatNode node) {
257260
return defaultVisit(node);
258261
}
@@ -446,6 +449,7 @@ public RubyNode visitCallNode(Nodes.CallNode node) {
446449
}
447450

448451
var translatedArguments = translate(arguments);
452+
var argumentsDescriptor = getKeywordArgumentsDescriptor(arguments);
449453

450454
// If the receiver is explicit or implicit 'self' then we can call private methods
451455
final boolean ignoreVisibility = node.receiver == null || node.receiver instanceof Nodes.SelfNode;
@@ -493,7 +497,7 @@ public RubyNode visitCallNode(Nodes.CallNode node) {
493497
receiver,
494498
methodName,
495499
null,
496-
EmptyArgumentsDescriptor.INSTANCE,
500+
argumentsDescriptor,
497501
translatedArguments,
498502
false,
499503
ignoreVisibility,
@@ -506,6 +510,35 @@ public RubyNode visitCallNode(Nodes.CallNode node) {
506510
return rubyNode;
507511
}
508512

513+
private ArgumentsDescriptor getKeywordArgumentsDescriptor(Nodes.Node[] arguments) {
514+
if (arguments.length == 0) {
515+
return EmptyArgumentsDescriptor.INSTANCE;
516+
}
517+
518+
Nodes.Node last = arguments[arguments.length - 1];
519+
520+
if (!(last instanceof Nodes.KeywordHashNode)) {
521+
return EmptyArgumentsDescriptor.INSTANCE;
522+
}
523+
524+
var keywords = (Nodes.KeywordHashNode) last;
525+
526+
final List<String> names = new ArrayList<>();
527+
528+
for (var n : keywords.elements) {
529+
// TODO: we ignore InterpolatedSymbolNode and other arbitrary possible key types here
530+
// not sure whether we need keys at all
531+
if (n instanceof Nodes.AssocNode assoc && assoc.key instanceof Nodes.SymbolNode symbol) {
532+
names.add(toString(symbol.unescaped));
533+
}
534+
}
535+
536+
var array = names.toArray(StringUtils.EMPTY_STRING_ARRAY);
537+
var manager = language.keywordArgumentsDescriptorManager;
538+
var descriptor = manager.getArgumentsDescriptor(array);
539+
return descriptor;
540+
}
541+
509542
public RubyNode visitCallOperatorWriteNode(Nodes.CallOperatorWriteNode node) {
510543
return defaultVisit(node);
511544
}
@@ -1287,7 +1320,9 @@ public RubyNode visitInterpolatedXStringNode(Nodes.InterpolatedXStringNode node)
12871320
}
12881321

12891322
public RubyNode visitKeywordHashNode(Nodes.KeywordHashNode node) {
1290-
return defaultVisit(node);
1323+
// store keyword arguments as a literal Hash
1324+
final var hash = new Nodes.HashNode(node.elements, node.startOffset, node.length);
1325+
return hash.accept(this);
12911326
}
12921327

12931328
public RubyNode visitLambdaNode(Nodes.LambdaNode node) {
@@ -2154,6 +2189,11 @@ protected String toString(Nodes.Node node) {
21542189
node.length, sourceEncoding.tencoding, false), sourceEncoding);
21552190
}
21562191

2192+
protected String toString(byte[] bytes) {
2193+
return TStringUtils.toJavaStringOrThrow(TruffleString.fromByteArrayUncached(bytes, 0,
2194+
bytes.length, sourceEncoding.tencoding, false), sourceEncoding);
2195+
}
2196+
21572197
protected SourceSection getSourceSection(Nodes.Node yarpNode) {
21582198
return source.createSection(yarpNode.startOffset, yarpNode.length);
21592199
}

0 commit comments

Comments
 (0)