Skip to content

Commit

Permalink
[Bug #20868] Fix Method#hash to not change after compaction
Browse files Browse the repository at this point in the history
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
  • Loading branch information
peterzhu2118 committed Nov 6, 2024
1 parent 96e695a commit 56ecc24
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 1 deletion.
21 changes: 21 additions & 0 deletions test/ruby/test_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,27 @@ def o.foo; end
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
Expand Down
2 changes: 1 addition & 1 deletion vm_method.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 56ecc24

Please sign in to comment.