summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c1447
1 files changed, 689 insertions, 758 deletions
diff --git a/variable.c b/variable.c
index 1cd1c604c3..687fa03631 100644
--- a/variable.c
+++ b/variable.c
@@ -20,12 +20,12 @@
#include "id.h"
#include "id_table.h"
#include "internal.h"
+#include "internal/box.h"
#include "internal/class.h"
#include "internal/compilers.h"
#include "internal/error.h"
#include "internal/eval.h"
#include "internal/hash.h"
-#include "internal/namespace.h"
#include "internal/object.h"
#include "internal/gc.h"
#include "internal/re.h"
@@ -63,7 +63,7 @@ static VALUE autoload_mutex;
static void check_before_mod_set(VALUE, ID, VALUE, const char *);
static void setup_const_entry(rb_const_entry_t *, VALUE, VALUE, rb_const_flag_t);
-static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility);
+static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility, VALUE *found_in);
static st_table *generic_fields_tbl_;
typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg);
@@ -279,7 +279,7 @@ set_sub_temporary_name(VALUE mod, VALUE name)
* m.name #=> nil
*
* c = Class.new
- * c.set_temporary_name("MyClass(with description)")
+ * c.set_temporary_name("MyClass(with description)") # => MyClass(with description)
*
* c.new # => #<MyClass(with description):0x0....>
*
@@ -321,6 +321,7 @@ rb_mod_set_temporary_name(VALUE mod, VALUE name)
}
name = rb_str_new_frozen(name);
+ RB_OBJ_SET_SHAREABLE(name);
// Set the temporary classpath to the given name:
RB_VM_LOCKING() {
@@ -432,6 +433,7 @@ rb_set_class_path_string(VALUE klass, VALUE under, VALUE name)
str = build_const_pathname(str, name);
}
+ RB_OBJ_SET_SHAREABLE(str);
RCLASS_SET_CLASSPATH(klass, str, permanent);
}
@@ -471,7 +473,7 @@ rb_path_to_class(VALUE pathname)
if (!id) {
goto undefined_class;
}
- c = rb_const_search(c, id, TRUE, FALSE, FALSE);
+ c = rb_const_search(c, id, TRUE, FALSE, FALSE, NULL);
if (UNDEF_P(c)) goto undefined_class;
if (!rb_namespace_p(c)) {
rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
@@ -531,7 +533,8 @@ struct rb_global_variable {
rb_gvar_marker_t *marker;
rb_gvar_compact_t *compactor;
struct trace_var *trace;
- bool namespace_ready;
+ bool box_ready;
+ bool box_dynamic;
};
struct rb_global_entry {
@@ -548,10 +551,10 @@ free_global_variable(struct rb_global_variable *var)
struct trace_var *trace = var->trace;
while (trace) {
struct trace_var *next = trace->next;
- xfree(trace);
+ SIZED_FREE(trace);
trace = next;
}
- xfree(var);
+ SIZED_FREE(var);
}
static enum rb_id_table_iterator_result
@@ -562,7 +565,7 @@ free_global_entry_i(VALUE val, void *arg)
if (entry->var->counter == 0) {
free_global_variable(entry->var);
}
- ruby_xfree(entry);
+ SIZED_FREE(entry);
return ID_TABLE_DELETE;
}
@@ -610,10 +613,17 @@ rb_gvar_ractor_local(const char *name)
}
void
-rb_gvar_namespace_ready(const char *name)
+rb_gvar_box_ready(const char *name)
{
struct rb_global_entry *entry = rb_find_global_entry(rb_intern(name));
- entry->var->namespace_ready = true;
+ entry->var->box_ready = true;
+}
+
+void
+rb_gvar_box_dynamic(const char *name)
+{
+ struct rb_global_entry *entry = rb_find_global_entry(rb_intern(name));
+ entry->var->box_dynamic = true;
}
static void
@@ -643,7 +653,8 @@ rb_global_entry(ID id)
var->block_trace = 0;
var->trace = 0;
- var->namespace_ready = false;
+ var->box_ready = false;
+ var->box_dynamic = false;
rb_id_table_insert(rb_global_tbl, id, (VALUE)entry);
}
}
@@ -860,7 +871,7 @@ rb_define_virtual_variable(
static void
rb_trace_eval(VALUE cmd, VALUE val)
{
- rb_eval_cmd_kw(cmd, rb_ary_new3(1, val), RB_NO_KEYWORDS);
+ rb_eval_cmd_call_kw(cmd, 1, &val, RB_NO_KEYWORDS);
}
VALUE
@@ -900,7 +911,7 @@ remove_trace(struct rb_global_variable *var)
next = trace->next;
if (next->removed) {
trace->next = next->next;
- xfree(next);
+ SIZED_FREE(next);
}
else {
trace = next;
@@ -998,28 +1009,35 @@ rb_gvar_set_entry(struct rb_global_entry *entry, VALUE val)
return val;
}
-#define USE_NAMESPACE_GVAR_TBL(ns,entry) \
- (NAMESPACE_OPTIONAL_P(ns) && \
- (!entry || !entry->var->namespace_ready || entry->var->setter != rb_gvar_readonly_setter))
+static inline bool
+gvar_use_box_tbl(const rb_box_t *box, const struct rb_global_entry *entry)
+{
+ return BOX_USER_P(box) &&
+ !entry->var->box_dynamic &&
+ (!entry->var->box_ready || entry->var->setter != rb_gvar_readonly_setter);
+}
VALUE
rb_gvar_set(ID id, VALUE val)
{
VALUE retval;
- struct rb_global_entry *entry;
- const rb_namespace_t *ns = rb_current_namespace();
+ struct rb_global_entry *entry = NULL;
+ const rb_box_t *box = rb_current_box();
+ bool use_box_tbl = false;
RB_VM_LOCKING() {
entry = rb_global_entry(id);
- if (USE_NAMESPACE_GVAR_TBL(ns, entry)) {
- rb_hash_aset(ns->gvar_tbl, rb_id2sym(entry->id), val);
+ if (gvar_use_box_tbl(box, entry)) {
+ use_box_tbl = true;
+ rb_hash_aset(box->gvar_tbl, rb_id2sym(entry->id), val);
retval = val;
// TODO: think about trace
}
- else {
- retval = rb_gvar_set_entry(entry, val);
- }
+ }
+
+ if (!use_box_tbl) {
+ retval = rb_gvar_set_entry(entry, val);
}
return retval;
}
@@ -1034,29 +1052,37 @@ VALUE
rb_gvar_get(ID id)
{
VALUE retval, gvars, key;
- const rb_namespace_t *ns = rb_current_namespace();
+ const rb_box_t *box = rb_current_box();
+ bool use_box_tbl = false;
+ struct rb_global_entry *entry = NULL;
+ struct rb_global_variable *var = NULL;
// TODO: use lock-free rb_id_table when it's available for use (doesn't yet exist)
RB_VM_LOCKING() {
- struct rb_global_entry *entry = rb_global_entry(id);
- struct rb_global_variable *var = entry->var;
+ entry = rb_global_entry(id);
+ var = entry->var;
- if (USE_NAMESPACE_GVAR_TBL(ns, entry)) {
- gvars = ns->gvar_tbl;
+ if (gvar_use_box_tbl(box, entry)) {
+ use_box_tbl = true;
+ gvars = box->gvar_tbl;
key = rb_id2sym(entry->id);
if (RTEST(rb_hash_has_key(gvars, key))) { // this gvar is already cached
retval = rb_hash_aref(gvars, key);
}
else {
- retval = (*var->getter)(entry->id, var->data);
- if (rb_obj_respond_to(retval, rb_intern("clone"), 1)) {
- retval = rb_funcall(retval, rb_intern("clone"), 0);
+ RB_VM_UNLOCK();
+ {
+ retval = (*var->getter)(entry->id, var->data);
+ if (rb_obj_respond_to(retval, rb_intern("clone"), 1)) {
+ retval = rb_funcall(retval, rb_intern("clone"), 0);
+ }
}
+ RB_VM_LOCK();
rb_hash_aset(gvars, key, retval);
}
}
- else {
- retval = (*var->getter)(entry->id, var->data);
- }
+ }
+ if (!use_box_tbl) {
+ retval = (*var->getter)(entry->id, var->data);
}
return retval;
}
@@ -1112,7 +1138,7 @@ rb_f_global_variables(void)
if (!rb_ractor_main_p()) {
rb_raise(rb_eRactorIsolationError, "can not access global variables from non-main Ractors");
}
- /* gvar access (get/set) in namespaces creates gvar entries globally */
+ /* gvar access (get/set) in boxes creates gvar entries globally */
rb_id_table_foreach(rb_global_tbl, gvar_i, (void *)ary);
if (!NIL_P(backref)) {
@@ -1150,13 +1176,14 @@ rb_alias_variable(ID name1, ID name2)
RB_VM_LOCKING() {
entry2 = rb_global_entry(name2);
if (!rb_id_table_lookup(gtbl, name1, &data1)) {
- entry1 = ALLOC(struct rb_global_entry);
+ entry1 = ZALLOC(struct rb_global_entry);
entry1->id = name1;
rb_id_table_insert(gtbl, name1, (VALUE)entry1);
}
else if ((entry1 = (struct rb_global_entry *)data1)->var != entry2->var) {
struct rb_global_variable *var = entry1->var;
if (var->block_trace) {
+ RB_VM_UNLOCK();
rb_raise(rb_eRuntimeError, "can't alias in tracer");
}
var->counter--;
@@ -1164,7 +1191,7 @@ rb_alias_variable(ID name1, ID name2)
free_global_variable(var);
}
}
- if (entry1) {
+ if (entry1->var != entry2->var) {
entry2->var->counter++;
entry1->var = entry2->var;
}
@@ -1181,10 +1208,23 @@ IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(ID id)
}
}
-#define CVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR() \
- if (UNLIKELY(!rb_ractor_main_p())) { \
- rb_raise(rb_eRactorIsolationError, "can not access class variables from non-main Ractors"); \
- }
+static void
+CVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(VALUE klass, ID id)
+{
+ if (UNLIKELY(!rb_ractor_main_p())) {
+ rb_raise(rb_eRactorIsolationError, "can not set class variables from non-main Ractors (%"PRIsVALUE" from %"PRIsVALUE")", rb_id2str(id), klass);
+ }
+}
+
+static void
+cvar_read_ractor_check(VALUE klass, ID id, VALUE val)
+{
+ if (UNLIKELY(!rb_ractor_main_p()) && !rb_ractor_shareable_p(val)) {
+ rb_raise(rb_eRactorIsolationError,
+ "can not read non-shareable class variable %"PRIsVALUE" from non-main Ractors (%"PRIsVALUE")",
+ rb_id2str(id), klass);
+ }
+}
static inline void
ivar_ractor_check(VALUE obj, ID id)
@@ -1223,20 +1263,29 @@ rb_mark_generic_ivar(VALUE obj)
}
VALUE
+rb_obj_fields_generic_uncached(VALUE obj)
+{
+ VALUE fields_obj = 0;
+ RB_VM_LOCKING() {
+ if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
+ rb_bug("Object is missing entry in generic_fields_tbl");
+ }
+ }
+ return fields_obj;
+}
+
+VALUE
rb_obj_fields(VALUE obj, ID field_name)
{
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO));
ivar_ractor_check(obj, field_name);
VALUE fields_obj = 0;
- if (rb_shape_obj_has_fields(obj)) {
+ if (rb_obj_shape_has_fields(obj)) {
switch (BUILTIN_TYPE(obj)) {
case T_DATA:
- if (LIKELY(RTYPEDDATA_P(obj))) {
- fields_obj = RTYPEDDATA(obj)->fields_obj;
- break;
- }
- goto generic_fields;
+ fields_obj = RTYPEDDATA(obj)->fields_obj;
+ break;
case T_STRUCT:
if (LIKELY(!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS))) {
fields_obj = RSTRUCT_FIELDS_OBJ(obj);
@@ -1247,15 +1296,12 @@ rb_obj_fields(VALUE obj, ID field_name)
generic_fields:
{
rb_execution_context_t *ec = GET_EC();
- if (ec->gen_fields_cache.obj == obj && rb_imemo_fields_owner(ec->gen_fields_cache.fields_obj) == obj) {
+ if (ec->gen_fields_cache.obj == obj && !UNDEF_P(ec->gen_fields_cache.fields_obj) && rb_imemo_fields_owner(ec->gen_fields_cache.fields_obj) == obj) {
fields_obj = ec->gen_fields_cache.fields_obj;
+ RUBY_ASSERT(fields_obj == rb_obj_fields_generic_uncached(obj));
}
else {
- RB_VM_LOCKING() {
- if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
- rb_bug("Object is missing entry in generic_fields_tbl");
- }
- }
+ fields_obj = rb_obj_fields_generic_uncached(obj);
ec->gen_fields_cache.fields_obj = fields_obj;
ec->gen_fields_cache.obj = obj;
}
@@ -1268,15 +1314,12 @@ rb_obj_fields(VALUE obj, ID field_name)
void
rb_free_generic_ivar(VALUE obj)
{
- if (rb_obj_exivar_p(obj)) {
+ if (rb_obj_gen_fields_p(obj)) {
st_data_t key = (st_data_t)obj, value;
switch (BUILTIN_TYPE(obj)) {
case T_DATA:
- if (LIKELY(RTYPEDDATA_P(obj))) {
- RB_OBJ_WRITE(obj, &RTYPEDDATA(obj)->fields_obj, 0);
- break;
- }
- goto generic_fields;
+ RB_OBJ_WRITE(obj, &RTYPEDDATA(obj)->fields_obj, 0);
+ break;
case T_STRUCT:
if (LIKELY(!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS))) {
RSTRUCT_SET_FIELDS_OBJ(obj, 0);
@@ -1286,36 +1329,44 @@ rb_free_generic_ivar(VALUE obj)
default:
generic_fields:
{
+ // Other EC may have stale caches, so fields_obj should be
+ // invalidated and the GC will replace with Qundef
rb_execution_context_t *ec = GET_EC();
if (ec->gen_fields_cache.obj == obj) {
ec->gen_fields_cache.obj = Qundef;
ec->gen_fields_cache.fields_obj = Qundef;
}
RB_VM_LOCKING() {
- st_delete(generic_fields_tbl_no_ractor_check(), &key, &value);
+ if (!st_delete(generic_fields_tbl_no_ractor_check(), &key, &value)) {
+ rb_bug("Object is missing entry in generic_fields_tbl");
+ }
}
}
}
- RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | ROOT_SHAPE_ID);
}
}
-void
+static void
rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fields_obj)
{
ivar_ractor_check(obj, field_name);
+ if (!fields_obj) {
+ RUBY_ASSERT(original_fields_obj);
+ rb_free_generic_ivar(obj);
+ rb_imemo_fields_clear(original_fields_obj);
+ return;
+ }
+
RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields));
RUBY_ASSERT(!original_fields_obj || IMEMO_TYPE_P(original_fields_obj, imemo_fields));
if (fields_obj != original_fields_obj) {
switch (BUILTIN_TYPE(obj)) {
case T_DATA:
- if (LIKELY(RTYPEDDATA_P(obj))) {
- RB_OBJ_WRITE(obj, &RTYPEDDATA(obj)->fields_obj, fields_obj);
- break;
- }
- goto generic_fields;
+ RB_OBJ_WRITE(obj, &RTYPEDDATA(obj)->fields_obj, fields_obj);
+ break;
case T_STRUCT:
if (LIKELY(!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS))) {
RSTRUCT_SET_FIELDS_OBJ(obj, fields_obj);
@@ -1344,7 +1395,7 @@ rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fie
}
}
- RBASIC_SET_SHAPE_ID(obj, RBASIC_SHAPE_ID(fields_obj));
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | RBASIC_SHAPE_ID(fields_obj));
}
void
@@ -1362,47 +1413,35 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id)
RUBY_ASSERT(!SPECIAL_CONST_P(obj));
RUBY_ASSERT(RSHAPE_TYPE_P(target_shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(target_shape_id, SHAPE_OBJ_ID));
- if (rb_shape_too_complex_p(target_shape_id)) {
- st_table *fields_hash;
- switch (BUILTIN_TYPE(obj)) {
- case T_CLASS:
- case T_MODULE:
- fields_hash = rb_imemo_fields_complex_tbl(RCLASS_WRITABLE_FIELDS_OBJ(obj));
- break;
- case T_OBJECT:
- fields_hash = ROBJECT_FIELDS_HASH(obj);
- break;
- case T_IMEMO:
- fields_hash = rb_imemo_fields_complex_tbl(obj);
- break;
- default:
- fields_hash = rb_imemo_fields_complex_tbl(rb_obj_fields(obj, RSHAPE_EDGE_NAME(target_shape_id)));
- break;
- }
- VALUE value = Qundef;
- st_lookup(fields_hash, RSHAPE_EDGE_NAME(target_shape_id), &value);
- RUBY_ASSERT(!UNDEF_P(value));
- return value;
- }
+ VALUE fields_obj;
- attr_index_t attr_index = RSHAPE_INDEX(target_shape_id);
- VALUE *fields;
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
- fields = rb_imemo_fields_ptr(RCLASS_WRITABLE_FIELDS_OBJ(obj));
+ fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
break;
case T_OBJECT:
- fields = ROBJECT_FIELDS(obj);
+ fields_obj = obj;
break;
case T_IMEMO:
- fields = rb_imemo_fields_ptr(obj);
+ RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields));
+ fields_obj = obj;
break;
default:
- fields = rb_imemo_fields_ptr(rb_obj_fields(obj, RSHAPE_EDGE_NAME(target_shape_id)));
+ fields_obj = rb_obj_fields(obj, RSHAPE_EDGE_NAME(target_shape_id));
break;
}
- return fields[attr_index];
+
+ if (UNLIKELY(rb_shape_complex_p(target_shape_id))) {
+ st_table *fields_hash = rb_imemo_fields_complex_tbl(fields_obj);
+ VALUE value = Qundef;
+ st_lookup(fields_hash, RSHAPE_EDGE_NAME(target_shape_id), &value);
+ RUBY_ASSERT(!UNDEF_P(value));
+ return value;
+ }
+
+ attr_index_t index = RSHAPE_INDEX(target_shape_id);
+ return rb_imemo_fields_ptr(fields_obj)[index];
}
VALUE
@@ -1410,93 +1449,54 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
{
if (SPECIAL_CONST_P(obj)) return undef;
- shape_id_t shape_id;
- VALUE *ivar_list;
+ VALUE fields_obj;
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
{
- VALUE val = undef;
- VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
- if (fields_obj) {
- val = rb_ivar_lookup(fields_obj, id, undef);
- }
-
+ VALUE val = rb_ivar_lookup(RCLASS_WRITABLE_FIELDS_OBJ(obj), id, undef);
if (val != undef &&
rb_is_instance_id(id) &&
UNLIKELY(!rb_ractor_main_p()) &&
!rb_ractor_shareable_p(val)) {
rb_raise(rb_eRactorIsolationError,
- "can not get unshareable values from instance variables of classes/modules from non-main Ractors");
+ "can not get unshareable values from instance variables of classes/modules from non-main Ractors (%"PRIsVALUE" from %"PRIsVALUE")",
+ rb_id2str(id), obj);
}
return val;
}
case T_IMEMO:
// Handled like T_OBJECT
- {
- RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields));
- shape_id = RBASIC_SHAPE_ID(obj);
+ RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields));
+ fields_obj = obj;
+ break;
+ case T_OBJECT:
+ fields_obj = obj;
+ break;
+ default:
+ fields_obj = rb_obj_fields(obj, id);
+ break;
+ }
- if (rb_shape_too_complex_p(shape_id)) {
- st_table *iv_table = rb_imemo_fields_complex_tbl(obj);
- VALUE val;
- if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
- return val;
- }
- else {
- return undef;
- }
- }
+ if (!fields_obj) {
+ return undef;
+ }
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
- ivar_list = rb_imemo_fields_ptr(obj);
- break;
- }
- case T_OBJECT:
- {
- shape_id = RBASIC_SHAPE_ID(obj);
- if (rb_shape_too_complex_p(shape_id)) {
- st_table *iv_table = ROBJECT_FIELDS_HASH(obj);
- VALUE val;
- if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
- return val;
- }
- else {
- return undef;
- }
- }
+ shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
- ivar_list = ROBJECT_FIELDS(obj);
- break;
- }
- default:
- {
- shape_id = RBASIC_SHAPE_ID(obj);
- VALUE fields_obj = rb_obj_fields(obj, id);
- if (fields_obj) {
- if (rb_shape_obj_too_complex_p(fields_obj)) {
- VALUE val;
- if (rb_st_lookup(rb_imemo_fields_complex_tbl(fields_obj), (st_data_t)id, (st_data_t *)&val)) {
- return val;
- }
- else {
- return undef;
- }
- }
- ivar_list = rb_imemo_fields_ptr(fields_obj);
- }
- else {
- return undef;
- }
- break;
+ if (UNLIKELY(rb_shape_complex_p(shape_id))) {
+ st_table *iv_table = rb_imemo_fields_complex_tbl(fields_obj);
+ VALUE val;
+ if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
+ return val;
}
+ return undef;
}
attr_index_t index = 0;
if (rb_shape_get_iv_index(shape_id, id, &index)) {
- return ivar_list[index];
+ return rb_imemo_fields_ptr(fields_obj)[index];
}
return undef;
@@ -1511,175 +1511,97 @@ rb_ivar_get(VALUE obj, ID id)
}
VALUE
-rb_attr_get(VALUE obj, ID id)
+rb_ivar_get_at(VALUE obj, attr_index_t index, ID id)
{
- return rb_ivar_lookup(obj, id, Qnil);
-}
+ RUBY_ASSERT(rb_is_instance_id(id));
+ // Used by JITs, but never for T_OBJECT.
-static VALUE
-rb_ivar_delete(VALUE obj, ID id, VALUE undef)
-{
- rb_check_frozen(obj);
-
- VALUE val = undef;
- if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
- IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ UNREACHABLE_RETURN(Qundef);
+ case T_CLASS:
+ case T_MODULE:
+ {
+ VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
+ VALUE val = rb_imemo_fields_ptr(fields_obj)[index];
- VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
- if (fields_obj) {
- if (rb_multi_ractor_p()) {
- fields_obj = rb_imemo_fields_clone(fields_obj);
- val = rb_ivar_delete(fields_obj, id, undef);
- RCLASS_WRITABLE_SET_FIELDS_OBJ(obj, fields_obj);
- }
- else {
- val = rb_ivar_delete(fields_obj, id, undef);
+ if (UNLIKELY(!rb_ractor_main_p()) && !rb_ractor_shareable_p(val)) {
+ rb_raise(rb_eRactorIsolationError,
+ "can not get unshareable values from instance variables of classes/modules from non-main Ractors");
}
- }
- return val;
- }
-
- shape_id_t old_shape_id = rb_obj_shape_id(obj);
- if (rb_shape_too_complex_p(old_shape_id)) {
- goto too_complex;
- }
-
- shape_id_t removed_shape_id = 0;
- shape_id_t next_shape_id = rb_shape_transition_remove_ivar(obj, id, &removed_shape_id);
-
- if (next_shape_id == old_shape_id) {
- return undef;
- }
- if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) {
- rb_evict_fields_to_hash(obj);
- goto too_complex;
+ return val;
+ }
+ default:
+ {
+ VALUE fields_obj = rb_obj_fields(obj, id);
+ return rb_imemo_fields_ptr(fields_obj)[index];
+ }
}
+}
- RUBY_ASSERT(RSHAPE_LEN(next_shape_id) == RSHAPE_LEN(old_shape_id) - 1);
+VALUE
+rb_ivar_get_at_no_ractor_check(VALUE obj, attr_index_t index)
+{
+ // Used by JITs, but never for T_OBJECT.
- VALUE *fields;
- switch(BUILTIN_TYPE(obj)) {
+ VALUE fields_obj;
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ UNREACHABLE_RETURN(Qundef);
case T_CLASS:
case T_MODULE:
- rb_bug("Unreachable");
- break;
- case T_IMEMO:
- RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields));
- fields = rb_imemo_fields_ptr(obj);
- break;
- case T_OBJECT:
- fields = ROBJECT_FIELDS(obj);
+ fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
break;
- default: {
- VALUE fields_obj = rb_obj_fields(obj, id);
- fields = rb_imemo_fields_ptr(fields_obj);
+ default:
+ fields_obj = rb_obj_fields_no_ractor_check(obj);
break;
- }
- }
-
- RUBY_ASSERT(removed_shape_id != INVALID_SHAPE_ID);
-
- attr_index_t removed_index = RSHAPE_INDEX(removed_shape_id);
- val = fields[removed_index];
-
- attr_index_t new_fields_count = RSHAPE_LEN(next_shape_id);
- if (new_fields_count) {
- size_t trailing_fields = new_fields_count - removed_index;
-
- MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields);
- }
- else {
- rb_free_generic_ivar(obj);
}
-
- if (RB_TYPE_P(obj, T_OBJECT) &&
- !RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
- rb_obj_embedded_size(new_fields_count) <= rb_gc_obj_slot_size(obj)) {
- // Re-embed objects when instances become small enough
- // This is necessary because YJIT assumes that objects with the same shape
- // have the same embeddedness for efficiency (avoid extra checks)
- RB_FL_SET_RAW(obj, ROBJECT_EMBED);
- MEMCPY(ROBJECT_FIELDS(obj), fields, VALUE, new_fields_count);
- xfree(fields);
- }
- rb_obj_set_shape_id(obj, next_shape_id);
-
- return val;
-
-too_complex:
- {
- st_table *table = NULL;
- switch (BUILTIN_TYPE(obj)) {
- case T_CLASS:
- case T_MODULE:
- rb_bug("Unreachable");
- break;
-
- case T_IMEMO:
- RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields));
- table = rb_imemo_fields_complex_tbl(obj);
- break;
-
- case T_OBJECT:
- table = ROBJECT_FIELDS_HASH(obj);
- break;
-
- default: {
- VALUE fields_obj = rb_obj_fields(obj, id);
- table = rb_imemo_fields_complex_tbl(fields_obj);
- break;
- }
- }
-
- if (table) {
- if (!st_delete(table, (st_data_t *)&id, (st_data_t *)&val)) {
- val = undef;
- }
- }
- }
-
- return val;
+ return rb_imemo_fields_ptr(fields_obj)[index];
}
VALUE
-rb_attr_delete(VALUE obj, ID id)
+rb_attr_get(VALUE obj, ID id)
{
- return rb_ivar_delete(obj, id, Qnil);
+ return rb_ivar_lookup(obj, id, Qnil);
}
+void rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table);
+static VALUE imemo_fields_complex_from_obj(VALUE owner, VALUE source_fields_obj, shape_id_t shape_id);
+
static shape_id_t
-obj_transition_too_complex(VALUE obj, st_table *table)
+obj_transition_complex(VALUE obj, st_table *table)
{
- if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
- return obj_transition_too_complex(RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(obj), table);
- }
-
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
- shape_id_t shape_id = rb_shape_transition_complex(obj);
+ RUBY_ASSERT(!rb_obj_shape_complex_p(obj));
+ shape_id_t shape_id = rb_obj_shape_transition_complex(obj);
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
{
VALUE *old_fields = NULL;
- if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
+ uint32_t old_fields_len = 0;
+ if (FL_TEST_RAW(obj, ROBJECT_HEAP)) {
old_fields = ROBJECT_FIELDS(obj);
+ old_fields_len = ROBJECT_FIELDS_CAPACITY(obj);
+ }
+ else {
+ FL_SET_RAW(obj, ROBJECT_HEAP);
}
RBASIC_SET_SHAPE_ID(obj, shape_id);
ROBJECT_SET_FIELDS_HASH(obj, table);
if (old_fields) {
- xfree(old_fields);
+ SIZED_FREE_N(old_fields, old_fields_len);
}
}
break;
case T_CLASS:
case T_MODULE:
- rb_bug("Unreachable");
+ case T_IMEMO:
+ UNREACHABLE;
break;
default:
{
- VALUE fields_obj = rb_imemo_fields_new_complex_tbl(rb_obj_class(obj), table);
- RBASIC_SET_SHAPE_ID(fields_obj, shape_id);
+ VALUE fields_obj = rb_imemo_fields_new_complex_tbl(obj, shape_id, table, RB_OBJ_SHAREABLE_P(obj));
rb_obj_replace_fields(obj, fields_obj);
}
}
@@ -1687,194 +1609,214 @@ obj_transition_too_complex(VALUE obj, st_table *table)
return shape_id;
}
-void
-rb_obj_init_too_complex(VALUE obj, st_table *table)
-{
- // This method is meant to be called on newly allocated object.
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
- RUBY_ASSERT(rb_shape_canonical_p(RBASIC_SHAPE_ID(obj)));
- RUBY_ASSERT(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)) == 0);
-
- obj_transition_too_complex(obj, table);
-}
-
-static int
-imemo_fields_complex_from_obj_i(ID key, VALUE val, st_data_t arg)
-{
- VALUE fields = (VALUE)arg;
- st_table *table = rb_imemo_fields_complex_tbl(fields);
-
- RUBY_ASSERT(!st_lookup(table, (st_data_t)key, NULL));
- st_add_direct(table, (st_data_t)key, (st_data_t)val);
- RB_OBJ_WRITTEN(fields, Qundef, val);
-
- return ST_CONTINUE;
-}
-
-static VALUE
-imemo_fields_complex_from_obj(VALUE owner, VALUE source_fields_obj, shape_id_t shape_id)
-{
- attr_index_t len = source_fields_obj ? RSHAPE_LEN(RBASIC_SHAPE_ID(source_fields_obj)) : 0;
- VALUE fields_obj = rb_imemo_fields_new_complex(owner, len + 1);
-
- rb_field_foreach(source_fields_obj, imemo_fields_complex_from_obj_i, (st_data_t)fields_obj, false);
- RBASIC_SET_SHAPE_ID(fields_obj, shape_id);
-
- return fields_obj;
-}
-
-static VALUE
-imemo_fields_copy_capa(VALUE owner, VALUE source_fields_obj, attr_index_t new_size)
-{
- VALUE fields_obj = rb_imemo_fields_new(owner, new_size);
- if (source_fields_obj) {
- attr_index_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(source_fields_obj));
- VALUE *fields = rb_imemo_fields_ptr(fields_obj);
- MEMCPY(fields, rb_imemo_fields_ptr(source_fields_obj), VALUE, fields_count);
- RBASIC_SET_SHAPE_ID(fields_obj, RBASIC_SHAPE_ID(source_fields_obj));
- for (attr_index_t i = 0; i < fields_count; i++) {
- RB_OBJ_WRITTEN(fields_obj, Qundef, fields[i]);
- }
- }
- return fields_obj;
-}
-
-void rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table);
-
// Copy all object fields, including ivars and internal object_id, etc
-shape_id_t
+static shape_id_t
rb_evict_fields_to_hash(VALUE obj)
{
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
+ RUBY_ASSERT(!rb_obj_shape_complex_p(obj));
st_table *table = st_init_numtable_with_size(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)));
rb_obj_copy_fields_to_hash_table(obj, table);
- shape_id_t new_shape_id = obj_transition_too_complex(obj, table);
+ shape_id_t new_shape_id = obj_transition_complex(obj, table);
- RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
+ RUBY_ASSERT(rb_obj_shape_complex_p(obj));
return new_shape_id;
}
void
rb_evict_ivars_to_hash(VALUE obj)
{
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
+ RUBY_ASSERT(!rb_obj_shape_complex_p(obj));
st_table *table = st_init_numtable_with_size(rb_ivar_count(obj));
// Evacuate all previous values from shape into id_table
rb_obj_copy_ivs_to_hash_table(obj, table);
- obj_transition_too_complex(obj, table);
+ obj_transition_complex(obj, table);
- RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
+ RUBY_ASSERT(rb_obj_shape_complex_p(obj));
}
-struct general_ivar_set_result {
- attr_index_t index;
- bool existing;
-};
+static VALUE
+rb_ivar_delete(VALUE obj, ID id, VALUE undef)
+{
+ rb_check_frozen(obj);
-static struct general_ivar_set_result
-general_ivar_set(VALUE obj, ID id, VALUE val, void *data,
- VALUE *(*shape_fields_func)(VALUE, void *),
- void (*shape_resize_fields_func)(VALUE, attr_index_t, attr_index_t, void *),
- void (*set_shape_id_func)(VALUE, shape_id_t, void *),
- shape_id_t (*transition_too_complex_func)(VALUE, void *),
- st_table *(*too_complex_table_func)(VALUE, void *))
-{
- struct general_ivar_set_result result = {
- .index = 0,
- .existing = true
- };
+ VALUE val = undef;
+ VALUE fields_obj;
+ bool concurrent = false;
+ int type = BUILTIN_TYPE(obj);
- shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj);
+ switch(type) {
+ case T_CLASS:
+ case T_MODULE:
+ IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
+
+ fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
+ if (rb_multi_ractor_p()) {
+ concurrent = true;
+ }
+ break;
+ case T_OBJECT:
+ fields_obj = obj;
+ break;
+ default: {
+ fields_obj = rb_obj_fields(obj, id);
+ break;
+ }
+ }
- if (UNLIKELY(rb_shape_too_complex_p(current_shape_id))) {
- goto too_complex;
+ if (!fields_obj) {
+ return undef;
}
- attr_index_t index;
- if (!rb_shape_get_iv_index(current_shape_id, id, &index)) {
- result.existing = false;
+ const VALUE original_fields_obj = fields_obj;
+ if (concurrent) {
+ fields_obj = rb_imemo_fields_clone(fields_obj);
+ }
+
+ shape_id_t old_shape_id = RBASIC_SHAPE_ID(fields_obj);
+ shape_id_t removed_shape_id;
+ shape_id_t next_shape_id = rb_obj_shape_transition_remove_ivar(fields_obj, id, &removed_shape_id);
- index = RSHAPE_LEN(current_shape_id);
- if (index >= SHAPE_MAX_FIELDS) {
- rb_raise(rb_eArgError, "too many instance variables");
+ if (UNLIKELY(rb_shape_complex_p(next_shape_id))) {
+ if (UNLIKELY(!rb_shape_complex_p(old_shape_id))) {
+ if (type == T_OBJECT) {
+ rb_evict_fields_to_hash(obj);
+ }
+ else {
+ fields_obj = imemo_fields_complex_from_obj(obj, fields_obj, next_shape_id);
+ }
+ }
+ st_data_t key = id;
+ if (!st_delete(rb_imemo_fields_complex_tbl(fields_obj), &key, (st_data_t *)&val)) {
+ val = undef;
+ }
+ }
+ else {
+ if (next_shape_id == old_shape_id) {
+ return undef;
}
- shape_id_t next_shape_id = rb_shape_transition_add_ivar(obj, id);
- if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) {
- current_shape_id = transition_too_complex_func(obj, data);
- goto too_complex;
+ RUBY_ASSERT(removed_shape_id != INVALID_SHAPE_ID);
+ RUBY_ASSERT(RSHAPE_LEN(next_shape_id) == RSHAPE_LEN(old_shape_id) - 1);
+
+ VALUE *fields = rb_imemo_fields_ptr(fields_obj);
+ attr_index_t removed_index = RSHAPE_INDEX(removed_shape_id);
+ val = fields[removed_index];
+
+ attr_index_t new_fields_count = RSHAPE_LEN(next_shape_id);
+ if (new_fields_count) {
+ size_t trailing_fields = new_fields_count - removed_index;
+
+ MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields);
+ RUBY_ASSERT(rb_shape_layout(next_shape_id) == SHAPE_ID_LAYOUT_ROBJECT);
+ RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
+
+ if (FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP)) {
+ if (rb_obj_embedded_size(new_fields_count) <= rb_gc_obj_slot_size(fields_obj)) {
+ // Re-embed objects when instances become small enough
+ // This is necessary because YJIT assumes that objects with the same shape
+ // have the same embeddedness for efficiency (avoid extra checks)
+ FL_UNSET_RAW(fields_obj, ROBJECT_HEAP);
+ MEMCPY(rb_imemo_fields_ptr(fields_obj), fields, VALUE, new_fields_count);
+ SIZED_FREE_N(fields, RSHAPE_CAPACITY(old_shape_id));
+ }
+ else if (RSHAPE_CAPACITY(old_shape_id) != RSHAPE_CAPACITY(next_shape_id)) {
+ IMEMO_OBJ_FIELDS(fields_obj)->as.external.ptr = ruby_xrealloc_sized(fields, RSHAPE_CAPACITY(next_shape_id) * sizeof(VALUE), RSHAPE_CAPACITY(old_shape_id) * sizeof(VALUE));
+ }
+ }
}
- else if (UNLIKELY(RSHAPE_CAPACITY(next_shape_id) != RSHAPE_CAPACITY(current_shape_id))) {
- RUBY_ASSERT(RSHAPE_CAPACITY(next_shape_id) > RSHAPE_CAPACITY(current_shape_id));
- shape_resize_fields_func(obj, RSHAPE_CAPACITY(current_shape_id), RSHAPE_CAPACITY(next_shape_id), data);
+ else {
+ fields_obj = 0;
+ rb_free_generic_ivar(obj);
}
+ }
- RUBY_ASSERT(RSHAPE_TYPE_P(next_shape_id, SHAPE_IVAR),
- "next_shape_id: 0x%" PRIx32 " RSHAPE_TYPE(next_shape_id): %d",
- next_shape_id, (int)RSHAPE_TYPE(next_shape_id));
- RUBY_ASSERT(index == (RSHAPE_INDEX(next_shape_id)));
- set_shape_id_func(obj, next_shape_id, data);
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | next_shape_id);
+ if (fields_obj != original_fields_obj) {
+ switch (type) {
+ case T_OBJECT:
+ break;
+ case T_CLASS:
+ case T_MODULE:
+ RCLASS_WRITABLE_SET_FIELDS_OBJ(obj, fields_obj);
+ break;
+ default:
+ rb_obj_set_fields(obj, fields_obj, id, original_fields_obj);
+ break;
+ }
}
- VALUE *table = shape_fields_func(obj, data);
- RB_OBJ_WRITE(obj, &table[index], val);
+ return val;
+}
- result.index = index;
- return result;
+VALUE
+rb_attr_delete(VALUE obj, ID id)
+{
+ return rb_ivar_delete(obj, id, Qnil);
+}
-too_complex:
- {
- RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
+void
+rb_obj_init_complex(VALUE obj, st_table *table)
+{
+ // This method is meant to be called on newly allocated object.
+ RUBY_ASSERT(rb_shape_canonical_p(RBASIC_SHAPE_ID(obj)));
+ RUBY_ASSERT(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)) == 0);
- st_table *table = too_complex_table_func(obj, data);
- result.existing = st_insert(table, (st_data_t)id, (st_data_t)val);
- result.index = 0;
- RB_OBJ_WRITTEN(obj, Qundef, val);
+ if (rb_obj_shape_complex_p(obj)) {
+ st_table *old_table = ROBJECT_FIELDS_HASH(obj);
+ ROBJECT_SET_FIELDS_HASH(obj, table);
+ if (old_table) st_free_table(old_table);
+ }
+ else {
+ obj_transition_complex(obj, table);
}
- return result;
}
-static void
-general_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val, void *data,
- VALUE *(*shape_fields_func)(VALUE, void *),
- void (*shape_resize_fields_func)(VALUE, attr_index_t, attr_index_t, void *),
- void (*set_shape_id_func)(VALUE, shape_id_t, void *),
- shape_id_t (*transition_too_complex_func)(VALUE, void *),
- st_table *(*too_complex_table_func)(VALUE, void *))
+static int
+imemo_fields_complex_from_obj_i(ID key, VALUE val, st_data_t arg)
{
- shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj);
+ VALUE fields = (VALUE)arg;
+ st_table *table = rb_imemo_fields_complex_tbl(fields);
- if (UNLIKELY(rb_shape_too_complex_p(target_shape_id))) {
- if (UNLIKELY(!rb_shape_too_complex_p(current_shape_id))) {
- current_shape_id = transition_too_complex_func(obj, data);
- }
+ RUBY_ASSERT(!st_lookup(table, (st_data_t)key, NULL));
+ st_add_direct(table, (st_data_t)key, (st_data_t)val);
+ RB_OBJ_WRITTEN(fields, Qundef, val);
- st_table *table = too_complex_table_func(obj, data);
+ return ST_CONTINUE;
+}
- if (RSHAPE_LEN(target_shape_id) > RSHAPE_LEN(current_shape_id)) {
- RBASIC_SET_SHAPE_ID(obj, target_shape_id);
- }
+static VALUE
+imemo_fields_complex_from_obj(VALUE owner, VALUE source_fields_obj, shape_id_t shape_id)
+{
+ attr_index_t len = source_fields_obj ? RSHAPE_LEN(RBASIC_SHAPE_ID(source_fields_obj)) : 0;
+ VALUE fields_obj = rb_imemo_fields_new_complex(owner, shape_id, len + 1, RB_OBJ_SHAREABLE_P(owner));
- RUBY_ASSERT(RSHAPE_EDGE_NAME(target_shape_id));
- st_insert(table, (st_data_t)RSHAPE_EDGE_NAME(target_shape_id), (st_data_t)val);
- RB_OBJ_WRITTEN(obj, Qundef, val);
- }
- else {
- attr_index_t index = RSHAPE_INDEX(target_shape_id);
- if (index >= RSHAPE_CAPACITY(current_shape_id)) {
- shape_resize_fields_func(obj, RSHAPE_CAPACITY(current_shape_id), RSHAPE_CAPACITY(target_shape_id), data);
- }
+ rb_field_foreach(source_fields_obj, imemo_fields_complex_from_obj_i, (st_data_t)fields_obj, false);
- if (RSHAPE_LEN(target_shape_id) > RSHAPE_LEN(current_shape_id)) {
- set_shape_id_func(obj, target_shape_id, data);
- }
+ return fields_obj;
+}
- VALUE *table = shape_fields_func(obj, data);
- RB_OBJ_WRITE(obj, &table[index], val);
+static VALUE
+imemo_fields_copy_append(VALUE owner, VALUE source_fields_obj, shape_id_t current_shape_id, shape_id_t target_shape_id, VALUE val)
+{
+ attr_index_t fields_count = RSHAPE_LEN(current_shape_id);
+
+ VALUE fields_obj = rb_imemo_fields_new(owner, target_shape_id, RB_OBJ_SHAREABLE_P(owner));
+
+ VALUE *fields = rb_imemo_fields_ptr(fields_obj);
+
+ if (source_fields_obj) {
+ MEMCPY(fields, rb_imemo_fields_ptr(source_fields_obj), VALUE, fields_count);
+ for (attr_index_t i = 0; i < fields_count; i++) {
+ RB_OBJ_WRITTEN(fields_obj, Qundef, fields[i]);
+ }
}
+
+ RB_OBJ_WRITE(fields_obj, &fields[fields_count], val);
+
+ return fields_obj;
}
static VALUE
@@ -1883,8 +1825,8 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f
const VALUE original_fields_obj = fields_obj;
shape_id_t current_shape_id = fields_obj ? RBASIC_SHAPE_ID(fields_obj) : ROOT_SHAPE_ID;
- if (UNLIKELY(rb_shape_too_complex_p(target_shape_id))) {
- if (rb_shape_too_complex_p(current_shape_id)) {
+ if (UNLIKELY(rb_shape_complex_p(target_shape_id))) {
+ if (rb_shape_complex_p(current_shape_id)) {
if (concurrent) {
// In multi-ractor case, we must always work on a copy because
// even if the field already exist, inserting in a st_table may
@@ -1902,26 +1844,26 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f
RUBY_ASSERT(field_name);
st_insert(table, (st_data_t)field_name, (st_data_t)val);
RB_OBJ_WRITTEN(fields_obj, Qundef, val);
- RBASIC_SET_SHAPE_ID(fields_obj, target_shape_id);
+ RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(target_shape_id));
}
else {
attr_index_t index = RSHAPE_INDEX(target_shape_id);
if (concurrent || index >= RSHAPE_CAPACITY(current_shape_id)) {
- fields_obj = imemo_fields_copy_capa(owner, original_fields_obj, RSHAPE_CAPACITY(target_shape_id));
+ return imemo_fields_copy_append(owner, original_fields_obj, current_shape_id, target_shape_id, val);
}
VALUE *table = rb_imemo_fields_ptr(fields_obj);
RB_OBJ_WRITE(fields_obj, &table[index], val);
- if (RSHAPE_LEN(target_shape_id) > RSHAPE_LEN(current_shape_id)) {
- RBASIC_SET_SHAPE_ID(fields_obj, target_shape_id);
+ if (index >= RSHAPE_LEN(current_shape_id)) {
+ RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(target_shape_id));
}
}
return fields_obj;
}
-static void
+static attr_index_t
generic_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE val)
{
if (!field_name) {
@@ -1933,6 +1875,7 @@ generic_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE va
VALUE fields_obj = imemo_fields_set(obj, original_fields_obj, target_shape_id, field_name, val, false);
rb_obj_set_fields(obj, fields_obj, field_name, original_fields_obj);
+ return rb_shape_complex_p(target_shape_id) ? ATTR_INDEX_NOT_SET : RSHAPE_INDEX(target_shape_id);
}
static shape_id_t
@@ -1942,14 +1885,10 @@ generic_shape_ivar(VALUE obj, ID id, bool *new_ivar_out)
shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj);
shape_id_t target_shape_id = current_shape_id;
- if (!rb_shape_too_complex_p(current_shape_id)) {
+ if (!rb_shape_complex_p(current_shape_id)) {
if (!rb_shape_find_ivar(current_shape_id, id, &target_shape_id)) {
- if (RSHAPE_LEN(current_shape_id) >= SHAPE_MAX_FIELDS) {
- rb_raise(rb_eArgError, "too many instance variables");
- }
-
new_ivar = true;
- target_shape_id = rb_shape_transition_add_ivar(obj, id);
+ target_shape_id = rb_obj_shape_transition_add_ivar(obj, id);
}
}
@@ -1957,29 +1896,29 @@ generic_shape_ivar(VALUE obj, ID id, bool *new_ivar_out)
return target_shape_id;
}
-static void
+static attr_index_t
generic_ivar_set(VALUE obj, ID id, VALUE val)
{
bool dontcare;
shape_id_t target_shape_id = generic_shape_ivar(obj, id, &dontcare);
- generic_field_set(obj, target_shape_id, id, val);
+ return generic_field_set(obj, target_shape_id, id, val);
}
void
rb_ensure_iv_list_size(VALUE obj, uint32_t current_len, uint32_t new_capacity)
{
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
+ RUBY_ASSERT(!rb_obj_shape_complex_p(obj));
- if (RBASIC(obj)->flags & ROBJECT_EMBED) {
+ if (FL_TEST_RAW(obj, ROBJECT_HEAP)) {
+ SIZED_REALLOC_N(ROBJECT(obj)->as.heap.fields, VALUE, new_capacity, current_len);
+ }
+ else {
VALUE *ptr = ROBJECT_FIELDS(obj);
VALUE *newptr = ALLOC_N(VALUE, new_capacity);
MEMCPY(newptr, ptr, VALUE, current_len);
- RB_FL_UNSET_RAW(obj, ROBJECT_EMBED);
+ FL_SET_RAW(obj, ROBJECT_HEAP);
ROBJECT(obj)->as.heap.fields = newptr;
}
- else {
- REALLOC_N(ROBJECT(obj)->as.heap.fields, VALUE, new_capacity);
- }
}
static int
@@ -2003,60 +1942,52 @@ rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table)
rb_field_foreach(obj, rb_obj_copy_ivs_to_hash_table_i, (st_data_t)table, false);
}
-static VALUE *
-obj_ivar_set_shape_fields(VALUE obj, void *_data)
+static attr_index_t
+obj_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE val)
{
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
+ shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj);
- return ROBJECT_FIELDS(obj);
-}
+ if (UNLIKELY(rb_shape_complex_p(target_shape_id))) {
+ if (UNLIKELY(!rb_shape_complex_p(current_shape_id))) {
+ current_shape_id = rb_evict_fields_to_hash(obj);
+ }
-static void
-obj_ivar_set_shape_resize_fields(VALUE obj, attr_index_t old_capa, attr_index_t new_capa, void *_data)
-{
- rb_ensure_iv_list_size(obj, old_capa, new_capa);
-}
+ if (RSHAPE_LEN(target_shape_id) > RSHAPE_LEN(current_shape_id)) {
+ RBASIC_SET_SHAPE_ID(obj, target_shape_id);
+ }
-static void
-obj_ivar_set_set_shape_id(VALUE obj, shape_id_t shape_id, void *_data)
-{
- rb_obj_set_shape_id(obj, shape_id);
-}
+ if (!field_name) {
+ field_name = RSHAPE_EDGE_NAME(target_shape_id);
+ RUBY_ASSERT(field_name);
+ }
-static shape_id_t
-obj_ivar_set_transition_too_complex(VALUE obj, void *_data)
-{
- return rb_evict_fields_to_hash(obj);
-}
+ st_insert(ROBJECT_FIELDS_HASH(obj), (st_data_t)field_name, (st_data_t)val);
+ RB_OBJ_WRITTEN(obj, Qundef, val);
-static st_table *
-obj_ivar_set_too_complex_table(VALUE obj, void *_data)
-{
- RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
+ return ATTR_INDEX_NOT_SET;
+ }
+ else {
+ attr_index_t index = RSHAPE_INDEX(target_shape_id);
- return ROBJECT_FIELDS_HASH(obj);
-}
+ if (index >= RSHAPE_LEN(current_shape_id)) {
+ if (UNLIKELY(index >= RSHAPE_CAPACITY(current_shape_id))) {
+ rb_ensure_iv_list_size(obj, RSHAPE_CAPACITY(current_shape_id), RSHAPE_CAPACITY(target_shape_id));
+ }
+ RBASIC_SET_SHAPE_ID(obj, target_shape_id);
+ }
-attr_index_t
-rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
-{
- return general_ivar_set(obj, id, val, NULL,
- obj_ivar_set_shape_fields,
- obj_ivar_set_shape_resize_fields,
- obj_ivar_set_set_shape_id,
- obj_ivar_set_transition_too_complex,
- obj_ivar_set_too_complex_table).index;
+ RB_OBJ_WRITE(obj, &ROBJECT_FIELDS(obj)[index], val);
+
+ return index;
+ }
}
-static void
-obj_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val)
+static attr_index_t
+obj_ivar_set(VALUE obj, ID id, VALUE val)
{
- general_field_set(obj, target_shape_id, val, NULL,
- obj_ivar_set_shape_fields,
- obj_ivar_set_shape_resize_fields,
- obj_ivar_set_set_shape_id,
- obj_ivar_set_transition_too_complex,
- obj_ivar_set_too_complex_table);
+ bool dontcare;
+ shape_id_t target_shape_id = generic_shape_ivar(obj, id, &dontcare);
+ return obj_field_set(obj, target_shape_id, id, val);
}
/* Set the instance variable +val+ on object +obj+ at ivar name +id+.
@@ -2067,23 +1998,12 @@ VALUE
rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val)
{
rb_check_frozen(obj);
- rb_obj_ivar_set(obj, id, val);
+ obj_ivar_set(obj, id, val);
return val;
}
-bool
-rb_obj_set_shape_id(VALUE obj, shape_id_t shape_id)
-{
- shape_id_t old_shape_id = rb_obj_shape_id(obj);
- if (old_shape_id == shape_id) {
- return false;
- }
-
- RB_SET_SHAPE_ID(obj, shape_id);
- return true;
-}
-
-void rb_obj_freeze_inline(VALUE x)
+void
+rb_obj_freeze_inline(VALUE x)
{
if (RB_FL_ABLE(x)) {
RB_FL_SET_RAW(x, RUBY_FL_FREEZE);
@@ -2091,34 +2011,45 @@ void rb_obj_freeze_inline(VALUE x)
RB_FL_UNSET_RAW(x, FL_USER2 | FL_USER3); // STR_CHILLED
}
- RB_SET_SHAPE_ID(x, rb_shape_transition_frozen(x));
+ // rb_obj_freeze_inline(String)
+ shape_id_t shape_id = rb_obj_shape_transition_frozen(x);
+ switch (BUILTIN_TYPE(x)) {
+ case T_CLASS:
+ case T_MODULE:
+ rb_obj_freeze_inline(RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(x));
+ // FIXME: How to do multi-shape?
+ RBASIC_SET_SHAPE_ID(x, shape_id);
+ break;
+ default:
+ RBASIC_SET_SHAPE_ID(x, shape_id);
+ break;
+ }
- if (RBASIC_CLASS(x)) {
+ if (RBASIC_CLASS(x) && RCLASS_SINGLETON_P(RBASIC_CLASS(x))) {
rb_freeze_singleton_class(x);
}
}
}
-static void
+static attr_index_t class_ivar_set(VALUE obj, ID id, VALUE val, bool *new_ivar);
+
+static attr_index_t
ivar_set(VALUE obj, ID id, VALUE val)
{
RB_DEBUG_COUNTER_INC(ivar_set_base);
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- {
- rb_obj_ivar_set(obj, id, val);
- break;
- }
+ return obj_ivar_set(obj, id, val);
case T_CLASS:
case T_MODULE:
- IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
- rb_class_ivar_set(obj, id, val);
-
- break;
+ {
+ IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
+ bool dontcare;
+ return class_ivar_set(obj, id, val, &dontcare);
+ }
default:
- generic_ivar_set(obj, id, val);
- break;
+ return generic_ivar_set(obj, id, val);
}
}
@@ -2130,6 +2061,12 @@ rb_ivar_set(VALUE obj, ID id, VALUE val)
return val;
}
+attr_index_t
+rb_ivar_set_index(VALUE obj, ID id, VALUE val)
+{
+ return ivar_set(obj, id, val);
+}
+
void
rb_ivar_set_internal(VALUE obj, ID id, VALUE val)
{
@@ -2139,21 +2076,19 @@ rb_ivar_set_internal(VALUE obj, ID id, VALUE val)
ivar_set(obj, id, val);
}
-void
+attr_index_t
rb_obj_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE val)
{
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
- obj_field_set(obj, target_shape_id, val);
- break;
+ return obj_field_set(obj, target_shape_id, field_name, val);
case T_CLASS:
case T_MODULE:
// The only field is object_id and T_CLASS handle it differently.
rb_bug("Unreachable");
break;
default:
- generic_field_set(obj, target_shape_id, field_name, val);
- break;
+ return generic_field_set(obj, target_shape_id, field_name, val);
}
}
@@ -2162,7 +2097,7 @@ ivar_defined0(VALUE obj, ID id)
{
attr_index_t index;
- if (rb_shape_obj_too_complex_p(obj)) {
+ if (rb_obj_shape_complex_p(obj)) {
VALUE idx;
st_table *table = NULL;
switch (BUILTIN_TYPE(obj)) {
@@ -2226,6 +2161,7 @@ struct iv_itr_data {
st_data_t arg;
rb_ivar_foreach_callback_func *func;
VALUE *fields;
+ shape_id_t shape_id;
bool ivar_only;
};
@@ -2241,12 +2177,12 @@ iterate_over_shapes_callback(shape_id_t shape_id, void *data)
VALUE *fields;
switch (BUILTIN_TYPE(itr_data->obj)) {
case T_OBJECT:
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(itr_data->obj));
+ RUBY_ASSERT(!rb_obj_shape_complex_p(itr_data->obj));
fields = ROBJECT_FIELDS(itr_data->obj);
break;
case T_IMEMO:
RUBY_ASSERT(IMEMO_TYPE_P(itr_data->obj, imemo_fields));
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(itr_data->obj));
+ RUBY_ASSERT(!rb_obj_shape_complex_p(itr_data->obj));
fields = rb_imemo_fields_ptr(itr_data->obj);
break;
@@ -2254,8 +2190,14 @@ iterate_over_shapes_callback(shape_id_t shape_id, void *data)
rb_bug("Unreachable");
}
+ RUBY_ASSERT(itr_data->shape_id == RBASIC_SHAPE_ID(itr_data->obj));
+
VALUE val = fields[RSHAPE_INDEX(shape_id)];
- return itr_data->func(RSHAPE_EDGE_NAME(shape_id), val, itr_data->arg);
+ int ret = itr_data->func(RSHAPE_EDGE_NAME(shape_id), val, itr_data->arg);
+
+ RUBY_ASSERT(itr_data->shape_id == RBASIC_SHAPE_ID(itr_data->obj));
+
+ return ret;
}
/*
@@ -2289,11 +2231,12 @@ obj_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b
};
shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
- if (rb_shape_too_complex_p(shape_id)) {
- rb_st_foreach(ROBJECT_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
+ if (rb_shape_complex_p(shape_id)) {
+ st_foreach_safe(ROBJECT_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
}
else {
itr_data.fields = ROBJECT_FIELDS(obj);
+ itr_data.shape_id = shape_id;
iterate_over_shapes(shape_id, func, &itr_data);
}
}
@@ -2311,11 +2254,12 @@ imemo_fields_each(VALUE fields_obj, rb_ivar_foreach_callback_func *func, st_data
};
shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
- if (rb_shape_too_complex_p(shape_id)) {
+ if (rb_shape_complex_p(shape_id)) {
rb_st_foreach(rb_imemo_fields_complex_tbl(fields_obj), each_hash_iv, (st_data_t)&itr_data);
}
else {
itr_data.fields = rb_imemo_fields_ptr(fields_obj);
+ itr_data.shape_id = shape_id;
iterate_over_shapes(shape_id, func, &itr_data);
}
}
@@ -2327,7 +2271,7 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
rb_check_frozen(dest);
- if (!rb_obj_exivar_p(obj)) {
+ if (!rb_obj_gen_fields_p(obj)) {
return;
}
@@ -2340,7 +2284,7 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
goto clear;
}
- if (rb_shape_too_complex_p(src_shape_id)) {
+ if (rb_shape_complex_p(src_shape_id)) {
rb_shape_copy_complex_ivars(dest, obj, src_shape_id, rb_imemo_fields_complex_tbl(fields_obj));
return;
}
@@ -2352,24 +2296,23 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
RUBY_ASSERT(RSHAPE_TYPE_P(initial_shape_id, SHAPE_ROOT));
dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id);
- if (UNLIKELY(rb_shape_too_complex_p(dest_shape_id))) {
+ if (UNLIKELY(rb_shape_complex_p(dest_shape_id))) {
st_table *table = rb_st_init_numtable_with_size(src_num_ivs);
rb_obj_copy_ivs_to_hash_table(obj, table);
- rb_obj_init_too_complex(dest, table);
+ rb_obj_init_complex(dest, table);
return;
}
}
if (!RSHAPE_LEN(dest_shape_id)) {
- rb_obj_set_shape_id(dest, dest_shape_id);
+ RBASIC_SET_SHAPE_ID(dest, rb_shape_layout(RBASIC_SHAPE_ID(dest)) | dest_shape_id);
return;
}
- new_fields_obj = rb_imemo_fields_new(dest, RSHAPE_CAPACITY(dest_shape_id));
+ new_fields_obj = rb_imemo_fields_new(dest, dest_shape_id, RB_OBJ_SHAREABLE_P(dest));
VALUE *src_buf = rb_imemo_fields_ptr(fields_obj);
VALUE *dest_buf = rb_imemo_fields_ptr(new_fields_obj);
rb_shape_copy_fields(new_fields_obj, dest_buf, dest_shape_id, src_buf, src_shape_id);
- RBASIC_SET_SHAPE_ID(new_fields_obj, dest_shape_id);
rb_obj_replace_fields(dest, new_fields_obj);
}
@@ -2428,12 +2371,47 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
}
}
+struct ivar_buf_entry {
+ ID name;
+ VALUE val;
+};
+
+static int
+collect_ivar_i(ID id, VALUE val, st_data_t arg)
+{
+ struct ivar_buf_entry **pos = (struct ivar_buf_entry **)arg;
+ (*pos)->name = id;
+ (*pos)->val = val;
+ (*pos)++;
+ return ST_CONTINUE;
+}
+
void
rb_ivar_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
{
rb_field_foreach(obj, func, arg, true);
}
+void
+rb_ivar_foreach_buffered(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
+{
+ st_index_t count = rb_ivar_count(obj);
+ if (count == 0) return;
+
+ VALUE tmpbuf;
+ struct ivar_buf_entry *buf = ALLOCV_N(struct ivar_buf_entry, tmpbuf, count);
+ struct ivar_buf_entry *pos = buf;
+
+ rb_field_foreach(obj, collect_ivar_i, (st_data_t)&pos, true);
+ RUBY_ASSERT((st_index_t)(pos - buf) == count);
+
+ for (st_index_t i = 0; i < count; i++) {
+ if (func(buf[i].name, buf[i].val, arg) == ST_STOP) break;
+ }
+
+ ALLOCV_END(tmpbuf);
+}
+
st_index_t
rb_ivar_count(VALUE obj)
{
@@ -2452,7 +2430,7 @@ rb_ivar_count(VALUE obj)
if (!fields_obj) {
return 0;
}
- if (rb_shape_obj_too_complex_p(fields_obj)) {
+ if (rb_obj_shape_complex_p(fields_obj)) {
iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj));
}
else {
@@ -2464,7 +2442,7 @@ rb_ivar_count(VALUE obj)
case T_IMEMO:
RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields));
- if (rb_shape_obj_too_complex_p(obj)) {
+ if (rb_obj_shape_complex_p(obj)) {
iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(obj));
}
else {
@@ -2476,8 +2454,8 @@ rb_ivar_count(VALUE obj)
{
VALUE fields_obj = rb_obj_fields_no_ractor_check(obj);
if (fields_obj) {
- if (rb_shape_obj_too_complex_p(fields_obj)) {
- rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj));
+ if (rb_obj_shape_complex_p(fields_obj)) {
+ iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj));
}
else {
iv_count = RBASIC_FIELDS_COUNT(obj);
@@ -2487,7 +2465,7 @@ rb_ivar_count(VALUE obj)
break;
}
- if (rb_shape_obj_has_id(obj)) {
+ if (rb_obj_shape_has_id(obj)) {
iv_count--;
}
@@ -2525,9 +2503,7 @@ ivar_i(ID key, VALUE v, st_data_t a)
VALUE
rb_obj_instance_variables(VALUE obj)
{
- VALUE ary;
-
- ary = rb_ary_new();
+ VALUE ary = rb_ary_new_capa(rb_ivar_count(obj));
rb_ivar_foreach(obj, ivar_i, ary);
return ary;
}
@@ -2743,9 +2719,9 @@ struct autoload_const {
// The shared "autoload_data" if multiple constants are defined from the same feature.
VALUE autoload_data_value;
- // The namespace object when the autoload is called in a user namespace
- // Otherwise, Qnil means the builtin namespace, Qfalse means unspecified.
- VALUE namespace;
+ // The box object when the autoload is called in a user box
+ // Otherwise, Qnil means the root box
+ VALUE box_value;
// The module we are loading a constant into.
VALUE module;
@@ -2798,7 +2774,7 @@ autoload_data_free(void *ptr)
ccan_list_del_init(&autoload_const->cnode);
}
- ruby_xfree(p);
+ SIZED_FREE(p);
}
static size_t
@@ -2822,7 +2798,7 @@ autoload_const_mark_and_move(void *ptr)
rb_gc_mark_and_move(&ac->autoload_data_value);
rb_gc_mark_and_move(&ac->value);
rb_gc_mark_and_move(&ac->file);
- rb_gc_mark_and_move(&ac->namespace);
+ rb_gc_mark_and_move(&ac->box_value);
}
static size_t
@@ -2837,13 +2813,13 @@ autoload_const_free(void *ptr)
struct autoload_const *autoload_const = ptr;
ccan_list_del(&autoload_const->cnode);
- ruby_xfree(ptr);
+ SIZED_FREE(autoload_const);
}
static const rb_data_type_t autoload_const_type = {
"autoload_const",
{autoload_const_mark_and_move, autoload_const_free, autoload_const_memsize, autoload_const_mark_and_move,},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
static struct autoload_data *
@@ -2868,17 +2844,17 @@ get_autoload_data(VALUE autoload_const_value, struct autoload_const **autoload_c
struct autoload_copy_table_data {
VALUE dst_tbl_value;
struct st_table *dst_tbl;
- const rb_namespace_t *ns;
+ const rb_box_t *box;
};
static int
-autoload_copy_table_for_namespace_i(st_data_t key, st_data_t value, st_data_t arg)
+autoload_copy_table_for_box_i(st_data_t key, st_data_t value, st_data_t arg)
{
struct autoload_const *autoload_const;
struct autoload_copy_table_data *data = (struct autoload_copy_table_data *)arg;
struct st_table *tbl = data->dst_tbl;
VALUE tbl_value = data->dst_tbl_value;
- const rb_namespace_t *ns = data->ns;
+ const rb_box_t *box = data->box;
VALUE src_value = (VALUE)value;
struct autoload_const *src_const = rb_check_typeddata(src_value, &autoload_const_type);
@@ -2887,12 +2863,12 @@ autoload_copy_table_for_namespace_i(st_data_t key, st_data_t value, st_data_t ar
struct autoload_data *autoload_data = rb_check_typeddata(autoload_data_value, &autoload_data_type);
VALUE new_value = TypedData_Make_Struct(0, struct autoload_const, &autoload_const_type, autoload_const);
- autoload_const->namespace = rb_get_namespace_object((rb_namespace_t *)ns);
- autoload_const->module = src_const->module;
+ RB_OBJ_WRITE(new_value, &autoload_const->box_value, rb_get_box_object((rb_box_t *)box));
+ RB_OBJ_WRITE(new_value, &autoload_const->module, src_const->module);
autoload_const->name = src_const->name;
- autoload_const->value = src_const->value;
+ RB_OBJ_WRITE(new_value, &autoload_const->value, src_const->value);
autoload_const->flag = src_const->flag;
- autoload_const->autoload_data_value = autoload_data_value;
+ RB_OBJ_WRITE(new_value, &autoload_const->autoload_data_value, autoload_data_value);
ccan_list_add_tail(&autoload_data->constants, &autoload_const->cnode);
st_insert(tbl, (st_data_t)autoload_const->name, (st_data_t)new_value);
@@ -2902,7 +2878,7 @@ autoload_copy_table_for_namespace_i(st_data_t key, st_data_t value, st_data_t ar
}
void
-rb_autoload_copy_table_for_namespace(st_table *iv_ptr, const rb_namespace_t *ns)
+rb_autoload_copy_table_for_box(st_table *iv_ptr, const rb_box_t *box)
{
struct st_table *src_tbl, *dst_tbl;
VALUE src_tbl_value, dst_tbl_value;
@@ -2922,10 +2898,10 @@ rb_autoload_copy_table_for_namespace(st_table *iv_ptr, const rb_namespace_t *ns)
struct autoload_copy_table_data data = {
.dst_tbl_value = dst_tbl_value,
.dst_tbl = dst_tbl,
- .ns = ns,
+ .box = box,
};
- st_foreach(src_tbl, autoload_copy_table_for_namespace_i, (st_data_t)&data);
+ st_foreach(src_tbl, autoload_copy_table_for_box_i, (st_data_t)&data);
st_insert(iv_ptr, (st_data_t)autoload, (st_data_t)dst_tbl_value);
}
@@ -2946,7 +2922,7 @@ struct autoload_arguments {
VALUE module;
ID name;
VALUE feature;
- VALUE namespace;
+ VALUE box_value;
};
static VALUE
@@ -3016,12 +2992,12 @@ autoload_synchronized(VALUE _arguments)
{
struct autoload_const *autoload_const;
VALUE autoload_const_value = TypedData_Make_Struct(0, struct autoload_const, &autoload_const_type, autoload_const);
- autoload_const->namespace = arguments->namespace;
- autoload_const->module = arguments->module;
+ RB_OBJ_WRITE(autoload_const_value, &autoload_const->box_value, arguments->box_value);
+ RB_OBJ_WRITE(autoload_const_value, &autoload_const->module, arguments->module);
autoload_const->name = arguments->name;
autoload_const->value = Qundef;
autoload_const->flag = CONST_PUBLIC;
- autoload_const->autoload_data_value = autoload_data_value;
+ RB_OBJ_WRITE(autoload_const_value, &autoload_const->autoload_data_value, autoload_data_value);
ccan_list_add_tail(&autoload_data->constants, &autoload_const->cnode);
st_insert(autoload_table, (st_data_t)arguments->name, (st_data_t)autoload_const_value);
RB_OBJ_WRITTEN(autoload_table_value, Qundef, autoload_const_value);
@@ -3033,8 +3009,8 @@ autoload_synchronized(VALUE _arguments)
void
rb_autoload_str(VALUE module, ID name, VALUE feature)
{
- const rb_namespace_t *ns = rb_current_namespace();
- VALUE current_namespace = rb_get_namespace_object((rb_namespace_t *)ns);
+ const rb_box_t *box = rb_current_box();
+ VALUE current_box_value = rb_get_box_object((rb_box_t *)box);
if (!rb_is_const_id(name)) {
rb_raise(rb_eNameError, "autoload must be constant name: %"PRIsVALUE"", QUOTE_ID(name));
@@ -3049,7 +3025,7 @@ rb_autoload_str(VALUE module, ID name, VALUE feature)
.module = module,
.name = name,
.feature = feature,
- .namespace = current_namespace,
+ .box_value = current_box_value,
};
VALUE result = rb_mutex_synchronize(autoload_mutex, autoload_synchronized, (VALUE)&arguments);
@@ -3308,43 +3284,6 @@ autoload_apply_constants(VALUE _arguments)
return Qtrue;
}
-struct autoload_feature_require_data {
- struct autoload_load_arguments *arguments;
- VALUE receiver;
- VALUE feature;
-};
-
-static VALUE
-autoload_feature_require_in_builtin(VALUE arg)
-{
- struct autoload_feature_require_data *data = (struct autoload_feature_require_data *)arg;
-
- VALUE result = rb_funcall(data->receiver, rb_intern("require"), 1, data->feature);
- if (RTEST(result)) {
- return rb_mutex_synchronize(autoload_mutex, autoload_apply_constants, (VALUE)data->arguments);
- }
- return Qnil;
-}
-
-static VALUE
-autoload_feature_require_ensure_in_builtin(VALUE _arg)
-{
- /*
- * The gccct should be cleared again after the rb_funcall() to remove
- * the inconsistent cache entry against the current namespace.
- */
- rb_gccct_clear_table(Qnil);
- rb_namespace_disable_builtin();
- return Qnil;
-}
-
-static VALUE
-autoload_feature_require_in_builtin_wrap(VALUE arg)
-{
- return rb_ensure(autoload_feature_require_in_builtin, arg,
- autoload_feature_require_ensure_in_builtin, Qnil);
-}
-
static VALUE
autoload_feature_require(VALUE _arguments)
{
@@ -3353,31 +3292,21 @@ autoload_feature_require(VALUE _arguments)
struct autoload_load_arguments *arguments = (struct autoload_load_arguments*)_arguments;
struct autoload_const *autoload_const = arguments->autoload_const;
- VALUE autoload_namespace = autoload_const->namespace;
+ VALUE autoload_box_value = autoload_const->box_value;
// We save this for later use in autoload_apply_constants:
arguments->autoload_data = rb_check_typeddata(autoload_const->autoload_data_value, &autoload_data_type);
- if (NIL_P(autoload_namespace)) {
- rb_namespace_enable_builtin();
- /*
- * Clear the global cc cache table because the require method can be different from the current
- * namespace's one and it may cause inconsistent cc-cme states.
- * For example, the assertion below may fail in gccct_method_search();
- * VM_ASSERT(vm_cc_check_cme(cc, rb_callable_method_entry(klass, mid)))
- */
- rb_gccct_clear_table(Qnil);
- struct autoload_feature_require_data data = {
- .arguments = arguments,
- .receiver = receiver,
- .feature = arguments->autoload_data->feature,
- };
- return rb_namespace_exec(rb_builtin_namespace(), autoload_feature_require_in_builtin_wrap, (VALUE)&data);
- }
+ if (rb_box_available() && BOX_OBJ_P(autoload_box_value))
+ receiver = autoload_box_value;
- if (RTEST(autoload_namespace) && NAMESPACE_OPTIONAL_P(rb_get_namespace_t(autoload_namespace))) {
- receiver = autoload_namespace;
- }
+ /*
+ * Clear the global cc cache table because the require method can be different from the current
+ * box's one and it may cause inconsistent cc-cme states.
+ * For example, the assertion below may fail in gccct_method_search();
+ * VM_ASSERT(vm_cc_check_cme(cc, rb_callable_method_entry(klass, mid)))
+ */
+ rb_gccct_clear_table();
VALUE result = rb_funcall(receiver, rb_intern("require"), 1, arguments->autoload_data->feature);
@@ -3504,11 +3433,12 @@ rb_const_warn_if_deprecated(const rb_const_entry_t *ce, VALUE klass, ID id)
static VALUE
rb_const_get_0(VALUE klass, ID id, int exclude, int recurse, int visibility)
{
- VALUE c = rb_const_search(klass, id, exclude, recurse, visibility);
+ VALUE found_in;
+ VALUE c = rb_const_search(klass, id, exclude, recurse, visibility, &found_in);
if (!UNDEF_P(c)) {
if (UNLIKELY(!rb_ractor_main_p())) {
if (!rb_ractor_shareable_p(c)) {
- rb_raise(rb_eRactorIsolationError, "can not access non-shareable objects in constant %"PRIsVALUE"::%s by non-main Ractor.", rb_class_path(klass), rb_id2name(id));
+ rb_raise(rb_eRactorIsolationError, "can not access non-shareable objects in constant %"PRIsVALUE"::%"PRIsVALUE" by non-main Ractor.", rb_class_path(found_in), rb_id2str(id));
}
}
return c;
@@ -3517,7 +3447,7 @@ rb_const_get_0(VALUE klass, ID id, int exclude, int recurse, int visibility)
}
static VALUE
-rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibility)
+rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibility, VALUE *found_in)
{
VALUE value, current;
bool first_iteration = true;
@@ -3554,13 +3484,17 @@ rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibilit
if (am == tmp) break;
am = tmp;
ac = autoloading_const_entry(tmp, id);
- if (ac) return ac->value;
+ if (ac) {
+ if (found_in) { *found_in = tmp; }
+ return ac->value;
+ }
rb_autoload_load(tmp, id);
continue;
}
if (exclude && tmp == rb_cObject) {
goto not_found;
}
+ if (found_in) { *found_in = tmp; }
return value;
}
if (!recurse) break;
@@ -3572,17 +3506,17 @@ rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibilit
}
static VALUE
-rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility)
+rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility, VALUE *found_in)
{
VALUE value;
if (klass == rb_cObject) exclude = FALSE;
- value = rb_const_search_from(klass, id, exclude, recurse, visibility);
+ value = rb_const_search_from(klass, id, exclude, recurse, visibility, found_in);
if (!UNDEF_P(value)) return value;
if (exclude) return value;
if (BUILTIN_TYPE(klass) != T_MODULE) return value;
/* search global const too, if klass is a module */
- return rb_const_search_from(rb_cObject, id, FALSE, recurse, visibility);
+ return rb_const_search_from(rb_cObject, id, FALSE, recurse, visibility, found_in);
}
VALUE
@@ -3718,7 +3652,8 @@ rb_const_remove(VALUE mod, ID id)
rb_check_frozen(mod);
ce = rb_const_lookup(mod, id);
- if (!ce || !rb_id_table_delete(RCLASS_WRITABLE_CONST_TBL(mod), id)) {
+
+ if (!ce) {
if (rb_const_defined_at(mod, id)) {
rb_name_err_raise("cannot remove %2$s::%1$s", mod, ID2SYM(id));
}
@@ -3726,6 +3661,14 @@ rb_const_remove(VALUE mod, ID id)
undefined_constant(mod, ID2SYM(id));
}
+ VALUE writable_ce = 0;
+ if (rb_id_table_lookup(RCLASS_WRITABLE_CONST_TBL(mod), id, &writable_ce)) {
+ rb_id_table_delete(RCLASS_WRITABLE_CONST_TBL(mod), id);
+ if ((rb_const_entry_t *)writable_ce != ce) {
+ SIZED_FREE((rb_const_entry_t *)writable_ce);
+ }
+ }
+
rb_const_warn_if_deprecated(ce, mod, id);
rb_clear_constant_cache_for_id(id);
@@ -3737,7 +3680,7 @@ rb_const_remove(VALUE mod, ID id)
}
if (ce != const_lookup(RCLASS_PRIME_CONST_TBL(mod), id)) {
- ruby_xfree(ce);
+ SIZED_FREE(ce);
}
// else - skip free'ing the ce because it still exists in the prime classext
@@ -3973,6 +3916,7 @@ static void
set_namespace_path(VALUE named_namespace, VALUE namespace_path)
{
struct rb_id_table *const_table = RCLASS_CONST_TBL(named_namespace);
+ RB_OBJ_SET_SHAREABLE(namespace_path);
RB_VM_LOCKING() {
RCLASS_WRITE_CLASSPATH(named_namespace, namespace_path, true);
@@ -4051,7 +3995,8 @@ const_set(VALUE klass, ID id, VALUE val)
set_namespace_path(val, build_const_path(parental_path, id));
}
else if (!parental_path_permanent && NIL_P(val_path)) {
- RCLASS_SET_CLASSPATH(val, build_const_path(parental_path, id), false);
+ VALUE path = build_const_path(parental_path, id);
+ RCLASS_SET_CLASSPATH(val, path, false);
}
}
}
@@ -4065,21 +4010,21 @@ rb_const_set(VALUE klass, ID id, VALUE val)
const_added(klass, id);
}
-static struct autoload_data *
-autoload_data_for_named_constant(VALUE module, ID name, struct autoload_const **autoload_const_pointer)
+static VALUE
+autoload_const_value_for_named_constant(VALUE module, ID name, struct autoload_const **autoload_const_pointer)
{
- VALUE autoload_data_value = autoload_data(module, name);
- if (!autoload_data_value) return 0;
+ VALUE autoload_const_value = autoload_data(module, name);
+ if (!autoload_const_value) return Qfalse;
- struct autoload_data *autoload_data = get_autoload_data(autoload_data_value, autoload_const_pointer);
- if (!autoload_data) return 0;
+ struct autoload_data *autoload_data = get_autoload_data(autoload_const_value, autoload_const_pointer);
+ if (!autoload_data) return Qfalse;
/* for autoloading thread, keep the defined value to autoloading storage */
if (autoload_by_current(autoload_data)) {
- return autoload_data;
+ return autoload_const_value;
}
- return 0;
+ return Qfalse;
}
static void
@@ -4099,13 +4044,13 @@ const_tbl_update(struct autoload_const *ac, int autoload_force)
RUBY_ASSERT_CRITICAL_SECTION_ENTER();
VALUE file = ac->file;
int line = ac->line;
- struct autoload_data *ele = autoload_data_for_named_constant(klass, id, &ac);
+ VALUE autoload_const_value = autoload_const_value_for_named_constant(klass, id, &ac);
- if (!autoload_force && ele) {
+ if (!autoload_force && autoload_const_value) {
rb_clear_constant_cache_for_id(id);
- ac->value = val; /* autoload_data is non-WB-protected */
- ac->file = rb_source_location(&ac->line);
+ RB_OBJ_WRITE(autoload_const_value, &ac->value, val);
+ RB_OBJ_WRITE(autoload_const_value, &ac->file, rb_source_location(&ac->line));
}
else {
/* otherwise autoloaded constant, allow to override */
@@ -4121,15 +4066,17 @@ const_tbl_update(struct autoload_const *ac, int autoload_force)
else {
VALUE name = QUOTE_ID(id);
visibility = ce->flag;
- if (klass == rb_cObject)
- rb_warn("already initialized constant %"PRIsVALUE"", name);
- else
- rb_warn("already initialized constant %"PRIsVALUE"::%"PRIsVALUE"",
- rb_class_name(klass), name);
+
+ VALUE previous = Qnil;
if (!NIL_P(ce->file) && ce->line) {
- rb_compile_warn(RSTRING_PTR(ce->file), ce->line,
- "previous definition of %"PRIsVALUE" was here", name);
+ previous = rb_sprintf("\n%"PRIsVALUE":%d: warning: previous definition of %"PRIsVALUE" was here", ce->file, ce->line, name);
}
+
+ if (klass == rb_cObject)
+ rb_warn("already initialized constant %"PRIsVALUE"%"PRIsVALUE"", name, previous);
+ else
+ rb_warn("already initialized constant %"PRIsVALUE"::%"PRIsVALUE"%"PRIsVALUE"",
+ rb_class_name(klass), name, previous);
}
rb_clear_constant_cache_for_id(id);
setup_const_entry(ce, klass, val, visibility);
@@ -4199,10 +4146,7 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv,
ce->flag &= ~mask;
ce->flag |= flag;
if (UNDEF_P(ce->value)) {
- struct autoload_data *ele;
-
- ele = autoload_data_for_named_constant(mod, id, &ac);
- if (ele) {
+ if (autoload_const_value_for_named_constant(mod, id, &ac)) {
ac->flag &= ~mask;
ac->flag |= flag;
}
@@ -4350,7 +4294,6 @@ cvar_overtaken(VALUE front, VALUE target, ID id)
}
#define CVAR_LOOKUP(v,r) do {\
- CVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(); \
if (cvar_lookup_at(klass, id, (v))) {r;}\
CVAR_FOREACH_ANCESTORS(klass, v, r);\
} while(0)
@@ -4369,22 +4312,11 @@ find_cvar(VALUE klass, VALUE * front, VALUE * target, ID id)
return v;
}
-static void
-check_for_cvar_table(VALUE subclass, VALUE key)
-{
- // Must not check ivar on ICLASS
- if (!RB_TYPE_P(subclass, T_ICLASS) && RTEST(rb_ivar_defined(subclass, key))) {
- RB_DEBUG_COUNTER_INC(cvar_class_invalidate);
- ruby_vm_global_cvar_state++;
- return;
- }
-
- rb_class_foreach_subclass(subclass, check_for_cvar_table, key);
-}
-
void
rb_cvar_set(VALUE klass, ID id, VALUE val)
{
+ CVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(klass, id);
+
VALUE tmp, front = 0, target = 0;
tmp = klass;
@@ -4403,22 +4335,29 @@ rb_cvar_set(VALUE klass, ID id, VALUE val)
bool new_cvar = rb_class_ivar_set(target, id, val);
- struct rb_id_table *rb_cvc_tbl = RCLASS_WRITABLE_CVC_TBL(target);
-
- if (!rb_cvc_tbl) {
- rb_cvc_tbl = rb_id_table_create(2);
- RCLASS_WRITE_CVC_TBL(target, rb_cvc_tbl);
- }
+ VALUE cvc_tbl = RCLASS_WRITABLE_CVC_TBL(target);
struct rb_cvar_class_tbl_entry *ent;
VALUE ent_data;
- if (!rb_id_table_lookup(rb_cvc_tbl, id, &ent_data)) {
- ent = ALLOC(struct rb_cvar_class_tbl_entry);
- ent->class_value = target;
+ if (!cvc_tbl || !rb_marked_id_table_lookup(cvc_tbl, id, &ent_data)) {
+ ent = (struct rb_cvar_class_tbl_entry *)SHAREABLE_IMEMO_NEW(struct rb_cvar_class_tbl_entry, imemo_cvar_entry, 0);
+ RB_OBJ_WRITE((VALUE)ent, &ent->class_value, target);
+ RB_OBJ_WRITE((VALUE)ent, &ent->cref, 0);
ent->global_cvar_state = GET_GLOBAL_CVAR_STATE();
- ent->cref = 0;
- rb_id_table_insert(rb_cvc_tbl, id, (VALUE)ent);
+
+ VALUE new_cvc_tbl = cvc_tbl;
+ if (!new_cvc_tbl) {
+ new_cvc_tbl = rb_marked_id_table_new(2);
+ }
+ else if (rb_multi_ractor_p()) {
+ new_cvc_tbl = rb_marked_id_table_dup(cvc_tbl);
+ }
+
+ rb_marked_id_table_insert(new_cvc_tbl, id, (VALUE)ent);
+ if (new_cvc_tbl != cvc_tbl) {
+ RCLASS_WRITE_CVC_TBL(target, new_cvc_tbl);
+ }
RB_DEBUG_COUNTER_INC(cvar_inline_miss);
}
else {
@@ -4426,15 +4365,11 @@ rb_cvar_set(VALUE klass, ID id, VALUE val)
ent->global_cvar_state = GET_GLOBAL_CVAR_STATE();
}
- // Break the cvar cache if this is a new class variable
- // and target is a module or a subclass with the same
- // cvar in this lookup.
+ // Break the cvar cache if this is a new class variable.
+ // Existing caches may have resolved this name to a different
+ // location in the hierarchy, so we must invalidate globally.
if (new_cvar) {
- if (RB_TYPE_P(target, T_CLASS)) {
- if (RCLASS_SUBCLASSES_FIRST(target)) {
- rb_class_foreach_subclass(target, check_for_cvar_table, id);
- }
- }
+ ruby_vm_global_cvar_state++;
}
}
@@ -4450,6 +4385,7 @@ rb_cvar_find(VALUE klass, ID id, VALUE *front)
klass, ID2SYM(id));
}
cvar_overtaken(*front, target, id);
+ cvar_read_ractor_check(klass, id, value);
return (VALUE)value;
}
@@ -4660,70 +4596,58 @@ rb_iv_set(VALUE obj, const char *name, VALUE val)
return rb_ivar_set(obj, id, val);
}
-static bool
-class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool concurrent, VALUE *new_fields_obj)
+static attr_index_t
+class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool concurrent, VALUE *new_fields_obj, bool *new_ivar_out)
{
- bool existing = true;
const VALUE original_fields_obj = fields_obj;
- fields_obj = original_fields_obj ? original_fields_obj : rb_imemo_fields_new(klass, 1);
+ fields_obj = original_fields_obj ? original_fields_obj : rb_imemo_fields_new(klass, ROOT_SHAPE_ID, true);
shape_id_t current_shape_id = RBASIC_SHAPE_ID(fields_obj);
- shape_id_t next_shape_id = current_shape_id;
-
- if (UNLIKELY(rb_shape_too_complex_p(current_shape_id))) {
- goto too_complex;
+ shape_id_t next_shape_id = current_shape_id; // for complex
+ if (UNLIKELY(rb_shape_complex_p(current_shape_id))) {
+ goto complex;
}
- attr_index_t index;
- if (!rb_shape_get_iv_index(current_shape_id, id, &index)) {
- existing = false;
-
- index = RSHAPE_LEN(current_shape_id);
- if (index >= SHAPE_MAX_FIELDS) {
- rb_raise(rb_eArgError, "too many instance variables");
- }
-
- next_shape_id = rb_shape_transition_add_ivar(fields_obj, id);
- if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) {
- fields_obj = imemo_fields_complex_from_obj(klass, fields_obj, next_shape_id);
- goto too_complex;
- }
-
- attr_index_t next_capacity = RSHAPE_CAPACITY(next_shape_id);
- attr_index_t current_capacity = RSHAPE_CAPACITY(current_shape_id);
+ bool new_ivar;
+ next_shape_id = generic_shape_ivar(fields_obj, id, &new_ivar);
- if (next_capacity > current_capacity) {
- // We allocate a new fields_obj even when concurrency isn't a concern
- // so that we're embedded as long as possible.
- fields_obj = imemo_fields_copy_capa(klass, fields_obj, next_capacity);
- }
-
- RUBY_ASSERT(RSHAPE(next_shape_id)->type == SHAPE_IVAR);
- RUBY_ASSERT(index == (RSHAPE_LEN(next_shape_id) - 1));
+ if (UNLIKELY(rb_shape_complex_p(next_shape_id))) {
+ fields_obj = imemo_fields_complex_from_obj(klass, fields_obj, next_shape_id);
+ goto complex;
}
- VALUE *fields = rb_imemo_fields_ptr(fields_obj);
-
- if (concurrent && original_fields_obj == fields_obj) {
- // In the concurrent case, if we're mutating the existing
- // fields_obj, we must use an atomic write, because if we're
- // adding a new field, the shape_id must be written after the field
- // and if we're updating an existing field, we at least need a relaxed
- // write to avoid reaping.
- RB_OBJ_ATOMIC_WRITE(fields_obj, &fields[index], val);
+ attr_index_t index = RSHAPE_INDEX(next_shape_id);
+ if (new_ivar && index >= RSHAPE_CAPACITY(current_shape_id)) {
+ // We allocate a new fields_obj even when concurrency isn't a concern
+ // so that we're embedded as long as possible.
+ fields_obj = imemo_fields_copy_append(klass, fields_obj, current_shape_id, next_shape_id, val);
}
else {
- RB_OBJ_WRITE(fields_obj, &fields[index], val);
- }
+ VALUE *fields = rb_imemo_fields_ptr(fields_obj);
+
+ if (concurrent && original_fields_obj == fields_obj) {
+ // In the concurrent case, if we're mutating the existing
+ // fields_obj, we must use an atomic write, because if we're
+ // adding a new field, the shape_id must be written after the field
+ // and if we're updating an existing field, we at least need a relaxed
+ // write to avoid reaping.
+ RB_OBJ_ATOMIC_WRITE(fields_obj, &fields[index], val);
+ }
+ else {
+ RB_OBJ_WRITE(fields_obj, &fields[index], val);
+ }
- if (!existing) {
- RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
+ if (new_ivar) {
+ RUBY_ASSERT(rb_shape_layout(next_shape_id) == SHAPE_ID_LAYOUT_ROBJECT);
+ RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
+ }
}
*new_fields_obj = fields_obj;
- return existing;
+ *new_ivar_out = new_ivar;
+ return index;
-too_complex:
+complex:
{
if (concurrent && fields_obj == original_fields_obj) {
// In multi-ractor case, we must always work on a copy because
@@ -4733,30 +4657,29 @@ too_complex:
}
st_table *table = rb_imemo_fields_complex_tbl(fields_obj);
- existing = st_insert(table, (st_data_t)id, (st_data_t)val);
+ new_ivar = !st_insert(table, (st_data_t)id, (st_data_t)val);
RB_OBJ_WRITTEN(fields_obj, Qundef, val);
if (fields_obj != original_fields_obj) {
+ RUBY_ASSERT(rb_shape_layout(next_shape_id) == SHAPE_ID_LAYOUT_ROBJECT);
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
}
}
*new_fields_obj = fields_obj;
- return existing;
+ *new_ivar_out = new_ivar;
+ return ATTR_INDEX_NOT_SET;
}
-bool
-rb_class_ivar_set(VALUE obj, ID id, VALUE val)
+static attr_index_t
+class_ivar_set(VALUE obj, ID id, VALUE val, bool *new_ivar)
{
- RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
- rb_check_frozen(obj);
-
rb_class_ensure_writable(obj);
const VALUE original_fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
VALUE new_fields_obj = 0;
- bool existing = class_fields_ivar_set(obj, original_fields_obj, id, val, rb_multi_ractor_p(), &new_fields_obj);
+ attr_index_t index = class_fields_ivar_set(obj, original_fields_obj, id, val, rb_multi_ractor_p(), &new_fields_obj, new_ivar);
if (new_fields_obj != original_fields_obj) {
RCLASS_WRITABLE_SET_FIELDS_OBJ(obj, new_fields_obj);
@@ -4764,11 +4687,20 @@ rb_class_ivar_set(VALUE obj, ID id, VALUE val)
// TODO: What should we set as the T_CLASS shape_id?
// In most case we can replicate the single `fields_obj` shape
- // but in namespaced case?
- // Perhaps INVALID_SHAPE_ID?
- RBASIC_SET_SHAPE_ID(obj, RBASIC_SHAPE_ID(new_fields_obj));
+ // but in namespaced case? Perhaps INVALID_SHAPE_ID?
+ RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | RBASIC_SHAPE_ID(new_fields_obj));
+ return index;
+}
- return !existing;
+bool
+rb_class_ivar_set(VALUE obj, ID id, VALUE val)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
+ rb_check_frozen(obj);
+
+ bool new_ivar;
+ class_ivar_set(obj, id, val, &new_ivar);
+ return new_ivar;
}
void
@@ -4781,7 +4713,7 @@ rb_fields_tbl_copy(VALUE dst, VALUE src)
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(src);
if (fields_obj) {
RCLASS_WRITABLE_SET_FIELDS_OBJ(dst, rb_imemo_fields_clone(fields_obj));
- RBASIC_SET_SHAPE_ID(dst, RBASIC_SHAPE_ID(src));
+ RBASIC_SET_SHAPE_ID(dst, rb_shape_layout(RBASIC_SHAPE_ID(dst)) | RBASIC_SHAPE_ID(src));
}
}
@@ -4805,4 +4737,3 @@ rb_const_lookup(VALUE klass, ID id)
{
return const_lookup(RCLASS_CONST_TBL(klass), id);
}
-