зеркало из https://github.com/github/ruby.git
merge revision(s) 54dbd8bea8a79bfcdefa471c1717c6cd28022f33: [Backport #19535]
Use an st table for "too complex" objects st tables will maintain insertion order so we can marshal dump / load objects with instance variables in the same order they were set on that particular instance [ruby-core:112926] [Bug #19535] Co-Authored-By: Jemma Issroff <jemmaissroff@gmail.com> --- gc.c | 10 ++++------ include/ruby/st.h | 2 ++ object.c | 2 +- ractor.c | 43 ++++++++++++++++++++++--------------------- shape.h | 6 +++--- st.c | 6 ++++++ test/ruby/test_shapes.rb | 21 +++++++++++++++++++++ variable.c | 28 ++++++++++++++-------------- vm_insnhelper.c | 2 +- 9 files changed, 74 insertions(+), 46 deletions(-)
This commit is contained in:
Родитель
a3911b965f
Коммит
fa72ba72f8
10
gc.c
10
gc.c
|
@ -3462,7 +3462,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
|
|||
case T_OBJECT:
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
RB_DEBUG_COUNTER_INC(obj_obj_too_complex);
|
||||
rb_id_table_free(ROBJECT_IV_HASH(obj));
|
||||
st_free_table(ROBJECT_IV_HASH(obj));
|
||||
}
|
||||
else if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) {
|
||||
RB_DEBUG_COUNTER_INC(obj_obj_embed);
|
||||
|
@ -4889,7 +4889,7 @@ obj_memsize_of(VALUE obj, int use_all_types)
|
|||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_OBJECT:
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
size += rb_id_table_memsize(ROBJECT_IV_HASH(obj));
|
||||
size += rb_st_memsize(ROBJECT_IV_HASH(obj));
|
||||
}
|
||||
else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
|
||||
size += ROBJECT_IV_CAPACITY(obj) * sizeof(VALUE);
|
||||
|
@ -7333,7 +7333,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
|
|||
{
|
||||
rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj));
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
mark_m_tbl(objspace, ROBJECT_IV_HASH(obj));
|
||||
mark_tbl_no_pin(objspace, ROBJECT_IV_HASH(obj));
|
||||
}
|
||||
else {
|
||||
const VALUE * const ptr = ROBJECT_IVPTR(obj);
|
||||
|
@ -10121,15 +10121,13 @@ gc_ref_update_array(rb_objspace_t * objspace, VALUE v)
|
|||
}
|
||||
}
|
||||
|
||||
static void update_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl);
|
||||
|
||||
static void
|
||||
gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
|
||||
{
|
||||
VALUE *ptr = ROBJECT_IVPTR(v);
|
||||
|
||||
if (rb_shape_obj_too_complex(v)) {
|
||||
update_m_tbl(objspace, ROBJECT_IV_HASH(v));
|
||||
rb_gc_update_tbl_refs(ROBJECT_IV_HASH(v));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,8 @@ struct st_table {
|
|||
|
||||
enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE};
|
||||
|
||||
size_t rb_st_table_size(const struct st_table *tbl);
|
||||
#define st_table_size rb_st_table_size
|
||||
st_table *rb_st_init_table(const struct st_hash_type *);
|
||||
#define st_init_table rb_st_init_table
|
||||
st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t);
|
||||
|
|
2
object.c
2
object.c
|
@ -275,7 +275,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
|
|||
rb_shape_t * src_shape = rb_shape_get_shape(obj);
|
||||
|
||||
if (rb_shape_id(src_shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
|
||||
struct rb_id_table * table = rb_id_table_create(rb_id_table_size(ROBJECT_IV_HASH(obj)));
|
||||
st_table * table = rb_st_init_numtable_with_size(rb_st_table_size(ROBJECT_IV_HASH(obj)));
|
||||
|
||||
rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
|
||||
rb_shape_set_too_complex(dest);
|
||||
|
|
43
ractor.c
43
ractor.c
|
@ -2250,17 +2250,17 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
|
|||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static enum rb_id_table_iterator_result
|
||||
obj_hash_iv_traverse_i(VALUE val, void *ptr)
|
||||
static int
|
||||
obj_hash_iv_traverse_i(st_data_t key, st_data_t val, st_data_t ptr)
|
||||
{
|
||||
struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
|
||||
|
||||
if (obj_traverse_i(val, d->data)) {
|
||||
if (obj_traverse_i((VALUE)val, d->data)) {
|
||||
d->stop = true;
|
||||
return ID_TABLE_STOP;
|
||||
return ST_STOP;
|
||||
}
|
||||
|
||||
return ID_TABLE_CONTINUE;
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2326,7 +2326,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
|
|||
.stop = false,
|
||||
.data = data,
|
||||
};
|
||||
rb_id_table_foreach_values(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, &d);
|
||||
rb_st_foreach(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, (st_data_t)&d);
|
||||
if (d.stop) return 1;
|
||||
}
|
||||
else {
|
||||
|
@ -2681,28 +2681,28 @@ obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int e
|
|||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static enum rb_id_table_iterator_result
|
||||
obj_iv_hash_traverse_replace_foreach_i(VALUE val, void *data)
|
||||
static int
|
||||
obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
|
||||
{
|
||||
return ID_TABLE_REPLACE;
|
||||
return ST_REPLACE;
|
||||
}
|
||||
|
||||
static enum rb_id_table_iterator_result
|
||||
obj_iv_hash_traverse_replace_i(VALUE *val, void *ptr, int exists)
|
||||
static int
|
||||
obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
|
||||
{
|
||||
struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr;
|
||||
struct obj_traverse_replace_data *data = d->data;
|
||||
|
||||
if (obj_traverse_replace_i(*val, data)) {
|
||||
if (obj_traverse_replace_i(*(VALUE *)val, data)) {
|
||||
d->stop = true;
|
||||
return ID_TABLE_STOP;
|
||||
return ST_STOP;
|
||||
}
|
||||
else if (*val != data->replacement) {
|
||||
VALUE v = *val = data->replacement;
|
||||
else if (*(VALUE *)val != data->replacement) {
|
||||
VALUE v = *(VALUE *)val = data->replacement;
|
||||
RB_OBJ_WRITTEN(d->src, Qundef, v);
|
||||
}
|
||||
|
||||
return ID_TABLE_CONTINUE;
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static struct st_table *
|
||||
|
@ -2806,16 +2806,17 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
|
|||
case T_OBJECT:
|
||||
{
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
struct rb_id_table * table = ROBJECT_IV_HASH(obj);
|
||||
st_table * table = ROBJECT_IV_HASH(obj);
|
||||
struct obj_traverse_replace_callback_data d = {
|
||||
.stop = false,
|
||||
.data = data,
|
||||
.src = obj,
|
||||
};
|
||||
rb_id_table_foreach_values_with_replace(table,
|
||||
obj_iv_hash_traverse_replace_foreach_i,
|
||||
obj_iv_hash_traverse_replace_i,
|
||||
(void *)&d);
|
||||
rb_st_foreach_with_replace(
|
||||
table,
|
||||
obj_iv_hash_traverse_replace_foreach_i,
|
||||
obj_iv_hash_traverse_replace_i,
|
||||
(st_data_t)&d);
|
||||
}
|
||||
else {
|
||||
#if USE_TRANSIENT_HEAP
|
||||
|
|
6
shape.h
6
shape.h
|
@ -162,12 +162,12 @@ ROBJECT_IV_CAPACITY(VALUE obj)
|
|||
return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->capacity;
|
||||
}
|
||||
|
||||
static inline struct rb_id_table *
|
||||
static inline st_table *
|
||||
ROBJECT_IV_HASH(VALUE obj)
|
||||
{
|
||||
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
|
||||
RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
|
||||
return (struct rb_id_table *)ROBJECT(obj)->as.heap.ivptr;
|
||||
return (st_table *)ROBJECT(obj)->as.heap.ivptr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -184,7 +184,7 @@ static inline uint32_t
|
|||
ROBJECT_IV_COUNT(VALUE obj)
|
||||
{
|
||||
if (ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID) {
|
||||
return (uint32_t)rb_id_table_size(ROBJECT_IV_HASH(obj));
|
||||
return (uint32_t)rb_st_table_size(ROBJECT_IV_HASH(obj));
|
||||
}
|
||||
else {
|
||||
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
|
||||
|
|
6
st.c
6
st.c
|
@ -569,6 +569,12 @@ st_init_table_with_size(const struct st_hash_type *type, st_index_t size)
|
|||
return tab;
|
||||
}
|
||||
|
||||
size_t
|
||||
st_table_size(const struct st_table *tbl)
|
||||
{
|
||||
return tbl->num_entries;
|
||||
}
|
||||
|
||||
/* Create and return table with TYPE which can hold a minimal number
|
||||
of entries (see comments for get_power2). */
|
||||
st_table *
|
||||
|
|
|
@ -3,6 +3,17 @@ require 'test/unit'
|
|||
|
||||
# These test the functionality of object shapes
|
||||
class TestShapes < Test::Unit::TestCase
|
||||
class IVOrder
|
||||
def expected_ivs
|
||||
%w{ @a @b @c @d @e @f @g @h @i @j @k }
|
||||
end
|
||||
|
||||
def set_ivs
|
||||
expected_ivs.each { instance_variable_set(_1, 1) }
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class ShapeOrder
|
||||
def initialize
|
||||
@b = :b # 5 => 6
|
||||
|
@ -80,6 +91,16 @@ class TestShapes < Test::Unit::TestCase
|
|||
refute_equal(shape1.id, shape2.id)
|
||||
end
|
||||
|
||||
def test_iv_order_correct_on_complex_objects
|
||||
(RubyVM::Shape::SHAPE_MAX_VARIATIONS + 1).times {
|
||||
IVOrder.new.instance_variable_set("@a#{_1}", 1)
|
||||
}
|
||||
|
||||
obj = IVOrder.new
|
||||
iv_list = obj.set_ivs.instance_variables
|
||||
assert_equal obj.expected_ivs, iv_list.map(&:to_s)
|
||||
end
|
||||
|
||||
def test_too_complex
|
||||
ensure_complex
|
||||
|
||||
|
|
28
variable.c
28
variable.c
|
@ -1176,9 +1176,9 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
|||
shape_id = ROBJECT_SHAPE_ID(obj);
|
||||
#endif
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
struct rb_id_table * iv_table = ROBJECT_IV_HASH(obj);
|
||||
st_table * iv_table = ROBJECT_IV_HASH(obj);
|
||||
VALUE val;
|
||||
if (rb_id_table_lookup(iv_table, id, &val)) {
|
||||
if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
|
||||
return val;
|
||||
}
|
||||
else {
|
||||
|
@ -1435,7 +1435,7 @@ rb_grow_iv_list(VALUE obj)
|
|||
int
|
||||
rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg)
|
||||
{
|
||||
rb_id_table_insert((struct rb_id_table *)arg, key, val);
|
||||
st_insert((st_table *)arg, (st_data_t)key, (st_data_t)val);
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -1448,8 +1448,8 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
|
|||
uint32_t num_iv = shape->capacity;
|
||||
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
struct rb_id_table * table = ROBJECT_IV_HASH(obj);
|
||||
rb_id_table_insert(table, id, val);
|
||||
st_table * table = ROBJECT_IV_HASH(obj);
|
||||
st_insert(table, (st_data_t)id, (st_data_t)val);
|
||||
RB_OBJ_WRITTEN(obj, Qundef, val);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1472,13 +1472,13 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
|
|||
rb_shape_t *next_shape = rb_shape_get_next(shape, obj, id);
|
||||
|
||||
if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
|
||||
struct rb_id_table * table = rb_id_table_create(shape->next_iv_index);
|
||||
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
|
||||
rb_id_table_insert(table, id, val);
|
||||
st_insert(table, (st_data_t)id, (st_data_t)val);
|
||||
RB_OBJ_WRITTEN(obj, Qundef, val);
|
||||
|
||||
rb_shape_set_too_complex(obj);
|
||||
|
@ -1627,7 +1627,7 @@ rb_ivar_defined(VALUE obj, ID id)
|
|||
if (SPECIAL_CONST_P(obj)) return Qfalse;
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
VALUE idx;
|
||||
if (!rb_id_table_lookup(ROBJECT_IV_HASH(obj), id, &idx)) {
|
||||
if (!rb_st_lookup(ROBJECT_IV_HASH(obj), id, &idx)) {
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
|
@ -1686,12 +1686,12 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu
|
|||
}
|
||||
}
|
||||
|
||||
static enum rb_id_table_iterator_result
|
||||
each_hash_iv(ID id, VALUE val, void *data)
|
||||
static int
|
||||
each_hash_iv(st_data_t id, st_data_t val, st_data_t data)
|
||||
{
|
||||
struct iv_itr_data * itr_data = (struct iv_itr_data *)data;
|
||||
rb_ivar_foreach_callback_func *callback = itr_data->func;
|
||||
return callback(id, val, itr_data->arg);
|
||||
return callback((ID)id, (VALUE)val, itr_data->arg);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1703,7 +1703,7 @@ obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
|||
itr_data.arg = arg;
|
||||
itr_data.func = func;
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
rb_id_table_foreach(ROBJECT_IV_HASH(obj), each_hash_iv, &itr_data);
|
||||
rb_st_foreach(ROBJECT_IV_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
else {
|
||||
iterate_over_shapes_with_callback(shape, func, &itr_data);
|
||||
|
@ -1997,8 +1997,8 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name)
|
|||
break;
|
||||
case T_OBJECT: {
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
if (rb_id_table_lookup(ROBJECT_IV_HASH(obj), id, &val)) {
|
||||
rb_id_table_delete(ROBJECT_IV_HASH(obj), id);
|
||||
if (rb_st_lookup(ROBJECT_IV_HASH(obj), (st_data_t)id, (st_data_t *)&val)) {
|
||||
rb_st_delete(ROBJECT_IV_HASH(obj), (st_data_t *)&id, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
|
||||
#define RUBY_VERSION_TEENY 2
|
||||
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
|
||||
#define RUBY_PATCHLEVEL 98
|
||||
#define RUBY_PATCHLEVEL 99
|
||||
|
||||
#include "ruby/version.h"
|
||||
#include "ruby/internal/abi.h"
|
||||
|
|
|
@ -1256,7 +1256,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
|
|||
rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
|
||||
|
||||
if (shape_id == OBJ_TOO_COMPLEX_SHAPE_ID) {
|
||||
if (!rb_id_table_lookup(ROBJECT_IV_HASH(obj), id, &val)) {
|
||||
if (!st_lookup(ROBJECT_IV_HASH(obj), id, &val)) {
|
||||
val = Qnil;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче