зеркало из https://github.com/github/ruby.git
[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.
This commit is contained in:
Родитель
40cd292f95
Коммит
29c480dd6f
|
@ -13032,6 +13032,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
|||
proc.$(OBJEXT): $(top_srcdir)/internal/error.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/eval.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/gc.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/hash.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/imemo.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/object.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/proc.h
|
||||
|
|
21
proc.c
21
proc.c
|
@ -15,6 +15,7 @@
|
|||
#include "internal/error.h"
|
||||
#include "internal/eval.h"
|
||||
#include "internal/gc.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/object.h"
|
||||
#include "internal/proc.h"
|
||||
#include "internal/symbol.h"
|
||||
|
@ -1437,8 +1438,24 @@ rb_hash_proc(st_index_t hash, VALUE prc)
|
|||
{
|
||||
rb_proc_t *proc;
|
||||
GetProcPtr(prc, proc);
|
||||
hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.code.val);
|
||||
hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.self);
|
||||
|
||||
switch (vm_block_type(&proc->block)) {
|
||||
case block_type_iseq:
|
||||
hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.iseq->body);
|
||||
break;
|
||||
case block_type_ifunc:
|
||||
hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->func);
|
||||
break;
|
||||
case block_type_symbol:
|
||||
hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.symbol));
|
||||
break;
|
||||
case block_type_proc:
|
||||
hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.proc));
|
||||
break;
|
||||
default:
|
||||
rb_bug("rb_hash_proc: unknown block type %d", vm_block_type(&proc->block));
|
||||
}
|
||||
|
||||
return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep);
|
||||
}
|
||||
|
||||
|
|
|
@ -168,6 +168,24 @@ class TestProc < Test::Unit::TestCase
|
|||
assert_operator(procs.map(&:hash).uniq.size, :>=, 500)
|
||||
end
|
||||
|
||||
def test_hash_does_not_change_after_compaction
|
||||
# [Bug #20853]
|
||||
[
|
||||
"proc {}", # iseq backed proc
|
||||
"{}.to_proc", # ifunc backed proc
|
||||
":hello.to_proc", # symbol backed proc
|
||||
].each do |proc|
|
||||
assert_separately([], <<~RUBY)
|
||||
p1 = #{proc}
|
||||
hash = p1.hash
|
||||
|
||||
GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||
|
||||
assert_equal(hash, p1.hash, "proc is `#{proc}`")
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
def test_block_par
|
||||
assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x})
|
||||
assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x})
|
||||
|
|
Загрузка…
Ссылка в новой задаче