зеркало из https://github.com/github/ruby.git
remove IV limit / support complex shapes on classes
This commit is contained in:
Родитель
27c7531939
Коммит
caf6a72348
17
shape.c
17
shape.c
|
@ -348,7 +348,7 @@ shape_alloc(void)
|
|||
shape_id_t shape_id = GET_SHAPE_TREE()->next_shape_id;
|
||||
GET_SHAPE_TREE()->next_shape_id++;
|
||||
|
||||
if (shape_id == MAX_SHAPE_ID) {
|
||||
if (shape_id == (MAX_SHAPE_ID + 1)) {
|
||||
// TODO: Make an OutOfShapesError ??
|
||||
rb_bug("Out of shapes");
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, b
|
|||
|
||||
*variation_created = false;
|
||||
|
||||
if (new_shape_necessary || (new_shapes_allowed && (shape->next_iv_index < SHAPE_MAX_NUM_IVS))) {
|
||||
if (new_shape_necessary || (new_shapes_allowed && (GET_SHAPE_TREE()->next_shape_id <= MAX_SHAPE_ID))) {
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
// If the current shape has children
|
||||
|
@ -633,6 +633,7 @@ rb_shape_t *
|
|||
rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
|
||||
{
|
||||
RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
|
||||
RUBY_ASSERT(shape->type != SHAPE_OBJ_TOO_COMPLEX);
|
||||
|
||||
bool allow_new_shape = true;
|
||||
|
||||
|
@ -643,11 +644,10 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
|
|||
|
||||
bool variation_created = false;
|
||||
// For non T_OBJECTS, force a new shape
|
||||
bool new_shape_necessary = BUILTIN_TYPE(obj) != T_OBJECT;
|
||||
bool new_shape_necessary = false;
|
||||
rb_shape_t * new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape, new_shape_necessary);
|
||||
|
||||
if (!new_shape) {
|
||||
RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
|
||||
new_shape = rb_shape_get_shape_by_id(OBJ_TOO_COMPLEX_SHAPE_ID);
|
||||
}
|
||||
|
||||
|
@ -850,7 +850,6 @@ rb_shape_obj_too_complex(VALUE obj)
|
|||
void
|
||||
rb_shape_set_too_complex(VALUE obj)
|
||||
{
|
||||
RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
|
||||
rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
|
||||
}
|
||||
|
@ -1005,6 +1004,13 @@ rb_shape_root_shape(VALUE self)
|
|||
return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape());
|
||||
}
|
||||
|
||||
/* :nodoc: */
|
||||
static VALUE
|
||||
rb_shape_shapes_available(VALUE self)
|
||||
{
|
||||
return INT2NUM(MAX_SHAPE_ID - (GET_SHAPE_TREE()->next_shape_id - 1));
|
||||
}
|
||||
|
||||
VALUE rb_obj_shape(rb_shape_t* shape);
|
||||
|
||||
static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref)
|
||||
|
@ -1183,5 +1189,6 @@ Init_shape(void)
|
|||
rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1);
|
||||
rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1);
|
||||
rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0);
|
||||
rb_define_singleton_method(rb_cShape, "shapes_available", rb_shape_shapes_available, 0);
|
||||
#endif
|
||||
}
|
||||
|
|
4
shape.h
4
shape.h
|
@ -35,7 +35,7 @@ typedef uint16_t redblack_id_t;
|
|||
# define SHAPE_MAX_VARIATIONS 8
|
||||
# define SHAPE_MAX_NUM_IVS (SHAPE_BUFFER_SIZE - 1)
|
||||
|
||||
# define MAX_SHAPE_ID SHAPE_BUFFER_SIZE
|
||||
# define MAX_SHAPE_ID (SHAPE_BUFFER_SIZE - 1)
|
||||
# define INVALID_SHAPE_ID SHAPE_MASK
|
||||
# define ROOT_SHAPE_ID 0x0
|
||||
|
||||
|
@ -191,7 +191,7 @@ ROBJECT_IV_HASH(VALUE obj)
|
|||
}
|
||||
|
||||
static inline void
|
||||
ROBJECT_SET_IV_HASH(VALUE obj, const struct rb_id_table *tbl)
|
||||
ROBJECT_SET_IV_HASH(VALUE obj, const st_table *tbl)
|
||||
{
|
||||
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
|
||||
RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
|
||||
|
|
212
variable.c
212
variable.c
|
@ -72,6 +72,22 @@ struct ivar_update {
|
|||
#endif
|
||||
};
|
||||
|
||||
static inline st_table *
|
||||
RCLASS_IV_HASH(VALUE obj)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
RUBY_ASSERT(RCLASS_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
|
||||
return (st_table *)RCLASS_IVPTR(obj);
|
||||
}
|
||||
|
||||
static inline void
|
||||
RCLASS_SET_IV_HASH(VALUE obj, const st_table *tbl)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
RUBY_ASSERT(RCLASS_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
|
||||
RCLASS_IVPTR(obj) = (VALUE *)tbl;
|
||||
}
|
||||
|
||||
void
|
||||
Init_var_tables(void)
|
||||
{
|
||||
|
@ -1231,7 +1247,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
|||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
{
|
||||
bool found;
|
||||
bool found = false;
|
||||
VALUE val;
|
||||
|
||||
RB_VM_LOCK_ENTER();
|
||||
|
@ -1240,18 +1256,30 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
|||
shape_id = RCLASS_SHAPE_ID(obj);
|
||||
#endif
|
||||
|
||||
attr_index_t index = 0;
|
||||
shape = rb_shape_get_shape_by_id(shape_id);
|
||||
found = rb_shape_get_iv_index(shape, id, &index);
|
||||
|
||||
if (found) {
|
||||
ivar_list = RCLASS_IVPTR(obj);
|
||||
RUBY_ASSERT(ivar_list);
|
||||
|
||||
val = ivar_list[index];
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
st_table * iv_table = RCLASS_IV_HASH(obj);
|
||||
if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
val = undef;
|
||||
}
|
||||
}
|
||||
else {
|
||||
val = undef;
|
||||
|
||||
attr_index_t index = 0;
|
||||
shape = rb_shape_get_shape_by_id(shape_id);
|
||||
found = rb_shape_get_iv_index(shape, id, &index);
|
||||
|
||||
if (found) {
|
||||
ivar_list = RCLASS_IVPTR(obj);
|
||||
RUBY_ASSERT(ivar_list);
|
||||
|
||||
val = ivar_list[index];
|
||||
}
|
||||
else {
|
||||
val = undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
|
@ -1439,6 +1467,60 @@ rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize)
|
|||
return ivtbl;
|
||||
}
|
||||
|
||||
static int
|
||||
rb_complex_ivar_set(VALUE obj, ID id, VALUE val)
|
||||
{
|
||||
st_table * table;
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex(obj));
|
||||
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_OBJECT:
|
||||
table = ROBJECT_IV_HASH(obj);
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
table = RCLASS_IV_HASH(obj);
|
||||
break;
|
||||
default:
|
||||
rb_bug("oh no");
|
||||
}
|
||||
|
||||
int found = st_insert(table, (st_data_t)id, (st_data_t)val);
|
||||
RB_OBJ_WRITTEN(obj, Qundef, val);
|
||||
return found;
|
||||
}
|
||||
|
||||
static void
|
||||
rb_evict_ivars_to_hash(VALUE obj, rb_shape_t * shape)
|
||||
{
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
|
||||
|
||||
st_table * table = st_init_numtable_with_size(shape->next_iv_index);
|
||||
|
||||
// Evacuate all previous values from shape into id_table
|
||||
rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
|
||||
|
||||
rb_shape_set_too_complex(obj);
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex(obj));
|
||||
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_OBJECT:
|
||||
if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
|
||||
xfree(ROBJECT(obj)->as.heap.ivptr);
|
||||
}
|
||||
|
||||
ROBJECT_SET_IV_HASH(obj, table);
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
xfree(RCLASS_IVPTR(obj));
|
||||
RCLASS_SET_IV_HASH(obj, table);
|
||||
break;
|
||||
default:
|
||||
rb_bug("oops!");
|
||||
}
|
||||
}
|
||||
|
||||
// @note May raise when there are too many instance variables.
|
||||
rb_shape_t *
|
||||
rb_grow_iv_list(VALUE obj)
|
||||
|
@ -1446,10 +1528,13 @@ rb_grow_iv_list(VALUE obj)
|
|||
rb_shape_t * initial_shape = rb_shape_get_shape(obj);
|
||||
RUBY_ASSERT(initial_shape->capacity > 0);
|
||||
rb_shape_t * res = rb_shape_transition_shape_capa(initial_shape);
|
||||
|
||||
rb_ensure_iv_list_size(obj, initial_shape->capacity, res->capacity);
|
||||
|
||||
rb_shape_set_shape(obj, res);
|
||||
if (res->type == SHAPE_OBJ_TOO_COMPLEX) { // Out of shapes
|
||||
rb_evict_ivars_to_hash(obj, initial_shape);
|
||||
}
|
||||
else {
|
||||
rb_ensure_iv_list_size(obj, initial_shape->capacity, res->capacity);
|
||||
rb_shape_set_shape(obj, res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -1470,12 +1555,12 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
|
|||
uint32_t num_iv = shape->capacity;
|
||||
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
st_table * table = ROBJECT_IV_HASH(obj);
|
||||
st_insert(table, (st_data_t)id, (st_data_t)val);
|
||||
RB_OBJ_WRITTEN(obj, Qundef, val);
|
||||
rb_complex_ivar_set(obj, id, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rb_shape_t *next_shape;
|
||||
|
||||
if (!rb_shape_get_iv_index(shape, id, &index)) {
|
||||
index = shape->next_iv_index;
|
||||
if (index >= MAX_IVARS) {
|
||||
|
@ -1487,31 +1572,20 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
|
|||
if (UNLIKELY(shape->next_iv_index >= num_iv)) {
|
||||
RUBY_ASSERT(shape->next_iv_index == num_iv);
|
||||
|
||||
shape = rb_grow_iv_list(obj);
|
||||
next_shape = rb_grow_iv_list(obj);
|
||||
if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
|
||||
rb_complex_ivar_set(obj, id, val);
|
||||
return 0;
|
||||
}
|
||||
shape = next_shape;
|
||||
RUBY_ASSERT(shape->type == SHAPE_CAPACITY_CHANGE);
|
||||
}
|
||||
|
||||
rb_shape_t *next_shape = rb_shape_get_next(shape, obj, id);
|
||||
next_shape = rb_shape_get_next(shape, obj, id);
|
||||
|
||||
if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
|
||||
st_table * table = st_init_numtable_with_size(shape->next_iv_index);
|
||||
|
||||
// Evacuate all previous values from shape into id_table
|
||||
rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
|
||||
|
||||
// Insert new value too
|
||||
st_insert(table, (st_data_t)id, (st_data_t)val);
|
||||
RB_OBJ_WRITTEN(obj, Qundef, val);
|
||||
|
||||
rb_shape_set_too_complex(obj);
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex(obj));
|
||||
|
||||
if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
|
||||
xfree(ROBJECT(obj)->as.heap.ivptr);
|
||||
}
|
||||
|
||||
ROBJECT(obj)->as.heap.ivptr = (VALUE *)table;
|
||||
|
||||
rb_evict_ivars_to_hash(obj, shape);
|
||||
rb_complex_ivar_set(obj, id, val);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
|
@ -1765,7 +1839,13 @@ class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
|||
struct iv_itr_data itr_data;
|
||||
itr_data.obj = obj;
|
||||
itr_data.arg = arg;
|
||||
iterate_over_shapes_with_callback(shape, func, &itr_data);
|
||||
itr_data.func = func;
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
rb_st_foreach(RCLASS_IV_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
else {
|
||||
iterate_over_shapes_with_callback(shape, func, &itr_data);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3989,40 +4069,50 @@ int
|
|||
rb_class_ivar_set(VALUE obj, ID key, VALUE value)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
|
||||
int found;
|
||||
int found = 0;
|
||||
rb_check_frozen(obj);
|
||||
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
rb_shape_t * shape = rb_shape_get_shape(obj);
|
||||
attr_index_t idx;
|
||||
found = rb_shape_get_iv_index(shape, key, &idx);
|
||||
|
||||
if (found) {
|
||||
// Changing an existing instance variable
|
||||
RUBY_ASSERT(RCLASS_IVPTR(obj));
|
||||
|
||||
RCLASS_IVPTR(obj)[idx] = value;
|
||||
RB_OBJ_WRITTEN(obj, Qundef, value);
|
||||
if (shape->type == SHAPE_OBJ_TOO_COMPLEX) {
|
||||
found = rb_complex_ivar_set(obj, key, value);
|
||||
}
|
||||
else {
|
||||
// Creating and setting a new instance variable
|
||||
attr_index_t idx;
|
||||
found = rb_shape_get_iv_index(shape, key, &idx);
|
||||
|
||||
// Move to a shape which fits the new ivar
|
||||
idx = shape->next_iv_index;
|
||||
shape = rb_shape_get_next(shape, obj, key);
|
||||
if (found) {
|
||||
// Changing an existing instance variable
|
||||
RUBY_ASSERT(RCLASS_IVPTR(obj));
|
||||
|
||||
// We always allocate a power of two sized IV array. This way we
|
||||
// only need to realloc when we expand into a new power of two size
|
||||
if ((idx & (idx - 1)) == 0) {
|
||||
size_t newsize = idx ? idx * 2 : 1;
|
||||
REALLOC_N(RCLASS_IVPTR(obj), VALUE, newsize);
|
||||
RCLASS_IVPTR(obj)[idx] = value;
|
||||
RB_OBJ_WRITTEN(obj, Qundef, value);
|
||||
}
|
||||
else {
|
||||
// Creating and setting a new instance variable
|
||||
|
||||
RUBY_ASSERT(RCLASS_IVPTR(obj));
|
||||
// Move to a shape which fits the new ivar
|
||||
idx = shape->next_iv_index;
|
||||
rb_shape_t * next_shape = rb_shape_get_next(shape, obj, key);
|
||||
if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
|
||||
rb_evict_ivars_to_hash(obj, shape);
|
||||
rb_complex_ivar_set(obj, key, value);
|
||||
}
|
||||
else {
|
||||
// We always allocate a power of two sized IV array. This way we
|
||||
// only need to realloc when we expand into a new power of two size
|
||||
if ((idx & (idx - 1)) == 0) {
|
||||
size_t newsize = idx ? idx * 2 : 1;
|
||||
REALLOC_N(RCLASS_IVPTR(obj), VALUE, newsize);
|
||||
}
|
||||
|
||||
RB_OBJ_WRITE(obj, &RCLASS_IVPTR(obj)[idx], value);
|
||||
rb_shape_set_shape(obj, shape);
|
||||
RUBY_ASSERT(RCLASS_IVPTR(obj));
|
||||
|
||||
RB_OBJ_WRITE(obj, &RCLASS_IVPTR(obj)[idx], value);
|
||||
rb_shape_set_shape(obj, next_shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
|
|
Загрузка…
Ссылка в новой задаче