Skip to content

Commit 63bc373

Browse files
committed
Fix parameters forwarding in eval
1 parent 9710d66 commit 63bc373

File tree

15 files changed

+139
-35
lines changed

15 files changed

+139
-35
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Bug fixes:
77

88
* Fix `Range#cover?` on begin-less ranges and non-integer values (@nirvdrum, @rwstauner).
99
* Fix `Time.new` with String argument and handle nanoseconds correctly (#3836, @andrykonchin).
10+
* Fix parameters forwarding to a method call executed with `Kernel#eval` (@andrykonchin).
1011

1112
Compatibility:
1213

spec/ruby/core/kernel/eval_spec.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,75 @@ class Object
175175
end
176176
end
177177

178+
context "parameter forwarding" do
179+
it "allows anonymous rest parameter forwarding" do
180+
object = Object.new
181+
def object.foo(a, b, c)
182+
[a, b, c]
183+
end
184+
def object.bar(*)
185+
eval "foo(*)"
186+
end
187+
188+
object.bar(1, 2, 3).should == [1, 2, 3]
189+
end
190+
191+
it "allows anonymous keyword parameters forwarding" do
192+
object = Object.new
193+
def object.foo(a:, b:, c:)
194+
[a, b, c]
195+
end
196+
def object.bar(**)
197+
eval "foo(**)"
198+
end
199+
200+
object.bar(a: 1, b: 2, c: 3).should == [1, 2, 3]
201+
end
202+
203+
it "allows anonymous block parameter forwarding" do
204+
object = Object.new
205+
def object.foo(&block)
206+
block.call
207+
end
208+
def object.bar(&)
209+
eval "foo(&)"
210+
end
211+
212+
object.bar { :foobar }.should == :foobar
213+
end
214+
215+
it "allows ... forwarding" do
216+
object = Object.new
217+
def object.foo(a, b:, &block)
218+
[a, b, block.call]
219+
end
220+
def object.bar(...)
221+
eval "foo(...)"
222+
end
223+
224+
object.bar(1, b: 2) { 3 }.should == [1, 2, 3]
225+
end
226+
227+
it "allows parameter forwarding to super" do
228+
m = Module.new do
229+
def foo(a, b:, &block)
230+
[a, b, block.call]
231+
end
232+
end
233+
234+
c = Class.new do
235+
include m
236+
237+
def foo(a, b:, &block)
238+
eval "super"
239+
end
240+
end
241+
242+
object = c.new
243+
object.foo(1, b: 2) { 3 }.should == [1, 2, 3]
244+
end
245+
end
246+
178247
ruby_version_is "3.3" do
179248
it "uses (eval at __FILE__:__LINE__) if none is provided" do
180249
eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"

spec/tags/core/kernel/eval_tags.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ fails:Kernel#eval returns from the method calling #eval when evaluating 'return'
88
fails:Kernel#eval returns from the method calling #eval when evaluating 'return' in BEGIN
99
fails:Kernel#eval uses (eval at __FILE__:__LINE__) if none is provided
1010
fails:Kernel#eval uses (eval at __FILE__:__LINE__) for __FILE__ and 1 for __LINE__ with a binding argument
11+
fails:Kernel#eval parameter forwarding allows parameter forwarding to super

spec/truffle/parsing/fixtures/def/argument_descriptors/with_block_without_name.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ast: |
1616
flags = 1
1717
isDefSingleton = false
1818
name = "foo"
19-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
19+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
2020
sourceCharIndex = 0
2121
sourceLength = 14
2222
call targets:
@@ -25,7 +25,7 @@ ast: |
2525
arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}
2626
callTarget = Object#foo
2727
checkArityProfile = false
28-
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%forward_block}
28+
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%unnamed_block}
2929
instrumentationBits = 0
3030
keywordArguments = false
3131
localReturnProfile = false
@@ -35,7 +35,7 @@ ast: |
3535
polyglotRef = org.truffleruby.RubyLanguage@...
3636
retryProfile = false
3737
returnID = org.truffleruby.language.control.ReturnID@...
38-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
38+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
3939
sourceSection = SourceSection(source=<parse_ast> [1:1 - 2:3], index=0, length=14, characters=def foo(&)\nend)
4040
split = HEURISTIC
4141
children:

spec/truffle/parsing/fixtures/def/parameters_declaration_in_a_frame/with_block_without_name.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ ast: |
2020
flags = 1
2121
isDefSingleton = false
2222
name = "foo"
23-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
23+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
2424
sourceCharIndex = 0
2525
sourceLength = 14
2626
call targets:
@@ -29,7 +29,7 @@ ast: |
2929
arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}
3030
callTarget = Object#foo
3131
checkArityProfile = false
32-
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%forward_block}
32+
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%unnamed_block}
3333
instrumentationBits = 0
3434
keywordArguments = false
3535
localReturnProfile = false
@@ -39,7 +39,7 @@ ast: |
3939
polyglotRef = org.truffleruby.RubyLanguage@...
4040
retryProfile = false
4141
returnID = org.truffleruby.language.control.ReturnID@...
42-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
42+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
4343
sourceSection = SourceSection(source=<parse_ast> [1:1 - 2:3], index=0, length=14, characters=def foo(&)\nend)
4444
split = HEURISTIC
4545
children:

spec/truffle/parsing/fixtures/def/parameters_to_local_variables/with_block_without_name.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ast: |
1616
flags = 1
1717
isDefSingleton = false
1818
name = "foo"
19-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
19+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
2020
sourceCharIndex = 0
2121
sourceLength = 14
2222
call targets:
@@ -25,7 +25,7 @@ ast: |
2525
arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}
2626
callTarget = Object#foo
2727
checkArityProfile = false
28-
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%forward_block}
28+
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%unnamed_block}
2929
instrumentationBits = 0
3030
keywordArguments = false
3131
localReturnProfile = false
@@ -35,7 +35,7 @@ ast: |
3535
polyglotRef = org.truffleruby.RubyLanguage@...
3636
retryProfile = false
3737
returnID = org.truffleruby.language.control.ReturnID@...
38-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
38+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
3939
sourceSection = SourceSection(source=<parse_ast> [1:1 - 2:3], index=0, length=14, characters=def foo(&)\nend)
4040
split = HEURISTIC
4141
children:

spec/truffle/parsing/fixtures/method_calls/parameter_forwarding/block_without_name.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ ast: |
1414
flags = 1
1515
isDefSingleton = false
1616
name = "foo"
17-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
17+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
1818
sourceCharIndex = 0
1919
sourceLength = 23
2020
call targets:
@@ -23,7 +23,7 @@ ast: |
2323
arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}
2424
callTarget = Object#foo
2525
checkArityProfile = false
26-
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%forward_block}
26+
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%unnamed_block}
2727
instrumentationBits = 0
2828
keywordArguments = false
2929
localReturnProfile = false
@@ -33,7 +33,7 @@ ast: |
3333
polyglotRef = org.truffleruby.RubyLanguage@...
3434
retryProfile = false
3535
returnID = org.truffleruby.language.control.ReturnID@...
36-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
36+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
3737
sourceSection = SourceSection(source=<parse_ast> [1:1 - 3:3], index=0, length=23, characters=def foo(&)\n bar(&)\nend)
3838
split = HEURISTIC
3939
children:
@@ -105,7 +105,7 @@ ast: |
105105
ReadLocalVariableNode
106106
attributes:
107107
flags = 0
108-
frameSlot = 3 # %forward_block
108+
frameSlot = 3 # %unnamed_block
109109
sourceCharIndex = -1
110110
sourceLength = 0
111111
type = FRAME_LOCAL

spec/truffle/parsing/fixtures/method_calls/super/in_method_body_without_explicit_arguments_when_declared_anonymous_block_parameter.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ ast: |
1515
flags = 1
1616
isDefSingleton = false
1717
name = "foo"
18-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
18+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
1919
sourceCharIndex = 0
2020
sourceLength = 22
2121
call targets:
@@ -24,7 +24,7 @@ ast: |
2424
arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}
2525
callTarget = Object#foo
2626
checkArityProfile = false
27-
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%forward_block}
27+
frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg, #3:%unnamed_block}
2828
instrumentationBits = 0
2929
keywordArguments = false
3030
localReturnProfile = false
@@ -34,7 +34,7 @@ ast: |
3434
polyglotRef = org.truffleruby.RubyLanguage@...
3535
retryProfile = false
3636
returnID = org.truffleruby.language.control.ReturnID@...
37-
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %forward_block, type = block)])
37+
sharedMethodInfo = SharedMethodInfo(staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, isImplicitRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = [ArgumentDescriptor(name = %unnamed_block, type = block)])
3838
sourceSection = SourceSection(source=<parse_ast> [1:1 - 3:3], index=0, length=22, characters=def foo(&)\n super\nend)
3939
split = HEURISTIC
4040
children:

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
2323

24+
import static org.truffleruby.parser.TranslatorEnvironment.DEFAULT_BLOCK_NAME;
2425
import static org.truffleruby.parser.TranslatorEnvironment.FORWARDED_BLOCK_NAME;
2526
import static org.truffleruby.parser.TranslatorEnvironment.FORWARDED_KEYWORD_REST_NAME;
2627
import static org.truffleruby.parser.TranslatorEnvironment.FORWARDED_REST_NAME;
@@ -58,7 +59,8 @@ private static RubyArray toArray(RubyLanguage language, RubyContext context, Arg
5859
store = new Object[]{ typeSymbol, language.coreSymbols.MULTIPLY };
5960
} else if (argType == ArgumentType.keyrest && name.equals(FORWARDED_KEYWORD_REST_NAME)) {
6061
store = new Object[]{ typeSymbol, language.coreSymbols.POW };
61-
} else if (argType == ArgumentType.block && name.equals(FORWARDED_BLOCK_NAME)) {
62+
} else if (argType == ArgumentType.block &&
63+
(name.equals(FORWARDED_BLOCK_NAME) || name.equals(DEFAULT_BLOCK_NAME))) {
6264
store = new Object[]{ typeSymbol, language.coreSymbols.AMPERSAND };
6365
} else if (argType.anonymous || name == null) {
6466
store = new Object[]{ typeSymbol };

0 commit comments

Comments
 (0)