From ebd398ac5a4147a1e652d6943c39a29a62f12e66 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Wed, 16 Jan 2019 10:48:30 +0000 Subject: remove RHash::iter_lev. iter_lev is used to detect the hash is iterating or not. Usually, iter_lev should be very small number (1 or 2) so `int` is overkill. This patch introduce iter_lev in flags (7 bits, FL13 to FL19) and if iter_lev exceeds this range, save it in hidden attribute. We can get 1 word in RHash. We can't modify frozen objects. Therefore I added new internal API `rb_ivar_set_internal()` which allows us to set an attribute even if the target object is frozen if the name is hidden ivar (the name without `@` prefix). --- test/ruby/test_hash.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'test/ruby') diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 243cad8d43..d973b1f763 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1720,6 +1720,31 @@ class TestHash < Test::Unit::TestCase assert_equal(keys, h.keys.map(&:hash), msg) end + def hrec h, n, &b + if n > 0 + h.each{hrec(h, n-1, &b)} + else + yield + end + end + + def test_huge_iter_level + h = @cls[a: 1] + assert_raise(RuntimeError){ + hrec(h, 1000){ h[:c] = 3 } + } + + h = @cls[a: 1] + hrec(h, 1000){} + h[:c] = 3 + assert_equal(3, h[:c]) + + h = @cls[a: 1] + h.freeze # set hidden attribute for a frozen object + hrec(h, 1000){} + assert_equal(1, h.size) + end + class TestSubHash < TestHash class SubHash < Hash def reject(*) -- cgit v1.2.3