зеркало из https://github.com/github/ruby.git
Copy hash compare_by_identity setting in more cases
This makes the compare_by_identity setting always copied for the following methods: * except * merge * reject * select * slice * transform_values Some of these methods did not copy the setting, or only copied the setting if the receiver was not empty. Fixes [Bug #17757] Co-authored-by: Kenichi Kamiya <kachick1@gmail.com>
This commit is contained in:
Родитель
fa87f72e1e
Коммит
95f8ffa5f6
32
hash.c
32
hash.c
|
@ -1559,6 +1559,17 @@ rb_hash_new(void)
|
|||
return hash_alloc(rb_cHash);
|
||||
}
|
||||
|
||||
static VALUE rb_hash_compare_by_id(VALUE hash);
|
||||
|
||||
static VALUE
|
||||
copy_compare_by_id(VALUE hash, VALUE basis)
|
||||
{
|
||||
if (rb_hash_compare_by_id_p(basis)) {
|
||||
return rb_hash_compare_by_id(hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
MJIT_FUNC_EXPORTED VALUE
|
||||
rb_hash_new_with_size(st_index_t size)
|
||||
{
|
||||
|
@ -1587,6 +1598,12 @@ hash_copy(VALUE ret, VALUE hash)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
hash_dup_with_compare_by_id(VALUE hash)
|
||||
{
|
||||
return hash_copy(copy_compare_by_id(rb_hash_new(), hash), hash);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
hash_dup(VALUE hash, VALUE klass, VALUE flags)
|
||||
{
|
||||
|
@ -2597,7 +2614,7 @@ rb_hash_reject(VALUE hash)
|
|||
rb_warn("extra states are no longer copied: %+"PRIsVALUE, hash);
|
||||
}
|
||||
}
|
||||
result = hash_copy(hash_alloc(rb_cHash), hash);
|
||||
result = hash_dup_with_compare_by_id(hash);
|
||||
if (!RHASH_EMPTY_P(hash)) {
|
||||
rb_hash_foreach(result, delete_if_i, result);
|
||||
}
|
||||
|
@ -2622,9 +2639,9 @@ rb_hash_slice(int argc, VALUE *argv, VALUE hash)
|
|||
VALUE key, value, result;
|
||||
|
||||
if (argc == 0 || RHASH_EMPTY_P(hash)) {
|
||||
return rb_hash_new();
|
||||
return copy_compare_by_id(rb_hash_new(), hash);
|
||||
}
|
||||
result = rb_hash_new_with_size(argc);
|
||||
result = copy_compare_by_id(rb_hash_new_with_size(argc), hash);
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
key = argv[i];
|
||||
|
@ -2653,8 +2670,7 @@ rb_hash_except(int argc, VALUE *argv, VALUE hash)
|
|||
int i;
|
||||
VALUE key, result;
|
||||
|
||||
result = hash_alloc(rb_cHash);
|
||||
hash_copy(result, hash);
|
||||
result = hash_dup_with_compare_by_id(hash);
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
key = argv[i];
|
||||
|
@ -2754,7 +2770,7 @@ rb_hash_select(VALUE hash)
|
|||
VALUE result;
|
||||
|
||||
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
|
||||
result = hash_copy(hash_alloc(rb_cHash), hash);
|
||||
result = hash_dup_with_compare_by_id(hash);
|
||||
if (!RHASH_EMPTY_P(hash)) {
|
||||
rb_hash_foreach(result, keep_if_i, result);
|
||||
}
|
||||
|
@ -3348,7 +3364,7 @@ rb_hash_transform_values(VALUE hash)
|
|||
VALUE result;
|
||||
|
||||
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
|
||||
result = hash_copy(hash_alloc(rb_cHash), hash);
|
||||
result = hash_dup_with_compare_by_id(hash);
|
||||
SET_DEFAULT(result, Qnil);
|
||||
|
||||
if (!RHASH_EMPTY_P(hash)) {
|
||||
|
@ -4102,7 +4118,7 @@ rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func)
|
|||
static VALUE
|
||||
rb_hash_merge(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
return rb_hash_update(argc, argv, rb_hash_dup(self));
|
||||
return rb_hash_update(argc, argv, copy_compare_by_id(rb_hash_dup(self), self));
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -749,6 +749,33 @@ class TestHash < Test::Unit::TestCase
|
|||
assert_not_send([h, :instance_variable_defined?, :@foo])
|
||||
end
|
||||
|
||||
def test_reject_on_identhash
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
h.compare_by_identity
|
||||
str1 = +'str'
|
||||
str2 = +'str'
|
||||
h[str1] = 1
|
||||
h[str2] = 2
|
||||
expected = {}.compare_by_identity
|
||||
expected[str1] = 1
|
||||
expected[str2] = 2
|
||||
h2 = h.reject{|k,| k != 'str'}
|
||||
assert_equal(expected, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
h2 = h.reject{true}
|
||||
assert_equal({}.compare_by_identity, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
|
||||
h = @cls[]
|
||||
h.compare_by_identity
|
||||
h2 = h.reject{true}
|
||||
assert_equal({}.compare_by_identity, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
h2 = h.reject{|k,| k != 'str'}
|
||||
assert_equal({}.compare_by_identity, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
end
|
||||
|
||||
def test_reject!
|
||||
base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
|
||||
h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
|
||||
|
@ -1076,6 +1103,33 @@ class TestHash < Test::Unit::TestCase
|
|||
assert_not_send([h, :instance_variable_defined?, :@foo])
|
||||
end
|
||||
|
||||
def test_select_on_identhash
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
h.compare_by_identity
|
||||
str1 = +'str'
|
||||
str2 = +'str'
|
||||
h[str1] = 1
|
||||
h[str2] = 2
|
||||
expected = {}.compare_by_identity
|
||||
expected[str1] = 1
|
||||
expected[str2] = 2
|
||||
h2 = h.select{|k,| k == 'str'}
|
||||
assert_equal(expected, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
h2 = h.select{false}
|
||||
assert_equal({}.compare_by_identity, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
|
||||
h = @cls[]
|
||||
h.compare_by_identity
|
||||
h2 = h.select{false}
|
||||
assert_equal({}.compare_by_identity, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
h2 = h.select{|k,| k == 'str'}
|
||||
assert_equal({}.compare_by_identity, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
end
|
||||
|
||||
def test_select!
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
assert_equal(h, h.select! {|k, v| k + v >= 7 })
|
||||
|
@ -1100,6 +1154,33 @@ class TestHash < Test::Unit::TestCase
|
|||
assert_equal({}, {}.slice)
|
||||
end
|
||||
|
||||
def test_slice_on_identhash
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
h.compare_by_identity
|
||||
str1 = +'str'
|
||||
str2 = +'str'
|
||||
h[str1] = 1
|
||||
h[str2] = 2
|
||||
sliced = h.slice(str1, str2)
|
||||
expected = {}.compare_by_identity
|
||||
expected[str1] = 1
|
||||
expected[str2] = 2
|
||||
assert_equal(expected, sliced)
|
||||
assert_equal(true, sliced.compare_by_identity?)
|
||||
sliced = h.slice
|
||||
assert_equal({}.compare_by_identity, sliced)
|
||||
assert_equal(true, sliced.compare_by_identity?)
|
||||
|
||||
h = @cls[]
|
||||
h.compare_by_identity
|
||||
sliced= h.slice
|
||||
assert_equal({}.compare_by_identity, sliced)
|
||||
assert_equal(true, sliced.compare_by_identity?)
|
||||
sliced = h.slice(str1, str2)
|
||||
assert_equal({}.compare_by_identity, sliced)
|
||||
assert_equal(true, sliced.compare_by_identity?)
|
||||
end
|
||||
|
||||
def test_except
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
assert_equal({5=>6}, h.except(1, 3))
|
||||
|
@ -1108,6 +1189,30 @@ class TestHash < Test::Unit::TestCase
|
|||
assert_equal({}, {}.except)
|
||||
end
|
||||
|
||||
def test_except_on_identhash
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
h.compare_by_identity
|
||||
str1 = +'str'
|
||||
str2 = +'str'
|
||||
h[str1] = 1
|
||||
h[str2] = 2
|
||||
excepted = h.except(str1, str2)
|
||||
assert_equal({1=>2,3=>4,5=>6}.compare_by_identity, excepted)
|
||||
assert_equal(true, excepted.compare_by_identity?)
|
||||
excepted = h.except
|
||||
assert_equal(h, excepted)
|
||||
assert_equal(true, excepted.compare_by_identity?)
|
||||
|
||||
h = @cls[]
|
||||
h.compare_by_identity
|
||||
excepted = h.except
|
||||
assert_equal({}.compare_by_identity, excepted)
|
||||
assert_equal(true, excepted.compare_by_identity?)
|
||||
excepted = h.except(str1, str2)
|
||||
assert_equal({}.compare_by_identity, excepted)
|
||||
assert_equal(true, excepted.compare_by_identity?)
|
||||
end
|
||||
|
||||
def test_filter
|
||||
assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].filter {|k, v| k + v >= 7 })
|
||||
|
||||
|
@ -1267,6 +1372,34 @@ class TestHash < Test::Unit::TestCase
|
|||
assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3) {|k, v1, v2| k + v1 + v2 })
|
||||
end
|
||||
|
||||
def test_merge_on_identhash
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
h.compare_by_identity
|
||||
str1 = +'str'
|
||||
str2 = +'str'
|
||||
h[str1] = 1
|
||||
h[str2] = 2
|
||||
expected = h.dup
|
||||
expected[7] = 8
|
||||
h2 = h.merge(7=>8)
|
||||
assert_equal(expected, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
h2 = h.merge({})
|
||||
assert_equal(h, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
|
||||
h = @cls[]
|
||||
h.compare_by_identity
|
||||
h1 = @cls[7=>8]
|
||||
h1.compare_by_identity
|
||||
h2 = h.merge(7=>8)
|
||||
assert_equal(h1, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
h2 = h.merge({})
|
||||
assert_equal(h, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
end
|
||||
|
||||
def test_merge!
|
||||
h = @cls[a: 1, b: 2, c: 3]
|
||||
assert_raise(FrozenError) do
|
||||
|
@ -1758,6 +1891,24 @@ class TestHash < Test::Unit::TestCase
|
|||
assert_equal({A: 1, B: 2, "c" => 3}, x.transform_keys({a: :A, b: :B, d: :D}, &:to_s))
|
||||
end
|
||||
|
||||
def test_transform_keys_on_identhash
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
h.compare_by_identity
|
||||
str1 = +'str'
|
||||
str2 = +'str'
|
||||
h[str1] = 1
|
||||
h[str2] = 2
|
||||
h2 = h.transform_keys(&:itself)
|
||||
assert_equal(Hash[h.to_a], h2)
|
||||
assert_equal(false, h2.compare_by_identity?)
|
||||
|
||||
h = @cls[]
|
||||
h.compare_by_identity
|
||||
h2 = h.transform_keys(&:itself)
|
||||
assert_equal({}, h2)
|
||||
assert_equal(false, h2.compare_by_identity?)
|
||||
end
|
||||
|
||||
def test_transform_keys_bang
|
||||
x = @cls[a: 1, b: 2, c: 3]
|
||||
y = x.transform_keys! {|k| :"#{k}!" }
|
||||
|
@ -1808,6 +1959,24 @@ class TestHash < Test::Unit::TestCase
|
|||
assert_equal(%w(1.0 2.1 3.2), y.values_at(:a, :b, :c))
|
||||
end
|
||||
|
||||
def test_transform_values_on_identhash
|
||||
h = @cls[1=>2,3=>4,5=>6]
|
||||
h.compare_by_identity
|
||||
str1 = +'str'
|
||||
str2 = +'str'
|
||||
h[str1] = 1
|
||||
h[str2] = 2
|
||||
h2 = h.transform_values(&:itself)
|
||||
assert_equal(h, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
|
||||
h = @cls[]
|
||||
h.compare_by_identity
|
||||
h2 = h.transform_values(&:itself)
|
||||
assert_equal({}.compare_by_identity, h2)
|
||||
assert_equal(true, h2.compare_by_identity?)
|
||||
end
|
||||
|
||||
def test_transform_values_bang
|
||||
x = @cls[a: 1, b: 2, c: 3]
|
||||
y = x.transform_values! {|v| v ** 2 }
|
||||
|
|
Загрузка…
Ссылка в новой задаче