Add array/hash implicit allocation tests

These are designed to prevent allocation regressions (commits that
increase the number of implicitly allocated arrays and hashes). We
have already had three commits in the last couple weeks to fix
allocation regressions:

* 15dc3aaa311b32203d8ffb414bcf9b8e55ce5691
* aceee71c35e0b387691836e756b4e008efd84cf1
* c38878494377c94f2425a81e598260ea944ef7f3

This test suite should hopefully allow us to find such regressions
in CI before commit, to avoid committing future allocation regressions.

This uses assert_separately around each set of tests.  Doing it for
each individual check was too slow.  Failures are gathered and
reported at the end of the the suite as a single assertion, with
the message describing all failures.
This commit is contained in:
Jeremy Evans 2024-03-22 13:53:35 -07:00
parent aa794cc5a2
commit e4d6479730

View File

@ -0,0 +1,656 @@
# frozen_string_literal: false
require 'test/unit'
class TestAllocation < Test::Unit::TestCase
def check_allocations(checks)
assert_separately([], <<~RUBY)
$allocations = [0, 0]
$counts = {}
failures = []
def self.num_allocations
ObjectSpace.count_objects($counts)
arrays = $counts[:T_ARRAY]
hashes = $counts[:T_HASH]
yield
ObjectSpace.count_objects($counts)
arrays -= $counts[:T_ARRAY]
hashes -= $counts[:T_HASH]
$allocations[0] = -arrays
$allocations[1] = -hashes
end
define_singleton_method(:check_allocations) do |num_arrays, num_hashes, check_code|
instance_eval <<~RB
empty_array = empty_array = []
empty_hash = empty_hash = {}
array1 = array1 = [1]
hash1 = hash1 = {a: 2}
nill = nill = nil
block = block = lambda{}
num_allocations do
\#{check_code}
end
RB
if num_arrays != $allocations[0]
failures << "Expected \#{num_arrays} array allocations for \#{check_code.inspect}, but \#{$allocations[0]} arrays allocated"
end
if num_hashes != $allocations[1]
failures << "Expected \#{num_hashes} hash allocations for \#{check_code.inspect}, but \#{$allocations[1]} hashes allocated"
end
end
GC.start
GC.disable
#{checks}
unless failures.empty?
assert_equal(true, false, failures.join("\n"))
end
RUBY
end
class Literal < self
def test_array_literal
check_allocations(<<~RUBY)
check_allocations(1, 0, "[]")
check_allocations(1, 0, "[1]")
check_allocations(1, 0, "[*empty_array]")
check_allocations(1, 0, "[*empty_array, 1, *empty_array]")
check_allocations(1, 0, "[*empty_array, *empty_array]")
check_allocations(1, 0, "[#{'1,'*100000}]")
RUBY
end
def test_hash_literal
check_allocations(<<~RUBY)
check_allocations(0, 1, "{}")
check_allocations(0, 1, "{a: 1}")
check_allocations(0, 1, "{**empty_hash}")
check_allocations(0, 1, "{**empty_hash, a: 1, **empty_hash}")
check_allocations(0, 1, "{**empty_hash, **empty_hash}")
check_allocations(0, 1, "{#{100000.times.map{|i| "a#{i}: 1"}.join(',')}}")
RUBY
end
end
class MethodCall < self
def block
''
end
def test_no_parameters
only_block = block.empty? ? block : block[2..]
check_allocations(<<~RUBY)
def self.none(#{only_block}); end
check_allocations(0, 0, "none(#{only_block})")
check_allocations(0, 0, "none(*empty_array#{block})")
check_allocations(0, 0, "none(**empty_hash#{block})")
check_allocations(0, 0, "none(*empty_array, **empty_hash#{block})")
check_allocations(1, 0, "none(*empty_array, *empty_array#{block})")
check_allocations(0, 1, "none(**empty_hash, **empty_hash#{block})")
check_allocations(1, 1, "none(*empty_array, *empty_array, **empty_hash, **empty_hash#{block})")
RUBY
end
def test_required_parameter
check_allocations(<<~RUBY)
def self.required(x#{block}); end
check_allocations(0, 0, "required(1#{block})")
check_allocations(0, 0, "required(1, *empty_array#{block})")
check_allocations(0, 0, "required(1, **empty_hash#{block})")
check_allocations(0, 0, "required(1, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "required(*array1#{block})")
check_allocations(0, 1, "required(**hash1#{block})")
check_allocations(1, 0, "required(*array1, *empty_array#{block})")
check_allocations(0, 1, "required(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "required(*array1, *empty_array, **empty_hash#{block})")
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_optional_parameter
check_allocations(<<~RUBY)
def self.optional(x=nil#{block}); end
check_allocations(0, 0, "optional(1#{block})")
check_allocations(0, 0, "optional(1, *empty_array#{block})")
check_allocations(0, 0, "optional(1, **empty_hash#{block})")
check_allocations(0, 0, "optional(1, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "optional(*array1#{block})")
check_allocations(0, 1, "optional(**hash1#{block})")
check_allocations(1, 0, "optional(*array1, *empty_array#{block})")
check_allocations(0, 1, "optional(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "optional(*array1, *empty_array, **empty_hash#{block})")
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "optional(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_positional_splat_parameter
check_allocations(<<~RUBY)
def self.splat(*x#{block}); end
check_allocations(1, 0, "splat(1#{block})")
check_allocations(1, 0, "splat(1, *empty_array#{block})")
check_allocations(1, 0, "splat(1, **empty_hash#{block})")
check_allocations(1, 0, "splat(1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "splat(*array1#{block})")
check_allocations(1, 0, "splat(*array1, *empty_array#{block})")
check_allocations(1, 0, "splat(*array1, **empty_hash#{block})")
check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "splat(1, *array1#{block})")
check_allocations(1, 0, "splat(1, *array1, *empty_array#{block})")
check_allocations(1, 0, "splat(1, *array1, **empty_hash#{block})")
check_allocations(1, 0, "splat(1, *array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "splat(*array1#{block})")
check_allocations(1, 1, "splat(**hash1#{block})")
check_allocations(1, 0, "splat(*array1, *empty_array#{block})")
check_allocations(1, 1, "splat(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_required_and_positional_splat_parameters
check_allocations(<<~RUBY)
def self.req_splat(x, *y#{block}); end
check_allocations(1, 0, "req_splat(1#{block})")
check_allocations(1, 0, "req_splat(1, *empty_array#{block})")
check_allocations(1, 0, "req_splat(1, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(*array1#{block})")
check_allocations(1, 0, "req_splat(*array1, *empty_array#{block})")
check_allocations(1, 0, "req_splat(*array1, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(1, *array1#{block})")
check_allocations(1, 0, "req_splat(1, *array1, *empty_array#{block})")
check_allocations(1, 0, "req_splat(1, *array1, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(1, *array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(*array1#{block})")
check_allocations(1, 1, "req_splat(**hash1#{block})")
check_allocations(1, 0, "req_splat(*array1, *empty_array#{block})")
check_allocations(1, 1, "req_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "req_splat(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_positional_splat_and_post_parameters
check_allocations(<<~RUBY)
def self.splat_post(*x, y#{block}); end
check_allocations(1, 0, "splat_post(1#{block})")
check_allocations(1, 0, "splat_post(1, *empty_array#{block})")
check_allocations(1, 0, "splat_post(1, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(*array1#{block})")
check_allocations(1, 0, "splat_post(*array1, *empty_array#{block})")
check_allocations(1, 0, "splat_post(*array1, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(1, *array1#{block})")
check_allocations(1, 0, "splat_post(1, *array1, *empty_array#{block})")
check_allocations(1, 0, "splat_post(1, *array1, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(1, *array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(*array1#{block})")
check_allocations(1, 1, "splat_post(**hash1#{block})")
check_allocations(1, 0, "splat_post(*array1, *empty_array#{block})")
check_allocations(1, 1, "splat_post(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat_post(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_keyword_parameter
check_allocations(<<~RUBY)
def self.keyword(a: nil#{block}); end
check_allocations(0, 0, "keyword(a: 2#{block})")
check_allocations(0, 0, "keyword(*empty_array, a: 2#{block})")
check_allocations(0, 1, "keyword(a:2, **empty_hash#{block})")
check_allocations(0, 1, "keyword(**empty_hash, a: 2#{block})")
check_allocations(0, 0, "keyword(**nil#{block})")
check_allocations(0, 0, "keyword(**empty_hash#{block})")
check_allocations(0, 0, "keyword(**hash1#{block})")
check_allocations(0, 0, "keyword(*empty_array, **hash1#{block})")
check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
check_allocations(0, 1, "keyword(**empty_hash, **hash1#{block})")
check_allocations(0, 0, "keyword(*empty_array#{block})")
check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "keyword(*empty_array#{block})")
check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})")
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_keyword_splat_parameter
check_allocations(<<~RUBY)
def self.keyword_splat(**kw#{block}); end
check_allocations(0, 1, "keyword_splat(a: 2#{block})")
check_allocations(0, 1, "keyword_splat(*empty_array, a: 2#{block})")
check_allocations(0, 1, "keyword_splat(a:2, **empty_hash#{block})")
check_allocations(0, 1, "keyword_splat(**empty_hash, a: 2#{block})")
check_allocations(0, 1, "keyword_splat(**nil#{block})")
check_allocations(0, 1, "keyword_splat(**empty_hash#{block})")
check_allocations(0, 1, "keyword_splat(**hash1#{block})")
check_allocations(0, 1, "keyword_splat(*empty_array, **hash1#{block})")
check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(0, 1, "keyword_splat(**empty_hash, **hash1#{block})")
check_allocations(0, 1, "keyword_splat(*empty_array#{block})")
check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 1, "keyword_splat(*empty_array#{block})")
check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_keyword_and_keyword_splat_parameter
check_allocations(<<~RUBY)
def self.keyword_and_keyword_splat(a: 1, **kw#{block}); end
check_allocations(0, 1, "keyword_and_keyword_splat(a: 2#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, a: 2#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(a:2, **empty_hash#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**empty_hash, a: 2#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**nil#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**empty_hash#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**hash1#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, **hash1#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**empty_hash, **hash1#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array#{block})")
check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
RUBY
end
def test_required_positional_and_keyword_parameter
check_allocations(<<~RUBY)
def self.required_and_keyword(b, a: nil#{block}); end
check_allocations(0, 0, "required_and_keyword(1, a: 2#{block})")
check_allocations(0, 0, "required_and_keyword(1, *empty_array, a: 2#{block})")
check_allocations(0, 1, "required_and_keyword(1, a:2, **empty_hash#{block})")
check_allocations(0, 1, "required_and_keyword(1, **empty_hash, a: 2#{block})")
check_allocations(0, 0, "required_and_keyword(1, **nil#{block})")
check_allocations(0, 0, "required_and_keyword(1, **empty_hash#{block})")
check_allocations(0, 0, "required_and_keyword(1, **hash1#{block})")
check_allocations(0, 0, "required_and_keyword(1, *empty_array, **hash1#{block})")
check_allocations(0, 1, "required_and_keyword(1, **hash1, **empty_hash#{block})")
check_allocations(0, 1, "required_and_keyword(1, **empty_hash, **hash1#{block})")
check_allocations(0, 0, "required_and_keyword(1, *empty_array#{block})")
check_allocations(0, 1, "required_and_keyword(1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "required_and_keyword(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "required_and_keyword(*array1, a: 2#{block})")
check_allocations(0, 0, "required_and_keyword(*array1, **nill#{block})")
check_allocations(0, 0, "required_and_keyword(*array1, **empty_hash#{block})")
check_allocations(0, 0, "required_and_keyword(*array1, **hash1#{block})")
check_allocations(1, 0, "required_and_keyword(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 0, "required_and_keyword(*array1, *empty_array#{block})")
check_allocations(1, 0, "required_and_keyword(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})")
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "required_and_keyword(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "required_and_keyword(*array1, **nil#{block})")
RUBY
end
def test_positional_splat_and_keyword_parameter
check_allocations(<<~RUBY)
def self.splat_and_keyword(*b, a: nil#{block}); end
check_allocations(1, 0, "splat_and_keyword(1, a: 2#{block})")
check_allocations(1, 0, "splat_and_keyword(1, *empty_array, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword(1, a:2, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword(1, **empty_hash, a: 2#{block})")
check_allocations(1, 0, "splat_and_keyword(1, **nil#{block})")
check_allocations(1, 0, "splat_and_keyword(1, **empty_hash#{block})")
check_allocations(1, 0, "splat_and_keyword(1, **hash1#{block})")
check_allocations(1, 0, "splat_and_keyword(1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "splat_and_keyword(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword(1, **empty_hash, **hash1#{block})")
check_allocations(1, 0, "splat_and_keyword(1, *empty_array#{block})")
check_allocations(1, 1, "splat_and_keyword(1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat_and_keyword(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, a: 2#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, **nill#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, **empty_hash#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, **hash1#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, *empty_array#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, **nil#{block})")
RUBY
end
def test_required_and_keyword_splat_parameter
check_allocations(<<~RUBY)
def self.required_and_keyword_splat(b, **kw#{block}); end
check_allocations(0, 1, "required_and_keyword_splat(1, a: 2#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array, a: 2#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, a:2, **empty_hash#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, **empty_hash, a: 2#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, **nil#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, **empty_hash#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, **hash1#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array, **hash1#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, **empty_hash, **hash1#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array#{block})")
check_allocations(0, 1, "required_and_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 1, "required_and_keyword_splat(*array1, a: 2#{block})")
check_allocations(0, 1, "required_and_keyword_splat(*array1, **nill#{block})")
check_allocations(0, 1, "required_and_keyword_splat(*array1, **empty_hash#{block})")
check_allocations(0, 1, "required_and_keyword_splat(*array1, **hash1#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, **nil#{block})")
RUBY
end
def test_positional_splat_and_keyword_splat_parameter
check_allocations(<<~RUBY)
def self.splat_and_keyword_splat(*b, **kw#{block}); end
check_allocations(1, 1, "splat_and_keyword_splat(1, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, a:2, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, **nil#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, **hash1#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, **empty_hash, **hash1#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **nill#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **hash1#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **nil#{block})")
RUBY
end
def test_anonymous_splat_and_anonymous_keyword_splat_parameters
check_allocations(<<~RUBY)
def self.anon_splat_and_anon_keyword_splat(*, **#{block}); end
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a: 2#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a:2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, a: 2#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **nil#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, a: 2#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nill#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
RUBY
end
def test_nested_anonymous_splat_and_anonymous_keyword_splat_parameters
check_allocations(<<~RUBY)
def self.anon_splat_and_anon_keyword_splat(*, **#{block}); t(*, **) end; def self.t(*, **#{block}); end
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a: 2#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, a:2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, a: 2#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **nil#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **empty_hash, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, a: 2#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nill#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash#{block})")
check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
RUBY
end
def test_argument_forwarding
check_allocations(<<~RUBY)
def self.argument_forwarding(...); end
check_allocations(1, 1, "argument_forwarding(1, a: 2#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(1, a:2, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, **empty_hash, a: 2#{block})")
check_allocations(1, 0, "argument_forwarding(1, **nil#{block})")
check_allocations(1, 0, "argument_forwarding(1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(1, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, **empty_hash, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array#{block})")
check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, a: 2#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, **nill#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, **empty_hash#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, *empty_array#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
RUBY
end
def test_nested_argument_forwarding
check_allocations(<<~RUBY)
def self.argument_forwarding(...); t(...) end; def self.t(...) end
check_allocations(1, 1, "argument_forwarding(1, a: 2#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(1, a:2, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, **empty_hash, a: 2#{block})")
check_allocations(1, 0, "argument_forwarding(1, **nil#{block})")
check_allocations(1, 0, "argument_forwarding(1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(1, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, **empty_hash, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array#{block})")
check_allocations(1, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(1, *empty_array, *empty_array, **empty_hash#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, a: 2#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, **nill#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, **empty_hash#{block})")
check_allocations(0, 0, "argument_forwarding(*array1, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **hash1#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, *empty_array#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
RUBY
end
class WithBlock < self
def block
', &block'
end
end
end
end