From ad63b668e22e21c352b852f3119ae98a7acf99f1 Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Mon, 3 Oct 2022 11:14:32 -0400 Subject: Revert "Revert "This commit implements the Object Shapes technique in CRuby."" This reverts commit 9a6803c90b817f70389cae10d60b50ad752da48f. --- test/-ext-/marshal/test_internal_ivar.rb | 1 + test/ruby/test_mjit.rb | 4 +- test/ruby/test_object.rb | 9 ++ test/ruby/test_shapes.rb | 173 +++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 test/ruby/test_shapes.rb (limited to 'test') diff --git a/test/-ext-/marshal/test_internal_ivar.rb b/test/-ext-/marshal/test_internal_ivar.rb index a32138f6e8..9359c7f113 100644 --- a/test/-ext-/marshal/test_internal_ivar.rb +++ b/test/-ext-/marshal/test_internal_ivar.rb @@ -7,6 +7,7 @@ module Bug end module Bug::Marshal class TestInternalIVar < Test::Unit::TestCase def test_marshal + pend "We don't support IVs with ID of 0" v = InternalIVar.new("hello", "world", "bye") assert_equal("hello", v.normal) assert_equal("world", v.internal) diff --git a/test/ruby/test_mjit.rb b/test/ruby/test_mjit.rb index e49195f763..4c6cc6f39f 100644 --- a/test/ruby/test_mjit.rb +++ b/test/ruby/test_mjit.rb @@ -831,7 +831,7 @@ class TestMJIT < Test::Unit::TestCase end def test_inlined_exivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 3, recompile_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 4, recompile_count: 2, min_calls: 2) begin; class Foo < Hash def initialize @@ -850,7 +850,7 @@ class TestMJIT < Test::Unit::TestCase end def test_inlined_undefined_ivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 3, min_calls: 3) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 2, min_calls: 2) begin; class Foo def initialize diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index 83208bbcdb..a9d5d4b13e 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -993,4 +993,13 @@ class TestObject < Test::Unit::TestCase end EOS end + + def test_frozen_inspect + obj = Object.new + obj.instance_variable_set(:@a, "a") + ins = obj.inspect + obj.freeze + + assert_equal(ins, obj.inspect) + end end diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb new file mode 100644 index 0000000000..807d485354 --- /dev/null +++ b/test/ruby/test_shapes.rb @@ -0,0 +1,173 @@ +# frozen_string_literal: false +require 'test/unit' + +# These test the functionality of object shapes +class TestShapes < Test::Unit::TestCase + class Example + def initialize + @a = 1 + end + end + + class RemoveAndAdd + def add_foo + @foo = 1 + end + + def remove + remove_instance_variable(:@foo) + end + + def add_bar + @bar = 1 + end + end + + # RubyVM.debug_shape returns new instances of shape objects for + # each call. This helper method allows us to define equality for + # shapes + def assert_shape_equal(shape1, shape2) + assert_equal(shape1.id, shape2.id) + assert_equal(shape1.parent_id, shape2.parent_id) + assert_equal(shape1.depth, shape2.depth) + assert_equal(shape1.type, shape2.type) + end + + def refute_shape_equal(shape1, shape2) + refute_equal(shape1.id, shape2.id) + end + + def test_iv_index + example = RemoveAndAdd.new + shape = RubyVM.debug_shape(example) + assert_equal 0, shape.iv_count + + example.add_foo # makes a transition + new_shape = RubyVM.debug_shape(example) + assert_equal([:@foo], example.instance_variables) + assert_equal(shape.id, new_shape.parent.id) + assert_equal(1, new_shape.iv_count) + + example.remove # makes a transition + remove_shape = RubyVM.debug_shape(example) + assert_equal([], example.instance_variables) + assert_equal(new_shape.id, remove_shape.parent.id) + assert_equal(1, remove_shape.iv_count) + + example.add_bar # makes a transition + bar_shape = RubyVM.debug_shape(example) + assert_equal([:@bar], example.instance_variables) + assert_equal(remove_shape.id, bar_shape.parent.id) + assert_equal(2, bar_shape.iv_count) + end + + def test_new_obj_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(Object.new)) + end + + def test_frozen_new_obj_has_frozen_root_shape + assert_shape_equal( + RubyVM.debug_frozen_root_shape, + RubyVM.debug_shape(Object.new.freeze) + ) + end + + def test_str_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape("")) + end + + def test_array_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape([])) + end + + def test_hash_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape({})) + end + + def test_true_has_frozen_root_shape + assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(true)) + end + + def test_nil_has_frozen_root_shape + assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(nil)) + end + + def test_basic_shape_transition + obj = Example.new + refute_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(obj)) + assert_shape_equal(RubyVM.debug_root_shape.edges[:@a], RubyVM.debug_shape(obj)) + assert_equal(obj.instance_variable_get(:@a), 1) + end + + def test_different_objects_make_same_transition + obj = Example.new + obj2 = "" + obj2.instance_variable_set(:@a, 1) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_duplicating_objects + obj = Example.new + obj2 = obj.dup + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_freezing_and_duplicating_object + obj = Object.new.freeze + obj2 = obj.dup + refute_predicate(obj2, :frozen?) + # dup'd objects shouldn't be frozen, and the shape should be the + # parent shape of the copied object + assert_equal(RubyVM.debug_shape(obj).parent.id, RubyVM.debug_shape(obj2).id) + end + + def test_freezing_and_duplicating_object_with_ivars + obj = Example.new.freeze + obj2 = obj.dup + refute_predicate(obj2, :frozen?) + refute_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_equal(obj2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_duplicating_string_with_ivars + str = "str" + str.instance_variable_set(:@a, 1) + str.freeze + str2 = str.dup + refute_predicate(str2, :frozen?) + refute_equal(RubyVM.debug_shape(str).id, RubyVM.debug_shape(str2).id) + assert_equal(str2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_cloning_objects + obj = Object.new.freeze + obj2 = obj.clone(freeze: true) + assert_predicate(obj2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_freezing_and_cloning_object_with_ivars + obj = Example.new.freeze + obj2 = obj.clone(freeze: true) + assert_predicate(obj2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_equal(obj2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_cloning_string + str = "str".freeze + str2 = str.clone(freeze: true) + assert_predicate(str2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + end + + def test_freezing_and_cloning_string_with_ivars + str = "str" + str.instance_variable_set(:@a, 1) + str.freeze + str2 = str.clone(freeze: true) + assert_predicate(str2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + assert_equal(str2.instance_variable_get(:@a), 1) + end +end -- cgit v1.2.3