ruby-changes:54256
From: nobu <ko1@a...>
Date: Thu, 20 Dec 2018 15:44:56 +0900 (JST)
Subject: [ruby-changes:54256] nobu:r66464 (trunk): Freeze hash literals embedded in duphash instructions
nobu 2018-12-20 15:44:50 +0900 (Thu, 20 Dec 2018) New Revision: 66464 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66464 Log: 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@j...> Modified files: trunk/compile.c trunk/test/ruby/test_literal.rb Index: compile.c =================================================================== --- compile.c (revision 66463) +++ compile.c (revision 66464) @@ -4026,6 +4026,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCH https://github.com/ruby/ruby/blob/trunk/compile.c#L4026 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); } Index: test/ruby/test_literal.rb =================================================================== --- test/ruby/test_literal.rb (revision 66463) +++ test/ruby/test_literal.rb (revision 66464) @@ -283,6 +283,22 @@ class TestRubyLiteral < Test::Unit::Test https://github.com/ruby/ruby/blob/trunk/test/ruby/test_literal.rb#L283 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] -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/