[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]