mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
class.c: add MRB_MT_FROZEN_BIT for method table layers
When mrb_mt_init_rom() is called on a class that already has a mutable method table (from prior mrb_define_method_id() calls), the mutable top layer is now frozen in place instead of being left as a writable layer that wastes RAM on embedded systems. The frozen bit (bit 29 of alloc field) marks heap-allocated method table layers as temporarily immutable. Unlike the readonly bit (bit 30, for true ROM), frozen layers are automatically unfrozen when methods are later added via mrb_define_method_raw() or removed via mrb_remove_method(). This preserves the c->mt pointer, which is critical because iclasses (from module inclusion) hold a copy of it. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -36,12 +36,13 @@ union mrb_mt_ptr {
|
||||
|
||||
typedef struct mrb_mt_tbl {
|
||||
int size;
|
||||
int alloc; /* bit 30: MRB_MT_READONLY_BIT */
|
||||
int alloc; /* bit 30: MRB_MT_READONLY_BIT, bit 29: MRB_MT_FROZEN_BIT */
|
||||
union mrb_mt_ptr *ptr;
|
||||
struct mrb_mt_tbl *next;
|
||||
} mrb_mt_tbl;
|
||||
|
||||
#define MRB_MT_READONLY_BIT (1 << 30)
|
||||
#define MRB_MT_FROZEN_BIT (1 << 29)
|
||||
#define MRB_MT_KEY_SHIFT 4
|
||||
#define MRB_MT_KEY(sym, flags) ((sym)<<MRB_MT_KEY_SHIFT|(flags))
|
||||
#define MRB_MT_FUNC 8 /* MRB_METHOD_FUNC_FL */
|
||||
|
||||
+17
-3
@@ -33,8 +33,10 @@
|
||||
#define MT_KEY_SYM(k) ((k)>>MRB_MT_KEY_SHIFT)
|
||||
#define MT_KEY_FLG(k) ((k)&MT_KEY_MASK)
|
||||
|
||||
#define MT_ALLOC(t) ((t)->alloc & ~MRB_MT_READONLY_BIT)
|
||||
#define MRB_MT_FLAG_BITS (MRB_MT_READONLY_BIT | MRB_MT_FROZEN_BIT)
|
||||
#define MT_ALLOC(t) ((t)->alloc & ~MRB_MT_FLAG_BITS)
|
||||
#define mt_readonly_p(t) ((t)->alloc & MRB_MT_READONLY_BIT)
|
||||
#define mt_frozen_p(t) ((t)->alloc & MRB_MT_FROZEN_BIT)
|
||||
|
||||
/* Helper to get keys array from method table */
|
||||
static inline mrb_sym*
|
||||
@@ -65,7 +67,7 @@ mt_grow(mrb_state *mrb, mrb_mt_tbl *t, int new_alloc)
|
||||
/* move the old key array up to its new position */
|
||||
memmove(new_keys, old_keys, old_alloc * sizeof(mrb_sym));
|
||||
}
|
||||
t->alloc = new_alloc;
|
||||
t->alloc = (t->alloc & MRB_MT_FLAG_BITS) | new_alloc;
|
||||
}
|
||||
|
||||
/* Creates a new empty method table */
|
||||
@@ -285,6 +287,10 @@ mrb_mt_init_rom(struct RClass *c, mrb_mt_tbl *rom)
|
||||
c->mt = rom;
|
||||
}
|
||||
else {
|
||||
/* freeze mutable top, insert ROM behind it;
|
||||
* c->mt must not change because iclasses (module inclusion)
|
||||
* hold a copy of the mt pointer */
|
||||
t->alloc |= MRB_MT_FROZEN_BIT;
|
||||
rom->next = t->next;
|
||||
t->next = rom;
|
||||
}
|
||||
@@ -1090,6 +1096,11 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_
|
||||
if (!h) {
|
||||
h = c->mt = mt_new(mrb);
|
||||
}
|
||||
else if (mt_frozen_p(h)) {
|
||||
/* unfreeze heap-allocated frozen layer to preserve c->mt pointer
|
||||
* (iclasses hold a copy of the mt pointer for included modules) */
|
||||
h->alloc &= ~MRB_MT_FROZEN_BIT;
|
||||
}
|
||||
else if (mt_readonly_p(h)) {
|
||||
/* COW: create mutable top layer, chain to ROM */
|
||||
mrb_mt_tbl *top = mt_new(mrb);
|
||||
@@ -3654,7 +3665,10 @@ mrb_remove_method(mrb_state *mrb, struct RClass *c0, mrb_sym mid)
|
||||
union mrb_mt_ptr tombstone;
|
||||
tombstone.func = NULL;
|
||||
found = TRUE;
|
||||
if (mt_readonly_p(h)) {
|
||||
if (mt_frozen_p(h)) {
|
||||
h->alloc &= ~MRB_MT_FROZEN_BIT;
|
||||
}
|
||||
else if (mt_readonly_p(h)) {
|
||||
mrb_mt_tbl *top = mt_new(mrb);
|
||||
top->next = h;
|
||||
h = c->mt = top;
|
||||
|
||||
Reference in New Issue
Block a user