Turn rb_classext_t.fields
into a T_IMEMO/class_fields
This behave almost exactly as a T_OBJECT, the layout is entirely compatible. This aims to solve two problems. First, it solves the problem of namspaced classes having a single `shape_id`. Now each namespaced classext has an object that can hold the namespace specific shape. Second, it open the door to later make class instance variable writes atomics, hence be able to read class variables without locking the VM. In the future, in multi-ractor mode, we can do the write on a copy of the `fields_obj` and then atomically swap it. Considerations: - Right now the `RClass` shape_id is always synchronized, but with namespace we should likely mark classes that have multiple namespace with a specific shape flag.
This commit is contained in:
parent
166ff187bd
commit
3abdd4241f
Notes:
git
2025-06-12 05:58:29 +00:00
12
class.c
12
class.c
@ -297,16 +297,8 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
|
||||
|
||||
RCLASSEXT_M_TBL(ext) = duplicate_classext_m_tbl(RCLASSEXT_M_TBL(orig), klass, dup_iclass);
|
||||
|
||||
// TODO: consider shapes for performance
|
||||
if (RCLASSEXT_FIELDS(orig)) {
|
||||
RUBY_ASSERT(!RB_TYPE_P(klass, T_ICLASS));
|
||||
RCLASSEXT_FIELDS(ext) = (VALUE *)st_copy((st_table *)RCLASSEXT_FIELDS(orig));
|
||||
rb_autoload_copy_table_for_namespace((st_table *)RCLASSEXT_FIELDS(ext), ns);
|
||||
}
|
||||
else {
|
||||
if (!RB_TYPE_P(klass, T_ICLASS)) {
|
||||
RCLASSEXT_FIELDS(ext) = (VALUE *)st_init_numtable();
|
||||
}
|
||||
if (orig->fields_obj) {
|
||||
RB_OBJ_WRITE(klass, &ext->fields_obj, rb_imemo_class_fields_clone(orig->fields_obj));
|
||||
}
|
||||
|
||||
if (RCLASSEXT_SHARED_CONST_TBL(orig)) {
|
||||
|
@ -8117,6 +8117,7 @@ imemo.$(OBJEXT): $(top_srcdir)/internal/namespace.h
|
||||
imemo.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
|
||||
imemo.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
||||
imemo.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
||||
imemo.$(OBJEXT): $(top_srcdir)/internal/st.h
|
||||
imemo.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
||||
imemo.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
||||
imemo.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
||||
|
@ -315,6 +315,7 @@ RB_DEBUG_COUNTER(obj_imemo_parser_strterm)
|
||||
RB_DEBUG_COUNTER(obj_imemo_callinfo)
|
||||
RB_DEBUG_COUNTER(obj_imemo_callcache)
|
||||
RB_DEBUG_COUNTER(obj_imemo_constcache)
|
||||
RB_DEBUG_COUNTER(obj_imemo_class_fields)
|
||||
|
||||
RB_DEBUG_COUNTER(opt_new_hit)
|
||||
RB_DEBUG_COUNTER(opt_new_miss)
|
||||
|
@ -504,6 +504,7 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self)
|
||||
INIT_IMEMO_TYPE_ID(imemo_callinfo);
|
||||
INIT_IMEMO_TYPE_ID(imemo_callcache);
|
||||
INIT_IMEMO_TYPE_ID(imemo_constcache);
|
||||
INIT_IMEMO_TYPE_ID(imemo_class_fields);
|
||||
#undef INIT_IMEMO_TYPE_ID
|
||||
}
|
||||
|
||||
|
54
gc.c
54
gc.c
@ -1201,7 +1201,6 @@ rb_data_free(void *objspace, VALUE obj)
|
||||
|
||||
struct classext_foreach_args {
|
||||
VALUE klass;
|
||||
bool obj_too_complex;
|
||||
rb_objspace_t *objspace; // used for update_*
|
||||
};
|
||||
|
||||
@ -1213,12 +1212,6 @@ classext_free(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg)
|
||||
|
||||
rb_id_table_free(RCLASSEXT_M_TBL(ext));
|
||||
rb_cc_tbl_free(RCLASSEXT_CC_TBL(ext), args->klass);
|
||||
if (args->obj_too_complex) {
|
||||
st_free_table((st_table *)RCLASSEXT_FIELDS(ext));
|
||||
}
|
||||
else {
|
||||
xfree(RCLASSEXT_FIELDS(ext));
|
||||
}
|
||||
if (!RCLASSEXT_SHARED_CONST_TBL(ext) && (tbl = RCLASSEXT_CONST_TBL(ext)) != NULL) {
|
||||
rb_free_const_table(tbl);
|
||||
}
|
||||
@ -1292,8 +1285,6 @@ rb_gc_obj_free(void *objspace, VALUE obj)
|
||||
case T_MODULE:
|
||||
case T_CLASS:
|
||||
args.klass = obj;
|
||||
args.obj_too_complex = rb_shape_obj_too_complex_p(obj) ? true : false;
|
||||
|
||||
rb_class_classext_foreach(obj, classext_free, (void *)&args);
|
||||
if (RCLASS(obj)->ns_classext_tbl) {
|
||||
st_free_table(RCLASS(obj)->ns_classext_tbl);
|
||||
@ -2305,18 +2296,6 @@ classext_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg)
|
||||
*size += s;
|
||||
}
|
||||
|
||||
static void
|
||||
classext_fields_hash_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg)
|
||||
{
|
||||
size_t *size = (size_t *)arg;
|
||||
size_t count;
|
||||
RB_VM_LOCKING() {
|
||||
count = rb_st_table_size((st_table *)RCLASSEXT_FIELDS(ext));
|
||||
}
|
||||
// class IV sizes are allocated as powers of two
|
||||
*size += SIZEOF_VALUE << bit_length(count);
|
||||
}
|
||||
|
||||
static void
|
||||
classext_superclasses_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg)
|
||||
{
|
||||
@ -2354,15 +2333,6 @@ rb_obj_memsize_of(VALUE obj)
|
||||
case T_MODULE:
|
||||
case T_CLASS:
|
||||
rb_class_classext_foreach(obj, classext_memsize, (void *)&size);
|
||||
|
||||
if (rb_shape_obj_too_complex_p(obj)) {
|
||||
rb_class_classext_foreach(obj, classext_fields_hash_memsize, (void *)&size);
|
||||
}
|
||||
else {
|
||||
// class IV sizes are allocated as powers of two
|
||||
size += SIZEOF_VALUE << bit_length(RCLASS_FIELDS_COUNT(obj));
|
||||
}
|
||||
|
||||
rb_class_classext_foreach(obj, classext_superclasses_memsize, (void *)&size);
|
||||
break;
|
||||
case T_ICLASS:
|
||||
@ -3135,10 +3105,7 @@ gc_mark_classext_module(rb_classext_t *ext, bool prime, VALUE namespace, void *a
|
||||
gc_mark_internal(RCLASSEXT_SUPER(ext));
|
||||
}
|
||||
mark_m_tbl(objspace, RCLASSEXT_M_TBL(ext));
|
||||
if (rb_shape_obj_too_complex_p(obj)) {
|
||||
gc_mark_tbl_no_pin((st_table *)RCLASSEXT_FIELDS(ext));
|
||||
// for the case ELSE is written in rb_gc_mark_children() because it's per RClass, not classext
|
||||
}
|
||||
gc_mark_internal(RCLASSEXT_FIELDS_OBJ(ext));
|
||||
if (!RCLASSEXT_SHARED_CONST_TBL(ext) && RCLASSEXT_CONST_TBL(ext)) {
|
||||
mark_const_tbl(objspace, RCLASSEXT_CONST_TBL(ext));
|
||||
}
|
||||
@ -3218,12 +3185,6 @@ rb_gc_mark_children(void *objspace, VALUE obj)
|
||||
foreach_args.objspace = objspace;
|
||||
foreach_args.obj = obj;
|
||||
rb_class_classext_foreach(obj, gc_mark_classext_module, (void *)&foreach_args);
|
||||
|
||||
if (!rb_shape_obj_too_complex_p(obj)) {
|
||||
for (attr_index_t i = 0; i < RCLASS_FIELDS_COUNT(obj); i++) {
|
||||
gc_mark_internal(RCLASS_PRIME_FIELDS(obj)[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case T_ICLASS:
|
||||
@ -3849,7 +3810,6 @@ static void
|
||||
update_classext(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg)
|
||||
{
|
||||
struct classext_foreach_args *args = (struct classext_foreach_args *)arg;
|
||||
VALUE klass = args->klass;
|
||||
rb_objspace_t *objspace = args->objspace;
|
||||
|
||||
if (RCLASSEXT_SUPER(ext)) {
|
||||
@ -3858,16 +3818,7 @@ update_classext(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg)
|
||||
|
||||
update_m_tbl(objspace, RCLASSEXT_M_TBL(ext));
|
||||
|
||||
if (args->obj_too_complex) {
|
||||
gc_ref_update_table_values_only((st_table *)RCLASSEXT_FIELDS(ext));
|
||||
}
|
||||
else {
|
||||
// Classext is not copied in this case
|
||||
for (attr_index_t i = 0; i < RCLASS_FIELDS_COUNT(klass); i++) {
|
||||
UPDATE_IF_MOVED(objspace, RCLASSEXT_FIELDS(RCLASS_EXT_PRIME(klass))[i]);
|
||||
}
|
||||
}
|
||||
|
||||
UPDATE_IF_MOVED(objspace, ext->fields_obj);
|
||||
if (!RCLASSEXT_SHARED_CONST_TBL(ext)) {
|
||||
update_const_tbl(objspace, RCLASSEXT_CONST_TBL(ext));
|
||||
}
|
||||
@ -4255,7 +4206,6 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
|
||||
// Continue to the shared T_CLASS/T_MODULE
|
||||
case T_MODULE:
|
||||
args.klass = obj;
|
||||
args.obj_too_complex = rb_shape_obj_too_complex_p(obj);
|
||||
args.objspace = objspace;
|
||||
rb_class_classext_foreach(obj, update_classext, (void *)&args);
|
||||
break;
|
||||
|
106
imemo.c
106
imemo.c
@ -3,6 +3,7 @@
|
||||
#include "id_table.h"
|
||||
#include "internal.h"
|
||||
#include "internal/imemo.h"
|
||||
#include "internal/st.h"
|
||||
#include "vm_callinfo.h"
|
||||
|
||||
size_t rb_iseq_memsize(const rb_iseq_t *iseq);
|
||||
@ -29,10 +30,10 @@ rb_imemo_name(enum imemo_type type)
|
||||
IMEMO_NAME(svar);
|
||||
IMEMO_NAME(throw_data);
|
||||
IMEMO_NAME(tmpbuf);
|
||||
IMEMO_NAME(class_fields);
|
||||
#undef IMEMO_NAME
|
||||
default:
|
||||
rb_bug("unreachable");
|
||||
}
|
||||
rb_bug("unreachable");
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
@ -109,6 +110,62 @@ rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt)
|
||||
return tmpbuf;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
imemo_class_fields_new(VALUE klass, size_t capa)
|
||||
{
|
||||
size_t embedded_size = offsetof(struct rb_class_fields, as.embed) + capa * sizeof(VALUE);
|
||||
if (rb_gc_size_allocatable_p(embedded_size)) {
|
||||
VALUE fields = rb_imemo_new(imemo_class_fields, klass, embedded_size);
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_class_fields));
|
||||
return fields;
|
||||
}
|
||||
else {
|
||||
VALUE fields = rb_imemo_new(imemo_class_fields, klass, sizeof(struct rb_class_fields));
|
||||
FL_SET_RAW(fields, OBJ_FIELD_EXTERNAL);
|
||||
IMEMO_OBJ_FIELDS(fields)->as.external.ptr = ALLOC_N(VALUE, capa);
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_imemo_class_fields_new(VALUE klass, size_t capa)
|
||||
{
|
||||
return imemo_class_fields_new(rb_singleton_class(klass), capa);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
imemo_class_fields_new_complex(VALUE klass, size_t capa)
|
||||
{
|
||||
VALUE fields = imemo_class_fields_new(klass, sizeof(struct rb_class_fields));
|
||||
IMEMO_OBJ_FIELDS(fields)->as.complex.table = st_init_numtable_with_size(capa);
|
||||
return fields;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_imemo_class_fields_new_complex(VALUE klass, size_t capa)
|
||||
{
|
||||
return imemo_class_fields_new_complex(rb_singleton_class(klass), capa);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_imemo_class_fields_clone(VALUE fields_obj)
|
||||
{
|
||||
shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
|
||||
VALUE clone;
|
||||
|
||||
if (rb_shape_too_complex_p(shape_id)) {
|
||||
clone = rb_imemo_class_fields_new_complex(CLASS_OF(fields_obj), 0);
|
||||
st_table *src_table = rb_imemo_class_fields_complex_tbl(fields_obj);
|
||||
st_replace(rb_imemo_class_fields_complex_tbl(clone), src_table);
|
||||
}
|
||||
else {
|
||||
clone = imemo_class_fields_new(CLASS_OF(fields_obj), RSHAPE_CAPACITY(shape_id));
|
||||
MEMCPY(rb_imemo_class_fields_ptr(clone), rb_imemo_class_fields_ptr(fields_obj), VALUE, RSHAPE_LEN(shape_id));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* memsize
|
||||
* ========================================================================= */
|
||||
@ -155,6 +212,14 @@ rb_imemo_memsize(VALUE obj)
|
||||
case imemo_tmpbuf:
|
||||
size += ((rb_imemo_tmpbuf_t *)obj)->cnt * sizeof(VALUE);
|
||||
|
||||
break;
|
||||
case imemo_class_fields:
|
||||
if (rb_shape_obj_too_complex_p(obj)) {
|
||||
size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table);
|
||||
}
|
||||
else if (FL_TEST_RAW(obj, OBJ_FIELD_EXTERNAL)) {
|
||||
size += RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)) * sizeof(VALUE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rb_bug("unreachable");
|
||||
@ -420,6 +485,27 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating)
|
||||
|
||||
break;
|
||||
}
|
||||
case imemo_class_fields: {
|
||||
rb_gc_mark_and_move((VALUE *)&RBASIC(obj)->klass);
|
||||
|
||||
if (rb_shape_obj_too_complex_p(obj)) {
|
||||
st_table *tbl = rb_imemo_class_fields_complex_tbl(obj);
|
||||
if (reference_updating) {
|
||||
rb_gc_ref_update_table_values_only(tbl);
|
||||
}
|
||||
else {
|
||||
rb_mark_tbl_no_pin(tbl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
VALUE *fields = rb_imemo_class_fields_ptr(obj);
|
||||
attr_index_t len = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
|
||||
for (attr_index_t i = 0; i < len; i++) {
|
||||
rb_gc_mark_and_move(&fields[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rb_bug("unreachable");
|
||||
}
|
||||
@ -513,6 +599,17 @@ rb_cc_tbl_free(struct rb_id_table *cc_tbl, VALUE klass)
|
||||
rb_id_table_free(cc_tbl);
|
||||
}
|
||||
|
||||
static inline void
|
||||
imemo_class_fields_free(struct rb_class_fields *fields)
|
||||
{
|
||||
if (rb_shape_obj_too_complex_p((VALUE)fields)) {
|
||||
st_free_table(fields->as.complex.table);
|
||||
}
|
||||
else if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_EXTERNAL)) {
|
||||
xfree(fields->as.external.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rb_imemo_free(VALUE obj)
|
||||
{
|
||||
@ -576,6 +673,7 @@ rb_imemo_free(VALUE obj)
|
||||
break;
|
||||
case imemo_svar:
|
||||
RB_DEBUG_COUNTER_INC(obj_imemo_svar);
|
||||
|
||||
break;
|
||||
case imemo_throw_data:
|
||||
RB_DEBUG_COUNTER_INC(obj_imemo_throw_data);
|
||||
@ -585,6 +683,10 @@ rb_imemo_free(VALUE obj)
|
||||
xfree(((rb_imemo_tmpbuf_t *)obj)->ptr);
|
||||
RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf);
|
||||
|
||||
break;
|
||||
case imemo_class_fields:
|
||||
imemo_class_fields_free(IMEMO_OBJ_FIELDS(obj));
|
||||
RB_DEBUG_COUNTER_INC(obj_imemo_class_fields);
|
||||
break;
|
||||
default:
|
||||
rb_bug("unreachable");
|
||||
|
@ -79,7 +79,7 @@ struct rb_cvar_class_tbl_entry {
|
||||
struct rb_classext_struct {
|
||||
const rb_namespace_t *ns;
|
||||
VALUE super;
|
||||
VALUE *fields; // Fields are either ivar or other internal properties stored inline
|
||||
VALUE fields_obj; // Fields are either ivar or other internal properties stored inline
|
||||
struct rb_id_table *m_tbl;
|
||||
struct rb_id_table *const_tbl;
|
||||
struct rb_id_table *callable_m_tbl;
|
||||
@ -175,7 +175,8 @@ static inline rb_classext_t * RCLASS_EXT_WRITABLE(VALUE obj);
|
||||
|
||||
#define RCLASSEXT_NS(ext) (ext->ns)
|
||||
#define RCLASSEXT_SUPER(ext) (ext->super)
|
||||
#define RCLASSEXT_FIELDS(ext) (ext->fields)
|
||||
#define RCLASSEXT_FIELDS(ext) (ext->fields_obj ? ROBJECT_FIELDS(ext->fields_obj) : NULL)
|
||||
#define RCLASSEXT_FIELDS_OBJ(ext) (ext->fields_obj)
|
||||
#define RCLASSEXT_M_TBL(ext) (ext->m_tbl)
|
||||
#define RCLASSEXT_CONST_TBL(ext) (ext->const_tbl)
|
||||
#define RCLASSEXT_CALLABLE_M_TBL(ext) (ext->callable_m_tbl)
|
||||
@ -205,7 +206,7 @@ static inline void RCLASSEXT_SET_INCLUDER(rb_classext_t *ext, VALUE klass, VALUE
|
||||
#define RCLASS_PRIME_NS(c) (RCLASS_EXT_PRIME(c)->ns)
|
||||
// To invalidate CC by inserting&invalidating method entry into tables containing the target cme
|
||||
// See clear_method_cache_by_id_in_class()
|
||||
#define RCLASS_PRIME_FIELDS(c) (RCLASS_EXT_PRIME(c)->fields)
|
||||
#define RCLASS_PRIME_FIELDS_OBJ(c) (RCLASS_EXT_PRIME(c)->fields_obj)
|
||||
#define RCLASS_PRIME_M_TBL(c) (RCLASS_EXT_PRIME(c)->m_tbl)
|
||||
#define RCLASS_PRIME_CONST_TBL(c) (RCLASS_EXT_PRIME(c)->const_tbl)
|
||||
#define RCLASS_PRIME_CALLABLE_M_TBL(c) (RCLASS_EXT_PRIME(c)->callable_m_tbl)
|
||||
@ -255,11 +256,6 @@ static inline void RCLASSEXT_SET_INCLUDER(rb_classext_t *ext, VALUE klass, VALUE
|
||||
|
||||
static inline void RCLASS_SET_SUPER(VALUE klass, VALUE super);
|
||||
static inline void RCLASS_WRITE_SUPER(VALUE klass, VALUE super);
|
||||
static inline st_table * RCLASS_FIELDS_HASH(VALUE obj);
|
||||
static inline st_table * RCLASS_WRITABLE_FIELDS_HASH(VALUE obj);
|
||||
static inline uint32_t RCLASS_FIELDS_COUNT(VALUE obj);
|
||||
static inline void RCLASS_SET_FIELDS_HASH(VALUE obj, const st_table *table);
|
||||
static inline void RCLASS_WRITE_FIELDS_HASH(VALUE obj, const st_table *table);
|
||||
// TODO: rename RCLASS_SET_M_TBL_WORKAROUND (and _WRITE_) to RCLASS_SET_M_TBL with write barrier
|
||||
static inline void RCLASS_SET_M_TBL_WORKAROUND(VALUE klass, struct rb_id_table *table, bool check_promoted);
|
||||
static inline void RCLASS_WRITE_M_TBL_WORKAROUND(VALUE klass, struct rb_id_table *table, bool check_promoted);
|
||||
@ -528,56 +524,60 @@ RCLASS_WRITE_SUPER(VALUE klass, VALUE super)
|
||||
RB_OBJ_WRITE(klass, &RCLASSEXT_SUPER(RCLASS_EXT_WRITABLE(klass)), super);
|
||||
}
|
||||
|
||||
static inline st_table *
|
||||
RCLASS_FIELDS_HASH(VALUE obj)
|
||||
static inline VALUE
|
||||
RCLASS_FIELDS_OBJ(VALUE obj)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
|
||||
return (st_table *)RCLASSEXT_FIELDS(RCLASS_EXT_READABLE(obj));
|
||||
return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_READABLE(obj));
|
||||
}
|
||||
|
||||
static inline st_table *
|
||||
RCLASS_WRITABLE_FIELDS_HASH(VALUE obj)
|
||||
static inline VALUE
|
||||
RCLASS_ENSURE_FIELDS_OBJ(VALUE obj)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
|
||||
return (st_table *)RCLASSEXT_FIELDS(RCLASS_EXT_WRITABLE(obj));
|
||||
rb_classext_t *ext = RCLASS_EXT_READABLE(obj);
|
||||
if (!ext->fields_obj) {
|
||||
RB_OBJ_WRITE(obj, &ext->fields_obj, rb_imemo_class_fields_new(obj, 1));
|
||||
}
|
||||
return ext->fields_obj;
|
||||
}
|
||||
|
||||
static inline VALUE
|
||||
RCLASS_WRITABLE_FIELDS_OBJ(VALUE obj)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_WRITABLE(obj));
|
||||
}
|
||||
|
||||
static inline void
|
||||
RCLASS_SET_FIELDS_HASH(VALUE obj, const st_table *tbl)
|
||||
RCLASSEXT_SET_FIELDS_OBJ(VALUE obj, rb_classext_t *ext, VALUE fields_obj)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
|
||||
RCLASSEXT_FIELDS(RCLASS_EXT_PRIME(obj)) = (VALUE *)tbl;
|
||||
RB_OBJ_WRITE(obj, &ext->fields_obj, fields_obj);
|
||||
}
|
||||
|
||||
static inline void
|
||||
RCLASS_WRITE_FIELDS_HASH(VALUE obj, const st_table *tbl)
|
||||
RCLASS_SET_FIELDS_OBJ(VALUE obj, VALUE fields_obj)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
|
||||
RCLASSEXT_FIELDS(RCLASS_EXT_WRITABLE(obj)) = (VALUE *)tbl;
|
||||
|
||||
RCLASSEXT_SET_FIELDS_OBJ(obj, RCLASS_EXT_PRIME(obj), fields_obj);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
RCLASS_FIELDS_COUNT(VALUE obj)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
|
||||
if (rb_shape_obj_too_complex_p(obj)) {
|
||||
uint32_t count;
|
||||
|
||||
// "Too complex" classes could have their IV hash mutated in
|
||||
// parallel, so lets lock around getting the hash size.
|
||||
RB_VM_LOCKING() {
|
||||
count = (uint32_t)rb_st_table_size(RCLASS_FIELDS_HASH(obj));
|
||||
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (fields_obj) {
|
||||
if (rb_shape_obj_too_complex_p(fields_obj)) {
|
||||
return (uint32_t)rb_st_table_size(rb_imemo_class_fields_complex_tbl(fields_obj));
|
||||
}
|
||||
else {
|
||||
return RSHAPE_LEN(RBASIC_SHAPE_ID(fields_obj));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
else {
|
||||
return RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RCLASS_SET_M_TBL_EVEN_WHEN_PROMOTED(klass, table) RCLASS_SET_M_TBL_WORKAROUND(klass, table, false)
|
||||
|
@ -42,6 +42,7 @@ enum imemo_type {
|
||||
imemo_callinfo = 11,
|
||||
imemo_callcache = 12,
|
||||
imemo_constcache = 13,
|
||||
imemo_class_fields = 14,
|
||||
};
|
||||
|
||||
/* CREF (Class REFerence) is defined in method.h */
|
||||
@ -257,4 +258,57 @@ MEMO_V2_SET(struct MEMO *m, VALUE v)
|
||||
RB_OBJ_WRITE(m, &m->v2, v);
|
||||
}
|
||||
|
||||
struct rb_class_fields {
|
||||
struct RBasic basic;
|
||||
union {
|
||||
struct {
|
||||
VALUE fields[1];
|
||||
} embed;
|
||||
struct {
|
||||
VALUE *ptr;
|
||||
} external;
|
||||
struct {
|
||||
// Note: the st_table could be embedded, but complex T_CLASS should be rare to
|
||||
// non-existent, so not really worth the trouble.
|
||||
st_table *table;
|
||||
} complex;
|
||||
} as;
|
||||
};
|
||||
|
||||
#define OBJ_FIELD_EXTERNAL IMEMO_FL_USER0
|
||||
#define IMEMO_OBJ_FIELDS(fields) ((struct rb_class_fields *)fields)
|
||||
|
||||
VALUE rb_imemo_class_fields_new(VALUE klass, size_t capa);
|
||||
VALUE rb_imemo_class_fields_new_complex(VALUE klass, size_t capa);
|
||||
VALUE rb_imemo_class_fields_clone(VALUE fields_obj);
|
||||
|
||||
static inline VALUE *
|
||||
rb_imemo_class_fields_ptr(VALUE obj_fields)
|
||||
{
|
||||
if (!obj_fields) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(obj_fields, imemo_class_fields));
|
||||
|
||||
if (RB_UNLIKELY(FL_TEST_RAW(obj_fields, OBJ_FIELD_EXTERNAL))) {
|
||||
return IMEMO_OBJ_FIELDS(obj_fields)->as.external.ptr;
|
||||
}
|
||||
else {
|
||||
return IMEMO_OBJ_FIELDS(obj_fields)->as.embed.fields;
|
||||
}
|
||||
}
|
||||
|
||||
static inline st_table *
|
||||
rb_imemo_class_fields_complex_tbl(VALUE obj_fields)
|
||||
{
|
||||
if (!obj_fields) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(obj_fields, imemo_class_fields));
|
||||
|
||||
return IMEMO_OBJ_FIELDS(obj_fields)->as.complex.table;
|
||||
}
|
||||
|
||||
#endif /* INTERNAL_IMEMO_H */
|
||||
|
18
shape.c
18
shape.c
@ -396,6 +396,13 @@ rb_obj_shape_id(VALUE obj)
|
||||
return SPECIAL_CONST_SHAPE_ID;
|
||||
}
|
||||
|
||||
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
|
||||
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (fields_obj) {
|
||||
return RBASIC_SHAPE_ID(fields_obj);
|
||||
}
|
||||
return ROOT_SHAPE_ID;
|
||||
}
|
||||
return RBASIC_SHAPE_ID(obj);
|
||||
}
|
||||
|
||||
@ -881,14 +888,11 @@ shape_get_next(rb_shape_t *shape, VALUE obj, ID id, bool emit_warnings)
|
||||
#endif
|
||||
|
||||
VALUE klass;
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
klass = rb_singleton_class(obj);
|
||||
break;
|
||||
default:
|
||||
if (IMEMO_TYPE_P(obj, imemo_class_fields)) { // HACK
|
||||
klass = CLASS_OF(obj);
|
||||
}
|
||||
else {
|
||||
klass = rb_obj_class(obj);
|
||||
break;
|
||||
}
|
||||
|
||||
bool allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS;
|
||||
|
5
shape.h
5
shape.h
@ -113,7 +113,7 @@ static inline shape_id_t
|
||||
RBASIC_SHAPE_ID(VALUE obj)
|
||||
{
|
||||
RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
|
||||
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO));
|
||||
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_class_fields));
|
||||
#if RBASIC_SHAPE_ID_FIELD
|
||||
return (shape_id_t)((RBASIC(obj)->shape_id));
|
||||
#else
|
||||
@ -137,8 +137,9 @@ static inline void
|
||||
RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
|
||||
{
|
||||
RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
|
||||
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO));
|
||||
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_class_fields));
|
||||
RUBY_ASSERT(rb_shape_verify_consistency(obj, shape_id));
|
||||
|
||||
#if RBASIC_SHAPE_ID_FIELD
|
||||
RBASIC(obj)->shape_id = (VALUE)shape_id;
|
||||
#else
|
||||
|
317
variable.c
317
variable.c
@ -1305,13 +1305,21 @@ 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 (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
|
||||
ASSERT_vm_locking();
|
||||
VALUE field_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (field_obj) {
|
||||
return rb_obj_field_get(field_obj, target_shape_id);
|
||||
}
|
||||
return Qundef;
|
||||
}
|
||||
|
||||
if (rb_shape_too_complex_p(target_shape_id)) {
|
||||
st_table *fields_hash;
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
ASSERT_vm_locking();
|
||||
fields_hash = RCLASS_FIELDS_HASH(obj);
|
||||
rb_bug("Unreachable");
|
||||
break;
|
||||
case T_OBJECT:
|
||||
fields_hash = ROBJECT_FIELDS_HASH(obj);
|
||||
@ -1342,8 +1350,7 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id)
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
ASSERT_vm_locking();
|
||||
fields = RCLASS_PRIME_FIELDS(obj);
|
||||
rb_bug("Unreachable");
|
||||
break;
|
||||
case T_OBJECT:
|
||||
fields = ROBJECT_FIELDS(obj);
|
||||
@ -1364,6 +1371,27 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
||||
{
|
||||
if (SPECIAL_CONST_P(obj)) return undef;
|
||||
|
||||
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
|
||||
VALUE val = undef;
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (fields_obj) {
|
||||
val = rb_ivar_lookup(fields_obj, id, undef);
|
||||
}
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
|
||||
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");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
shape_id_t shape_id;
|
||||
VALUE * ivar_list;
|
||||
shape_id = RBASIC_SHAPE_ID(obj);
|
||||
@ -1372,43 +1400,27 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
{
|
||||
bool found = false;
|
||||
VALUE val;
|
||||
rb_bug("Unreachable");
|
||||
}
|
||||
case T_IMEMO:
|
||||
// Handled like T_OBJECT
|
||||
{
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields));
|
||||
|
||||
RB_VM_LOCKING() {
|
||||
if (rb_shape_too_complex_p(shape_id)) {
|
||||
st_table * iv_table = RCLASS_FIELDS_HASH(obj);
|
||||
if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
val = undef;
|
||||
}
|
||||
if (rb_shape_too_complex_p(shape_id)) {
|
||||
st_table * iv_table = rb_imemo_class_fields_complex_tbl(obj);
|
||||
VALUE val;
|
||||
if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
|
||||
return val;
|
||||
}
|
||||
else {
|
||||
attr_index_t index = 0;
|
||||
found = rb_shape_get_iv_index(shape_id, id, &index);
|
||||
|
||||
if (found) {
|
||||
ivar_list = RCLASS_PRIME_FIELDS(obj);
|
||||
RUBY_ASSERT(ivar_list);
|
||||
|
||||
val = ivar_list[index];
|
||||
}
|
||||
else {
|
||||
val = undef;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
if (found &&
|
||||
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");
|
||||
}
|
||||
return val;
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
|
||||
ivar_list = rb_imemo_class_fields_ptr(obj);
|
||||
break;
|
||||
}
|
||||
case T_OBJECT:
|
||||
{
|
||||
@ -1476,13 +1488,19 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
|
||||
{
|
||||
rb_check_frozen(obj);
|
||||
|
||||
bool locked = false;
|
||||
unsigned int lev = 0;
|
||||
VALUE val = undef;
|
||||
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
|
||||
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
|
||||
RB_VM_LOCK_ENTER_LEV(&lev);
|
||||
locked = true;
|
||||
|
||||
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (fields_obj) {
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
val = rb_ivar_delete(fields_obj, id, undef);
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
shape_id_t old_shape_id = rb_obj_shape_id(obj);
|
||||
@ -1494,9 +1512,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
|
||||
shape_id_t next_shape_id = rb_shape_transition_remove_ivar(obj, id, &removed_shape_id);
|
||||
|
||||
if (next_shape_id == old_shape_id) {
|
||||
if (locked) {
|
||||
RB_VM_LOCK_LEAVE_LEV(&lev);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
@ -1511,7 +1526,11 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
|
||||
switch(BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
fields = RCLASS_PRIME_FIELDS(obj);
|
||||
rb_bug("Unreachable");
|
||||
break;
|
||||
case T_IMEMO:
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields));
|
||||
fields = rb_imemo_class_fields_ptr(obj);
|
||||
break;
|
||||
case T_OBJECT:
|
||||
fields = ROBJECT_FIELDS(obj);
|
||||
@ -1546,10 +1565,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
|
||||
}
|
||||
rb_obj_set_shape_id(obj, next_shape_id);
|
||||
|
||||
if (locked) {
|
||||
RB_VM_LOCK_LEAVE_LEV(&lev);
|
||||
}
|
||||
|
||||
return val;
|
||||
|
||||
too_complex:
|
||||
@ -1558,7 +1573,12 @@ too_complex:
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
table = RCLASS_WRITABLE_FIELDS_HASH(obj);
|
||||
rb_bug("Unreachable");
|
||||
break;
|
||||
|
||||
case T_IMEMO:
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields));
|
||||
table = rb_imemo_class_fields_complex_tbl(obj);
|
||||
break;
|
||||
|
||||
case T_OBJECT:
|
||||
@ -1581,10 +1601,6 @@ too_complex:
|
||||
}
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
RB_VM_LOCK_LEAVE_LEV(&lev);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -1597,6 +1613,11 @@ rb_attr_delete(VALUE obj, ID id)
|
||||
static shape_id_t
|
||||
obj_transition_too_complex(VALUE obj, st_table *table)
|
||||
{
|
||||
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
|
||||
RUBY_ASSERT(RCLASS_FIELDS_OBJ(obj));
|
||||
return obj_transition_too_complex(RCLASS_FIELDS_OBJ(obj), table);
|
||||
}
|
||||
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
|
||||
shape_id_t shape_id = rb_shape_transition_complex(obj);
|
||||
|
||||
@ -1612,9 +1633,7 @@ obj_transition_too_complex(VALUE obj, st_table *table)
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
old_fields = RCLASS_PRIME_FIELDS(obj);
|
||||
RBASIC_SET_SHAPE_ID(obj, shape_id);
|
||||
RCLASS_SET_FIELDS_HASH(obj, table);
|
||||
rb_bug("Unreachable");
|
||||
break;
|
||||
default:
|
||||
RB_VM_LOCKING() {
|
||||
@ -2035,11 +2054,20 @@ rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val)
|
||||
bool
|
||||
rb_obj_set_shape_id(VALUE obj, shape_id_t shape_id)
|
||||
{
|
||||
if (rb_obj_shape_id(obj) == shape_id) {
|
||||
shape_id_t old_shape_id = rb_obj_shape_id(obj);
|
||||
if (old_shape_id == shape_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
|
||||
// Avoid creating the fields_obj just to freeze the class
|
||||
if (!(shape_id == SPECIAL_CONST_SHAPE_ID && old_shape_id == ROOT_SHAPE_ID)) {
|
||||
RBASIC_SET_SHAPE_ID(RCLASS_ENSURE_FIELDS_OBJ(obj), shape_id);
|
||||
}
|
||||
}
|
||||
// FIXME: How to do multi-shape?
|
||||
RBASIC_SET_SHAPE_ID(obj, shape_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2131,7 +2159,12 @@ ivar_defined0(VALUE obj, ID id)
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
table = (st_table *)RCLASS_FIELDS_HASH(obj);
|
||||
rb_bug("Unreachable");
|
||||
break;
|
||||
|
||||
case T_IMEMO:
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields));
|
||||
table = rb_imemo_class_fields_complex_tbl(obj);
|
||||
break;
|
||||
|
||||
case T_OBJECT:
|
||||
@ -2163,12 +2196,15 @@ rb_ivar_defined(VALUE obj, ID id)
|
||||
{
|
||||
if (SPECIAL_CONST_P(obj)) return Qfalse;
|
||||
|
||||
VALUE defined;
|
||||
VALUE defined = Qfalse;
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
RB_VM_LOCKING() {
|
||||
defined = ivar_defined0(obj, id);
|
||||
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (fields_obj) {
|
||||
defined = ivar_defined0(fields_obj, id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -2183,6 +2219,7 @@ struct iv_itr_data {
|
||||
struct gen_fields_tbl *fields_tbl;
|
||||
st_data_t arg;
|
||||
rb_ivar_foreach_callback_func *func;
|
||||
VALUE *fields;
|
||||
bool ivar_only;
|
||||
};
|
||||
|
||||
@ -2203,8 +2240,12 @@ iterate_over_shapes_callback(shape_id_t shape_id, void *data)
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
rb_bug("Unreachable");
|
||||
case T_IMEMO:
|
||||
RUBY_ASSERT(IMEMO_TYPE_P(itr_data->obj, imemo_class_fields));
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex_p(itr_data->obj));
|
||||
iv_list = RCLASS_PRIME_FIELDS(itr_data->obj);
|
||||
|
||||
iv_list = rb_imemo_class_fields_ptr(itr_data->obj);
|
||||
break;
|
||||
default:
|
||||
iv_list = itr_data->fields_tbl->as.shape.fields;
|
||||
@ -2247,6 +2288,7 @@ obj_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b
|
||||
rb_st_foreach(ROBJECT_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
else {
|
||||
itr_data.fields = ROBJECT_FIELDS(obj);
|
||||
iterate_over_shapes(shape_id, func, &itr_data);
|
||||
}
|
||||
}
|
||||
@ -2270,27 +2312,29 @@ gen_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b
|
||||
rb_st_foreach(fields_tbl->as.complex.table, each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
else {
|
||||
itr_data.fields = fields_tbl->as.shape.fields;
|
||||
iterate_over_shapes(shape_id, func, &itr_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
class_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only)
|
||||
class_fields_each(VALUE fields_obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
|
||||
IMEMO_TYPE_P(fields_obj, imemo_class_fields);
|
||||
|
||||
struct iv_itr_data itr_data = {
|
||||
.obj = obj,
|
||||
.obj = fields_obj,
|
||||
.arg = arg,
|
||||
.func = func,
|
||||
.ivar_only = ivar_only,
|
||||
};
|
||||
|
||||
shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
|
||||
shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
|
||||
if (rb_shape_too_complex_p(shape_id)) {
|
||||
rb_st_foreach(RCLASS_WRITABLE_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
|
||||
rb_st_foreach(rb_imemo_class_fields_complex_tbl(fields_obj), each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
else {
|
||||
itr_data.fields = rb_imemo_class_fields_ptr(fields_obj);
|
||||
iterate_over_shapes(shape_id, func, &itr_data);
|
||||
}
|
||||
}
|
||||
@ -2399,6 +2443,11 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
|
||||
{
|
||||
if (SPECIAL_CONST_P(obj)) return;
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_IMEMO:
|
||||
if (IMEMO_TYPE_P(obj, imemo_class_fields)) {
|
||||
class_fields_each(obj, func, arg, ivar_only);
|
||||
}
|
||||
break;
|
||||
case T_OBJECT:
|
||||
obj_fields_each(obj, func, arg, ivar_only);
|
||||
break;
|
||||
@ -2406,11 +2455,14 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
|
||||
case T_MODULE:
|
||||
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(0);
|
||||
RB_VM_LOCKING() {
|
||||
class_fields_each(obj, func, arg, ivar_only);
|
||||
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (fields_obj) {
|
||||
class_fields_each(fields_obj, func, arg, ivar_only);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (FL_TEST(obj, FL_EXIVAR)) {
|
||||
if (FL_TEST_RAW(obj, FL_EXIVAR)) {
|
||||
gen_fields_each(obj, func, arg, ivar_only);
|
||||
}
|
||||
break;
|
||||
@ -2435,8 +2487,16 @@ rb_ivar_count(VALUE obj)
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
iv_count = RCLASS_FIELDS_COUNT(obj);
|
||||
break;
|
||||
{
|
||||
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (!fields_obj) {
|
||||
return 0;
|
||||
}
|
||||
if (rb_shape_obj_too_complex_p(fields_obj)) {
|
||||
return rb_st_table_size(rb_imemo_class_fields_complex_tbl(fields_obj));
|
||||
}
|
||||
return RBASIC_FIELDS_COUNT(fields_obj);
|
||||
}
|
||||
default:
|
||||
if (FL_TEST(obj, FL_EXIVAR)) {
|
||||
struct gen_fields_tbl *fields_tbl;
|
||||
@ -4642,38 +4702,91 @@ rb_iv_set(VALUE obj, const char *name, VALUE val)
|
||||
return rb_ivar_set(obj, id, val);
|
||||
}
|
||||
|
||||
static VALUE *
|
||||
class_ivar_set_shape_fields(VALUE obj, void *_data)
|
||||
static int
|
||||
class_ivar_set(VALUE obj, ID id, VALUE val)
|
||||
{
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
|
||||
bool existing = true;
|
||||
const VALUE original_fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
VALUE fields_obj = original_fields_obj ? original_fields_obj : rb_imemo_class_fields_new(obj, 1);
|
||||
|
||||
return RCLASS_PRIME_FIELDS(obj);
|
||||
}
|
||||
shape_id_t next_shape_id = 0;
|
||||
shape_id_t current_shape_id = RBASIC_SHAPE_ID(fields_obj);
|
||||
if (UNLIKELY(rb_shape_too_complex_p(current_shape_id))) {
|
||||
goto too_complex;
|
||||
}
|
||||
|
||||
static void
|
||||
class_ivar_set_shape_resize_fields(VALUE obj, attr_index_t _old_capa, attr_index_t new_capa, void *_data)
|
||||
{
|
||||
REALLOC_N(RCLASS_PRIME_FIELDS(obj), VALUE, new_capa);
|
||||
}
|
||||
attr_index_t index;
|
||||
if (!rb_shape_get_iv_index(current_shape_id, id, &index)) {
|
||||
existing = false;
|
||||
|
||||
static void
|
||||
class_ivar_set_set_shape_id(VALUE obj, shape_id_t shape_id, void *_data)
|
||||
{
|
||||
rb_obj_set_shape_id(obj, shape_id);
|
||||
}
|
||||
index = RSHAPE_LEN(current_shape_id);
|
||||
if (index >= SHAPE_MAX_FIELDS) {
|
||||
rb_raise(rb_eArgError, "too many instance variables");
|
||||
}
|
||||
|
||||
static shape_id_t
|
||||
class_ivar_set_transition_too_complex(VALUE obj, void *_data)
|
||||
{
|
||||
return rb_evict_fields_to_hash(obj);
|
||||
}
|
||||
next_shape_id = rb_shape_transition_add_ivar(fields_obj, id);
|
||||
if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) {
|
||||
attr_index_t current_len = RSHAPE_LEN(current_shape_id);
|
||||
fields_obj = rb_imemo_class_fields_new_complex(obj, current_len + 1);
|
||||
if (current_len) {
|
||||
rb_obj_copy_fields_to_hash_table(original_fields_obj, rb_imemo_class_fields_complex_tbl(fields_obj));
|
||||
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
|
||||
}
|
||||
goto too_complex;
|
||||
}
|
||||
|
||||
static st_table *
|
||||
class_ivar_set_too_complex_table(VALUE obj, void *_data)
|
||||
{
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
|
||||
attr_index_t next_capacity = RSHAPE_CAPACITY(next_shape_id);
|
||||
attr_index_t current_capacity = RSHAPE_CAPACITY(current_shape_id);
|
||||
|
||||
return RCLASS_WRITABLE_FIELDS_HASH(obj);
|
||||
if (UNLIKELY(next_capacity != current_capacity)) {
|
||||
RUBY_ASSERT(next_capacity > current_capacity);
|
||||
// We allocate a new fields_obj so that we're embedded as long as possible
|
||||
fields_obj = rb_imemo_class_fields_new(obj, next_capacity);
|
||||
if (original_fields_obj) {
|
||||
MEMCPY(rb_imemo_class_fields_ptr(fields_obj), rb_imemo_class_fields_ptr(original_fields_obj), VALUE, RSHAPE_LEN(current_shape_id));
|
||||
}
|
||||
}
|
||||
|
||||
RUBY_ASSERT(RSHAPE(next_shape_id)->type == SHAPE_IVAR);
|
||||
RUBY_ASSERT(index == (RSHAPE_LEN(next_shape_id) - 1));
|
||||
}
|
||||
|
||||
VALUE *fields = rb_imemo_class_fields_ptr(fields_obj);
|
||||
RB_OBJ_WRITE(fields_obj, &fields[index], val);
|
||||
if (!existing) {
|
||||
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
|
||||
}
|
||||
|
||||
if (fields_obj != original_fields_obj) {
|
||||
RCLASS_SET_FIELDS_OBJ(obj, fields_obj);
|
||||
// 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, next_shape_id);
|
||||
}
|
||||
|
||||
RB_GC_GUARD(fields_obj);
|
||||
return existing;
|
||||
|
||||
too_complex:
|
||||
{
|
||||
st_table *table = rb_imemo_class_fields_complex_tbl(fields_obj);
|
||||
existing = st_insert(table, (st_data_t)id, (st_data_t)val);
|
||||
RB_OBJ_WRITTEN(fields_obj, Qundef, val);
|
||||
|
||||
if (fields_obj != original_fields_obj) {
|
||||
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
|
||||
RCLASS_SET_FIELDS_OBJ(obj, fields_obj);
|
||||
// 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, next_shape_id);
|
||||
}
|
||||
}
|
||||
RB_GC_GUARD(fields_obj);
|
||||
return existing;
|
||||
}
|
||||
|
||||
int
|
||||
@ -4686,12 +4799,7 @@ rb_class_ivar_set(VALUE obj, ID id, VALUE val)
|
||||
rb_class_ensure_writable(obj);
|
||||
|
||||
RB_VM_LOCKING() {
|
||||
existing = general_ivar_set(obj, id, val, NULL,
|
||||
class_ivar_set_shape_fields,
|
||||
class_ivar_set_shape_resize_fields,
|
||||
class_ivar_set_set_shape_id,
|
||||
class_ivar_set_transition_too_complex,
|
||||
class_ivar_set_too_complex_table).existing;
|
||||
existing = class_ivar_set(obj, id, val);
|
||||
}
|
||||
|
||||
return existing;
|
||||
@ -4701,12 +4809,7 @@ static void
|
||||
class_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
|
||||
general_field_set(obj, target_shape_id, val, NULL,
|
||||
class_ivar_set_shape_fields,
|
||||
class_ivar_set_shape_resize_fields,
|
||||
class_ivar_set_set_shape_id,
|
||||
class_ivar_set_transition_too_complex,
|
||||
class_ivar_set_too_complex_table);
|
||||
obj_field_set(RCLASS_ENSURE_FIELDS_OBJ(obj), target_shape_id, val);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -4722,9 +4825,7 @@ rb_fields_tbl_copy(VALUE dst, VALUE src)
|
||||
{
|
||||
RUBY_ASSERT(rb_type(dst) == rb_type(src));
|
||||
RUBY_ASSERT(RB_TYPE_P(dst, T_CLASS) || RB_TYPE_P(dst, T_MODULE));
|
||||
|
||||
RUBY_ASSERT(RSHAPE_TYPE_P(RBASIC_SHAPE_ID(dst), SHAPE_ROOT));
|
||||
RUBY_ASSERT(!RCLASS_PRIME_FIELDS(dst));
|
||||
|
||||
rb_ivar_foreach(src, tbl_copy_i, dst);
|
||||
}
|
||||
|
@ -1213,9 +1213,10 @@ ALWAYS_INLINE(static VALUE vm_getivar(VALUE, ID, const rb_iseq_t *, IVC, const s
|
||||
static inline VALUE
|
||||
vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, VALUE default_value)
|
||||
{
|
||||
VALUE fields_obj;
|
||||
#if OPT_IC_FOR_IVAR
|
||||
VALUE val = Qundef;
|
||||
VALUE * ivar_list;
|
||||
VALUE *ivar_list;
|
||||
|
||||
if (SPECIAL_CONST_P(obj)) {
|
||||
return default_value;
|
||||
@ -1247,7 +1248,13 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
|
||||
}
|
||||
}
|
||||
|
||||
ivar_list = RCLASS_PRIME_FIELDS(obj);
|
||||
fields_obj = RCLASS_FIELDS_OBJ(obj);
|
||||
if (!fields_obj) {
|
||||
return default_value;
|
||||
}
|
||||
ivar_list = rb_imemo_class_fields_ptr(fields_obj);
|
||||
shape_id = rb_obj_shape_id(fields_obj);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -1318,7 +1325,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
table = (st_table *)RCLASS_FIELDS_HASH(obj);
|
||||
table = rb_imemo_class_fields_complex_tbl(fields_obj);
|
||||
break;
|
||||
|
||||
case T_OBJECT:
|
||||
@ -1374,6 +1381,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
|
||||
RUBY_ASSERT(!UNDEF_P(val));
|
||||
}
|
||||
|
||||
RB_GC_GUARD(fields_obj);
|
||||
return val;
|
||||
|
||||
general_path:
|
||||
|
1
yjit/src/cruby_bindings.inc.rs
generated
1
yjit/src/cruby_bindings.inc.rs
generated
@ -409,6 +409,7 @@ pub const imemo_parser_strterm: imemo_type = 10;
|
||||
pub const imemo_callinfo: imemo_type = 11;
|
||||
pub const imemo_callcache: imemo_type = 12;
|
||||
pub const imemo_constcache: imemo_type = 13;
|
||||
pub const imemo_class_fields: imemo_type = 14;
|
||||
pub type imemo_type = u32;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
1
zjit/src/cruby_bindings.inc.rs
generated
1
zjit/src/cruby_bindings.inc.rs
generated
@ -226,6 +226,7 @@ pub const imemo_parser_strterm: imemo_type = 10;
|
||||
pub const imemo_callinfo: imemo_type = 11;
|
||||
pub const imemo_callcache: imemo_type = 12;
|
||||
pub const imemo_constcache: imemo_type = 13;
|
||||
pub const imemo_class_fields: imemo_type = 14;
|
||||
pub type imemo_type = u32;
|
||||
pub const METHOD_VISI_UNDEF: rb_method_visibility_t = 0;
|
||||
pub const METHOD_VISI_PUBLIC: rb_method_visibility_t = 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user