Skip to content

Commit 6db39f4

Browse files
committed
merge revision(s) 29c480d: [Backport #20853]
[Bug #20853] Fix Proc#hash to not change after compaction The hash value of a Proc must remain constant after a compaction, otherwise it may not work as the key in a hash table.
1 parent edeb031 commit 6db39f4

File tree

4 files changed

+39
-3
lines changed

4 files changed

+39
-3
lines changed

common.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12590,6 +12590,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/compilers.h
1259012590
proc.$(OBJEXT): $(top_srcdir)/internal/error.h
1259112591
proc.$(OBJEXT): $(top_srcdir)/internal/eval.h
1259212592
proc.$(OBJEXT): $(top_srcdir)/internal/gc.h
12593+
proc.$(OBJEXT): $(top_srcdir)/internal/hash.h
1259312594
proc.$(OBJEXT): $(top_srcdir)/internal/imemo.h
1259412595
proc.$(OBJEXT): $(top_srcdir)/internal/object.h
1259512596
proc.$(OBJEXT): $(top_srcdir)/internal/proc.h

proc.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "internal/error.h"
1616
#include "internal/eval.h"
1717
#include "internal/gc.h"
18+
#include "internal/hash.h"
1819
#include "internal/object.h"
1920
#include "internal/proc.h"
2021
#include "internal/symbol.h"
@@ -1426,8 +1427,24 @@ rb_hash_proc(st_index_t hash, VALUE prc)
14261427
{
14271428
rb_proc_t *proc;
14281429
GetProcPtr(prc, proc);
1429-
hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.code.val);
1430-
hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.self);
1430+
1431+
switch (vm_block_type(&proc->block)) {
1432+
case block_type_iseq:
1433+
hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.iseq->body);
1434+
break;
1435+
case block_type_ifunc:
1436+
hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->func);
1437+
break;
1438+
case block_type_symbol:
1439+
hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.symbol));
1440+
break;
1441+
case block_type_proc:
1442+
hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.proc));
1443+
break;
1444+
default:
1445+
rb_bug("rb_hash_proc: unknown block type %d", vm_block_type(&proc->block));
1446+
}
1447+
14311448
return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep);
14321449
}
14331450

test/ruby/test_proc.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,24 @@ def self.capture(&block)
168168
assert_operator(procs.map(&:hash).uniq.size, :>=, 500)
169169
end
170170

171+
def test_hash_does_not_change_after_compaction
172+
# [Bug #20853]
173+
[
174+
"proc {}", # iseq backed proc
175+
"{}.to_proc", # ifunc backed proc
176+
":hello.to_proc", # symbol backed proc
177+
].each do |proc|
178+
assert_separately([], <<~RUBY)
179+
p1 = #{proc}
180+
hash = p1.hash
181+
182+
GC.verify_compaction_references(expand_heap: true, toward: :empty)
183+
184+
assert_equal(hash, p1.hash, "proc is `#{proc}`")
185+
RUBY
186+
end
187+
end
188+
171189
def test_block_par
172190
assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x})
173191
assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x})

version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
1212
#define RUBY_VERSION_TEENY 5
1313
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
14-
#define RUBY_PATCHLEVEL 107
14+
#define RUBY_PATCHLEVEL 108
1515

1616
#include "ruby/version.h"
1717
#include "ruby/internal/abi.h"

0 commit comments

Comments
 (0)