зеркало из https://github.com/github/ruby.git
Differentiate T_OBJECT shapes from other objects
We would like to differentiate types of objects via their shape. This commit adds a special T_OBJECT shape when we allocate an instance of T_OBJECT. This allows us to avoid testing whether an object is an instance of a T_OBJECT or not, we can just check the shape.
This commit is contained in:
Родитель
f0ce118662
Коммит
10788166e7
8
gc.c
8
gc.c
|
@ -2942,6 +2942,12 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected)
|
|||
#endif
|
||||
|
||||
VALUE obj = newobj_of(klass, flags, 0, 0, 0, wb_protected, size);
|
||||
RUBY_ASSERT(rb_shape_get_shape(obj)->type == SHAPE_ROOT ||
|
||||
rb_shape_get_shape(obj)->type == SHAPE_INITIAL_CAPACITY);
|
||||
|
||||
// Set the shape to the specific T_OBJECT shape which is always
|
||||
// SIZE_POOL_COUNT away from the root shape.
|
||||
ROBJECT_SET_SHAPE_ID(obj, ROBJECT_SHAPE_ID(obj) + SIZE_POOL_COUNT);
|
||||
|
||||
#if RUBY_DEBUG
|
||||
VALUE *ptr = ROBJECT_IVPTR(obj);
|
||||
|
@ -10030,7 +10036,7 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
|
|||
}
|
||||
ptr = ROBJECT(v)->as.ary;
|
||||
size_t size_pool_shape_id = size_pool_idx_for_size(embed_size);
|
||||
rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id);
|
||||
rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id + SIZE_POOL_COUNT);
|
||||
rb_shape_t * new_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_shape(v));
|
||||
rb_shape_set_shape(v, new_shape);
|
||||
}
|
||||
|
|
2
object.c
2
object.c
|
@ -293,7 +293,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
|
|||
rb_shape_t * initial_shape = rb_shape_get_shape(dest);
|
||||
|
||||
if (initial_shape->size_pool_index != src_shape->size_pool_index) {
|
||||
RUBY_ASSERT(initial_shape->parent_id == ROOT_SHAPE_ID || initial_shape->type == SHAPE_ROOT);
|
||||
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
|
||||
|
||||
shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape);
|
||||
}
|
||||
|
|
23
shape.c
23
shape.c
|
@ -8,6 +8,7 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
static ID id_frozen;
|
||||
static ID id_t_object;
|
||||
static ID size_pool_edge_names[SIZE_POOL_COUNT];
|
||||
|
||||
/*
|
||||
|
@ -152,6 +153,7 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type)
|
|||
case SHAPE_CAPACITY_CHANGE:
|
||||
case SHAPE_IVAR_UNDEF:
|
||||
case SHAPE_FROZEN:
|
||||
case SHAPE_T_OBJECT:
|
||||
new_shape->next_iv_index = shape->next_iv_index;
|
||||
break;
|
||||
case SHAPE_INITIAL_CAPACITY:
|
||||
|
@ -264,6 +266,7 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
|
|||
case SHAPE_IVAR_UNDEF:
|
||||
case SHAPE_ROOT:
|
||||
case SHAPE_INITIAL_CAPACITY:
|
||||
case SHAPE_T_OBJECT:
|
||||
return false;
|
||||
case SHAPE_FROZEN:
|
||||
rb_bug("Ivar should not exist on transition\n");
|
||||
|
@ -333,14 +336,16 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
|
|||
{
|
||||
rb_shape_t * midway_shape;
|
||||
|
||||
if (dest_shape->type != SHAPE_ROOT) {
|
||||
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
|
||||
|
||||
if (dest_shape->type != initial_shape->type) {
|
||||
midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape));
|
||||
}
|
||||
else {
|
||||
midway_shape = initial_shape;
|
||||
}
|
||||
|
||||
switch (dest_shape->type) {
|
||||
switch ((enum shape_type)dest_shape->type) {
|
||||
case SHAPE_IVAR:
|
||||
if (midway_shape->capacity <= midway_shape->next_iv_index) {
|
||||
// There isn't enough room to write this IV, so we need to increase the capacity
|
||||
|
@ -355,6 +360,8 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
|
|||
case SHAPE_ROOT:
|
||||
case SHAPE_FROZEN:
|
||||
case SHAPE_CAPACITY_CHANGE:
|
||||
case SHAPE_INITIAL_CAPACITY:
|
||||
case SHAPE_T_OBJECT:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -592,6 +599,7 @@ void
|
|||
Init_default_shapes(void)
|
||||
{
|
||||
id_frozen = rb_make_internal_id();
|
||||
id_t_object = rb_make_internal_id();
|
||||
|
||||
// Shapes by size pool
|
||||
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
|
||||
|
@ -615,6 +623,16 @@ Init_default_shapes(void)
|
|||
RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i);
|
||||
}
|
||||
|
||||
// Make shapes for T_OBJECT
|
||||
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
|
||||
rb_shape_t * shape = rb_shape_get_shape_by_id(i);
|
||||
#if RUBY_DEBUG
|
||||
rb_shape_t * t_object_shape =
|
||||
#endif
|
||||
get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT);
|
||||
RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT));
|
||||
}
|
||||
|
||||
// Special const shape
|
||||
#if RUBY_DEBUG
|
||||
rb_shape_t * special_const_shape =
|
||||
|
@ -644,6 +662,7 @@ Init_shape(void)
|
|||
rb_define_method(rb_cShape, "capacity", rb_shape_capacity, 0);
|
||||
rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT));
|
||||
rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR));
|
||||
rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT));
|
||||
rb_define_const(rb_cShape, "SHAPE_IVAR_UNDEF", INT2NUM(SHAPE_IVAR_UNDEF));
|
||||
rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN));
|
||||
rb_define_const(rb_cShape, "SHAPE_BITS", INT2NUM(SHAPE_BITS));
|
||||
|
|
3
shape.h
3
shape.h
|
@ -42,7 +42,7 @@ typedef uint16_t shape_id_t;
|
|||
# define ROOT_SHAPE_ID 0x0
|
||||
// We use SIZE_POOL_COUNT number of shape IDs for transitions out of different size pools
|
||||
// The next available shapd ID will be the SPECIAL_CONST_SHAPE_ID
|
||||
# define SPECIAL_CONST_SHAPE_ID SIZE_POOL_COUNT
|
||||
# define SPECIAL_CONST_SHAPE_ID (SIZE_POOL_COUNT * 2)
|
||||
|
||||
struct rb_shape {
|
||||
struct rb_id_table * edges; // id_table from ID (ivar) to next shape
|
||||
|
@ -63,6 +63,7 @@ enum shape_type {
|
|||
SHAPE_CAPACITY_CHANGE,
|
||||
SHAPE_IVAR_UNDEF,
|
||||
SHAPE_INITIAL_CAPACITY,
|
||||
SHAPE_T_OBJECT,
|
||||
};
|
||||
|
||||
#if SHAPE_IN_BASIC_FLAGS
|
||||
|
|
|
@ -88,8 +88,8 @@ class TestShapes < Test::Unit::TestCase
|
|||
|
||||
class TestObject; end
|
||||
|
||||
def test_new_obj_has_root_shape
|
||||
assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new))
|
||||
def test_new_obj_has_t_object_shape
|
||||
assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new).parent)
|
||||
end
|
||||
|
||||
def test_str_has_root_shape
|
||||
|
@ -114,14 +114,23 @@ class TestShapes < Test::Unit::TestCase
|
|||
|
||||
def test_basic_shape_transition
|
||||
obj = Example.new
|
||||
refute_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(obj))
|
||||
assert_shape_equal(RubyVM::Shape.root_shape.edges[:@a], RubyVM::Shape.of(obj))
|
||||
shape = RubyVM::Shape.of(obj)
|
||||
refute_equal(RubyVM::Shape.root_shape, shape)
|
||||
assert_equal :@a, shape.edge_name
|
||||
assert_equal RubyVM::Shape::SHAPE_IVAR, shape.type
|
||||
|
||||
shape = shape.parent
|
||||
assert_equal RubyVM::Shape::SHAPE_T_OBJECT, shape.type
|
||||
|
||||
shape = shape.parent
|
||||
assert_equal(RubyVM::Shape.root_shape.id, shape.id)
|
||||
assert_equal(obj.instance_variable_get(:@a), 1)
|
||||
end
|
||||
|
||||
def test_different_objects_make_same_transition
|
||||
obj = Example.new
|
||||
obj = []
|
||||
obj2 = ""
|
||||
obj.instance_variable_set(:@a, 1)
|
||||
obj2.instance_variable_set(:@a, 1)
|
||||
assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2))
|
||||
end
|
||||
|
|
|
@ -1606,6 +1606,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu
|
|||
case SHAPE_CAPACITY_CHANGE:
|
||||
case SHAPE_FROZEN:
|
||||
case SHAPE_IVAR_UNDEF:
|
||||
case SHAPE_T_OBJECT:
|
||||
iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data);
|
||||
return;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче