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

ruby-changes:60565

From: nagachika <ko1@a...>
Date: Mon, 30 Mar 2020 11:23:09 +0900 (JST)
Subject: [ruby-changes:60565] b52717cb48 (ruby_2_6): merge revision(s) 4c019f5a626523e99e2827ed917802e3097c380d,c3584dfacce4d0f2058d8403de6fdce4fd4d686b: [Backport #16676]

https://git.ruby-lang.org/ruby.git/commit/?id=b52717cb48

From b52717cb486fa6650ed07ca9a1e14cc548004210 Mon Sep 17 00:00:00 2001
From: nagachika <nagachika@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
Date: Mon, 30 Mar 2020 02:22:51 +0000
Subject: merge revision(s)
 4c019f5a626523e99e2827ed917802e3097c380d,c3584dfacce4d0f2058d8403de6fdce4fd4d686b:
 [Backport #16676]

	check ar_table after `#hash` call

	ar_table can be converted to st_table just after `ar_do_hash()`
	function which calls `#hash` method. We need to check
	the representation to detect this mutation.
	[Bug #16676]

	check ar_table first.

	RHASH_AR_TABLE_SIZE() has assertion that it is a ar_talbe.
	The last commit breaks this assumption so check ar_table first.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_6@67858 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

diff --git a/hash.c b/hash.c
index 3bba88a..e8be71b 100644
--- a/hash.c
+++ b/hash.c
@@ -607,10 +607,14 @@ ar_try_convert_table(VALUE hash) https://github.com/ruby/ruby/blob/trunk/hash.c#L607
 {
     st_table *new_tab;
     ar_table_entry *entry;
-    const unsigned size = RHASH_AR_TABLE_SIZE(hash);
+    unsigned size;
     st_index_t i;
 
-    if (!RHASH_AR_TABLE_P(hash) || size < RHASH_AR_TABLE_MAX_SIZE) {
+    if (!RHASH_AR_TABLE_P(hash)) return;
+
+    size = RHASH_AR_TABLE_SIZE(hash);
+
+    if (size < RHASH_AR_TABLE_MAX_SIZE) {
         return;
     }
 
@@ -824,6 +828,11 @@ ar_update(VALUE hash, st_data_t key, https://github.com/ruby/ruby/blob/trunk/hash.c#L828
     st_data_t value = 0, old_key;
     st_hash_t hash_value = do_hash(key);
 
+    if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+        /* `#hash` changes ar_table -> st_table */
+        return -1;
+    }
+
     if (RHASH_AR_TABLE_SIZE(hash) > 0) {
         bin = find_entry(hash, hash_value, key);
         existing = (bin != RHASH_AR_TABLE_MAX_BOUND) ? TRUE : FALSE;
@@ -872,6 +881,11 @@ ar_insert(VALUE hash, st_data_t key, st_data_t value) https://github.com/ruby/ruby/blob/trunk/hash.c#L881
     unsigned bin = RHASH_AR_TABLE_BOUND(hash);
     st_hash_t hash_value = do_hash(key);
 
+    if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+        /* `#hash` changes ar_table -> st_table */
+        return -1;
+    }
+
     hash_ar_table(hash); /* prepare ltbl */
 
     bin = find_entry(hash, hash_value, key);
@@ -900,7 +914,12 @@ static int https://github.com/ruby/ruby/blob/trunk/hash.c#L914
 ar_lookup(VALUE hash, st_data_t key, st_data_t *value)
 {
     st_hash_t hash_value = do_hash(key);
-    unsigned bin = find_entry(hash, hash_value, key);
+    unsigned bin;
+    if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+        /* `#hash` changes ar_table -> st_table */
+        return st_lookup(RHASH_ST_TABLE(hash), key, value);
+    }
+    bin = find_entry(hash, hash_value, key);
 
     if (bin == RHASH_AR_TABLE_MAX_BOUND) {
         return 0;
@@ -920,6 +939,10 @@ ar_delete(VALUE hash, st_data_t *key, st_data_t *value) https://github.com/ruby/ruby/blob/trunk/hash.c#L939
     unsigned bin;
     st_hash_t hash_value = do_hash(*key);
 
+    if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
+        /* `#hash` changes ar_table -> st_table */
+        return st_delete(RHASH_ST_TABLE(hash), key, value);
+    }
 
     bin = find_entry(hash, hash_value, *key);
 
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index c934d10..e1b6e72 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -1733,4 +1733,62 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L1733
       super
     end
   end
+
+  def test_ar2st
+    # insert
+    obj = Object.new
+    obj.instance_variable_set(:@h, h = {})
+    def obj.hash
+      10.times{|i| @h[i] = i}
+      0
+    end
+    def obj.inspect
+      'test'
+    end
+    h[obj] = true
+    assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, test=>true}', h.inspect
+
+    # delete
+    obj = Object.new
+    obj.instance_variable_set(:@h, h = {})
+    def obj.hash
+      10.times{|i| @h[i] = i}
+      0
+    end
+    def obj.inspect
+      'test'
+    end
+    def obj.eql? other
+      other.class == Object
+    end
+    obj2 = Object.new
+    def obj2.hash
+      0
+    end
+
+    h[obj2] = true
+    h.delete obj
+    assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9}', h.inspect
+
+    # lookup
+    obj = Object.new
+    obj.instance_variable_set(:@h, h = {})
+    def obj.hash
+      10.times{|i| @h[i] = i}
+      0
+    end
+    def obj.inspect
+      'test'
+    end
+    def obj.eql? other
+      other.class == Object
+    end
+    obj2 = Object.new
+    def obj2.hash
+      0
+    end
+
+    h[obj2] = true
+    assert_equal true, h[obj]
+  end
 end
diff --git a/version.h b/version.h
index 3389f3b..df96720 100644
--- a/version.h
+++ b/version.h
@@ -1,10 +1,10 @@ https://github.com/ruby/ruby/blob/trunk/version.h#L1
 #define RUBY_VERSION "2.6.6"
-#define RUBY_RELEASE_DATE "2020-03-20"
-#define RUBY_PATCHLEVEL 143
+#define RUBY_RELEASE_DATE "2020-03-30"
+#define RUBY_PATCHLEVEL 144
 
 #define RUBY_RELEASE_YEAR 2020
 #define RUBY_RELEASE_MONTH 3
-#define RUBY_RELEASE_DAY 20
+#define RUBY_RELEASE_DAY 30
 
 #include "ruby/version.h"
 
-- 
cgit v0.10.2


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

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