Skip to content

Commit 3d2603d

Browse files
committed
Basic static keyword argument descriptors
1 parent 762323b commit 3d2603d

File tree

4 files changed

+60
-35
lines changed

4 files changed

+60
-35
lines changed

spec/truffle/arguments_descriptor_spec.rb

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def descriptor(*)
3838
splat = [1, 2]
3939
descriptor(*splat).should == []
4040

41-
descriptor(a: 1).should == [:keywords]
41+
descriptor(a: 1).should == [:keywords, :a]
4242
kw = { b: 2 }
4343
descriptor(**kw).should == [:keywords]
4444
empty_kw = {}
@@ -64,7 +64,7 @@ def no_kws(a, b)
6464
it "contain keyword arguments" do
6565
info = only_kws(a: 1, b: 2)
6666
info.values.should == [1, 2]
67-
info.descriptor.should == [:keywords] if truffleruby?
67+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
6868
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
6969
end
7070

@@ -75,12 +75,12 @@ def only_kws_and_opt(a:, b: 101)
7575

7676
info = only_kws_and_opt(a: 1, b: 2)
7777
info.values.should == [1, 2]
78-
info.descriptor.should == [:keywords] if truffleruby?
78+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
7979
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
8080

8181
info = only_kws_and_opt(a: 1)
8282
info.values.should == [1, 101]
83-
info.descriptor.should == [:keywords] if truffleruby?
83+
info.descriptor.should == [:keywords, :a] if truffleruby?
8484
info.arguments.should == [{a: 1}] if truffleruby?
8585
end
8686

@@ -91,7 +91,7 @@ def posn_kws(a, b:)
9191

9292
info = posn_kws(1, b: 2)
9393
info.values.should == [1, 2]
94-
info.descriptor.should == [:keywords] if truffleruby?
94+
info.descriptor.should == [:keywords, :b] if truffleruby?
9595
info.arguments.should == [1, {b: 2}] if truffleruby?
9696
end
9797

@@ -102,34 +102,34 @@ def posn_kws_defaults(a, b: 101, c: 102)
102102
it "contain optional keyword arguments with positional arguments" do
103103
info = posn_kws_defaults(1, b: 2, c: 3)
104104
info.values.should == [1, 2, 3]
105-
info.descriptor.should == [:keywords] if truffleruby?
105+
info.descriptor.should == [:keywords, :b, :c] if truffleruby?
106106
info.arguments.should == [1, {b: 2, c: 3}] if truffleruby?
107107
end
108108

109109
it "do not contain missing optional keyword arguments" do
110110
info = posn_kws_defaults(1, b: 2)
111111
info.values.should == [1, 2, 102]
112-
info.descriptor.should == [:keywords] if truffleruby?
112+
info.descriptor.should == [:keywords, :b] if truffleruby?
113113
info.arguments.should == [1, {b: 2}] if truffleruby?
114114

115115
info = posn_kws_defaults(1, c: 3)
116116
info.values.should == [1, 101, 3]
117-
info.descriptor.should == [:keywords] if truffleruby?
117+
info.descriptor.should == [:keywords, :c] if truffleruby?
118118
info.arguments.should == [1, {c: 3}] if truffleruby?
119119
end
120120

121121
it "do not contain the name of distant explicitly splatted keyword arguments" do
122122
distant = {b: 2}
123123
info = only_kws(a: 1, **distant)
124124
info.values.should == [1, 2]
125-
info.descriptor.should == [:keywords] if truffleruby?
125+
info.descriptor.should == [:keywords, :a] if truffleruby?
126126
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
127127
end
128128

129129
it "do not contain the name of near explicitly splatted keyword arguments" do
130130
info = only_kws(a: 1, **{b: 2})
131131
info.values.should == [1, 2]
132-
info.descriptor.should == [:keywords] if truffleruby?
132+
info.descriptor.should == [:keywords, :a] if truffleruby?
133133
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
134134
end
135135

@@ -140,7 +140,7 @@ def opt_and_kws(a, b=2, c: nil)
140140

141141
info = opt_and_kws(1, c: 3)
142142
info.values.should == [1, 2, 3]
143-
info.descriptor.should == [:keywords] if truffleruby?
143+
info.descriptor.should == [:keywords, :c] if truffleruby?
144144
info.arguments.should == [1, {c: 3}] if truffleruby?
145145
end
146146

@@ -197,7 +197,7 @@ def struct_new_like(a, *b, c: 101)
197197

198198
info = struct_new_like('A', :a, :b, c: 1)
199199
info.values.should == ['A', [:a, :b], 1]
200-
info.descriptor.should == [:keywords] if truffleruby?
200+
info.descriptor.should == [:keywords, :c] if truffleruby?
201201
info.arguments.should == ['A', :a, :b, {c: 1}] if truffleruby?
202202

203203
info = struct_new_like('A', :a, :b, {c: 1})
@@ -240,7 +240,7 @@ def describe_like(a, b = nil)
240240

241241
info = describe_like(1, b: 2)
242242
info.values.should == [1, {b: 2}]
243-
info.descriptor.should == [:keywords] if truffleruby?
243+
info.descriptor.should == [:keywords, :b] if truffleruby?
244244
info.arguments.should == [1, {b: 2}] if truffleruby?
245245
end
246246

@@ -262,7 +262,7 @@ def rest(*a)
262262

263263
info = rest(a: 1, b: 2)
264264
info.values.should == [[{a: 1, b: 2}]]
265-
info.descriptor.should == [:keywords] if truffleruby?
265+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
266266
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
267267
end
268268

@@ -273,7 +273,7 @@ def kw_rest(**a)
273273

274274
info = kw_rest(a: 1, b: 2)
275275
info.values.should == [{a: 1, b: 2}]
276-
info.descriptor.should == [:keywords] if truffleruby?
276+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
277277
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
278278
end
279279

@@ -284,17 +284,17 @@ def kw_and_kw_rest(a:, **b)
284284

285285
info = kw_and_kw_rest(a: 1)
286286
info.values.should == [1, {}]
287-
info.descriptor.should == [:keywords] if truffleruby?
287+
info.descriptor.should == [:keywords, :a] if truffleruby?
288288
info.arguments.should == [{a: 1}] if truffleruby?
289289

290290
info = kw_and_kw_rest(a: 1, b: 2, c: 3)
291291
info.values.should == [1, {b: 2, c: 3}]
292-
info.descriptor.should == [:keywords] if truffleruby?
292+
info.descriptor.should == [:keywords, :a, :b, :c] if truffleruby?
293293
info.arguments.should == [{a: 1, b: 2, c: 3}] if truffleruby?
294294

295295
info = kw_and_kw_rest("abc" => 123, a: 1, b: 2)
296296
info.values.should == [1, {"abc" => 123, b: 2}]
297-
info.descriptor.should == [:keywords] if truffleruby?
297+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
298298
info.arguments.should == [{"abc" => 123, a: 1, b: 2}] if truffleruby?
299299
end
300300

@@ -310,7 +310,7 @@ def mixture(a, b = nil, c = nil, d, e: nil, **f)
310310

311311
info = mixture(1, 2, e: 3)
312312
info.values.should == [1, nil, nil, 2, 3, {}]
313-
info.descriptor.should == [:keywords] if truffleruby?
313+
info.descriptor.should == [:keywords, :e] if truffleruby?
314314
info.arguments.should == [1, 2, {e: 3}] if truffleruby?
315315

316316
info = mixture(1, 2, {foo: :bar})
@@ -345,7 +345,7 @@ def self.new(a:, b:)
345345

346346
info = new_kw.new(a: 1, b: 2)
347347
info.values.should == [1, 2]
348-
info.descriptor.should == [:keywords] if truffleruby?
348+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
349349
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
350350
end
351351

@@ -358,11 +358,26 @@ def self.new(*a)
358358

359359
info = new_rest.new(a: 1, b: 2)
360360
info.values.should == [[{a: 1, b: 2}]]
361+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
362+
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
363+
end
364+
365+
it "work through delegation, but by reifying the keyword argument hash" do
366+
def delegator(...)
367+
delegated(...)
368+
end
369+
370+
def delegated(a:, b:)
371+
ArgumentsDescriptorSpecs::Info.new([a, b], Primitive.arguments_descriptor, Primitive.arguments)
372+
end
373+
374+
info = delegator(a: 1, b: 2)
375+
info.values.should == [1, 2]
361376
info.descriptor.should == [:keywords] if truffleruby?
362377
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
363378
end
364379

365-
it "work through implicit super" do
380+
it "work through implicit super, but by reifying the keyword argument hash" do
366381
parent = Class.new do
367382
def implicit_super(a:, b:)
368383
ArgumentsDescriptorSpecs::Info.new([a, b], Primitive.arguments_descriptor, Primitive.arguments)
@@ -396,7 +411,7 @@ def explicit_super(a:, b:)
396411

397412
info = child.new.explicit_super(a: 1, b: 2)
398413
info.values.should == [1, 2]
399-
info.descriptor.should == [:keywords] if truffleruby?
414+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
400415
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
401416
end
402417

@@ -413,7 +428,7 @@ def use_yielder(x, y)
413428

414429
info = use_yielder(1, 2)
415430
info.values.should == [1, 2]
416-
info.descriptor.should == [:keywords] if truffleruby?
431+
info.descriptor.should == [:keywords, :a, :b] if truffleruby?
417432
info.arguments.should == [{a: 1, b: 2}] if truffleruby?
418433
end
419434

@@ -422,7 +437,7 @@ def use_yielder(x, y)
422437
info([args, kwargs], Primitive.arguments_descriptor, Primitive.arguments)
423438
end
424439
info.values.should == [[], {a: 1}]
425-
info.descriptor.should == [:keywords] if truffleruby?
440+
info.descriptor.should == [:keywords, :a] if truffleruby?
426441
info.arguments.should == [{a: 1}] if truffleruby?
427442
end
428443

@@ -431,7 +446,7 @@ def use_yielder(x, y)
431446
ArgumentsDescriptorSpecs::Info.new([args, kwargs], Primitive.arguments_descriptor, Primitive.arguments)
432447
end
433448
info.values.should == [[], {a: 1}]
434-
info.descriptor.should == [:keywords] if truffleruby?
449+
info.descriptor.should == [:keywords, :a] if truffleruby?
435450
info.arguments.should == [{a: 1}] if truffleruby?
436451
end
437452

@@ -440,11 +455,11 @@ def use_yielder(x, y)
440455
ArgumentsDescriptorSpecs::Info.new([args, kwargs], Primitive.arguments_descriptor, Primitive.arguments)
441456
end.call(a: 1)
442457
info.values.should == [[], {a: 1}]
443-
info.descriptor.should == [:keywords] if truffleruby?
458+
info.descriptor.should == [:keywords, :a] if truffleruby?
444459
info.arguments.should == [{a: 1}] if truffleruby?
445460
end
446461

447-
it "work through Thread#new" do
462+
it "work through Thread#new, but by reifying the keyword argument hash" do
448463
info = Thread.new(a: 1) do |*args, **kwargs|
449464
ArgumentsDescriptorSpecs::Info.new([args, kwargs], Primitive.arguments_descriptor, Primitive.arguments)
450465
end.value
@@ -458,7 +473,7 @@ def use_yielder(x, y)
458473
ArgumentsDescriptorSpecs::Info.new([args, kwargs], Primitive.arguments_descriptor, Primitive.arguments)
459474
end.resume(a: 1)
460475
info.values.should == [[], {a: 1}]
461-
info.descriptor.should == [:keywords] if truffleruby?
476+
info.descriptor.should == [:keywords, :a] if truffleruby?
462477
info.arguments.should == [{a: 1}] if truffleruby?
463478

464479
Fiber.new(&-> { true }).resume(**{}).should == true

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ protected Object sendWithoutCExtLock(
319319
return sendWithoutCExtLock(frame, receiver, method, block, EmptyArgumentsDescriptor.INSTANCE, args,
320320
dispatchNode, ownedProfile);
321321
} else {
322-
return sendWithoutCExtLock(frame, receiver, method, block, getLanguage().keywordArgumentsDescriptorManager.EMPTY, args,
322+
return sendWithoutCExtLock(frame, receiver, method, block,
323+
getLanguage().keywordArgumentsDescriptorManager.EMPTY, args,
323324
dispatchNode, ownedProfile);
324325
}
325326
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,8 +616,8 @@ private RubyArray descriptorToArray(ArgumentsDescriptor descriptor) {
616616
final KeywordArgumentsDescriptor keywordArgumentsDescriptor = (KeywordArgumentsDescriptor) descriptor;
617617

618618
final Stream<RubySymbol> keywords = Stream.concat(
619-
Stream.of("keywords"),
620-
Arrays.stream(keywordArgumentsDescriptor.getKeywords()))
619+
Stream.of("keywords"),
620+
Arrays.stream(keywordArgumentsDescriptor.getKeywords()))
621621
.map(getLanguage()::getSymbol);
622622

623623
return createArray(keywords.toArray());

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3127,23 +3127,32 @@ private static ArgumentsDescriptor getKeywordArgumentsDescriptor(RubyLanguage la
31273127
return EmptyArgumentsDescriptor.INSTANCE;
31283128
}
31293129

3130+
final List<String> keywords = new ArrayList<>();
3131+
boolean splat = false;
3132+
boolean nonKeywordKeys = false;
3133+
31303134
for (ParseNodeTuple pair : keywordHashArgumentNode.getPairs()) {
31313135
final ParseNode key = pair.getKey();
31323136
final ParseNode value = pair.getValue();
31333137

31343138
if (key instanceof SymbolParseNode &&
31353139
((SymbolParseNode) key).getName() != null) {
3136-
return language.keywordArgumentsDescriptorManager.EMPTY;
3140+
keywords.add(((SymbolParseNode) key).getName());
31373141
} else if (key == null && value != null) {
31383142
// A splat keyword hash
3139-
return language.keywordArgumentsDescriptorManager.EMPTY;
3143+
splat = true;
31403144
} else {
31413145
// For non-symbol keys
3142-
return language.keywordArgumentsDescriptorManager.EMPTY;
3146+
nonKeywordKeys = true;
31433147
}
31443148
}
31453149

3146-
return EmptyArgumentsDescriptor.INSTANCE;
3150+
if (splat || nonKeywordKeys || !keywords.isEmpty()) {
3151+
return language.keywordArgumentsDescriptorManager
3152+
.getArgumentsDescriptor(keywords.toArray(n -> new String[n]));
3153+
} else {
3154+
return EmptyArgumentsDescriptor.INSTANCE;
3155+
}
31473156
}
31483157

31493158
/* This is carefully written so ArrayParseNode is only considered if it is the argsNode itself, or as the RHS of an

0 commit comments

Comments
 (0)