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

ruby-changes:53872

From: ko1 <ko1@a...>
Date: Thu, 29 Nov 2018 17:04:00 +0900 (JST)
Subject: [ruby-changes:53872] ko1:r66091 (trunk): clear dst Hash on Hash#replace. [Bug #15358]

ko1	2018-11-29 17:03:55 +0900 (Thu, 29 Nov 2018)

  New Revision: 66091

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66091

  Log:
    clear dst Hash on Hash#replace. [Bug #15358]
    
    * hash.c (linear_copy): solve two issues on `Hash#replace`.
      (1) fix memory leak
          (1-1) don't allocate memory if destination already
                has a memory area.
          (1-2) free destination memory if src is NULL.
      (2) clear transient heap flag if src is NULL. [Bug #15358]

  Modified files:
    trunk/hash.c
    trunk/test/ruby/test_hash.rb
Index: hash.c
===================================================================
--- hash.c	(revision 66090)
+++ hash.c	(revision 66091)
@@ -493,6 +493,7 @@ static void https://github.com/ruby/ruby/blob/trunk/hash.c#L493
 hash_array_set(VALUE hash, struct li_table *li)
 {
     HASH_ASSERT(RHASH_ARRAY_P(hash));
+    HASH_ASSERT((RHASH_TRANSIENT_P(hash) && li == NULL) ? FALSE : TRUE);
     RHASH(hash)->as.li = li;
     hash_verify(hash);
 }
@@ -993,15 +994,17 @@ linear_copy(VALUE hash1, VALUE hash2) https://github.com/ruby/ruby/blob/trunk/hash.c#L994
 {
     li_table *old_tab = RHASH_ARRAY(hash2);
 
-    if (old_tab) {
-        li_table *new_tab;
-        new_tab = (li_table*) rb_transient_heap_alloc(hash1, sizeof(li_table));
-        if (new_tab != NULL) {
-            RHASH_SET_TRANSIENT_FLAG(hash1);
-        }
-        else {
-            RHASH_UNSET_TRANSIENT_FLAG(hash1);
-            new_tab = (li_table*)ruby_xmalloc(sizeof(li_table));
+    if (old_tab != NULL) {
+        li_table *new_tab = RHASH_ARRAY(hash1);
+        if (new_tab == NULL) {
+            new_tab = (li_table*) rb_transient_heap_alloc(hash1, sizeof(li_table));
+            if (new_tab != NULL) {
+                RHASH_SET_TRANSIENT_FLAG(hash1);
+            }
+            else {
+                RHASH_UNSET_TRANSIENT_FLAG(hash1);
+                new_tab = (li_table*)ruby_xmalloc(sizeof(li_table));
+            }
         }
         *new_tab = *old_tab;
         RHASH_ARRAY_BOUND_SET(hash1, RHASH_ARRAY_BOUND(hash2));
@@ -1014,7 +1017,15 @@ linear_copy(VALUE hash1, VALUE hash2) https://github.com/ruby/ruby/blob/trunk/hash.c#L1017
     else {
         RHASH_ARRAY_BOUND_SET(hash1, RHASH_ARRAY_BOUND(hash2));
         RHASH_ARRAY_SIZE_SET(hash1, RHASH_ARRAY_SIZE(hash2));
-        RHASH_ARRAY_SET(hash1, old_tab);
+
+        if (RHASH_TRANSIENT_P(hash1)) {
+            RHASH_UNSET_TRANSIENT_FLAG(hash1);
+        }
+        else if (RHASH_ARRAY(hash1)) {
+            ruby_xfree(RHASH_ARRAY(hash1));
+        }
+
+        RHASH_ARRAY_SET(hash1, NULL);
 
         rb_gc_writebarrier_remember(hash1);
         return old_tab;
Index: test/ruby/test_hash.rb
===================================================================
--- test/ruby/test_hash.rb	(revision 66090)
+++ test/ruby/test_hash.rb	(revision 66091)
@@ -755,6 +755,14 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L755
     assert_predicate(h, :compare_by_identity?)
   end
 
+  def test_replace_bug15358
+    h1 = {}
+    h2 = {a:1,b:2,c:3,d:4,e:5}
+    h2.replace(h1)
+    GC.start
+    assert(true)
+  end
+
   def test_shift
     h = @h.dup
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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