Skip to content

Commit 12a72e3

Browse files
authored
[fix] Hash#rehash: fix deduplicating keys (jruby#9340)
1 parent bb33fa0 commit 12a72e3

2 files changed

Lines changed: 29 additions & 2 deletions

File tree

core/src/main/java/org/jruby/RubyHash.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,7 +1129,7 @@ public RubyHash rehash(ThreadContext context) {
11291129
RubyHashEntry tmpNext = entry.nextAdded;
11301130
RubyHashEntry tmpPrev = entry.prevAdded;
11311131
tmpPrev.nextAdded = tmpNext;
1132-
tmpPrev.prevAdded = tmpPrev;
1132+
tmpNext.prevAdded = tmpPrev;
11331133
size--;
11341134
} else {
11351135
// replace entry if hash changed
@@ -1159,7 +1159,7 @@ public RubyHash rehash(ThreadContext context) {
11591159
RubyHashEntry tmpNext = entry.nextAdded;
11601160
RubyHashEntry tmpPrev = entry.prevAdded;
11611161
tmpPrev.nextAdded = tmpNext;
1162-
tmpPrev.prevAdded = tmpPrev;
1162+
tmpNext.prevAdded = tmpPrev;
11631163
size--;
11641164
}
11651165
nextEntry = nextEntry.next;

test/jruby/test_hash.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,31 @@ def test_compare_by_identity
108108
assert_equal 2, hash[arr2]
109109
end
110110

111+
# GH-9340: Hash#rehash corrupts insertion-order linked list when
112+
# deduplicating keys. After rehash removes a duplicate, new
113+
# insertions would disconnect existing entries from iteration.
114+
def test_rehash_dedup_preserves_insertion_order
115+
a = [1]; b = [2]
116+
h = { a => "a", b => "b" }
117+
a[0] = 2 # a now equals b
118+
h.rehash # deduplicates, should keep b's value
119+
h[[3]] = "c"
120+
121+
assert_equal 2, h.size
122+
assert_equal [[2], [3]], h.keys
123+
assert_equal ["b", "c"], h.values
124+
end
125+
126+
def test_rehash_dedup_reverse_each
127+
a = [1]; b = [2]; c = [3]
128+
h = { a => "a", b => "b", c => "c" }
129+
a[0] = 2
130+
h.rehash
131+
h[[10]] = "d"
132+
133+
result = []
134+
h.reverse_each { |k, v| result << k }
135+
assert_equal [[10], [3], [2]], result
136+
end
137+
111138
end

0 commit comments

Comments
 (0)