diff options
Diffstat (limited to 'test/ruby/test_allocation.rb')
| -rw-r--r-- | test/ruby/test_allocation.rb | 440 |
1 files changed, 310 insertions, 130 deletions
diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb index fbe0548899..90d7c04f9b 100644 --- a/test/ruby/test_allocation.rb +++ b/test/ruby/test_allocation.rb @@ -2,7 +2,22 @@ require 'test/unit' class TestAllocation < Test::Unit::TestCase + def setup + # The namespace changes on i686 platform triggers a bug to allocate objects unexpectedly. + # For now, skip these tests only on i686 + pend if RUBY_PLATFORM =~ /^i686/ + end + + def munge_checks(checks) + checks + end + def check_allocations(checks) + dups = checks.split("\n").reject(&:empty?).tally.select{|_,v| v > 1} + raise "duplicate checks:\n#{dups.keys.join("\n")}" unless dups.empty? + + checks = munge_checks(checks) + assert_separately([], <<~RUBY) $allocations = [0, 0] $counts = {} @@ -51,9 +66,7 @@ class TestAllocation < Test::Unit::TestCase #{checks} - unless failures.empty? - assert_equal(true, false, failures.join("\n")) - end + assert_empty(failures) RUBY end @@ -85,13 +98,14 @@ class TestAllocation < Test::Unit::TestCase def block '' end + alias only_block block 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(*nil#{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})") @@ -123,8 +137,7 @@ class TestAllocation < Test::Unit::TestCase check_allocations(0, 0, "required(*r2k_empty_array1#{block})") check_allocations(0, 1, "required(*r2k_array#{block})") - # Currently allocates 1 array unnecessarily due to splatarray true - check_allocations(1, 1, "required(*empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "required(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -148,8 +161,10 @@ class TestAllocation < Test::Unit::TestCase check_allocations(0, 0, "optional(*r2k_empty_array1#{block})") check_allocations(0, 1, "optional(*r2k_array#{block})") - # Currently allocates 1 array unnecessarily due to splatarray true - check_allocations(1, 1, "optional(*empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "optional(*empty_array#{block})") + check_allocations(0, 0, "optional(*nil#{block})") + check_allocations(0, 0, "optional(#{only_block})") + check_allocations(0, 1, "optional(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -172,12 +187,11 @@ class TestAllocation < Test::Unit::TestCase 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, 0, "splat(*nil#{block})") + check_allocations(1, 0, "splat(#{only_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})") check_allocations(1, 0, "splat(*r2k_empty_array#{block})") @@ -192,6 +206,7 @@ class TestAllocation < Test::Unit::TestCase def self.req_splat(x, *y#{block}); end check_allocations(1, 0, "req_splat(1#{block})") + check_allocations(1, 0, "req_splat(1, *nil#{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})") @@ -206,12 +221,9 @@ class TestAllocation < Test::Unit::TestCase 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})") check_allocations(1, 0, "req_splat(*r2k_empty_array1#{block})") @@ -225,6 +237,7 @@ class TestAllocation < Test::Unit::TestCase def self.splat_post(*x, y#{block}); end check_allocations(1, 0, "splat_post(1#{block})") + check_allocations(1, 0, "splat_post(1, *nil#{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})") @@ -239,12 +252,9 @@ class TestAllocation < Test::Unit::TestCase 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})") check_allocations(1, 0, "splat_post(*r2k_empty_array1#{block})") @@ -269,20 +279,15 @@ class TestAllocation < Test::Unit::TestCase check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})") check_allocations(0, 1, "keyword(**empty_hash, **hash1#{block})") + check_allocations(0, 0, "keyword(*nil#{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})") - check_allocations(1, 0, "keyword(*r2k_empty_array#{block})") - check_allocations(1, 1, "keyword(*r2k_array#{block})") + check_allocations(0, 0, "keyword(*r2k_empty_array#{block})") + check_allocations(0, 0, "keyword(*r2k_array#{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})") + check_allocations(0, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -302,20 +307,15 @@ class TestAllocation < Test::Unit::TestCase 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(*nil#{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})") - check_allocations(1, 1, "keyword_splat(*r2k_empty_array#{block})") - check_allocations(1, 1, "keyword_splat(*r2k_array#{block})") + check_allocations(0, 1, "keyword_splat(*r2k_empty_array#{block})") + check_allocations(0, 1, "keyword_splat(*r2k_array#{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})") + check_allocations(0, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -335,20 +335,15 @@ class TestAllocation < Test::Unit::TestCase 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(*nil#{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})") - check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_empty_array#{block})") - check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_array#{block})") + check_allocations(0, 1, "keyword_and_keyword_splat(*r2k_empty_array#{block})") + check_allocations(0, 1, "keyword_and_keyword_splat(*r2k_array#{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})") + check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})") RUBY end @@ -368,8 +363,8 @@ class TestAllocation < Test::Unit::TestCase 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, *nil#{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})") @@ -385,15 +380,14 @@ class TestAllocation < Test::Unit::TestCase 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})") - check_allocations(1, 0, "required_and_keyword(*r2k_empty_array1#{block})") - check_allocations(1, 1, "required_and_keyword(*r2k_array1#{block})") + check_allocations(0, 0, "required_and_keyword(*r2k_empty_array1#{block})") + check_allocations(0, 0, "required_and_keyword(*r2k_array1#{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})") + check_allocations(0, 1, "required_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "required_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "required_and_keyword(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "required_and_keyword(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "required_and_keyword(*array1, **nil#{block})") RUBY end @@ -413,8 +407,8 @@ class TestAllocation < Test::Unit::TestCase 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, *nil#{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})") @@ -437,9 +431,9 @@ class TestAllocation < Test::Unit::TestCase check_allocations(1, 0, "splat_and_keyword(*array1, **nil#{block})") check_allocations(1, 0, "splat_and_keyword(*r2k_empty_array#{block})") - check_allocations(1, 1, "splat_and_keyword(*r2k_array#{block})") + check_allocations(1, 0, "splat_and_keyword(*r2k_array#{block})") check_allocations(1, 0, "splat_and_keyword(*r2k_empty_array1#{block})") - check_allocations(1, 1, "splat_and_keyword(*r2k_array1#{block})") + check_allocations(1, 0, "splat_and_keyword(*r2k_array1#{block})") RUBY end @@ -459,8 +453,8 @@ class TestAllocation < Test::Unit::TestCase 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, *nil#{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})") @@ -476,15 +470,14 @@ class TestAllocation < Test::Unit::TestCase 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})") - check_allocations(1, 1, "required_and_keyword_splat(*r2k_empty_array1#{block})") - check_allocations(1, 1, "required_and_keyword_splat(*r2k_array1#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*r2k_empty_array1#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*r2k_array1#{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})") + check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "required_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "required_and_keyword_splat(*array1, **nil#{block})") RUBY end @@ -504,8 +497,8 @@ class TestAllocation < Test::Unit::TestCase 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, *nil#{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})") @@ -534,7 +527,61 @@ class TestAllocation < Test::Unit::TestCase RUBY end + def test_anonymous_splat_parameter + only_block = block.empty? ? block : block[2..] + check_allocations(<<~RUBY) + def self.anon_splat(*#{block}); end + + check_allocations(1, 1, "anon_splat(1, a: 2#{block})") + check_allocations(1, 1, "anon_splat(1, *empty_array, a: 2#{block})") + check_allocations(1, 1, "anon_splat(1, a:2, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, **empty_hash, a: 2#{block})") + + check_allocations(1, 0, "anon_splat(1, **nil#{block})") + check_allocations(1, 0, "anon_splat(1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, **hash1#{block})") + check_allocations(1, 1, "anon_splat(1, *empty_array, **hash1#{block})") + check_allocations(1, 1, "anon_splat(1, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, **empty_hash, **hash1#{block})") + + check_allocations(1, 0, "anon_splat(1, *empty_array#{block})") + check_allocations(1, 0, "anon_splat(1, *empty_array, *empty_array, **empty_hash#{block})") + + check_allocations(1, 1, "anon_splat(*array1, a: 2#{block})") + + check_allocations(0, 0, "anon_splat(*nil, **nill#{block})") + check_allocations(0, 0, "anon_splat(*array1, **nill#{block})") + check_allocations(0, 0, "anon_splat(*array1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(*array1, **hash1#{block})") + check_allocations(1, 1, "anon_splat(*array1, *empty_array, **hash1#{block})") + + check_allocations(1, 0, "anon_splat(*array1, *empty_array#{block})") + check_allocations(1, 0, "anon_splat(*array1, *empty_array, **empty_hash#{block})") + + check_allocations(1, 1, "anon_splat(*array1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(*array1, *empty_array, **hash1, **empty_hash#{block})") + + check_allocations(0, 0, "anon_splat(#{only_block})") + check_allocations(1, 1, "anon_splat(a: 2#{block})") + check_allocations(0, 0, "anon_splat(**empty_hash#{block})") + + check_allocations(1, 1, "anon_splat(1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(1, 1, "anon_splat(*array1, **hash1, **empty_hash#{block})") + + unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled? + check_allocations(0, 0, "anon_splat(*array1, **nil#{block})") + check_allocations(1, 0, "anon_splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "anon_splat(*r2k_array#{block})") + check_allocations(1, 0, "anon_splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "anon_splat(*r2k_array1#{block})") + end + RUBY + end + def test_anonymous_splat_and_anonymous_keyword_splat_parameters + only_block = block.empty? ? block : block[2..] check_allocations(<<~RUBY) def self.anon_splat_and_anon_keyword_splat(*, **#{block}); end @@ -551,11 +598,11 @@ class TestAllocation < Test::Unit::TestCase 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(*nil, **nill#{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})") @@ -567,11 +614,15 @@ class TestAllocation < Test::Unit::TestCase 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(0, 0, "anon_splat_and_anon_keyword_splat(#{only_block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(a: 2#{block})") + check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(**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})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})") @@ -581,8 +632,10 @@ class TestAllocation < Test::Unit::TestCase end def test_nested_anonymous_splat_and_anonymous_keyword_splat_parameters + only_block = block.empty? ? block : block[2..] check_allocations(<<~RUBY) - def self.anon_splat_and_anon_keyword_splat(*, **#{block}); t(*, **) end; def self.t(*, **#{block}); end + def self.t(*, **#{block}); end + def self.anon_splat_and_anon_keyword_splat(*, **#{block}); t(*, **) 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})") @@ -597,11 +650,11 @@ class TestAllocation < Test::Unit::TestCase 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(*nil, **nill#{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})") @@ -613,11 +666,15 @@ class TestAllocation < Test::Unit::TestCase 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(0, 0, "anon_splat_and_anon_keyword_splat(#{only_block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(a: 2#{block})") + check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(**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})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})") check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})") @@ -630,24 +687,25 @@ class TestAllocation < Test::Unit::TestCase 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(0, 0, "argument_forwarding(1, a: 2#{block})") + check_allocations(0, 0, "argument_forwarding(1, *empty_array, a: 2#{block})") + check_allocations(0, 1, "argument_forwarding(1, a:2, **empty_hash#{block})") + check_allocations(0, 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(0, 0, "argument_forwarding(1, **nil#{block})") + check_allocations(0, 0, "argument_forwarding(1, **empty_hash#{block})") + check_allocations(0, 0, "argument_forwarding(1, **hash1#{block})") + check_allocations(0, 0, "argument_forwarding(1, *empty_array, **hash1#{block})") + check_allocations(0, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})") + check_allocations(0, 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(0, 0, "argument_forwarding(1, *empty_array#{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(**nill#{block})") + check_allocations(0, 0, "argument_forwarding(*nil, **nill#{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})") @@ -659,41 +717,43 @@ class TestAllocation < Test::Unit::TestCase 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})") + check_allocations(0, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "argument_forwarding(*array1, **nil#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_empty_array#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_array#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_empty_array1#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_array1#{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})") + def self.t(...) end + def self.argument_forwarding(...); t(...) end + + check_allocations(0, 0, "argument_forwarding(1, a: 2#{block})") + check_allocations(0, 0, "argument_forwarding(1, *empty_array, a: 2#{block})") + check_allocations(0, 1, "argument_forwarding(1, a:2, **empty_hash#{block})") + check_allocations(0, 1, "argument_forwarding(1, **empty_hash, a: 2#{block})") + + check_allocations(0, 0, "argument_forwarding(1, **nil#{block})") + check_allocations(0, 0, "argument_forwarding(1, **empty_hash#{block})") + check_allocations(0, 0, "argument_forwarding(1, **hash1#{block})") + check_allocations(0, 0, "argument_forwarding(1, *empty_array, **hash1#{block})") + check_allocations(0, 1, "argument_forwarding(1, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "argument_forwarding(1, **empty_hash, **hash1#{block})") + + check_allocations(0, 0, "argument_forwarding(1, *empty_array#{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(**nill#{block})") + check_allocations(0, 0, "argument_forwarding(*nil, **nill#{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})") @@ -705,16 +765,16 @@ class TestAllocation < Test::Unit::TestCase 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})") + check_allocations(0, 1, "argument_forwarding(1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(0, 1, "argument_forwarding(1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})") + check_allocations(0, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})") + check_allocations(0, 0, "argument_forwarding(*array1, **nil#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})") - check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_empty_array#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_array#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_empty_array1#{block})") + check_allocations(0, 0, "argument_forwarding(*r2k_array1#{block})") RUBY end @@ -729,25 +789,26 @@ class TestAllocation < Test::Unit::TestCase check_allocations(1, 1, "r2k(1, **empty_hash, a: 2#{block})") check_allocations(1, 0, "r2k(1, **nil#{block})") - check_allocations(1, 1, "r2k(1, **empty_hash#{block})") + check_allocations(1, 0, "r2k(1, **empty_hash#{block})") check_allocations(1, 1, "r2k(1, **hash1#{block})") check_allocations(1, 1, "r2k(1, *empty_array, **hash1#{block})") check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})") check_allocations(1, 1, "r2k(1, **empty_hash, **hash1#{block})") check_allocations(1, 0, "r2k(1, *empty_array#{block})") - check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})") - check_allocations(1, 1, "r2k(1, *empty_array, *empty_array, **empty_hash#{block})") + check_allocations(1, 0, "r2k(1, *empty_array, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, a: 2#{block})") + check_allocations(1, 0, "r2k(**nill#{block})") + check_allocations(1, 0, "r2k(*nil, **nill#{block})") check_allocations(1, 0, "r2k(*array1, **nill#{block})") - check_allocations(1, 1, "r2k(*array1, **empty_hash#{block})") + check_allocations(1, 0, "r2k(*array1, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, **hash1#{block})") check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1#{block})") check_allocations(1, 0, "r2k(*array1, *empty_array#{block})") - check_allocations(1, 1, "r2k(*array1, *empty_array, **empty_hash#{block})") + check_allocations(1, 0, "r2k(*array1, *empty_array, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, *empty_array, a: 2, **empty_hash#{block})") check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1, **empty_hash#{block})") @@ -759,19 +820,138 @@ class TestAllocation < Test::Unit::TestCase check_allocations(1, 0, "r2k(*array1, **nil#{block})") check_allocations(1, 0, "r2k(*r2k_empty_array#{block})") - check_allocations(1, 1, "r2k(*r2k_array#{block})") unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled? # YJIT may or may not allocate depending on arch? + check_allocations(1, 1, "r2k(*r2k_array#{block})") check_allocations(1, 0, "r2k(*r2k_empty_array1#{block})") check_allocations(1, 1, "r2k(*r2k_array1#{block})") end RUBY end + def test_no_array_allocation_with_splat_and_nonstatic_keywords + check_allocations(<<~RUBY) + def self.keyword(a: nil, b: nil#{block}); end + def self.Object; Object end + + check_allocations(0, 1, "keyword(*nil, a: empty_array#{block})") # LVAR + check_allocations(0, 1, "keyword(*empty_array, a: empty_array#{block})") # LVAR + check_allocations(0, 1, "->{keyword(*empty_array, a: empty_array#{block})}.call") # DVAR + check_allocations(0, 1, "$x = empty_array; keyword(*empty_array, a: $x#{block})") # GVAR + check_allocations(0, 1, "@x = empty_array; keyword(*empty_array, a: @x#{block})") # IVAR + check_allocations(0, 1, "self.class.const_set(:X, empty_array); keyword(*empty_array, a: X#{block})") # CONST + check_allocations(0, 1, "keyword(*empty_array, a: Object::X#{block})") # COLON2 - safe + check_allocations(1, 1, "keyword(*empty_array, a: Object()::X#{block})") # COLON2 - unsafe + check_allocations(0, 1, "keyword(*empty_array, a: ::X#{block})") # COLON3 + check_allocations(0, 1, "T = self; #{'B = block' unless block.empty?}; class Object; @@x = X; T.keyword(*X, a: @@x#{', &B' unless block.empty?}) end") # CVAR + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: 1#{block})") # INTEGER + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: 1.0#{block})") # FLOAT + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: 1.0r#{block})") # RATIONAL + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: 1.0i#{block})") # IMAGINARY + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: 'a'#{block})") # STR + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: :b#{block})") # SYM + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: /a/#{block})") # REGX + check_allocations(0, 1, "keyword(*empty_array, a: self#{block})") # SELF + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: nil#{block})") # NIL + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: true#{block})") # TRUE + check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: false#{block})") # FALSE + check_allocations(0, 1, "keyword(*empty_array, a: ->{}#{block})") # LAMBDA + check_allocations(0, 1, "keyword(*empty_array, a: $1#{block})") # NTH_REF + check_allocations(0, 1, "keyword(*empty_array, a: $`#{block})") # BACK_REF + + # LIST: Only 1 array (literal [:c]), not 2 (one for [:c] and one for *empty_array) + check_allocations(1, 1, "keyword(*empty_array, a: empty_array, b: [:c]#{block})") + check_allocations(1, 1, "keyword(*empty_array, a: empty_array, b: [:c, $x]#{block})") + # LIST unsafe: 2 (one for [Object()] and one for *empty_array) + check_allocations(2, 1, "keyword(*empty_array, a: empty_array, b: [Object()]#{block})") + check_allocations(2, 1, "keyword(*empty_array, a: empty_array, b: [:c, $x, Object()]#{block})") + RUBY + end + + class WithBlock < self + def block + ', &block' + end + def only_block + '&block' + end + end + end + + class ProcCall < MethodCall + def munge_checks(checks) + return checks if @no_munge + sub = rep = nil + checks.split("\n").map do |line| + case line + when "singleton_class.send(:ruby2_keywords, :r2k)" + "r2k.ruby2_keywords" + when /\Adef self.([a-z0-9_]+)\((.*)\);(.*)end\z/ + sub = $1 + '(' + rep = $1 + '.(' + "#{$1} = #{$1} = proc{ |#{$2}| #{$3} }" + when /check_allocations/ + line.gsub(sub, rep) + else + line + end + end.join("\n") + end + + # Generic argument forwarding not supported in proc definitions + undef_method :test_argument_forwarding + undef_method :test_nested_argument_forwarding + + # Proc anonymous arguments cannot be used directly + undef_method :test_nested_anonymous_splat_and_anonymous_keyword_splat_parameters + + def test_no_array_allocation_with_splat_and_nonstatic_keywords + @no_munge = true + + check_allocations(<<~RUBY) + keyword = keyword = proc{ |a: nil, b: nil #{block}| } + def self.Object; Object end + + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array#{block})") # LVAR + check_allocations(0, 1, "->{keyword.(*empty_array, a: empty_array#{block})}.call") # DVAR + check_allocations(0, 1, "$x = empty_array; keyword.(*empty_array, a: $x#{block})") # GVAR + check_allocations(0, 1, "@x = empty_array; keyword.(*empty_array, a: @x#{block})") # IVAR + check_allocations(0, 1, "self.class.const_set(:X, empty_array); keyword.(*empty_array, a: X#{block})") # CONST + check_allocations(0, 1, "keyword.(*empty_array, a: Object::X#{block})") # COLON2 - safe + check_allocations(1, 1, "keyword.(*empty_array, a: Object()::X#{block})") # COLON2 - unsafe + check_allocations(0, 1, "keyword.(*empty_array, a: ::X#{block})") # COLON3 + check_allocations(0, 1, "T = keyword; #{'B = block' unless block.empty?}; class Object; @@x = X; T.(*X, a: @@x#{', &B' unless block.empty?}) end") # CVAR + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: 1#{block})") # INTEGER + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: 1.0#{block})") # FLOAT + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: 1.0r#{block})") # RATIONAL + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: 1.0i#{block})") # IMAGINARY + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: 'a'#{block})") # STR + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: :b#{block})") # SYM + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: /a/#{block})") # REGX + check_allocations(0, 1, "keyword.(*empty_array, a: self#{block})") # SELF + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: nil#{block})") # NIL + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: true#{block})") # TRUE + check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: false#{block})") # FALSE + check_allocations(0, 1, "keyword.(*empty_array, a: ->{}#{block})") # LAMBDA + check_allocations(0, 1, "keyword.(*empty_array, a: $1#{block})") # NTH_REF + check_allocations(0, 1, "keyword.(*empty_array, a: $`#{block})") # BACK_REF + + # LIST safe: Only 1 array (literal [:c]), not 2 (one for [:c] and one for *empty_array) + check_allocations(1, 1, "keyword.(*empty_array, a: empty_array, b: [:c]#{block})") + check_allocations(1, 1, "keyword.(*empty_array, a: empty_array, b: [:c, $x]#{block})") + # LIST unsafe: 2 (one for [:c] and one for *empty_array) + check_allocations(2, 1, "keyword.(*empty_array, a: empty_array, b: [Object()]#{block})") + check_allocations(2, 1, "keyword.(*empty_array, a: empty_array, b: [:c, $x, Object()]#{block})") + RUBY + end + class WithBlock < self def block ', &block' end + def only_block + '&block' + end end end end |
