summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-20 06:44:50 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-20 06:44:50 +0000
commitec7b1e475b1f8cb3ca775fec27227c6761d3b203 (patch)
treee9076717079404af996b46dce24cded888dfc0b6
parentf89238ec0d689f3989c55c4716da500b8c898900 (diff)
Freeze hash literals embedded in duphash instructions
Previously, these hash literals were not frozen, and thus could be modified by ObjectSpace, resulting in undesired behavior. Example: ```ruby require 'objspace' def a(b={0=>1,1=>4,2=>17}) b end p a ObjectSpace.each_object(Hash) do |a| a[3] = 8 if a.class == Hash && a[0] == 1 && a[1] == 4 && a[2] == 17 end p a ``` It may be desirable to hide such hashes from ObjectSpace, since they are internal, but I'm not sure how to do that. From: Jeremy Evans <code@jeremyevans.net> git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66464 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--compile.c1
-rw-r--r--test/ruby/test_literal.rb16
2 files changed, 17 insertions, 0 deletions
diff --git a/compile.c b/compile.c
index 731b05323b..cb168438d8 100644
--- a/compile.c
+++ b/compile.c
@@ -4026,6 +4026,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro
hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR_TRANSIENT(ary), hash);
+ rb_hash_freeze(hash);
iseq_add_mark_object_compile_time(iseq, hash);
ADD_INSN1(ret, line, duphash, hash);
}
diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb
index ef2467e177..37755422a1 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -283,6 +283,22 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal "literal", h["string"]
end
+ def frozen_hash_literal_arg
+ {0=>1,1=>4,2=>17}
+ end
+
+ def test_hash_literal_frozen
+ assert_not_include frozen_hash_literal_arg, 3
+ assert_raise(FrozenError) do
+ ObjectSpace.each_object(Hash) do |a|
+ if a.class == Hash and !a.default_proc and a.size == 3
+ a[3] = 8 if a[0] == 1 and a[1] == 4 and a[2] == 17
+ end
+ end
+ end
+ assert_not_include frozen_hash_literal_arg, 3
+ end
+
def test_big_array_and_hash_literal
assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).map{'x'}.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).to_a.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]