[Bug #20868] Fix Method#hash to not change after compaction

The hash value of a Method must remain constant after a compaction, otherwise
it may not work as the key in a hash table.

For example:

    def a; end

    # Need this method here because otherwise the iseq may be on the C stack
    # which would get pinned and not move during compaction
    def get_hash
      method(:a).hash
    end

    puts get_hash # => 2993401401091578131

    GC.verify_compaction_references(expand_heap: true, toward: :empty)

    puts get_hash # => -2162775864511574135
This commit is contained in:
Peter Zhu 2024-11-05 10:45:39 -05:00
Родитель 96e695ad00
Коммит 56ecc243e2
2 изменённых файлов: 22 добавлений и 1 удалений

Просмотреть файл

@ -209,6 +209,27 @@ class TestMethod < Test::Unit::TestCase
assert_kind_of(String, o.method(:foo).hash.to_s)
end
def test_hash_does_not_change_after_compaction
omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
# iseq backed method
assert_separately([], <<~RUBY)
def a; end
# Need this method here because otherwise the iseq may be on the C stack
# which would get pinned and not move during compaction
def get_hash
method(:a).hash
end
hash = get_hash
GC.verify_compaction_references(expand_heap: true, toward: :empty)
assert_equal(hash, get_hash)
RUBY
end
def test_owner
c = Class.new do
def foo; end

Просмотреть файл

@ -2249,7 +2249,7 @@ rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def)
switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
return rb_hash_uint(hash, (st_index_t)def->body.iseq.iseqptr);
return rb_hash_uint(hash, (st_index_t)def->body.iseq.iseqptr->body);
case VM_METHOD_TYPE_CFUNC:
hash = rb_hash_uint(hash, (st_index_t)def->body.cfunc.func);
return rb_hash_uint(hash, def->body.cfunc.argc);