ruby/test/ruby/test_call.rb
Jeremy Evans 29f2cb83fb
Fix evaluation order issue in f(**h, &h.delete(key))
Previously, this would delete the key in `h` before keyword
splatting `h`.  This goes against how ruby handles `f(*a, &a.pop)`
and similar expressions.

Fix this by having the compiler check whether the block pass
expression is safe.  If it is not safe, then dup the keyword
splatted hash before evaluating the block pass expression.

For expression: `h=nil; f(**h, &h.delete(:key))`

VM instructions before:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 getlocal_WC_0                          h@0
0006 getlocal_WC_0                          h@0
0008 putobject                              :key
0010 opt_send_without_block                 <calldata!mid:delete, argc:1, ARGS_SIMPLE>
0012 splatkw
0013 send                                   <calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT>, nil
0016 leave
```

VM instructions after:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 putspecialobject                       1
0006 newhash                                0
0008 getlocal_WC_0                          h@0
0010 opt_send_without_block                 <calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE>
0012 getlocal_WC_0                          h@0
0014 putobject                              :key
0016 opt_send_without_block                 <calldata!mid:delete, argc:1, ARGS_SIMPLE>
0018 send                                   <calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT|KW_SPLAT_MUT>, nil
0021 leave
```

This is the same as 07d3bf4832, except that
it removes unnecessary hash allocations when using the prism compiler.

Fixes [Bug #20640]
2024-09-18 12:46:07 -07:00

1392 lines
50 KiB
Ruby

# frozen_string_literal: false
require 'test/unit'
require '-test-/iter'
class TestCall < Test::Unit::TestCase
# These dummy method definitions prevent warnings "the block passed to 'a'..."
def a(&) = nil
def b(&) = nil
def c(&) = nil
def d(&) = nil
def e(&) = nil
def f(&) = nil
def g(&) = nil
def h(&) = nil
def i(&) = nil
def j(&) = nil
def k(&) = nil
def l(&) = nil
def m(&) = nil
def n(&) = nil
def o(&) = nil
def aaa(a, b=100, *rest, &)
res = [a, b]
res += rest if rest
return res
end
def test_call
assert_raise(ArgumentError) {aaa()}
assert_raise(ArgumentError) {aaa}
assert_equal([1, 100], aaa(1))
assert_equal([1, 2], aaa(1, 2))
assert_equal([1, 2, 3, 4], aaa(1, 2, 3, 4))
assert_equal([1, 2, 3, 4], aaa(1, *[2, 3, 4]))
end
def test_callinfo
bug9622 = '[ruby-core:61422] [Bug #9622]'
o = Class.new do
def foo(*args)
bar(:foo, *args)
end
def bar(name)
name
end
end.new
e = assert_raise(ArgumentError) {o.foo(100)}
assert_nothing_raised(ArgumentError) {o.foo}
assert_raise_with_message(ArgumentError, e.message, bug9622) {o.foo(100)}
end
def test_safe_call
s = Struct.new(:x, :y, :z)
o = s.new("x")
assert_equal("X", o.x&.upcase)
assert_nil(o.y&.upcase)
assert_equal("x", o.x)
o&.x = 6
assert_equal(6, o.x)
o&.x *= 7
assert_equal(42, o.x)
o&.y = 5
assert_equal(5, o.y)
o&.z ||= 6
assert_equal(6, o.z)
o&.z &&= 7
assert_equal(7, o.z)
o = nil
assert_nil(o&.x)
assert_nothing_raised(NoMethodError) {o&.x = raise}
assert_nothing_raised(NoMethodError) {o&.x = raise; nil}
assert_nothing_raised(NoMethodError) {o&.x *= raise}
assert_nothing_raised(NoMethodError) {o&.x *= raise; nil}
assert_nothing_raised(NoMethodError) {o&.x ||= raise}
assert_nothing_raised(NoMethodError) {o&.x ||= raise; nil}
assert_nothing_raised(NoMethodError) {o&.x &&= raise}
assert_nothing_raised(NoMethodError) {o&.x &&= raise; nil}
end
def test_safe_call_evaluate_arguments_only_method_call_is_made
count = 0
proc = proc { count += 1; 1 }
s = Struct.new(:x, :y)
o = s.new(["a", "b", "c"])
o.y&.at(proc.call)
assert_equal(0, count)
o.x&.at(proc.call)
assert_equal(1, count)
end
def test_safe_call_block_command
assert_nil(("a".sub! "b" do end&.foo 1))
end
def test_safe_call_block_call
assert_nil(("a".sub! "b" do end&.foo))
end
def test_safe_call_block_call_brace
assert_nil(("a".sub! "b" do end&.foo {}))
assert_nil(("a".sub! "b" do end&.foo do end))
end
def test_safe_call_block_call_command
assert_nil(("a".sub! "b" do end&.foo 1 do end))
end
def test_invalid_safe_call
h = nil
assert_raise(NoMethodError) {
h[:foo] = nil
}
end
def test_frozen_splat_and_keywords
a = [1, 2].freeze
def self.f(*a); a end
assert_equal([1, 2, {kw: 3}], f(*a, kw: 3))
end
def test_call_bmethod_proc
pr = proc{|sym| sym}
define_singleton_method(:a, &pr)
ary = [10]
assert_equal(10, a(*ary))
end
def test_call_bmethod_proc_restarg
pr = proc{|*sym| sym}
define_singleton_method(:a, &pr)
ary = [10]
assert_equal([10], a(*ary))
assert_equal([10], a(10))
end
def test_call_op_asgn_keywords
h = Class.new do
attr_reader :get, :set
def v; yield; [*@get, *@set] end
def [](*a, **b, &c) @get = [a, b, c]; @set = []; 3 end
def []=(*a, **b, &c) @set = [a, b, c] end
end.new
a = []
kw = {}
b = lambda{}
# Prevent "assigned but unused variable" warnings
_ = [h, a, kw, b]
message = /keyword arg given in index assignment/
# +=, without block, non-popped
assert_syntax_error(%q{h[**kw] += 1}, message)
assert_syntax_error(%q{h[0, **kw] += 1}, message)
assert_syntax_error(%q{h[0, *a, **kw] += 1}, message)
assert_syntax_error(%q{h[kw: 5] += 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] += 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] += 1}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2] += 1}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] += 1}, message)
# +=, with block, non-popped
assert_syntax_error(%q{h[**kw, &b] += 1}, message)
assert_syntax_error(%q{h[0, **kw, &b] += 1}, message)
assert_syntax_error(%q{h[0, *a, **kw, &b] += 1}, message)
assert_syntax_error(%q{h[kw: 5, &b] += 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] += 1}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] += 1}, message)
# +=, without block, popped
assert_syntax_error(%q{h[**kw] += 1; nil}, message)
assert_syntax_error(%q{h[0, **kw] += 1; nil}, message)
assert_syntax_error(%q{h[0, *a, **kw] += 1; nil}, message)
assert_syntax_error(%q{h[kw: 5] += 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] += 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] += 1; nil}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2] += 1; nil}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] += 1; nil}, message)
# +=, with block, popped
assert_syntax_error(%q{h[**kw, &b] += 1; nil}, message)
assert_syntax_error(%q{h[0, **kw, &b] += 1; nil}, message)
assert_syntax_error(%q{h[0, *a, **kw, &b] += 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, &b] += 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] += 1; nil}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] += 1; nil}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] += 1; nil}, message)
# &&=, without block, non-popped
assert_syntax_error(%q{h[**kw] &&= 1}, message)
assert_syntax_error(%q{h[0, **kw] &&= 1}, message)
assert_syntax_error(%q{h[0, *a, **kw] &&= 1}, message)
assert_syntax_error(%q{h[kw: 5] &&= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2] &&= 1}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] &&= 1}, message)
# &&=, with block, non-popped
assert_syntax_error(%q{h[**kw, &b] &&= 1}, message)
assert_syntax_error(%q{h[0, **kw, &b] &&= 1}, message)
assert_syntax_error(%q{h[0, *a, **kw, &b] &&= 1}, message)
assert_syntax_error(%q{h[kw: 5, &b] &&= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] &&= 1}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] &&= 1}, message)
# &&=, without block, popped
assert_syntax_error(%q{h[**kw] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, **kw] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, **kw] &&= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5] &&= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] &&= 1; nil}, message)
# &&=, with block, popped
assert_syntax_error(%q{h[**kw, &b] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, **kw, &b] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, **kw, &b] &&= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, &b] &&= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] &&= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] &&= 1; nil}, message)
# ||=, without block, non-popped
assert_syntax_error(%q{h[**kw] ||= 1}, message)
assert_syntax_error(%q{h[0, **kw] ||= 1}, message)
assert_syntax_error(%q{h[0, *a, **kw] ||= 1}, message)
assert_syntax_error(%q{h[kw: 5] ||= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2] ||= 1}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] ||= 1}, message)
# ||=, with block, non-popped
assert_syntax_error(%q{h[**kw, &b] ||= 1}, message)
assert_syntax_error(%q{h[0, **kw, &b] ||= 1}, message)
assert_syntax_error(%q{h[0, *a, **kw, &b] ||= 1}, message)
assert_syntax_error(%q{h[kw: 5, &b] ||= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] ||= 1}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] ||= 1}, message)
# ||=, without block, popped
assert_syntax_error(%q{h[**kw] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, **kw] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, **kw] ||= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5] ||= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, nil: 3] ||= 1; nil}, message)
# ||=, with block, popped
assert_syntax_error(%q{h[**kw, &b] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, **kw, &b] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, **kw, &b] ||= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, &b] ||= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1; nil}, message)
assert_syntax_error(%q{h[kw: 5, a: 2, &b] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, kw: 5, a: 2, &b] ||= 1; nil}, message)
assert_syntax_error(%q{h[0, *a, kw: 5, a: 2, b: 3, &b] ||= 1; nil}, message)
end
def test_kwsplat_block_order_op_asgn
o = Object.new
ary = []
o.define_singleton_method(:to_a) {ary << :to_a; []}
o.define_singleton_method(:to_hash) {ary << :to_hash; {}}
o.define_singleton_method(:to_proc) {ary << :to_proc; lambda{}}
def o.[](...) 2 end
def o.[]=(...) end
message = /keyword arg given in index assignment/
assert_syntax_error(%q{o[kw: 1] += 1}, message)
assert_syntax_error(%q{o[**o] += 1}, message)
assert_syntax_error(%q{o[**o, &o] += 1}, message)
assert_syntax_error(%q{o[*o, **o, &o] += 1}, message)
end
def test_call_op_asgn_keywords_mutable
h = Class.new do
attr_reader :get, :set
def v; yield; [*@get, *@set] end
def [](*a, **b)
@get = [a.dup, b.dup]
a << :splat_modified
b[:kw_splat_modified] = true
@set = []
3
end
def []=(*a, **b) @set = [a, b] end
end.new
message = /keyword arg given in index assignment/
a = []
kw = {}
# Prevent "assigned but unused variable" warnings
_ = [h, a, kw]
assert_syntax_error(%q{h[*a, 2, b: 5, **kw] += 1}, message)
end
def test_call_splat_post_order
bug12860 = '[ruby-core:77701] [Bug# 12860]'
ary = [1, 2]
assert_equal([1, 2, 1], aaa(*ary, ary.shift), bug12860)
ary = [1, 2]
assert_equal([0, 1, 2, 1], aaa(0, *ary, ary.shift), bug12860)
end
def test_call_splat_block_order
bug16504 = '[ruby-core:96769] [Bug# 16504]'
b = proc{}
ary = [1, 2, b]
assert_equal([1, 2, b], aaa(*ary, &ary.pop), bug16504)
ary = [1, 2, b]
assert_equal([0, 1, 2, b], aaa(0, *ary, &ary.pop), bug16504)
end
def test_call_splat_kw_order
b = {}
ary = [1, 2, b]
assert_equal([1, 2, b, {a: b}], aaa(*ary, a: ary.pop))
ary = [1, 2, b]
assert_equal([0, 1, 2, b, {a: b}], aaa(0, *ary, a: ary.pop))
end
def test_call_splat_kw_splat_order
b = {}
ary = [1, 2, b]
assert_equal([1, 2, b], aaa(*ary, **ary.pop))
ary = [1, 2, b]
assert_equal([0, 1, 2, b], aaa(0, *ary, **ary.pop))
end
def test_call_args_splat_with_nonhash_keyword_splat
o = Object.new
def o.to_hash; {a: 1} end
def self.f(*a, **kw)
kw
end
assert_equal Hash, f(*[], **o).class
end
def test_call_args_splat_with_pos_arg_kw_splat_is_not_mutable
o = Object.new
def o.foo(a, **h)= h[:splat_modified] = true
a = []
b = {splat_modified: false}
o.foo(*a, :x, **b)
assert_equal({splat_modified: false}, b)
end
def test_kwsplat_block_eval_order
def self.t(**kw, &b) [kw, b] end
pr = ->{}
h = {a: pr}
a = []
ary = t(**h, &h.delete(:a))
assert_equal([{a: pr}, pr], ary)
h = {a: pr}
ary = t(*a, **h, &h.delete(:a))
assert_equal([{a: pr}, pr], ary)
end
def test_kwsplat_block_order
o = Object.new
ary = []
o.define_singleton_method(:to_a) {ary << :to_a; []}
o.define_singleton_method(:to_hash) {ary << :to_hash; {}}
o.define_singleton_method(:to_proc) {ary << :to_proc; lambda{}}
def self.t(...) end
t(**o, &o)
assert_equal([:to_hash, :to_proc], ary)
ary.clear
t(*o, **o, &o)
assert_equal([:to_a, :to_hash, :to_proc], ary)
end
def test_kwsplat_block_order_super
def self.t(splat)
o = Object.new
ary = []
o.define_singleton_method(:to_a) {ary << :to_a; []}
o.define_singleton_method(:to_hash) {ary << :to_hash; {}}
o.define_singleton_method(:to_proc) {ary << :to_proc; lambda{}}
if splat
super(*o, **o, &o)
else
super(**o, &o)
end
ary
end
extend Module.new{def t(...) end}
assert_equal([:to_hash, :to_proc], t(false))
assert_equal([:to_a, :to_hash, :to_proc], t(true))
end
OVER_STACK_LEN = (ENV['RUBY_OVER_STACK_LEN'] || 150).to_i # Greater than VM_ARGC_STACK_MAX
OVER_STACK_ARGV = OVER_STACK_LEN.times.to_a.freeze
def test_call_cfunc_splat_large_array_bug_4040
a = OVER_STACK_ARGV
assert_equal(a, [].push(*a))
assert_equal(a, [].push(a[0], *a[1..]))
assert_equal(a, [].push(a[0], a[1], *a[2..]))
assert_equal(a, [].push(*a[0..1], *a[2..]))
assert_equal(a, [].push(*a[...-1], a[-1]))
assert_equal(a, [].push(a[0], *a[1...-1], a[-1]))
assert_equal(a, [].push(a[0], a[1], *a[2...-1], a[-1]))
assert_equal(a, [].push(*a[0..1], *a[2...-1], a[-1]))
assert_equal(a, [].push(*a[...-2], a[-2], a[-1]))
assert_equal(a, [].push(a[0], *a[1...-2], a[-2], a[-1]))
assert_equal(a, [].push(a[0], a[1], *a[2...-2], a[-2], a[-1]))
assert_equal(a, [].push(*a[0..1], *a[2...-2], a[-2], a[-1]))
kw = {x: 1}
a_kw = a + [kw]
assert_equal(a_kw, [].push(*a, **kw))
assert_equal(a_kw, [].push(a[0], *a[1..], **kw))
assert_equal(a_kw, [].push(a[0], a[1], *a[2..], **kw))
assert_equal(a_kw, [].push(*a[0..1], *a[2..], **kw))
assert_equal(a_kw, [].push(*a[...-1], a[-1], **kw))
assert_equal(a_kw, [].push(a[0], *a[1...-1], a[-1], **kw))
assert_equal(a_kw, [].push(a[0], a[1], *a[2...-1], a[-1], **kw))
assert_equal(a_kw, [].push(*a[0..1], *a[2...-1], a[-1], **kw))
assert_equal(a_kw, [].push(*a[...-2], a[-2], a[-1], **kw))
assert_equal(a_kw, [].push(a[0], *a[1...-2], a[-2], a[-1], **kw))
assert_equal(a_kw, [].push(a[0], a[1], *a[2...-2], a[-2], a[-1], **kw))
assert_equal(a_kw, [].push(*a[0..1], *a[2...-2], a[-2], a[-1], **kw))
assert_equal(a_kw, [].push(*a, x: 1))
assert_equal(a_kw, [].push(a[0], *a[1..], x: 1))
assert_equal(a_kw, [].push(a[0], a[1], *a[2..], x: 1))
assert_equal(a_kw, [].push(*a[0..1], *a[2..], x: 1))
assert_equal(a_kw, [].push(*a[...-1], a[-1], x: 1))
assert_equal(a_kw, [].push(a[0], *a[1...-1], a[-1], x: 1))
assert_equal(a_kw, [].push(a[0], a[1], *a[2...-1], a[-1], x: 1))
assert_equal(a_kw, [].push(*a[0..1], *a[2...-1], a[-1], x: 1))
assert_equal(a_kw, [].push(*a[...-2], a[-2], a[-1], x: 1))
assert_equal(a_kw, [].push(a[0], *a[1...-2], a[-2], a[-1], x: 1))
assert_equal(a_kw, [].push(a[0], a[1], *a[2...-2], a[-2], a[-1], x: 1))
assert_equal(a_kw, [].push(*a[0..1], *a[2...-2], a[-2], a[-1], x: 1))
a_kw[-1][:y] = 2
kw = {y: 2}
assert_equal(a_kw, [].push(*a, x: 1, **kw))
assert_equal(a_kw, [].push(a[0], *a[1..], x: 1, **kw))
assert_equal(a_kw, [].push(a[0], a[1], *a[2..], x: 1, **kw))
assert_equal(a_kw, [].push(*a[0..1], *a[2..], x: 1, **kw))
assert_equal(a_kw, [].push(*a[...-1], a[-1], x: 1, **kw))
assert_equal(a_kw, [].push(a[0], *a[1...-1], a[-1], x: 1, **kw))
assert_equal(a_kw, [].push(a[0], a[1], *a[2...-1], a[-1], x: 1, **kw))
assert_equal(a_kw, [].push(*a[0..1], *a[2...-1], a[-1], x: 1, **kw))
assert_equal(a_kw, [].push(*a[...-2], a[-2], a[-1], x: 1, **kw))
assert_equal(a_kw, [].push(a[0], *a[1...-2], a[-2], a[-1], x: 1, **kw))
assert_equal(a_kw, [].push(a[0], a[1], *a[2...-2], a[-2], a[-1], x: 1, **kw))
assert_equal(a_kw, [].push(*a[0..1], *a[2...-2], a[-2], a[-1], x: 1, **kw))
kw = {}
assert_equal(a, [].push(*a, **kw))
assert_equal(a, [].push(a[0], *a[1..], **kw))
assert_equal(a, [].push(a[0], a[1], *a[2..], **kw))
assert_equal(a, [].push(*a[0..1], *a[2..], **kw))
assert_equal(a, [].push(*a[...-1], a[-1], **kw))
assert_equal(a, [].push(a[0], *a[1...-1], a[-1], **kw))
assert_equal(a, [].push(a[0], a[1], *a[2...-1], a[-1], **kw))
assert_equal(a, [].push(*a[0..1], *a[2...-1], a[-1], **kw))
assert_equal(a, [].push(*a[...-2], a[-2], a[-1], **kw))
assert_equal(a, [].push(a[0], *a[1...-2], a[-2], a[-1], **kw))
assert_equal(a, [].push(a[0], a[1], *a[2...-2], a[-2], a[-1], **kw))
assert_equal(a, [].push(*a[0..1], *a[2...-2], a[-2], a[-1], **kw))
a_kw = a + [Hash.ruby2_keywords_hash({})]
assert_equal(a, [].push(*a_kw))
# Single test with value that would cause SystemStackError.
# Not all tests use such a large array to reduce testing time.
assert_equal(1380888, [].push(*1380888.times.to_a).size)
end
def test_call_iseq_large_array_splat_fail
def self.a; end
def self.b(a=1); end
def self.c(k: 1); end
def self.d(**kw); end
def self.e(k: 1, **kw); end
def self.f(a=1, k: 1); end
def self.g(a=1, **kw); end
def self.h(a=1, k: 1, **kw); end
(:a..:h).each do |meth|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
instance_eval("#{meth}(*OVER_STACK_ARGV)", __FILE__, __LINE__)
end
end
end
def test_call_iseq_large_array_splat_pass
def self.a(*a); a.length end
assert_equal OVER_STACK_LEN, a(*OVER_STACK_ARGV)
def self.b(_, *a); a.length end
assert_equal (OVER_STACK_LEN - 1), b(*OVER_STACK_ARGV)
def self.c(_, *a, _); a.length end
assert_equal (OVER_STACK_LEN - 2), c(*OVER_STACK_ARGV)
def self.d(b=1, *a); a.length end
assert_equal (OVER_STACK_LEN - 1), d(*OVER_STACK_ARGV)
def self.e(b=1, *a, _); a.length end
assert_equal (OVER_STACK_LEN - 2), e(*OVER_STACK_ARGV)
def self.f(b, *a); a.length end
assert_equal (OVER_STACK_LEN - 1), f(*OVER_STACK_ARGV)
def self.g(*a, k: 1); a.length end
assert_equal OVER_STACK_LEN, g(*OVER_STACK_ARGV)
def self.h(*a, **kw); a.length end
assert_equal OVER_STACK_LEN, h(*OVER_STACK_ARGV)
def self.i(*a, k: 1, **kw); a.length end
assert_equal OVER_STACK_LEN, i(*OVER_STACK_ARGV)
def self.j(b=1, *a, k: 1); a.length end
assert_equal (OVER_STACK_LEN - 1), j(*OVER_STACK_ARGV)
def self.k(b=1, *a, **kw); a.length end
assert_equal (OVER_STACK_LEN - 1), k(*OVER_STACK_ARGV)
def self.l(b=1, *a, k: 1, **kw); a.length end
assert_equal (OVER_STACK_LEN - 1), l(*OVER_STACK_ARGV)
def self.m(b=1, *a, _, k: 1); a.length end
assert_equal (OVER_STACK_LEN - 2), m(*OVER_STACK_ARGV)
def self.n(b=1, *a, _, **kw); a.length end
assert_equal (OVER_STACK_LEN - 2), n(*OVER_STACK_ARGV)
def self.o(b=1, *a, _, k: 1, **kw); a.length end
assert_equal (OVER_STACK_LEN - 2), o(*OVER_STACK_ARGV)
end
def test_call_iseq_large_array_splat_with_large_number_of_parameters
args = OVER_STACK_ARGV.map{|i| "a#{i}"}.join(',')
args1 = (OVER_STACK_LEN-1).times.map{|i| "a#{i}"}.join(',')
singleton_class.class_eval("def a(#{args}); [#{args}] end")
assert_equal OVER_STACK_ARGV, a(*OVER_STACK_ARGV)
singleton_class.class_eval("def b(#{args}, b=0); [#{args}, b] end")
assert_equal(OVER_STACK_ARGV + [0], b(*OVER_STACK_ARGV))
singleton_class.class_eval("def c(#{args}, *b); [#{args}, b] end")
assert_equal(OVER_STACK_ARGV + [[]], c(*OVER_STACK_ARGV))
singleton_class.class_eval("def d(#{args1}, *b); [#{args1}, b] end")
assert_equal(OVER_STACK_ARGV[0...-1] + [[OVER_STACK_ARGV.last]], d(*OVER_STACK_ARGV))
end if OVER_STACK_LEN < 200
def test_call_proc_large_array_splat_pass
[
proc{0} ,
proc{|a=1|a},
proc{|k: 1|0},
proc{|**kw| 0},
proc{|k: 1, **kw| 0},
proc{|a=1, k: 1| a},
proc{|a=1, **kw| a},
proc{|a=1, k: 1, **kw| a},
].each do |l|
assert_equal 0, l.call(*OVER_STACK_ARGV)
end
assert_equal OVER_STACK_LEN, proc{|*a| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), proc{|_, *a| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), proc{|_, *a, _| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), proc{|b=1, *a| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), proc{|b=1, *a, _| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), proc{|b=1, *a| a.length}.(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, proc{|*a, k: 1| a.length}.(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, proc{|*a, **kw| a.length}.(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, proc{|*a, k: 1, **kw| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), proc{|b=1, *a, k: 1| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), proc{|b=1, *a, **kw| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), proc{|b=1, *a, k: 1, **kw| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), proc{|b=1, *a, _, k: 1| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), proc{|b=1, *a, _, **kw| a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), proc{|b=1, *a, _, k: 1, **kw| a.length}.(*OVER_STACK_ARGV)
end
def test_call_proc_large_array_splat_with_large_number_of_parameters
args = OVER_STACK_ARGV.map{|i| "a#{i}"}.join(',')
args1 = (OVER_STACK_LEN-1).times.map{|i| "a#{i}"}.join(',')
l = instance_eval("proc{|#{args}| [#{args}]}")
assert_equal OVER_STACK_ARGV, l.(*OVER_STACK_ARGV)
l = instance_eval("proc{|#{args}, b| [#{args}, b]}")
assert_equal(OVER_STACK_ARGV + [nil], l.(*OVER_STACK_ARGV))
l = instance_eval("proc{|#{args1}| [#{args1}]}")
assert_equal(OVER_STACK_ARGV[0...-1], l.(*OVER_STACK_ARGV))
l = instance_eval("proc{|#{args}, *b| [#{args}, b]}")
assert_equal(OVER_STACK_ARGV + [[]], l.(*OVER_STACK_ARGV))
l = instance_eval("proc{|#{args1}, *b| [#{args1}, b]}")
assert_equal(OVER_STACK_ARGV[0...-1] + [[OVER_STACK_ARGV.last]], l.(*OVER_STACK_ARGV))
l = instance_eval("proc{|#{args}, b, *c| [#{args}, b, c]}")
assert_equal(OVER_STACK_ARGV + [nil, []], l.(*OVER_STACK_ARGV))
l = instance_eval("proc{|#{args}, b, *c, d| [#{args}, b, c, d]}")
assert_equal(OVER_STACK_ARGV + [nil, [], nil], l.(*OVER_STACK_ARGV))
end if OVER_STACK_LEN < 200
def test_call_lambda_large_array_splat_fail
[
->{} ,
->(a=1){},
->(k: 1){},
->(**kw){},
->(k: 1, **kw){},
->(a=1, k: 1){},
->(a=1, **kw){},
->(a=1, k: 1, **kw){},
].each do |l|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
l.call(*OVER_STACK_ARGV)
end
end
end
def test_call_lambda_large_array_splat_pass
assert_equal OVER_STACK_LEN, ->(*a){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), ->(_, *a){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), ->(_, *a, _){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), ->(b=1, *a){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), ->(b=1, *a, _){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), ->(b, *a){a.length}.(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, ->(*a, k: 1){a.length}.(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, ->(*a, **kw){a.length}.(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, ->(*a, k: 1, **kw){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), ->(b=1, *a, k: 1){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), ->(b=1, *a, **kw){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 1), ->(b=1, *a, k: 1, **kw){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), ->(b=1, *a, _, k: 1){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), ->(b=1, *a, _, **kw){a.length}.(*OVER_STACK_ARGV)
assert_equal (OVER_STACK_LEN - 2), ->(b=1, *a, _, k: 1, **kw){a.length}.(*OVER_STACK_ARGV)
end
def test_call_yield_block_large_array_splat_pass
def self.a
yield(*OVER_STACK_ARGV)
end
[
proc{0} ,
proc{|a=1|a},
proc{|k: 1|0},
proc{|**kw| 0},
proc{|k: 1, **kw| 0},
proc{|a=1, k: 1| a},
proc{|a=1, **kw| a},
proc{|a=1, k: 1, **kw| a},
].each do |l|
assert_equal 0, a(&l)
end
assert_equal OVER_STACK_LEN, a{|*a| a.length}
assert_equal (OVER_STACK_LEN - 1), a{|_, *a| a.length}
assert_equal (OVER_STACK_LEN - 2), a{|_, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 1), a{|b=1, *a| a.length}
assert_equal (OVER_STACK_LEN - 2), a{|b=1, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 1), a{|b, *a| a.length}
assert_equal OVER_STACK_LEN, a{|*a, k: 1| a.length}
assert_equal OVER_STACK_LEN, a{|*a, **kw| a.length}
assert_equal OVER_STACK_LEN, a{|*a, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), a{|b=1, *a, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 1), a{|b=1, *a, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), a{|b=1, *a, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), a{|b=1, *a, _, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 2), a{|b=1, *a, _, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), a{|b=1, *a, _, k: 1, **kw| a.length}
end
def test_call_yield_large_array_splat_with_large_number_of_parameters
def self.a
yield(*OVER_STACK_ARGV)
end
args = OVER_STACK_ARGV.map{|i| "a#{i}"}.join(',')
args1 = (OVER_STACK_LEN-1).times.map{|i| "a#{i}"}.join(',')
assert_equal OVER_STACK_ARGV, instance_eval("a{|#{args}| [#{args}]}", __FILE__, __LINE__)
assert_equal(OVER_STACK_ARGV + [nil], instance_eval("a{|#{args}, b| [#{args}, b]}", __FILE__, __LINE__))
assert_equal(OVER_STACK_ARGV[0...-1], instance_eval("a{|#{args1}| [#{args1}]}", __FILE__, __LINE__))
assert_equal(OVER_STACK_ARGV + [[]], instance_eval("a{|#{args}, *b| [#{args}, b]}", __FILE__, __LINE__))
assert_equal(OVER_STACK_ARGV[0...-1] + [[OVER_STACK_ARGV.last]], instance_eval("a{|#{args1}, *b| [#{args1}, b]}", __FILE__, __LINE__))
assert_equal(OVER_STACK_ARGV + [nil, []], instance_eval("a{|#{args}, b, *c| [#{args}, b, c]}", __FILE__, __LINE__))
assert_equal(OVER_STACK_ARGV + [nil, [], nil], instance_eval("a{|#{args}, b, *c, d| [#{args}, b, c, d]}", __FILE__, __LINE__))
end if OVER_STACK_LEN < 200
def test_call_yield_lambda_large_array_splat_fail
def self.a
yield(*OVER_STACK_ARGV)
end
[
->{} ,
->(a=1){},
->(k: 1){},
->(**kw){},
->(k: 1, **kw){},
->(a=1, k: 1){},
->(a=1, **kw){},
->(a=1, k: 1, **kw){},
].each do |l|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
a(&l)
end
end
end
def test_call_yield_lambda_large_array_splat_pass
def self.a
yield(*OVER_STACK_ARGV)
end
assert_equal OVER_STACK_LEN, a(&->(*a){a.length})
assert_equal (OVER_STACK_LEN - 1), a(&->(_, *a){a.length})
assert_equal (OVER_STACK_LEN - 2), a(&->(_, *a, _){a.length})
assert_equal (OVER_STACK_LEN - 1), a(&->(b=1, *a){a.length})
assert_equal (OVER_STACK_LEN - 2), a(&->(b=1, *a, _){a.length})
assert_equal (OVER_STACK_LEN - 1), a(&->(b, *a){a.length})
assert_equal OVER_STACK_LEN, a(&->(*a, k: 1){a.length})
assert_equal OVER_STACK_LEN, a(&->(*a, **kw){a.length})
assert_equal OVER_STACK_LEN, a(&->(*a, k: 1, **kw){a.length})
assert_equal (OVER_STACK_LEN - 1), a(&->(b=1, *a, k: 1){a.length})
assert_equal (OVER_STACK_LEN - 1), a(&->(b=1, *a, **kw){a.length})
assert_equal (OVER_STACK_LEN - 1), a(&->(b=1, *a, k: 1, **kw){a.length})
assert_equal (OVER_STACK_LEN - 2), a(&->(b=1, *a, _, k: 1){a.length})
assert_equal (OVER_STACK_LEN - 2), a(&->(b=1, *a, _, **kw){a.length})
assert_equal (OVER_STACK_LEN - 2), a(&->(b=1, *a, _, k: 1, **kw){a.length})
end
def test_call_send_iseq_large_array_splat_fail
def self.a; end
def self.b(a=1); end
def self.c(k: 1); end
def self.d(**kw); end
def self.e(k: 1, **kw); end
def self.f(a=1, k: 1); end
def self.g(a=1, **kw); end
def self.h(a=1, k: 1, **kw); end
(:a..:h).each do |meth|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
send(meth, *OVER_STACK_ARGV)
end
end
end
def test_call_send_iseq_large_array_splat_pass
def self.a(*a); a.length end
assert_equal OVER_STACK_LEN, send(:a, *OVER_STACK_ARGV)
def self.b(_, *a); a.length end
assert_equal (OVER_STACK_LEN - 1), send(:b, *OVER_STACK_ARGV)
def self.c(_, *a, _); a.length end
assert_equal (OVER_STACK_LEN - 2), send(:c, *OVER_STACK_ARGV)
def self.d(b=1, *a); a.length end
assert_equal (OVER_STACK_LEN - 1), send(:d, *OVER_STACK_ARGV)
def self.e(b=1, *a, _); a.length end
assert_equal (OVER_STACK_LEN - 2), send(:e, *OVER_STACK_ARGV)
def self.f(b, *a); a.length end
assert_equal (OVER_STACK_LEN - 1), send(:f, *OVER_STACK_ARGV)
def self.g(*a, k: 1); a.length end
assert_equal OVER_STACK_LEN, send(:g, *OVER_STACK_ARGV)
def self.h(*a, **kw); a.length end
assert_equal OVER_STACK_LEN, send(:h, *OVER_STACK_ARGV)
def self.i(*a, k: 1, **kw); a.length end
assert_equal OVER_STACK_LEN, send(:i, *OVER_STACK_ARGV)
def self.j(b=1, *a, k: 1); a.length end
assert_equal (OVER_STACK_LEN - 1), send(:j, *OVER_STACK_ARGV)
def self.k(b=1, *a, **kw); a.length end
assert_equal (OVER_STACK_LEN - 1), send(:k, *OVER_STACK_ARGV)
def self.l(b=1, *a, k: 1, **kw); a.length end
assert_equal (OVER_STACK_LEN - 1), send(:l, *OVER_STACK_ARGV)
def self.m(b=1, *a, _, k: 1); a.length end
assert_equal (OVER_STACK_LEN - 2), send(:m, *OVER_STACK_ARGV)
def self.n(b=1, *a, _, **kw); a.length end
assert_equal (OVER_STACK_LEN - 2), send(:n, *OVER_STACK_ARGV)
def self.o(b=1, *a, _, k: 1, **kw); a.length end
assert_equal (OVER_STACK_LEN - 2), send(:o, *OVER_STACK_ARGV)
end
def test_call_send_iseq_large_array_splat_with_large_number_of_parameters
args = OVER_STACK_ARGV.map{|i| "a#{i}"}.join(',')
args1 = (OVER_STACK_LEN-1).times.map{|i| "a#{i}"}.join(',')
singleton_class.class_eval("def a(#{args}); [#{args}] end")
assert_equal OVER_STACK_ARGV, send(:a, *OVER_STACK_ARGV)
singleton_class.class_eval("def b(#{args}, b=0); [#{args}, b] end")
assert_equal(OVER_STACK_ARGV + [0], send(:b, *OVER_STACK_ARGV))
singleton_class.class_eval("def c(#{args}, *b); [#{args}, b] end")
assert_equal(OVER_STACK_ARGV + [[]], send(:c, *OVER_STACK_ARGV))
singleton_class.class_eval("def d(#{args1}, *b); [#{args1}, b] end")
assert_equal(OVER_STACK_ARGV[0...-1] + [[OVER_STACK_ARGV.last]], send(:d, *OVER_STACK_ARGV))
end if OVER_STACK_LEN < 200
def test_call_send_cfunc_large_array_splat_fail
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
send(:object_id, *OVER_STACK_ARGV)
end
end
def test_call_send_cfunc_large_array_splat_pass
assert_equal OVER_STACK_LEN, [].send(:push, *OVER_STACK_ARGV).length
end
def test_call_attr_reader_large_array_splat_fail
singleton_class.send(:attr_reader, :a)
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
a(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
send(:a, *OVER_STACK_ARGV)
end
end
def test_call_attr_writer_large_array_splat_fail
singleton_class.send(:attr_writer, :a)
singleton_class.send(:alias_method, :a, :a=)
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 1)") do
a(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 1)") do
send(:a, *OVER_STACK_ARGV)
end
end
def test_call_struct_aref_large_array_splat_fail
s = Struct.new(:a).new
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
s.a(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
s.send(:a, *OVER_STACK_ARGV)
end
end
def test_call_struct_aset_large_array_splat_fail
s = Struct.new(:a) do
alias b a=
end.new
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 1)") do
s.b(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 1)") do
s.send(:b, *OVER_STACK_ARGV)
end
end
def test_call_alias_large_array_splat
c = Class.new do
def a; end
def c(*a); a.length end
attr_accessor :e
end
sc = Class.new(c) do
alias b a
alias d c
alias f e
alias g e=
end
obj = sc.new
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
obj.b(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
obj.f(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 1)") do
obj.g(*OVER_STACK_ARGV)
end
assert_equal OVER_STACK_LEN, obj.d(*OVER_STACK_ARGV)
end
def test_call_zsuper_large_array_splat
c = Class.new do
private
def a; end
def c(*a); a.length end
attr_reader :e
end
sc = Class.new(c) do
public :a
public :c
public :e
end
obj = sc.new
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
obj.a(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
obj.e(*OVER_STACK_ARGV)
end
assert_equal OVER_STACK_LEN, obj.c(*OVER_STACK_ARGV)
end
class RefinedModuleLargeArrayTest
c = self
using(Module.new do
refine c do
def a; end
def c(*a) a.length end
attr_reader :e
end
end)
def b
a(*OVER_STACK_ARGV)
end
def d
c(*OVER_STACK_ARGV)
end
def f
e(*OVER_STACK_ARGV)
end
end
def test_call_refined_large_array_splat_fail
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
RefinedModuleLargeArrayTest.new.b
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN}, expected 0)") do
RefinedModuleLargeArrayTest.new.f
end
end
def test_call_refined_large_array_splat_pass
assert_equal OVER_STACK_LEN, RefinedModuleLargeArrayTest.new.d
end
def test_call_method_missing_iseq_large_array_splat_fail
def self.method_missing(_) end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN+1}, expected 1)") do
nonexistent_method(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN+1}, expected 1)") do
send(:nonexistent_method, *OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN+1}, expected 1)") do
send("nonexistent_method123", *OVER_STACK_ARGV)
end
end
def test_call_method_missing_iseq_large_array_splat_pass
def self.method_missing(m, *a)
a.length
end
assert_equal OVER_STACK_LEN, nonexistent_method(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, send(:nonexistent_method, *OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, send("nonexistent_method123", *OVER_STACK_ARGV)
end
def test_call_bmethod_large_array_splat_fail
define_singleton_method(:a){}
define_singleton_method(:b){|a=1|}
define_singleton_method(:c){|k: 1|}
define_singleton_method(:d){|**kw|}
define_singleton_method(:e){|k: 1, **kw|}
define_singleton_method(:f){|a=1, k: 1|}
define_singleton_method(:g){|a=1, **kw|}
define_singleton_method(:h){|a=1, k: 1, **kw|}
(:a..:h).each do |meth|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
instance_eval("#{meth}(*OVER_STACK_ARGV)", __FILE__, __LINE__)
end
end
end
def test_call_bmethod_large_array_splat_pass
define_singleton_method(:a){|*a| a.length}
assert_equal OVER_STACK_LEN, a(*OVER_STACK_ARGV)
define_singleton_method(:b){|_, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), b(*OVER_STACK_ARGV)
define_singleton_method(:c){|_, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 2), c(*OVER_STACK_ARGV)
define_singleton_method(:d){|b=1, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), d(*OVER_STACK_ARGV)
define_singleton_method(:e){|b=1, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 2), e(*OVER_STACK_ARGV)
define_singleton_method(:f){|b, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), f(*OVER_STACK_ARGV)
define_singleton_method(:g){|*a, k: 1| a.length}
assert_equal OVER_STACK_LEN, g(*OVER_STACK_ARGV)
define_singleton_method(:h){|*a, **kw| a.length}
assert_equal OVER_STACK_LEN, h(*OVER_STACK_ARGV)
define_singleton_method(:i){|*a, k: 1, **kw| a.length}
assert_equal OVER_STACK_LEN, i(*OVER_STACK_ARGV)
define_singleton_method(:j){|b=1, *a, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 1), j(*OVER_STACK_ARGV)
define_singleton_method(:k){|b=1, *a, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), k(*OVER_STACK_ARGV)
define_singleton_method(:l){|b=1, *a, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), l(*OVER_STACK_ARGV)
define_singleton_method(:m){|b=1, *a, _, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 2), m(*OVER_STACK_ARGV)
define_singleton_method(:n){|b=1, *a, _, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), n(*OVER_STACK_ARGV)
define_singleton_method(:o){|b=1, *a, _, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), o(*OVER_STACK_ARGV)
end
def test_call_method_missing_bmethod_large_array_splat_fail
define_singleton_method(:method_missing){|_|}
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN+1}, expected 1)") do
nonexistent_method(*OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN+1}, expected 1)") do
send(:nonexistent_method, *OVER_STACK_ARGV)
end
assert_raise_with_message(ArgumentError, "wrong number of arguments (given #{OVER_STACK_LEN+1}, expected 1)") do
send("nonexistent_method123", *OVER_STACK_ARGV)
end
end
def test_call_method_missing_bmethod_large_array_splat_pass
define_singleton_method(:method_missing){|_, *a| a.length}
assert_equal OVER_STACK_LEN, nonexistent_method(*OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, send(:nonexistent_method, *OVER_STACK_ARGV)
assert_equal OVER_STACK_LEN, send("nonexistent_method123", *OVER_STACK_ARGV)
end
def test_call_symproc_large_array_splat_fail
define_singleton_method(:a){}
define_singleton_method(:b){|a=1|}
define_singleton_method(:c){|k: 1|}
define_singleton_method(:d){|**kw|}
define_singleton_method(:e){|k: 1, **kw|}
define_singleton_method(:f){|a=1, k: 1|}
define_singleton_method(:g){|a=1, **kw|}
define_singleton_method(:h){|a=1, k: 1, **kw|}
(:a..:h).each do |meth|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
instance_eval(":#{meth}.to_proc.(self, *OVER_STACK_ARGV)", __FILE__, __LINE__)
end
end
end
def test_call_symproc_large_array_splat_pass
define_singleton_method(:a){|*a| a.length}
assert_equal OVER_STACK_LEN, :a.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:b){|_, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), :b.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:c){|_, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 2), :c.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:d){|b=1, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), :d.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:e){|b=1, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 2), :e.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:f){|b, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), :f.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:g){|*a, k: 1| a.length}
assert_equal OVER_STACK_LEN, :g.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:h){|*a, **kw| a.length}
assert_equal OVER_STACK_LEN, :h.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:i){|*a, k: 1, **kw| a.length}
assert_equal OVER_STACK_LEN, :i.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:j){|b=1, *a, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 1), :j.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:k){|b=1, *a, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), :k.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:l){|b=1, *a, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), :l.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:m){|b=1, *a, _, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 2), :m.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:n){|b=1, *a, _, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), :n.to_proc.(self, *OVER_STACK_ARGV)
define_singleton_method(:o){|b=1, *a, _, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), :o.to_proc.(self, *OVER_STACK_ARGV)
end
def test_call_rb_call_iseq_large_array_splat_fail
extend Bug::Iter::Yield
l = ->(*a){}
def self.a; end
def self.b(a=1) end
def self.c(k: 1) end
def self.d(**kw) end
def self.e(k: 1, **kw) end
def self.f(a=1, k: 1) end
def self.g(a=1, **kw) end
def self.h(a=1, k: 1, **kw) end
(:a..:h).each do |meth|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
yield_block(meth, *OVER_STACK_ARGV, &l)
end
end
end
def test_call_rb_call_iseq_large_array_splat_pass
extend Bug::Iter::Yield
l = ->(*a){a.length}
def self.a(*a) a.length end
assert_equal OVER_STACK_LEN, yield_block(:a, *OVER_STACK_ARGV, &l)
def self.b(_, *a) a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:b, *OVER_STACK_ARGV, &l)
def self.c(_, *a, _) a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:c, *OVER_STACK_ARGV, &l)
def self.d(b=1, *a) a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:d, *OVER_STACK_ARGV, &l)
def self.e(b=1, *a, _) a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:e, *OVER_STACK_ARGV, &l)
def self.f(b, *a) a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:f, *OVER_STACK_ARGV, &l)
def self.g(*a, k: 1) a.length end
assert_equal OVER_STACK_LEN, yield_block(:g, *OVER_STACK_ARGV, &l)
def self.h(*a, **kw) a.length end
assert_equal OVER_STACK_LEN, yield_block(:h, *OVER_STACK_ARGV, &l)
def self.i(*a, k: 1, **kw) a.length end
assert_equal OVER_STACK_LEN, yield_block(:h, *OVER_STACK_ARGV, &l)
def self.j(b=1, *a, k: 1) a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:j, *OVER_STACK_ARGV, &l)
def self.k(b=1, *a, **kw) a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:k, *OVER_STACK_ARGV, &l)
def self.l(b=1, *a, k: 1, **kw) a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:l, *OVER_STACK_ARGV, &l)
def self.m(b=1, *a, _, k: 1) a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:m, *OVER_STACK_ARGV, &l)
def self.n(b=1, *a, _, **kw) a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:n, *OVER_STACK_ARGV, &l)
def self.o(b=1, *a, _, k: 1, **kw) a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:o, *OVER_STACK_ARGV, &l)
end
def test_call_rb_call_bmethod_large_array_splat_fail
extend Bug::Iter::Yield
l = ->(*a){}
define_singleton_method(:a){||}
define_singleton_method(:b){|a=1|}
define_singleton_method(:c){|k: 1|}
define_singleton_method(:d){|**kw|}
define_singleton_method(:e){|k: 1, **kw|}
define_singleton_method(:f){|a=1, k: 1|}
define_singleton_method(:g){|a=1, **kw|}
define_singleton_method(:h){|a=1, k: 1, **kw|}
(:a..:h).each do |meth|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
yield_block(meth, *OVER_STACK_ARGV, &l)
end
end
end
def test_call_rb_call_bmethod_large_array_splat_pass
extend Bug::Iter::Yield
l = ->(*a){a.length}
define_singleton_method(:a){|*a| a.length}
assert_equal OVER_STACK_LEN, yield_block(:a, *OVER_STACK_ARGV, &l)
define_singleton_method(:b){|_, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), yield_block(:b, *OVER_STACK_ARGV, &l)
define_singleton_method(:c){|_, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 2), yield_block(:c, *OVER_STACK_ARGV, &l)
define_singleton_method(:d){|b=1, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), yield_block(:d, *OVER_STACK_ARGV, &l)
define_singleton_method(:e){|b=1, *a, _| a.length}
assert_equal (OVER_STACK_LEN - 2), yield_block(:e, *OVER_STACK_ARGV, &l)
define_singleton_method(:f){|b, *a| a.length}
assert_equal (OVER_STACK_LEN - 1), yield_block(:f, *OVER_STACK_ARGV, &l)
define_singleton_method(:g){|*a, k: 1| a.length}
assert_equal OVER_STACK_LEN, yield_block(:g, *OVER_STACK_ARGV, &l)
define_singleton_method(:h){|*a, **kw| a.length}
assert_equal OVER_STACK_LEN, yield_block(:h, *OVER_STACK_ARGV, &l)
define_singleton_method(:i){|*a, k: 1, **kw| a.length}
assert_equal OVER_STACK_LEN, yield_block(:h, *OVER_STACK_ARGV, &l)
define_singleton_method(:j){|b=1, *a, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 1), yield_block(:j, *OVER_STACK_ARGV, &l)
define_singleton_method(:k){|b=1, *a, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), yield_block(:k, *OVER_STACK_ARGV, &l)
define_singleton_method(:l){|b=1, *a, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 1), yield_block(:l, *OVER_STACK_ARGV, &l)
define_singleton_method(:m){|b=1, *a, _, k: 1| a.length}
assert_equal (OVER_STACK_LEN - 2), yield_block(:m, *OVER_STACK_ARGV, &l)
define_singleton_method(:n){|b=1, *a, _, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), yield_block(:n, *OVER_STACK_ARGV, &l)
define_singleton_method(:o){|b=1, *a, _, k: 1, **kw| a.length}
assert_equal (OVER_STACK_LEN - 2), yield_block(:o, *OVER_STACK_ARGV, &l)
end
def test_call_ifunc_iseq_large_array_splat_fail
extend Bug::Iter::Yield
def self.a(*a)
yield(*a)
end
[
->(){},
->(a=1){},
->(k: 1){},
->(**kw){},
->(k: 1, **kw){},
->(a=1, k: 1){},
->(a=1, **kw){},
->(a=1, k: 1, **kw){},
].each do |l|
assert_raise_with_message(ArgumentError, /wrong number of arguments \(given #{OVER_STACK_LEN}, expected 0(\.\.[12])?\)/) do
yield_block(:a, *OVER_STACK_ARGV, &l)
end
end
end
def test_call_ifunc_iseq_large_array_splat_pass
extend Bug::Iter::Yield
def self.a(*a)
yield(*a)
end
l = ->(*a) do a.length end
assert_equal OVER_STACK_LEN, yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(_, *a) do a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(_, *a, _) do a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a) do a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a, _) do a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b, *a) do a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(*a, k: 1) do a.length end
assert_equal OVER_STACK_LEN, yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(*a, **kw) do a.length end
assert_equal OVER_STACK_LEN, yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(*a, k: 1, **kw) do a.length end
assert_equal OVER_STACK_LEN, yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a, k: 1) do a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a, **kw) do a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a, k: 1, **kw) do a.length end
assert_equal (OVER_STACK_LEN - 1), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a, _, k: 1) do a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a, _, **kw) do a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:a, *OVER_STACK_ARGV, &l)
l = ->(b=1, *a, _, k: 1, **kw) do a.length end
assert_equal (OVER_STACK_LEN - 2), yield_block(:a, *OVER_STACK_ARGV, &l)
end
end