merge revision(s) 56ecc243e2: [Backport #20868]

[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:
Takashi Kokubun 2025-01-14 17:40:39 -08:00
parent 2330146397
commit 42f043c189
3 changed files with 23 additions and 2 deletions

View file

@ -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

View file

@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 6
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
#define RUBY_PATCHLEVEL 109
#define RUBY_PATCHLEVEL 110
#include "ruby/version.h"
#include "ruby/internal/abi.h"

View file

@ -2237,7 +2237,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);