summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
authorJemma Issroff <jemmaissroff@gmail.com>2022-09-23 13:54:42 -0400
committerAaron Patterson <tenderlove@ruby-lang.org>2022-09-28 08:26:21 -0700
commitd594a5a8bd0756f65c078fcf5ce0098250cba141 (patch)
tree3930e12366c80e7bcbc330fe880205a3d212b5aa /variable.c
parenta05b2614645594df896aaf44a2e5701ee7fb5fec (diff)
This commit implements the Object Shapes technique in CRuby.
Object Shapes is used for accessing instance variables and representing the "frozenness" of objects. Object instances have a "shape" and the shape represents some attributes of the object (currently which instance variables are set and the "frozenness"). Shapes form a tree data structure, and when a new instance variable is set on an object, that object "transitions" to a new shape in the shape tree. Each shape has an ID that is used for caching. The shape structure is independent of class, so objects of different types can have the same shape. For example: ```ruby class Foo def initialize # Starts with shape id 0 @a = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end class Bar def initialize # Starts with shape id 0 @a = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end foo = Foo.new # `foo` has shape id 2 bar = Bar.new # `bar` has shape id 2 ``` Both `foo` and `bar` instances have the same shape because they both set instance variables of the same name in the same order. This technique can help to improve inline cache hits as well as generate more efficient machine code in JIT compilers. This commit also adds some methods for debugging shapes on objects. See `RubyVM::Shape` for more details. For more context on Object Shapes, see [Feature: #18776] Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org> Co-Authored-By: Eileen M. Uchitelle <eileencodes@gmail.com> Co-Authored-By: John Hawthorn <john@hawthorn.email>
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c771
1 files changed, 352 insertions, 419 deletions
diff --git a/variable.c b/variable.c
index 056a1000b8..a250bf5e20 100644
--- a/variable.c
+++ b/variable.c
@@ -34,6 +34,7 @@
#include "ruby/st.h"
#include "ruby/util.h"
#include "transient_heap.h"
+#include "shape.h"
#include "variable.h"
#include "vm_core.h"
#include "ractor_core.h"
@@ -63,12 +64,9 @@ static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int v
static st_table *generic_iv_tbl_;
struct ivar_update {
- union {
- st_table *iv_index_tbl;
- struct gen_ivtbl *ivtbl;
- } u;
- st_data_t index;
- int iv_extended;
+ struct gen_ivtbl *ivtbl;
+ uint32_t iv_index;
+ rb_shape_t* shape;
};
void
@@ -896,30 +894,6 @@ rb_alias_variable(ID name1, ID name2)
entry1->var = entry2->var;
}
-static bool
-iv_index_tbl_lookup(struct st_table *tbl, ID id, uint32_t *indexp)
-{
- st_data_t ent_data;
- int r;
-
- if (tbl == NULL) return false;
-
- RB_VM_LOCK_ENTER();
- {
- r = st_lookup(tbl, (st_data_t)id, &ent_data);
- }
- RB_VM_LOCK_LEAVE();
-
- if (r) {
- struct rb_iv_index_tbl_entry *ent = (void *)ent_data;
- *indexp = ent->index;
- return true;
- }
- else {
- return false;
- }
-}
-
static void
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(ID id)
{
@@ -957,7 +931,20 @@ generic_ivtbl_no_ractor_check(VALUE obj)
}
static int
-gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl)
+gen_ivtbl_get_unlocked(VALUE obj, ID id, struct gen_ivtbl **ivtbl)
+{
+ st_data_t data;
+
+ if (st_lookup(generic_ivtbl(obj, id, false), (st_data_t)obj, &data)) {
+ *ivtbl = (struct gen_ivtbl *)data;
+ return 1;
+ }
+
+ return 0;
+}
+
+MJIT_FUNC_EXPORTED int
+rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl)
{
st_data_t data;
int r = 0;
@@ -977,63 +964,7 @@ gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl)
MJIT_FUNC_EXPORTED int
rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **ivtbl)
{
- return gen_ivtbl_get(obj, 0, ivtbl);
-}
-
-MJIT_FUNC_EXPORTED VALUE
-rb_ivar_generic_lookup_with_index(VALUE obj, ID id, uint32_t index)
-{
- struct gen_ivtbl *ivtbl;
-
- if (gen_ivtbl_get(obj, id, &ivtbl)) {
- if (LIKELY(index < ivtbl->numiv)) {
- VALUE val = ivtbl->ivptr[index];
- return val;
- }
- }
-
- return Qundef;
-}
-
-static VALUE
-generic_ivar_delete(VALUE obj, ID id, VALUE undef)
-{
- struct gen_ivtbl *ivtbl;
-
- if (gen_ivtbl_get(obj, id, &ivtbl)) {
- st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
- uint32_t index;
-
- if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &index)) {
- if (index < ivtbl->numiv) {
- VALUE ret = ivtbl->ivptr[index];
-
- ivtbl->ivptr[index] = Qundef;
- return ret == Qundef ? undef : ret;
- }
- }
- }
- return undef;
-}
-
-static VALUE
-generic_ivar_get(VALUE obj, ID id, VALUE undef)
-{
- struct gen_ivtbl *ivtbl;
-
- if (gen_ivtbl_get(obj, id, &ivtbl)) {
- st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
- uint32_t index;
-
- if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &index)) {
- if (index < ivtbl->numiv) {
- VALUE ret = ivtbl->ivptr[index];
-
- return ret == Qundef ? undef : ret;
- }
- }
- }
- return undef;
+ return rb_gen_ivtbl_get(obj, 0, ivtbl);
}
static size_t
@@ -1045,6 +976,8 @@ gen_ivtbl_bytes(size_t n)
static struct gen_ivtbl *
gen_ivtbl_resize(struct gen_ivtbl *old, uint32_t n)
{
+ RUBY_ASSERT(n > 0);
+
uint32_t len = old ? old->numiv : 0;
struct gen_ivtbl *ivtbl = xrealloc(old, gen_ivtbl_bytes(n));
@@ -1069,18 +1002,6 @@ gen_ivtbl_dup(const struct gen_ivtbl *orig)
}
#endif
-static uint32_t
-iv_index_tbl_newsize(struct ivar_update *ivup)
-{
- if (!ivup->iv_extended) {
- return (uint32_t)ivup->u.iv_index_tbl->num_entries;
- }
- else {
- uint32_t index = (uint32_t)ivup->index; /* should not overflow */
- return (index+1) + (index+1)/4; /* (index+1)*1.25 */
- }
-}
-
static int
generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing)
{
@@ -1091,53 +1012,22 @@ generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing)
if (existing) {
ivtbl = (struct gen_ivtbl *)*v;
- if (ivup->index < ivtbl->numiv) {
- ivup->u.ivtbl = ivtbl;
+ if (ivup->iv_index < ivtbl->numiv) {
+ ivup->ivtbl = ivtbl;
return ST_STOP;
}
}
FL_SET((VALUE)*k, FL_EXIVAR);
- uint32_t newsize = iv_index_tbl_newsize(ivup);
- ivtbl = gen_ivtbl_resize(ivtbl, newsize);
+ ivtbl = gen_ivtbl_resize(ivtbl, ivup->shape->iv_count);
+ // Reinsert in to the hash table because ivtbl might be a newly resized chunk of memory
*v = (st_data_t)ivtbl;
- ivup->u.ivtbl = ivtbl;
+ ivup->ivtbl = ivtbl;
+#if !SHAPE_IN_BASIC_FLAGS
+ ivtbl->shape_id = rb_shape_id(ivup->shape);
+#endif
return ST_CONTINUE;
}
-static VALUE
-generic_ivar_defined(VALUE obj, ID id)
-{
- struct gen_ivtbl *ivtbl;
- st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
- uint32_t index;
-
- if (!iv_index_tbl_lookup(iv_index_tbl, id, &index)) return Qfalse;
- if (!gen_ivtbl_get(obj, id, &ivtbl)) return Qfalse;
-
- return RBOOL((index < ivtbl->numiv) && (ivtbl->ivptr[index] != Qundef));
-}
-
-static int
-generic_ivar_remove(VALUE obj, ID id, VALUE *valp)
-{
- struct gen_ivtbl *ivtbl;
- uint32_t index;
- st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
-
- if (!iv_index_tbl) return 0;
- if (!iv_index_tbl_lookup(iv_index_tbl, id, &index)) return 0;
- if (!gen_ivtbl_get(obj, id, &ivtbl)) return 0;
-
- if (index < ivtbl->numiv) {
- if (ivtbl->ivptr[index] != Qundef) {
- *valp = ivtbl->ivptr[index];
- ivtbl->ivptr[index] = Qundef;
- return 1;
- }
- }
- return 0;
-}
-
static void
gen_ivtbl_mark(const struct gen_ivtbl *ivtbl)
{
@@ -1153,8 +1043,8 @@ rb_mark_generic_ivar(VALUE obj)
{
struct gen_ivtbl *ivtbl;
- if (gen_ivtbl_get(obj, 0, &ivtbl)) {
- gen_ivtbl_mark(ivtbl);
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ gen_ivtbl_mark(ivtbl);
}
}
@@ -1182,11 +1072,35 @@ rb_generic_ivar_memsize(VALUE obj)
{
struct gen_ivtbl *ivtbl;
- if (gen_ivtbl_get(obj, 0, &ivtbl))
- return gen_ivtbl_bytes(ivtbl->numiv);
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl))
+ return gen_ivtbl_bytes(ivtbl->numiv);
return 0;
}
+#if !SHAPE_IN_BASIC_FLAGS
+MJIT_FUNC_EXPORTED shape_id_t
+rb_generic_shape_id(VALUE obj)
+{
+ struct gen_ivtbl *ivtbl = 0;
+ shape_id_t shape_id = 0;
+
+ RB_VM_LOCK_ENTER();
+ {
+ st_table* global_iv_table = generic_ivtbl(obj, 0, false);
+
+ if (global_iv_table && st_lookup(global_iv_table, obj, (st_data_t *)&ivtbl)) {
+ shape_id = ivtbl->shape_id;
+ }
+ else if (OBJ_FROZEN(obj)) {
+ shape_id = FROZEN_ROOT_SHAPE_ID;
+ }
+ }
+ RB_VM_LOCK_LEAVE();
+
+ return shape_id;
+}
+#endif
+
static size_t
gen_ivtbl_count(const struct gen_ivtbl *ivtbl)
{
@@ -1254,23 +1168,16 @@ VALUE
rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
{
if (SPECIAL_CONST_P(obj)) return undef;
+
+ shape_id_t shape_id;
+ VALUE * ivar_list;
+ rb_shape_t * shape;
+
+#if SHAPE_IN_BASIC_FLAGS
+ shape_id = RBASIC_SHAPE_ID(obj);
+#endif
+
switch (BUILTIN_TYPE(obj)) {
- case T_OBJECT:
- {
- uint32_t index;
- uint32_t len = ROBJECT_NUMIV(obj);
- VALUE *ptr = ROBJECT_IVPTR(obj);
- VALUE val;
-
- if (iv_index_tbl_lookup(ROBJECT_IV_INDEX_TBL(obj), id, &index) &&
- index < len &&
- (val = ptr[index]) != Qundef) {
- return val;
- }
- else {
- break;
- }
- }
case T_CLASS:
case T_MODULE:
{
@@ -1287,14 +1194,37 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
return val;
}
else {
- break;
+ return undef;
}
}
+ case T_OBJECT:
+ {
+#if !SHAPE_IN_BASIC_FLAGS
+ shape_id = ROBJECT_SHAPE_ID(obj);
+#endif
+ ivar_list = ROBJECT_IVPTR(obj);
+ break;
+ }
default:
- if (FL_TEST(obj, FL_EXIVAR))
- return generic_ivar_get(obj, id, undef);
+ if (FL_TEST_RAW(obj, FL_EXIVAR)) {
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+#if !SHAPE_IN_BASIC_FLAGS
+ shape_id = ivtbl->shape_id;
+#endif
+ ivar_list = ivtbl->ivptr;
+ } else {
+ return undef;
+ }
break;
}
+
+ attr_index_t index = 0;
+ shape = rb_shape_get_shape_by_id(shape_id);
+ if (rb_shape_get_iv_index(shape, id, &index)) {
+ return ivar_list[index];
+ }
+
return undef;
}
@@ -1315,26 +1245,12 @@ rb_attr_get(VALUE obj, ID id)
static VALUE
rb_ivar_delete(VALUE obj, ID id, VALUE undef)
{
- VALUE *ptr;
- struct st_table *iv_index_tbl;
- uint32_t len, index;
-
rb_check_frozen(obj);
- switch (BUILTIN_TYPE(obj)) {
- case T_OBJECT:
- len = ROBJECT_NUMIV(obj);
- ptr = ROBJECT_IVPTR(obj);
- iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj);
- if (iv_index_tbl_lookup(iv_index_tbl, id, &index) &&
- index < len) {
- VALUE val = ptr[index];
- ptr[index] = Qundef;
- if (val != Qundef) {
- return val;
- }
- }
- break;
+ VALUE val = Qnil;
+ attr_index_t index;
+
+ switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
@@ -1345,11 +1261,33 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
}
}
break;
- default:
- if (FL_TEST(obj, FL_EXIVAR))
- return generic_ivar_delete(obj, id, undef);
+ case T_OBJECT: {
+ rb_shape_t * shape = rb_shape_get_shape(obj);
+ if (rb_shape_get_iv_index(shape, id, &index)) {
+ rb_shape_transition_shape_remove_ivar(obj, id, shape);
+ val = ROBJECT_IVPTR(obj)[index];
+ ROBJECT_IVPTR(obj)[index] = Qundef;
+ return val;
+ }
+
+ break;
+ }
+ default: {
+ rb_shape_t * shape = rb_shape_get_shape(obj);
+
+ if (rb_shape_get_iv_index(shape, id, &index)) {
+ rb_shape_transition_shape_remove_ivar(obj, id, shape);
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+ val = ivtbl->ivptr[index];
+ ivtbl->ivptr[index] = Qundef;
+ return val;
+ }
+
break;
+ }
}
+
return undef;
}
@@ -1359,67 +1297,31 @@ rb_attr_delete(VALUE obj, ID id)
return rb_ivar_delete(obj, id, Qnil);
}
-static st_table *
-iv_index_tbl_make(VALUE obj, VALUE klass)
-{
- st_table *iv_index_tbl;
-
- if (UNLIKELY(!klass)) {
- rb_raise(rb_eTypeError, "hidden object cannot have instance variables");
- }
-
- if ((iv_index_tbl = RCLASS_IV_INDEX_TBL(klass)) == NULL) {
- RB_VM_LOCK_ENTER();
- if ((iv_index_tbl = RCLASS_IV_INDEX_TBL(klass)) == NULL) {
- iv_index_tbl = RCLASS_IV_INDEX_TBL(klass) = st_init_numtable();
- }
- RB_VM_LOCK_LEAVE();
- }
-
- return iv_index_tbl;
-}
-
-static void
-iv_index_tbl_extend(struct ivar_update *ivup, ID id, VALUE klass)
-{
- ASSERT_vm_locking();
- st_data_t ent_data;
- struct rb_iv_index_tbl_entry *ent;
-
- if (st_lookup(ivup->u.iv_index_tbl, (st_data_t)id, &ent_data)) {
- ent = (void *)ent_data;
- ivup->index = ent->index;
- return;
- }
- if (ivup->u.iv_index_tbl->num_entries >= INT_MAX) {
- rb_raise(rb_eArgError, "too many instance variables");
- }
- ent = ALLOC(struct rb_iv_index_tbl_entry);
- ent->index = ivup->index = (uint32_t)ivup->u.iv_index_tbl->num_entries;
- ent->class_value = klass;
- ent->class_serial = RCLASS_SERIAL(klass);
- st_add_direct(ivup->u.iv_index_tbl, (st_data_t)id, (st_data_t)ent);
- ivup->iv_extended = 1;
-}
-
static void
generic_ivar_set(VALUE obj, ID id, VALUE val)
{
- VALUE klass = rb_obj_class(obj);
struct ivar_update ivup;
- ivup.iv_extended = 0;
- ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass);
+ // The returned shape will have `id` in its iv_table
+ rb_shape_t * shape = rb_shape_get_next(rb_shape_get_shape(obj), obj, id);
+ ivup.shape = shape;
RB_VM_LOCK_ENTER();
{
- iv_index_tbl_extend(&ivup, id, klass);
- st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update,
- (st_data_t)&ivup);
+ attr_index_t ent_data;
+ if (rb_shape_get_iv_index(shape, id, &ent_data)) {
+ ivup.iv_index = (uint32_t) ent_data;
+ }
+ else {
+ rb_bug("unreachable. Shape was not found for id: %s", rb_id2name(id));
+ }
+
+ st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, (st_data_t)&ivup);
}
RB_VM_LOCK_LEAVE();
- ivup.u.ivtbl->ivptr[ivup.index] = val;
+ ivup.ivtbl->ivptr[ivup.iv_index] = val;
+ rb_shape_set_shape(obj, shape);
RB_OBJ_WRITTEN(obj, Qundef, val);
}
@@ -1486,8 +1388,8 @@ rb_obj_transient_heap_evacuate(VALUE obj, int promote)
}
#endif
-static void
-init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl)
+void
+rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize)
{
VALUE *ptr = ROBJECT_IVPTR(obj);
VALUE *newptr;
@@ -1510,35 +1412,34 @@ init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl)
#else
ROBJECT(obj)->as.heap.numiv = newsize;
#endif
- ROBJECT(obj)->as.heap.iv_index_tbl = index_tbl;
}
-void
-rb_init_iv_list(VALUE obj)
-{
- st_table *index_tbl = ROBJECT_IV_INDEX_TBL(obj);
- uint32_t newsize = (uint32_t)index_tbl->num_entries;
- uint32_t len = ROBJECT_NUMIV(obj);
- init_iv_list(obj, len, newsize, index_tbl);
-}
-
-// Retrieve or create the id-to-index mapping for a given object and an
-// instance variable name.
-static struct ivar_update
-obj_ensure_iv_index_mapping(VALUE obj, ID id)
+struct gen_ivtbl *
+rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize)
{
- VALUE klass = rb_obj_class(obj);
- struct ivar_update ivup;
- ivup.iv_extended = 0;
- ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass);
+ struct gen_ivtbl * ivtbl = 0;
RB_VM_LOCK_ENTER();
{
- iv_index_tbl_extend(&ivup, id, klass);
+ if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) {
+ ivtbl = gen_ivtbl_resize(ivtbl, newsize);
+ st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)obj, (st_data_t)ivtbl);
+ FL_SET_RAW(obj, FL_EXIVAR);
+ }
}
RB_VM_LOCK_LEAVE();
- return ivup;
+ RUBY_ASSERT(ivtbl);
+
+ return ivtbl;
+}
+
+void
+rb_init_iv_list(VALUE obj)
+{
+ uint32_t newsize = rb_shape_get_shape(obj)->iv_count * 2.0;
+ uint32_t len = ROBJECT_NUMIV(obj);
+ rb_ensure_iv_list_size(obj, len, newsize < len ? len : newsize);
}
// Return the instance variable index for a given name and T_OBJECT object. The
@@ -1552,26 +1453,108 @@ uint32_t
rb_obj_ensure_iv_index_mapping(VALUE obj, ID id)
{
RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT));
- // This uint32_t cast shouldn't lose information as it's checked in
- // iv_index_tbl_extend(). The index is stored as an uint32_t in
- // struct rb_iv_index_tbl_entry.
- return (uint32_t)obj_ensure_iv_index_mapping(obj, id).index;
+ attr_index_t index;
+
+ // Ensure there is a transition for IVAR +id+
+ rb_shape_transition_shape(obj, id, rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)));
+
+ // Get the current shape
+ rb_shape_t * shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj));
+
+ if (!rb_shape_get_iv_index(shape, id, &index)) {
+ rb_bug("unreachable. Shape was not found for id: %s", rb_id2name(id));
+ }
+
+ uint32_t len = ROBJECT_NUMIV(obj);
+ if (len <= index) {
+ uint32_t newsize = (shape->iv_count + 1) * 1.25;
+ rb_ensure_iv_list_size(obj, len, newsize);
+ }
+ RUBY_ASSERT(index <= ROBJECT_NUMIV(obj));
+ return index;
}
static VALUE
obj_ivar_set(VALUE obj, ID id, VALUE val)
{
- uint32_t len;
- struct ivar_update ivup = obj_ensure_iv_index_mapping(obj, id);
+ attr_index_t index = rb_obj_ensure_iv_index_mapping(obj, id);
+ RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], val);
+ return val;
+}
+
+/* Set the instance variable +val+ on object +obj+ at ivar name +id+.
+ * This function only works with T_OBJECT objects, so make sure
+ * +obj+ is of type T_OBJECT before using this function.
+ */
+VALUE
+rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val)
+{
+ rb_check_frozen_internal(obj);
+ obj_ivar_set(obj, id, val);
+ return val;
+}
- len = ROBJECT_NUMIV(obj);
- if (len <= ivup.index) {
- uint32_t newsize = iv_index_tbl_newsize(&ivup);
- init_iv_list(obj, len, newsize, ivup.u.iv_index_tbl);
+bool
+rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id)
+{
+ if (rb_shape_get_shape_id(obj) == shape_id) {
+ return false;
}
- RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val);
- return val;
+#if SHAPE_IN_BASIC_FLAGS
+ RBASIC_SET_SHAPE_ID(obj, shape_id);
+#else
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ ROBJECT_SET_SHAPE_ID(obj, shape_id);
+ break;
+ case T_CLASS:
+ case T_MODULE:
+ {
+ RCLASS_EXT(obj)->shape_id = shape_id;
+ break;
+ }
+ default:
+ {
+ if (shape_id != FROZEN_ROOT_SHAPE_ID) {
+ struct gen_ivtbl *ivtbl = 0;
+ RB_VM_LOCK_ENTER();
+ {
+ st_table* global_iv_table = generic_ivtbl(obj, 0, false);
+
+ if (st_lookup(global_iv_table, obj, (st_data_t *)&ivtbl)) {
+ ivtbl->shape_id = shape_id;
+ }
+ else {
+ rb_bug("Expected shape_id entry in global iv table");
+ }
+ }
+ RB_VM_LOCK_LEAVE();
+ }
+ }
+ }
+#endif
+
+ return true;
+}
+
+/**
+ * Prevents further modifications to the given object. ::rb_eFrozenError shall
+ * be raised if modification is attempted.
+ *
+ * @param[out] x Object in question.
+ */
+void rb_obj_freeze_inline(VALUE x)
+{
+ if (RB_FL_ABLE(x)) {
+ RB_OBJ_FREEZE_RAW(x);
+
+ rb_shape_transition_shape_frozen(x);
+
+ if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) {
+ rb_freeze_singleton_class(x);
+ }
+ }
}
static void
@@ -1581,10 +1564,14 @@ ivar_set(VALUE obj, ID id, VALUE val)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- obj_ivar_set(obj, id, val);
- break;
+ {
+ obj_ivar_set(obj, id, val);
+ break;
+ }
case T_CLASS:
case T_MODULE:
+ // TODO: Transition shapes on classes
+ //rb_shape_transition_shape(obj, id, rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj)));
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
rb_class_ivar_set(obj, id, val);
break;
@@ -1614,161 +1601,86 @@ rb_ivar_set_internal(VALUE obj, ID id, VALUE val)
VALUE
rb_ivar_defined(VALUE obj, ID id)
{
- VALUE val;
- struct st_table *iv_index_tbl;
- uint32_t index;
+ attr_index_t index;
if (SPECIAL_CONST_P(obj)) return Qfalse;
switch (BUILTIN_TYPE(obj)) {
- case T_OBJECT:
- iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj);
- if (iv_index_tbl_lookup(iv_index_tbl, id, &index) &&
- index < ROBJECT_NUMIV(obj) &&
- (val = ROBJECT_IVPTR(obj)[index]) != Qundef) {
- return Qtrue;
- }
- break;
case T_CLASS:
case T_MODULE:
- if (RCLASS_IV_TBL(obj) && lock_st_is_member(RCLASS_IV_TBL(obj), (st_data_t)id))
+ if (RCLASS_IV_TBL(obj) && lock_st_is_member(RCLASS_IV_TBL(obj), (st_data_t)id)) {
return Qtrue;
- break;
+ }
+ else {
+ return Qfalse;
+ }
default:
- if (FL_TEST(obj, FL_EXIVAR))
- return generic_ivar_defined(obj, id);
- break;
+ return RBOOL(rb_shape_get_iv_index(rb_shape_get_shape(obj), id, &index));
}
- return Qfalse;
}
typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg);
st_data_t rb_st_nth_key(st_table *tab, st_index_t index);
-static ID
-iv_index_tbl_nth_id(st_table *iv_index_tbl, uint32_t index)
-{
- st_data_t key;
- RB_VM_LOCK_ENTER();
- {
- key = rb_st_nth_key(iv_index_tbl, index);
- }
- RB_VM_LOCK_LEAVE();
- return (ID)key;
-}
-
-static inline bool
-ivar_each_i(st_table *iv_index_tbl, VALUE val, uint32_t i, rb_ivar_foreach_callback_func *func, st_data_t arg)
-{
- if (val != Qundef) {
- ID id = iv_index_tbl_nth_id(iv_index_tbl, i);
- switch (func(id, val, arg)) {
- case ST_CHECK:
- case ST_CONTINUE:
- break;
- case ST_STOP:
- return true;
- default:
- rb_bug("unreachable");
- }
+static void
+iterate_over_shapes_with_callback(rb_shape_t *shape, VALUE* iv_list, rb_ivar_foreach_callback_func *callback, st_data_t arg) {
+ switch ((enum shape_type)shape->type) {
+ case SHAPE_ROOT:
+ return;
+ case SHAPE_IVAR:
+ iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg);
+ VALUE val = iv_list[shape->iv_count - 1];
+ if (val != Qundef) {
+ callback(shape->edge_name, val, arg);
+ }
+ return;
+ case SHAPE_IVAR_UNDEF:
+ case SHAPE_FROZEN:
+ iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg);
+ return;
}
- return false;
}
static void
obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
{
- st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj);
- if (!iv_index_tbl) return;
- uint32_t i=0;
-
- for (i=0; i < ROBJECT_NUMIV(obj); i++) {
- VALUE val = ROBJECT_IVPTR(obj)[i];
- if (ivar_each_i(iv_index_tbl, val, i, func, arg)) {
- return;
- }
- }
+ rb_shape_t* shape = rb_shape_get_shape(obj);
+ iterate_over_shapes_with_callback(shape, ROBJECT_IVPTR(obj), func, arg);
}
static void
gen_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
{
+ rb_shape_t *shape = rb_shape_get_shape(obj);
struct gen_ivtbl *ivtbl;
- st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
- if (!iv_index_tbl) return;
- if (!gen_ivtbl_get(obj, 0, &ivtbl)) return;
+ if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) return;
- for (uint32_t i=0; i<ivtbl->numiv; i++) {
- VALUE val = ivtbl->ivptr[i];
- if (ivar_each_i(iv_index_tbl, val, i, func, arg)) {
- return;
- }
- }
-}
-
-struct givar_copy {
- VALUE obj;
- VALUE klass;
- st_table *iv_index_tbl;
- struct gen_ivtbl *ivtbl;
-};
-
-static int
-gen_ivar_copy(ID id, VALUE val, st_data_t arg)
-{
- struct givar_copy *c = (struct givar_copy *)arg;
- struct ivar_update ivup;
-
- ivup.iv_extended = 0;
- ivup.u.iv_index_tbl = c->iv_index_tbl;
-
- RB_VM_LOCK_ENTER();
- {
- iv_index_tbl_extend(&ivup, id, c->klass);
- }
- RB_VM_LOCK_LEAVE();
-
- if (ivup.index >= c->ivtbl->numiv) {
- uint32_t newsize = iv_index_tbl_newsize(&ivup);
- c->ivtbl = gen_ivtbl_resize(c->ivtbl, newsize);
- }
- c->ivtbl->ivptr[ivup.index] = val;
-
- RB_OBJ_WRITTEN(c->obj, Qundef, val);
-
- return ST_CONTINUE;
+ iterate_over_shapes_with_callback(shape, ivtbl->ivptr, func, arg);
}
void
rb_copy_generic_ivar(VALUE clone, VALUE obj)
{
- struct gen_ivtbl *ivtbl;
+ struct gen_ivtbl *obj_ivtbl;
+ struct gen_ivtbl *new_ivtbl;
rb_check_frozen(clone);
if (!FL_TEST(obj, FL_EXIVAR)) {
goto clear;
}
- if (gen_ivtbl_get(obj, 0, &ivtbl)) {
- struct givar_copy c;
- uint32_t i;
- if (gen_ivtbl_count(ivtbl) == 0)
+ if (rb_gen_ivtbl_get(obj, 0, &obj_ivtbl)) {
+ if (gen_ivtbl_count(obj_ivtbl) == 0)
goto clear;
- if (gen_ivtbl_get(clone, 0, &c.ivtbl)) {
- for (i = 0; i < c.ivtbl->numiv; i++)
- c.ivtbl->ivptr[i] = Qundef;
- }
- else {
- c.ivtbl = gen_ivtbl_resize(0, ivtbl->numiv);
- FL_SET(clone, FL_EXIVAR);
+ new_ivtbl = gen_ivtbl_resize(0, obj_ivtbl->numiv);
+ FL_SET(clone, FL_EXIVAR);
+
+ for (uint32_t i=0; i<obj_ivtbl->numiv; i++) {
+ new_ivtbl->ivptr[i] = obj_ivtbl->ivptr[i];
+ RB_OBJ_WRITTEN(clone, Qundef, &new_ivtbl[i]);
}
- VALUE klass = rb_obj_class(clone);
- c.iv_index_tbl = iv_index_tbl_make(clone, klass);
- c.obj = clone;
- c.klass = klass;
- gen_ivar_each(obj, gen_ivar_copy, (st_data_t)&c);
/*
* c.ivtbl may change in gen_ivar_copy due to realloc,
* no need to free
@@ -1776,9 +1688,17 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj)
RB_VM_LOCK_ENTER();
{
generic_ivtbl_no_ractor_check(clone);
- st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)c.ivtbl);
+ st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)new_ivtbl);
}
RB_VM_LOCK_LEAVE();
+
+ rb_shape_t * obj_shape = rb_shape_get_shape(obj);
+ if (rb_shape_frozen_shape_p(obj_shape)) {
+ rb_shape_set_shape(clone, obj_shape->parent);
+ }
+ else {
+ rb_shape_set_shape(clone, obj_shape);
+ }
}
return;
@@ -1846,17 +1766,17 @@ rb_ivar_count(VALUE obj)
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- if (ROBJECT_IV_INDEX_TBL(obj) != 0) {
- st_index_t i, count, num = ROBJECT_NUMIV(obj);
- const VALUE *const ivptr = ROBJECT_IVPTR(obj);
- for (i = count = 0; i < num; ++i) {
- if (ivptr[i] != Qundef) {
- count++;
- }
- }
- return count;
- }
- break;
+ if (rb_shape_get_shape(obj)->iv_count > 0) {
+ st_index_t i, count, num = ROBJECT_NUMIV(obj);
+ const VALUE *const ivptr = ROBJECT_IVPTR(obj);
+ for (i = count = 0; i < num; ++i) {
+ if (ivptr[i] != Qundef) {
+ count++;
+ }
+ }
+ return count;
+ }
+ break;
case T_CLASS:
case T_MODULE:
if ((tbl = RCLASS_IV_TBL(obj)) != 0) {
@@ -1867,11 +1787,11 @@ rb_ivar_count(VALUE obj)
if (FL_TEST(obj, FL_EXIVAR)) {
struct gen_ivtbl *ivtbl;
- if (gen_ivtbl_get(obj, 0, &ivtbl)) {
- return gen_ivtbl_count(ivtbl);
- }
- }
- break;
+ if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
+ return gen_ivtbl_count(ivtbl);
+ }
+ }
+ break;
}
return 0;
}
@@ -1965,40 +1885,53 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name)
{
VALUE val = Qnil;
const ID id = id_for_var(obj, name, an, instance);
- st_data_t n, v;
- struct st_table *iv_index_tbl;
- uint32_t index;
+ // Frozen check comes here because it's expected that we raise a
+ // NameError (from the id_for_var check) before we raise a FrozenError
rb_check_frozen(obj);
+
+ attr_index_t index;
+
if (!id) {
goto not_defined;
}
switch (BUILTIN_TYPE(obj)) {
- case T_OBJECT:
- iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj);
- if (iv_index_tbl_lookup(iv_index_tbl, id, &index) &&
- index < ROBJECT_NUMIV(obj) &&
- (val = ROBJECT_IVPTR(obj)[index]) != Qundef) {
- ROBJECT_IVPTR(obj)[index] = Qundef;
- return val;
- }
- break;
case T_CLASS:
case T_MODULE:
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
- n = id;
- if (RCLASS_IV_TBL(obj) && lock_st_delete(RCLASS_IV_TBL(obj), &n, &v)) {
- return (VALUE)v;
+ if (RCLASS_IV_TBL(obj)) {
+ st_data_t id_data = (st_data_t)id, val;
+ if (lock_st_delete(RCLASS_IV_TBL(obj), &id_data, &val)) {
+ return (VALUE)val;
+ }
}
break;
- default:
- if (FL_TEST(obj, FL_EXIVAR)) {
- if (generic_ivar_remove(obj, id, &val)) {
- return val;
- }
+ case T_OBJECT: {
+ rb_shape_t * shape = rb_shape_get_shape(obj);
+ if (rb_shape_get_iv_index(shape, id, &index)) {
+ rb_shape_transition_shape_remove_ivar(obj, id, shape);
+ val = ROBJECT_IVPTR(obj)[index];
+ ROBJECT_IVPTR(obj)[index] = Qundef;
+ return val;
+ }
+
+ break;
+ }
+ default: {
+ rb_shape_t * shape = rb_shape_get_shape(obj);
+
+ if (rb_shape_get_iv_index(shape, id, &index)) {
+ rb_shape_transition_shape_remove_ivar(obj, id, shape);
+ struct gen_ivtbl *ivtbl;
+ rb_gen_ivtbl_get(obj, id, &ivtbl);
+ val = ivtbl->ivptr[index];
+ ivtbl->ivptr[index] = Qundef;
+ return val;
}
+
break;
+ }
}
not_defined: