Move object_id in object fields.

And get rid of the `obj_to_id_tbl`

It's no longer needed, the `object_id` is now stored inline
in the object alongside instance variables.

We still need the inverse table in case `_id2ref` is invoked, but
we lazily build it by walking the heap if that happens.

The `object_id` concern is also no longer a GC implementation
concern, but a generic implementation.

Co-Authored-By: Matt Valentine-House <matt@eightbitraptor.com>
This commit is contained in:
Jean Boussier 2025-04-21 16:16:07 +09:00
parent d34c150547
commit f48e45d1e9
Notes: git 2025-05-08 05:58:19 +00:00
23 changed files with 1140 additions and 560 deletions

177
test/ruby/test_object_id.rb Normal file
View file

@ -0,0 +1,177 @@
require 'test/unit'
class TestObjectId < Test::Unit::TestCase
def setup
@obj = Object.new
end
def test_dup_new_id
id = @obj.object_id
refute_equal id, @obj.dup.object_id
end
def test_dup_with_ivar_and_id
id = @obj.object_id
@obj.instance_variable_set(:@foo, 42)
copy = @obj.dup
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
end
def test_dup_with_id_and_ivar
@obj.instance_variable_set(:@foo, 42)
id = @obj.object_id
copy = @obj.dup
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
end
def test_dup_with_id_and_ivar_and_frozen
@obj.instance_variable_set(:@foo, 42)
@obj.freeze
id = @obj.object_id
copy = @obj.dup
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
refute_predicate copy, :frozen?
end
def test_clone_new_id
id = @obj.object_id
refute_equal id, @obj.clone.object_id
end
def test_clone_with_ivar_and_id
id = @obj.object_id
@obj.instance_variable_set(:@foo, 42)
copy = @obj.clone
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
end
def test_clone_with_id_and_ivar
@obj.instance_variable_set(:@foo, 42)
id = @obj.object_id
copy = @obj.clone
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
end
def test_clone_with_id_and_ivar_and_frozen
@obj.instance_variable_set(:@foo, 42)
@obj.freeze
id = @obj.object_id
copy = @obj.clone
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
assert_predicate copy, :frozen?
end
def test_marshal_new_id
return pass if @obj.is_a?(Module)
id = @obj.object_id
refute_equal id, Marshal.load(Marshal.dump(@obj)).object_id
end
def test_marshal_with_ivar_and_id
return pass if @obj.is_a?(Module)
id = @obj.object_id
@obj.instance_variable_set(:@foo, 42)
copy = Marshal.load(Marshal.dump(@obj))
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
end
def test_marshal_with_id_and_ivar
return pass if @obj.is_a?(Module)
@obj.instance_variable_set(:@foo, 42)
id = @obj.object_id
copy = Marshal.load(Marshal.dump(@obj))
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
end
def test_marshal_with_id_and_ivar_and_frozen
return pass if @obj.is_a?(Module)
@obj.instance_variable_set(:@foo, 42)
@obj.freeze
id = @obj.object_id
copy = Marshal.load(Marshal.dump(@obj))
refute_equal id, copy.object_id
assert_equal 42, copy.instance_variable_get(:@foo)
refute_predicate copy, :frozen?
end
end
class TestObjectIdClass < TestObjectId
def setup
@obj = Class.new
end
end
class TestObjectIdGeneric < TestObjectId
def setup
@obj = Array.new
end
end
class TestObjectIdTooComplex < TestObjectId
class TooComplex
end
def setup
if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS)
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
end
8.times do |i|
TooComplex.new.instance_variable_set("@a#{i}", 1)
end
@obj = TooComplex.new
@obj.instance_variable_set(:@test, 1)
end
end
class TestObjectIdTooComplexClass < TestObjectId
class TooComplex < Module
end
def setup
if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS)
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
end
8.times do |i|
TooComplex.new.instance_variable_set("@a#{i}", 1)
end
@obj = TooComplex.new
@obj.instance_variable_set(:@test, 1)
end
end
class TestObjectIdTooComplexGeneric < TestObjectId
class TooComplex < Array
end
def setup
if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS)
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
end
8.times do |i|
TooComplex.new.instance_variable_set("@a#{i}", 1)
end
@obj = TooComplex.new
@obj.instance_variable_set(:@test, 1)
end
end

View file

@ -622,6 +622,73 @@ class TestShapes < Test::Unit::TestCase
end;
end
def test_too_complex_and_frozen
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
$VERBOSE = nil
class TooComplex
attr_reader :very_unique
end
RubyVM::Shape::SHAPE_MAX_VARIATIONS.times do
TooComplex.new.instance_variable_set(:"@unique_#{_1}", Object.new)
end
tc = TooComplex.new
tc.instance_variable_set(:"@very_unique", 3)
shape = RubyVM::Shape.of(tc)
assert_predicate shape, :too_complex?
refute_predicate shape, :shape_frozen?
tc.freeze
frozen_shape = RubyVM::Shape.of(tc)
refute_equal shape.id, frozen_shape.id
assert_predicate frozen_shape, :too_complex?
assert_predicate frozen_shape, :shape_frozen?
assert_equal 3, tc.very_unique
assert_equal 3, Ractor.make_shareable(tc).very_unique
end;
end
def test_too_complex_and_frozen_and_object_id
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
$VERBOSE = nil
class TooComplex
attr_reader :very_unique
end
RubyVM::Shape::SHAPE_MAX_VARIATIONS.times do
TooComplex.new.instance_variable_set(:"@unique_#{_1}", Object.new)
end
tc = TooComplex.new
tc.instance_variable_set(:"@very_unique", 3)
shape = RubyVM::Shape.of(tc)
assert_predicate shape, :too_complex?
refute_predicate shape, :shape_frozen?
tc.freeze
frozen_shape = RubyVM::Shape.of(tc)
refute_equal shape.id, frozen_shape.id
assert_predicate frozen_shape, :too_complex?
assert_predicate frozen_shape, :shape_frozen?
refute_predicate frozen_shape, :has_object_id?
tc.object_id
id_shape = RubyVM::Shape.of(tc)
refute_equal frozen_shape.id, id_shape.id
assert_predicate id_shape, :too_complex?
assert_predicate id_shape, :shape_frozen?
assert_predicate id_shape, :has_object_id?
assert_equal 3, tc.very_unique
assert_equal 3, Ractor.make_shareable(tc).very_unique
end;
end
def test_too_complex_obj_ivar_ractor_share
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;