summaryrefslogtreecommitdiff
path: root/src/variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/variable.c')
-rw-r--r--src/variable.c1221
1 files changed, 951 insertions, 270 deletions
diff --git a/src/variable.c b/src/variable.c
index 57f2b15..45e4e02 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -11,7 +11,6 @@
#include <mruby/string.h>
#include <mruby/variable.h>
#include <mruby/internal.h>
-#include <mruby/presym.h>
/* Instance variable table structure */
typedef struct iv_tbl {
@@ -19,9 +18,7 @@ typedef struct iv_tbl {
mrb_value *ptr;
} iv_tbl;
-#define IV_EMPTY 0
-#define IV_DELETED (1UL<<31)
-#define IV_KEY_P(k) (((k)&~((uint32_t)IV_DELETED))!=0)
+
/* Creates the instance variable table. */
static iv_tbl*
@@ -39,185 +36,197 @@ iv_new(mrb_state *mrb)
static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val);
-#define IV_INITIAL_SIZE 4
+#define IV_INITIAL_SIZE 2
static void
iv_rehash(mrb_state *mrb, iv_tbl *t)
{
int old_alloc = t->alloc;
int new_alloc = old_alloc > 0 ? old_alloc << 1 : IV_INITIAL_SIZE;
- mrb_value *old_ptr = t->ptr;
- t->ptr = (mrb_value*)mrb_calloc(mrb, sizeof(mrb_value)+sizeof(mrb_sym), new_alloc);
- t->size = 0;
+ if (old_alloc == 0) {
+ /* first-time init */
+ t->ptr = (mrb_value*)mrb_calloc(mrb, new_alloc, sizeof(mrb_value)+sizeof(mrb_sym));
+ t->alloc = new_alloc;
+ return;
+ }
+
+ /* realloc may extend in place, avoiding malloc+memcpy+free */
+ size_t new_size = (size_t)new_alloc * (sizeof(mrb_value) + sizeof(mrb_sym));
+ t->ptr = (mrb_value*)mrb_realloc(mrb, t->ptr, new_size);
+
+ /* move keys from old position to new position */
+ mrb_sym *old_keys = (mrb_sym*)&t->ptr[old_alloc];
+ mrb_sym *new_keys = (mrb_sym*)&t->ptr[new_alloc];
+ memmove(new_keys, old_keys, sizeof(mrb_sym) * t->size);
+
+ /* clear extended value region (where old keys were + new slots) */
+ memset(&t->ptr[old_alloc], 0, sizeof(mrb_value) * (new_alloc - old_alloc));
+
+ /* clear extended key region */
+ memset(&new_keys[t->size], 0, sizeof(mrb_sym) * (new_alloc - t->size));
+
t->alloc = new_alloc;
- if (old_alloc == 0) return;
+}
- mrb_sym *keys = (mrb_sym*)&old_ptr[old_alloc];
- mrb_value *vals = old_ptr;
- for (int i = 0; i < old_alloc; i++) {
- if (IV_KEY_P(keys[i])) {
- iv_put(mrb, t, keys[i], vals[i]);
- }
+/* Branch-free binary search helper: returns the index where `target` should be inserted/found. */
+static inline int
+iv_bsearch_idx(mrb_sym *keys, int size, mrb_sym target) {
+ if (size == 0) return 0;
+ int n = size;
+ mrb_sym *p = keys;
+ /* While more than one element remains, halve the range each iteration */
+ while (n > 1) {
+ int half = n >> 1;
+ MRB_MEM_PREFETCH(p + (half >> 1));
+ MRB_MEM_PREFETCH(p + half + (half >> 1));
+ mrb_sym mid_sym = p[half];
+ /*
+ * Update pointer p without a branch:
+ * If mid_sym < target, move p forward by half; otherwise keep p unchanged.
+ * Compiler will emit a CMOV or equivalent.
+ */
+ p = (mid_sym < target) ? p + half : p;
+ n -= half;
}
- mrb_free(mrb, old_ptr);
+ /* Final adjustment: if the remaining element is still less than target, advance by one */
+ return (int)(p - keys) + (p[0] < target);
}
-/* Set the value for the symbol in the instance variable table. */
+/* Set (insert or update) the value for `sym` in the instance variable table using branch-free search. */
static void
iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
{
- int hash, pos, start, dpos = -1;
-
+ /* If table is uninitialized, allocate and initialize */
if (t->alloc == 0) {
iv_rehash(mrb, t);
}
- mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
- mrb_value *vals = t->ptr;
- hash = mrb_int_hash_func(mrb, sym);
- start = pos = hash & (t->alloc-1);
- for (;;) {
- mrb_sym key = keys[pos];
- if (key == sym) {
- vals[pos] = val;
- return;
- }
- else if (key == IV_EMPTY) {
- t->size++;
- keys[pos] = sym;
- vals[pos] = val;
- return;
- }
- else if (key == IV_DELETED && dpos < 0) {
- dpos = pos;
- }
- pos = (pos+1) & (t->alloc-1);
- if (pos == start) { /* not found */
- if (dpos >= 0) {
- t->size++;
- keys[dpos] = sym;
- vals[dpos] = val;
- return;
- }
- /* no room */
- iv_rehash(mrb, t);
- keys = (mrb_sym*)&t->ptr[t->alloc];
- vals = t->ptr;
- start = pos = hash & (t->alloc-1);
- }
+ /* Obtain pointers to keys and values arrays */
+ mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
+ mrb_value *vals = t->ptr;
+
+ /* Determine insertion/update index:
+ * If table has entries, use branch-free search; otherwise index = 0.
+ */
+ int lo = iv_bsearch_idx(keys, t->size, sym);
+
+ /* If the key already exists, update its value and return */
+ if (lo < t->size && keys[lo] == sym) {
+ vals[lo] = val;
+ return;
+ }
+
+ /* Grow table if full, then recompute position */
+ if (t->size == t->alloc) {
+ iv_rehash(mrb, t);
+ keys = (mrb_sym*)&t->ptr[t->alloc];
+ vals = t->ptr;
+ lo = iv_bsearch_idx(keys, t->size, sym);
}
+
+ /* Shift existing entries right to make room at index lo */
+ int move_count = t->size - lo;
+ if (move_count > 0) {
+ memmove(&keys[lo + 1], &keys[lo], move_count * sizeof(mrb_sym));
+ memmove(&vals[lo + 1], &vals[lo], move_count * sizeof(mrb_value));
+ }
+
+ /* Insert the new key and value */
+ keys[lo] = sym;
+ vals[lo] = val;
+ t->size++;
}
-/* Get a value for a symbol from the instance variable table. */
+/* Get a value for `sym` from the instance variable table using branch-free search. */
static int
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
- int hash, pos, start;
+ /* Return 0 if table is null, uninitialized, or empty */
+ if (t == NULL || t->alloc == 0 || t->size == 0) return 0;
- if (t == NULL) return 0;
- if (t->alloc == 0) return 0;
- if (t->size == 0) return 0;
+ mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
+ mrb_value *vals = t->ptr;
- mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
- mrb_value *vals = t->ptr;
- hash = mrb_int_hash_func(mrb, sym);
- start = pos = hash & (t->alloc-1);
- for (;;) {
- mrb_sym key = keys[pos];
- if (key == sym) {
- if (vp) *vp = vals[pos];
- return pos+1;
- }
- else if (key == IV_EMPTY) {
- return 0;
- }
- pos = (pos+1) & (t->alloc-1);
- if (pos == start) { /* not found */
- return 0;
- }
+ /* Find index in a branch-free manner */
+ int lo = iv_bsearch_idx(keys, t->size, sym);
+
+ /* If found, store value (if vp provided) and return 1-based position */
+ if (lo < t->size && keys[lo] == sym) {
+ if (vp) *vp = vals[lo];
+ return lo + 1;
}
+
+ /* Not found */
+ return 0;
}
-/* Deletes the value for the symbol from the instance variable table. */
+/* Delete the entry for `sym` from the instance variable table using branch-free search. */
static mrb_bool
iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
- int hash, pos, start;
+ /* Return FALSE if table is null, uninitialized, or empty */
+ if (t == NULL || t->alloc == 0 || t->size == 0) return FALSE;
- if (t == NULL) return FALSE;
- if (t->alloc == 0) return FALSE;
- if (t->size == 0) return FALSE;
+ mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
+ mrb_value *vals = t->ptr;
- mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
- mrb_value *vals = t->ptr;
- hash = mrb_int_hash_func(mrb, sym);
- start = pos = hash & (t->alloc-1);
- for (;;) {
- mrb_sym key = keys[pos];
- if (key == sym) {
- if (vp) *vp = vals[pos];
- t->size--;
- keys[pos] = IV_DELETED;
- return TRUE;
- }
- else if (key == IV_EMPTY) {
- return FALSE;
- }
- pos = (pos+1) & (t->alloc-1);
- if (pos == start) { /* not found */
- return FALSE;
+ /* Find index in a branch-free manner */
+ int lo = iv_bsearch_idx(keys, t->size, sym);
+
+ /* If found, optionally return value and shift entries left to delete */
+ if (lo < t->size && keys[lo] == sym) {
+ if (vp) *vp = vals[lo];
+ int move_count = t->size - lo - 1;
+ if (move_count > 0) {
+ memmove(&keys[lo], &keys[lo + 1], move_count * sizeof(mrb_sym));
+ memmove(&vals[lo], &vals[lo + 1], move_count * sizeof(mrb_value));
}
+ t->size--;
+ return TRUE;
}
+
+ /* Not found */
+ return FALSE;
}
/* Iterates over the instance variable table. */
static void
iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_iv_foreach_func *func, void *p)
{
- if (t == NULL) return;
- if (t->alloc == 0) return;
- if (t->size == 0) return;
-
- mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
- mrb_value *vals = t->ptr;
- for (int i=0; i<t->alloc; i++) {
- if (IV_KEY_P(keys[i])) {
- if ((*func)(mrb, keys[i], vals[i], p) != 0) {
- return;
- }
- }
+ if (t == NULL || t->alloc == 0 || t->size == 0) return;
+ for (int i = 0; i < t->size; i++) {
+ mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
+ mrb_value *vals = t->ptr;
+ if ((*func)(mrb, keys[i], vals[i], p) != 0) return;
}
- return;
}
/* Get the size of the instance variable table. */
-/* Size is approximated by the allocated table size. */
static size_t
iv_size(mrb_state *mrb, iv_tbl *t)
{
- if (t == NULL) return 0;
- return (size_t)t->size;
+ return t ? t->size : 0;
}
-/* Copy the instance variable table. */
+/* Copy the sorted table */
static iv_tbl*
iv_copy(mrb_state *mrb, iv_tbl *t)
{
- iv_tbl *t2;
+ if (t == NULL || t->alloc == 0 || t->size == 0) return NULL;
- if (t == NULL) return NULL;
- if (t->alloc == 0) return NULL;
- if (t->size == 0) return NULL;
+ /* create new table and mirror alloc/size */
+ iv_tbl *t2 = iv_new(mrb);
+ t2->alloc = t->alloc;
+ t2->size = t->size;
+
+ /* allocate the same block shape */
+ t2->ptr = (mrb_value*)mrb_calloc(mrb, t2->alloc, sizeof(mrb_value)+sizeof(mrb_sym));
+
+ /* copy values[0...size] and keys[0...size] */
+ memcpy(t2->ptr, t->ptr, sizeof(mrb_value)*t2->size);
+ memcpy(&t2->ptr[t2->alloc], &t->ptr[t->alloc], sizeof(mrb_sym)*t2->size);
- mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
- mrb_value *vals = t->ptr;
- t2 = iv_new(mrb);
- for (int i=0; i<t->alloc; i++) {
- if (IV_KEY_P(keys[i])) {
- iv_put(mrb, t2, keys[i], vals[i]);
- }
- }
return t2;
}
@@ -229,6 +238,287 @@ iv_free(mrb_state *mrb, iv_tbl *t)
mrb_free(mrb, t);
}
+/*
+ * Object Shape (Hidden Class) structures.
+ *
+ * A shape describes the IV layout of an object: which syms are stored
+ * at which indices. Shapes form a tree rooted at the empty root shape.
+ * Each child adds one IV (its "edge" sym). Objects sharing the same
+ * set of IVs (assigned in the same order) share the same shape,
+ * eliminating per-object key storage.
+ *
+ * Only MRB_TT_OBJECT instances are shaped. RClass, RHash, etc. keep
+ * traditional iv_tbl.
+ */
+
+/* Maximum IV count before de-shaping to iv_tbl */
+#define MRB_SHAPE_MAX_IVS 16
+
+/* Shape descriptor -- shared across objects with same IV layout */
+typedef struct mrb_iv_shape {
+ struct mrb_iv_shape *parent; /* parent shape (one fewer IV) */
+ struct mrb_iv_shape *children; /* linked list of child shapes */
+ struct mrb_iv_shape *sibling; /* next child of same parent */
+ mrb_sym edge; /* IV sym added from parent */
+ uint16_t count; /* number of IV slots */
+} mrb_iv_shape;
+
+/* Per-object shaped IV storage (allocated via struct hack) */
+typedef struct mrb_shaped_iv {
+ mrb_iv_shape *shape;
+ mrb_value values[1]; /* shape->count elements */
+} mrb_shaped_iv;
+
+/* Create the empty root shape */
+static mrb_iv_shape*
+shape_root(mrb_state *mrb)
+{
+ mrb_iv_shape *s = (mrb_iv_shape*)mrb_calloc(mrb, 1, sizeof(mrb_iv_shape));
+ return s;
+}
+
+/* Find a child shape with the given edge sym */
+static mrb_iv_shape*
+shape_find_child(mrb_iv_shape *shape, mrb_sym sym)
+{
+ mrb_iv_shape *c = shape->children;
+ while (c) {
+ if (c->edge == sym) return c;
+ c = c->sibling;
+ }
+ return NULL;
+}
+
+/* Find or create a child shape for adding sym */
+static mrb_iv_shape*
+shape_transition(mrb_state *mrb, mrb_iv_shape *shape, mrb_sym sym)
+{
+ mrb_iv_shape *child = shape_find_child(shape, sym);
+ if (child) return child;
+
+ /* create new child shape */
+ child = (mrb_iv_shape*)mrb_malloc(mrb, sizeof(mrb_iv_shape));
+ child->parent = shape;
+ child->children = NULL;
+ child->sibling = shape->children;
+ child->edge = sym;
+ child->count = shape->count + 1;
+ shape->children = child;
+ return child;
+}
+
+/*
+ * Look up sym in shape by walking the parent chain.
+ * Returns the value index (0-based), or -1 if not found.
+ */
+static int
+shape_lookup(mrb_iv_shape *shape, mrb_sym sym)
+{
+ mrb_iv_shape *s = shape;
+ while (s->count > 0) {
+ if (s->edge == sym) return s->count - 1;
+ s = s->parent;
+ }
+ return -1;
+}
+
+/* Recursively free all shapes in the tree */
+static void
+shape_free_tree(mrb_state *mrb, mrb_iv_shape *shape)
+{
+ mrb_iv_shape *c = shape->children;
+ while (c) {
+ mrb_iv_shape *next = c->sibling;
+ shape_free_tree(mrb, c);
+ c = next;
+ }
+ mrb_free(mrb, shape);
+}
+
+/* Allocate a mrb_shaped_iv with room for count values */
+static mrb_shaped_iv*
+shaped_iv_alloc(mrb_state *mrb, mrb_iv_shape *shape)
+{
+ size_t sz = offsetof(mrb_shaped_iv, values) +
+ sizeof(mrb_value) * shape->count;
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)mrb_malloc(mrb, sz);
+ siv->shape = shape;
+ return siv;
+}
+
+/* Convert a shaped object back to traditional iv_tbl (de-shape) */
+static void
+shaped_to_iv_tbl(mrb_state *mrb, struct RObject *obj)
+{
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)obj->iv;
+ iv_tbl *t = NULL;
+
+ if (siv) {
+ mrb_iv_shape *shape = siv->shape;
+
+ if (shape->count > 0) {
+ /* reconstruct keys from parent chain */
+ mrb_sym keys[MRB_SHAPE_MAX_IVS];
+ mrb_iv_shape *s = shape;
+ while (s->count > 0) {
+ keys[s->count - 1] = s->edge;
+ s = s->parent;
+ }
+
+ t = iv_new(mrb);
+ for (int i = 0; i < shape->count; i++) {
+ if (!mrb_undef_p(siv->values[i])) {
+ iv_put(mrb, t, keys[i], siv->values[i]);
+ }
+ }
+ }
+ mrb_free(mrb, siv);
+ }
+ obj->iv = t;
+ obj->flags &= ~MRB_FL_OBJ_SHAPED;
+}
+
+/* --- Shaped IV operations --- */
+
+static void
+shaped_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+{
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)obj->iv;
+ mrb_iv_shape *shape = siv ? siv->shape : mrb->root_shape;
+
+ /* check if sym already exists in current shape */
+ int idx = shape_lookup(shape, sym);
+ if (idx >= 0) {
+ siv->values[idx] = v;
+ return;
+ }
+
+ /* transition to new shape */
+ mrb_iv_shape *new_shape = shape_transition(mrb, shape, sym);
+
+ /* de-shape if too many IVs */
+ if (new_shape->count > MRB_SHAPE_MAX_IVS) {
+ shaped_to_iv_tbl(mrb, obj);
+ if (!obj->iv) {
+ obj->iv = iv_new(mrb);
+ }
+ iv_put(mrb, obj->iv, sym, v);
+ return;
+ }
+
+ /* allocate new shaped_iv with room for new shape */
+ mrb_shaped_iv *new_siv = shaped_iv_alloc(mrb, new_shape);
+
+ /* copy old values (they are a prefix) */
+ if (siv) {
+ memcpy(new_siv->values, siv->values,
+ sizeof(mrb_value) * shape->count);
+ mrb_free(mrb, siv);
+ }
+ /* new IV goes in the last slot */
+ new_siv->values[new_shape->count - 1] = v;
+
+ obj->iv = (iv_tbl*)new_siv;
+}
+
+static mrb_value
+shaped_iv_get(struct RObject *obj, mrb_sym sym)
+{
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)obj->iv;
+ if (!siv) return mrb_nil_value();
+ int idx = shape_lookup(siv->shape, sym);
+ if (idx >= 0 && !mrb_undef_p(siv->values[idx]))
+ return siv->values[idx];
+ return mrb_nil_value();
+}
+
+static mrb_bool
+shaped_iv_defined(struct RObject *obj, mrb_sym sym)
+{
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)obj->iv;
+ if (!siv) return FALSE;
+ int idx = shape_lookup(siv->shape, sym);
+ if (idx >= 0 && !mrb_undef_p(siv->values[idx]))
+ return TRUE;
+ return FALSE;
+}
+
+static void
+shaped_iv_foreach(mrb_state *mrb, struct RObject *obj,
+ mrb_iv_foreach_func *func, void *p)
+{
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)obj->iv;
+ if (!siv) return;
+ mrb_iv_shape *shape = siv->shape;
+ if (shape->count == 0) return;
+
+ /* reconstruct keys from parent chain */
+ mrb_sym keys[MRB_SHAPE_MAX_IVS];
+ mrb_iv_shape *s = shape;
+ while (s->count > 0) {
+ keys[s->count - 1] = s->edge;
+ s = s->parent;
+ }
+
+ for (int i = 0; i < shape->count; i++) {
+ if (!mrb_undef_p(siv->values[i])) {
+ if ((*func)(mrb, keys[i], siv->values[i], p) != 0) return;
+ }
+ }
+}
+
+static size_t
+shaped_iv_mark(mrb_state *mrb, struct RObject *obj)
+{
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)obj->iv;
+ if (!siv) return 0;
+ mrb_iv_shape *shape = siv->shape;
+ for (int i = 0; i < shape->count; i++) {
+ if (!mrb_undef_p(siv->values[i])) {
+ mrb_gc_mark_value(mrb, siv->values[i]);
+ }
+ }
+ return shape->count;
+}
+
+static void
+shaped_iv_free(mrb_state *mrb, struct RObject *obj)
+{
+ if (obj->iv) {
+ mrb_free(mrb, obj->iv);
+ }
+}
+
+static void
+shaped_iv_copy(mrb_state *mrb, struct RObject *dst, struct RObject *src)
+{
+ mrb_shaped_iv *ssiv = (mrb_shaped_iv*)src->iv;
+ if (!ssiv) {
+ dst->iv = NULL;
+ return;
+ }
+ mrb_iv_shape *shape = ssiv->shape;
+ mrb_shaped_iv *dsiv = shaped_iv_alloc(mrb, shape);
+ memcpy(dsiv->values, ssiv->values, sizeof(mrb_value) * shape->count);
+ dst->iv = (iv_tbl*)dsiv;
+}
+
+/* Public init/free for shape tree (called from state.c) */
+void
+mrb_init_shape(mrb_state *mrb)
+{
+ mrb->root_shape = shape_root(mrb);
+}
+
+void
+mrb_free_shape(mrb_state *mrb)
+{
+ if (mrb->root_shape) {
+ shape_free_tree(mrb, mrb->root_shape);
+ mrb->root_shape = NULL;
+ }
+}
+
static int
iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
@@ -260,6 +550,9 @@ mrb_gc_free_gv(mrb_state *mrb)
size_t
mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj)
{
+ if (MRB_OBJ_SHAPED_P(obj)) {
+ return shaped_iv_mark(mrb, obj);
+ }
mark_tbl(mrb, obj->iv);
return iv_size(mrb, obj->iv);
}
@@ -267,6 +560,10 @@ mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj)
void
mrb_gc_free_iv(mrb_state *mrb, struct RObject *obj)
{
+ if (MRB_OBJ_SHAPED_P(obj)) {
+ shaped_iv_free(mrb, obj);
+ return;
+ }
if (obj->iv) {
iv_free(mrb, obj->iv);
}
@@ -306,16 +603,48 @@ class_iv_ptr(struct RClass *c)
return c->tt == MRB_TT_ICLASS ? c->c->iv : c->iv;
}
+/*
+ * Retrieves an instance variable from an object.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The object from which to retrieve the instance variable.
+ * sym: The symbol representing the name of the instance variable.
+ *
+ * Returns:
+ * The value of the instance variable, or mrb_nil_value() if the
+ * instance variable is not defined.
+ */
MRB_API mrb_value
mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
{
- mrb_value v;
+ if (MRB_OBJ_SHAPED_P(obj)) {
+ return shaped_iv_get(obj, sym);
+ }
+ mrb_value v;
if (obj->iv && iv_get(mrb, obj->iv, sym, &v))
return v;
return mrb_nil_value();
}
+/*
+ * Retrieves an instance variable from an mrb_value.
+ *
+ * This function is a wrapper around mrb_obj_iv_get. It checks if the
+ * given mrb_value is an object that can have instance variables before
+ * attempting to retrieve the variable.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The mrb_value from which to retrieve the instance variable.
+ * sym: The symbol representing the name of the instance variable.
+ *
+ * Returns:
+ * The value of the instance variable, or mrb_nil_value() if the
+ * instance variable is not defined or if the object cannot have
+ * instance variables.
+ */
MRB_API mrb_value
mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
@@ -363,6 +692,11 @@ mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value
if (namespace_p(obj->tt)) {
assign_class_name(mrb, obj, sym, v);
}
+ if (MRB_OBJ_SHAPED_P(obj)) {
+ shaped_iv_set(mrb, obj, sym, v);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)obj, v);
+ return;
+ }
if (!obj->iv) {
obj->iv = iv_new(mrb);
}
@@ -370,6 +704,18 @@ mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value
mrb_field_write_barrier_value(mrb, (struct RBasic*)obj, v);
}
+/*
+ * Sets an instance variable on an object.
+ *
+ * This function checks if the object is frozen before setting the variable.
+ * It then calls mrb_obj_iv_set_force to actually set the variable.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The object on which to set the instance variable.
+ * sym: The symbol representing the name of the instance variable.
+ * v: The value to set for the instance variable.
+ */
MRB_API void
mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
@@ -377,14 +723,49 @@ mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
mrb_obj_iv_set_force(mrb, obj, sym, v);
}
-/* Iterates over the instance variable table. */
+/*
+ * Iterates over the instance variables of an object.
+ *
+ * This function calls the provided callback function for each instance
+ * variable in the object.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The mrb_value whose instance variables to iterate over.
+ * func: The callback function to call for each instance variable.
+ * The function should take mrb_state*, mrb_sym, mrb_value, and void*
+ * as arguments and return an int. If the callback returns a non-zero
+ * value, iteration stops.
+ * p: A pointer to user data that will be passed to the callback function.
+ */
MRB_API void
mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p)
{
if (!obj_iv_p(obj)) return;
+ if (MRB_OBJ_SHAPED_P(mrb_obj_ptr(obj))) {
+ shaped_iv_foreach(mrb, mrb_obj_ptr(obj), func, p);
+ return;
+ }
iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p);
}
+/*
+ * Sets an instance variable on an mrb_value.
+ *
+ * This function is a wrapper around mrb_obj_iv_set. It checks if the
+ * given mrb_value is an object that can have instance variables before
+ * attempting to set the variable. If the object cannot have instance
+ * variables, it raises an E_ARGUMENT_ERROR.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The mrb_value on which to set the instance variable.
+ * sym: The symbol representing the name of the instance variable.
+ * v: The value to set for the instance variable.
+ *
+ * Raises:
+ * E_ARGUMENT_ERROR: If the object cannot have instance variables.
+ */
MRB_API void
mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v)
{
@@ -396,16 +777,45 @@ mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v)
}
}
+/*
+ * Checks if an instance variable is defined on an object.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The object to check.
+ * sym: The symbol representing the name of the instance variable.
+ *
+ * Returns:
+ * TRUE if the instance variable is defined, FALSE otherwise.
+ */
MRB_API mrb_bool
mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
{
- iv_tbl *t;
+ if (MRB_OBJ_SHAPED_P(obj)) {
+ return shaped_iv_defined(obj, sym);
+ }
- t = obj->iv;
+ iv_tbl *t = obj->iv;
if (t && iv_get(mrb, t, sym, NULL)) return TRUE;
return FALSE;
}
+/*
+ * Checks if an instance variable is defined on an mrb_value.
+ *
+ * This function is a wrapper around mrb_obj_iv_defined. It checks if the
+ * given mrb_value is an object that can have instance variables before
+ * attempting to check for the variable.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The mrb_value to check.
+ * sym: The symbol representing the name of the instance variable.
+ *
+ * Returns:
+ * TRUE if the instance variable is defined and the object can have
+ * instance variables, FALSE otherwise.
+ */
MRB_API mrb_bool
mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
@@ -413,19 +823,45 @@ mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym);
}
+/*
+ * Checks if a symbol is a valid instance variable name.
+ *
+ * A valid instance variable name must:
+ * - Be at least 2 characters long.
+ * - Start with '@'.
+ * - Not have a digit as the second character.
+ * - The rest of the name must be a valid identifier.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * iv_name: The symbol to check.
+ *
+ * Returns:
+ * TRUE if the symbol is a valid instance variable name, FALSE otherwise.
+ */
MRB_API mrb_bool
mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name)
{
- const char *s;
mrb_int len;
+ const char *s = mrb_sym_name_len(mrb, iv_name, &len);
- s = mrb_sym_name_len(mrb, iv_name, &len);
if (len < 2) return FALSE;
if (s[0] != '@') return FALSE;
if (ISDIGIT(s[1])) return FALSE;
return mrb_ident_p(s+1, len-1);
}
+/*
+ * Checks if a symbol is a valid instance variable name and raises a
+ * name error if it's not.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * iv_name: The symbol to check.
+ *
+ * Raises:
+ * E_NAME_ERROR: If the symbol is not a valid instance variable name.
+ */
MRB_API void
mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
{
@@ -434,82 +870,112 @@ mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
}
}
+/*
+ * Copies instance variables from one object to another.
+ *
+ * If the destination object already has instance variables, they are freed
+ * before copying.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * dest: The destination object (mrb_value).
+ * src: The source object (mrb_value).
+ */
MRB_API void
mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src)
{
struct RObject *d = mrb_obj_ptr(dest);
struct RObject *s = mrb_obj_ptr(src);
- if (d->iv) {
- iv_free(mrb, d->iv);
- d->iv = 0;
+ /* free dest's existing IVs */
+ if (MRB_OBJ_SHAPED_P(d)) {
+ shaped_iv_free(mrb, d);
+ d->iv = NULL;
}
- if (s->iv) {
- mrb_write_barrier(mrb, (struct RBasic*)d);
- d->iv = iv_copy(mrb, s->iv);
+ else if (d->iv) {
+ iv_free(mrb, d->iv);
+ d->iv = NULL;
}
-}
-
-static int
-inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
-{
- mrb_value str = *(mrb_value*)p;
- const char *s;
- mrb_int len;
- mrb_value ins;
- char *sp = RSTRING_PTR(str);
- /* need not to show internal data */
- if (sp[0] == '-') { /* first element */
- sp[0] = '#';
- mrb_str_cat_lit(mrb, str, " ");
+ if (MRB_OBJ_SHAPED_P(s) && MRB_OBJ_SHAPED_P(d)) {
+ /* both shaped: share shape, memcpy values */
+ if (s->iv) {
+ mrb_write_barrier(mrb, (struct RBasic*)d);
+ shaped_iv_copy(mrb, d, s);
+ }
}
- else {
- mrb_str_cat_lit(mrb, str, ", ");
+ else if (MRB_OBJ_SHAPED_P(s)) {
+ /* src shaped, dest unshaped: convert src to iv_tbl copy */
+ mrb_shaped_iv *ssiv = (mrb_shaped_iv*)s->iv;
+ if (ssiv) {
+ mrb_iv_shape *shape = ssiv->shape;
+ if (shape->count > 0) {
+ mrb_sym keys[MRB_SHAPE_MAX_IVS];
+ mrb_iv_shape *sh = shape;
+ while (sh->count > 0) {
+ keys[sh->count - 1] = sh->edge;
+ sh = sh->parent;
+ }
+ iv_tbl *t = iv_new(mrb);
+ for (int i = 0; i < shape->count; i++) {
+ if (!mrb_undef_p(ssiv->values[i])) {
+ iv_put(mrb, t, keys[i], ssiv->values[i]);
+ }
+ }
+ mrb_write_barrier(mrb, (struct RBasic*)d);
+ d->iv = t;
+ }
+ }
}
- s = mrb_sym_name_len(mrb, sym, &len);
- mrb_str_cat(mrb, str, s, len);
- mrb_str_cat_lit(mrb, str, "=");
- ins = mrb_inspect(mrb, v);
- mrb_str_cat_str(mrb, str, ins);
- return 0;
-}
-
-mrb_value
-mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj)
-{
- iv_tbl *t = obj->iv;
- size_t len = iv_size(mrb, t);
-
- if (len > 0) {
- const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj));
- mrb_value str = mrb_str_new_capa(mrb, 30);
-
- mrb_str_cat_lit(mrb, str, "-<");
- mrb_str_cat_cstr(mrb, str, cn);
- mrb_str_cat_lit(mrb, str, ":");
- mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, obj));
-
- if (mrb_inspect_recursive_p(mrb, mrb_obj_value(obj))) {
- mrb_str_cat_lit(mrb, str, " ...>");
- return str;
+ else {
+ /* both unshaped or dest shaped but src unshaped */
+ if (MRB_OBJ_SHAPED_P(d)) {
+ d->flags &= ~MRB_FL_OBJ_SHAPED;
+ }
+ if (s->iv) {
+ mrb_write_barrier(mrb, (struct RBasic*)d);
+ d->iv = iv_copy(mrb, s->iv);
}
- iv_foreach(mrb, t, inspect_i, &str);
- mrb_str_cat_lit(mrb, str, ">");
- return str;
}
- return mrb_any_to_s(mrb, mrb_obj_value(obj));
}
+/*
+ * Removes an instance variable from an object.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The object (mrb_value) from which to remove the instance variable.
+ * sym: The symbol representing the name of the instance variable.
+ *
+ * Returns:
+ * The value of the removed instance variable, or mrb_undef_value() if
+ * the instance variable was not defined or if the object cannot have
+ * instance variables.
+ */
MRB_API mrb_value
mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
if (obj_iv_p(obj)) {
struct RObject *o = mrb_obj_ptr(obj);
+ mrb_check_frozen(mrb, o);
+
+ if (MRB_OBJ_SHAPED_P(o)) {
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)o->iv;
+ if (siv) {
+ int idx = shape_lookup(siv->shape, sym);
+ if (idx >= 0 && !mrb_undef_p(siv->values[idx])) {
+ mrb_value val = siv->values[idx];
+ /* de-shape, then remove the key */
+ shaped_to_iv_tbl(mrb, o);
+ iv_del(mrb, o->iv, sym, NULL);
+ return val;
+ }
+ }
+ return mrb_undef_value();
+ }
+
iv_tbl *t = o->iv;
mrb_value val;
-
- mrb_check_frozen(mrb, o);
if (iv_del(mrb, t, sym, &val)) {
return val;
}
@@ -520,12 +986,10 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
static int
iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
- mrb_value ary;
- const char* s;
+ mrb_value ary = *(mrb_value*)p;
mrb_int len;
+ const char* s = mrb_sym_name_len(mrb, sym, &len);
- ary = *(mrb_value*)p;
- s = mrb_sym_name_len(mrb, sym, &len);
if (len > 1 && s[0] == '@' && s[1] != '@') {
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
@@ -552,11 +1016,16 @@ iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_value
mrb_obj_instance_variables(mrb_state *mrb, mrb_value self)
{
- mrb_value ary;
+ mrb_value ary = mrb_ary_new(mrb);
- ary = mrb_ary_new(mrb);
if (obj_iv_p(self)) {
- iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary);
+ struct RObject *obj = mrb_obj_ptr(self);
+ if (MRB_OBJ_SHAPED_P(obj)) {
+ shaped_iv_foreach(mrb, obj, iv_i, &ary);
+ }
+ else {
+ iv_foreach(mrb, obj->iv, iv_i, &ary);
+ }
}
return ary;
}
@@ -564,12 +1033,10 @@ mrb_obj_instance_variables(mrb_state *mrb, mrb_value self)
static int
cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
- mrb_value ary;
- const char* s;
+ mrb_value ary = *(mrb_value*)p;
mrb_int len;
+ const char* s = mrb_sym_name_len(mrb, sym, &len);
- ary = *(mrb_value*)p;
- s = mrb_sym_name_len(mrb, sym, &len);
if (len > 2 && s[0] == '@' && s[1] == '@') {
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
@@ -581,7 +1048,7 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
* call-seq:
* mod.class_variables(inherit=true) -> array
*
- * Returns an array of the names of class variables in <i>mod</i>.
+ * Returns an array of the names of class variables in *mod*.
*
* class One
* @@var1 = 1
@@ -595,13 +1062,11 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_value
mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
{
- mrb_value ary;
- struct RClass *c;
mrb_bool inherit = TRUE;
mrb_get_args(mrb, "|b", &inherit);
- ary = mrb_ary_new(mrb);
- c = mrb_class_ptr(mod);
+ mrb_value ary = mrb_ary_new(mrb);
+ struct RClass *c = mrb_class_ptr(mod);
while (c) {
iv_foreach(mrb, class_iv_ptr(c), cv_i, &ary);
if (!inherit) break;
@@ -614,7 +1079,7 @@ mrb_value
mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
{
struct RClass * cls = c;
- mrb_value v;
+ mrb_value v = mrb_nil_value();
mrb_bool given = FALSE;
while (c) {
@@ -624,10 +1089,9 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
c = c->super;
}
if (given) return v;
- if (cls && cls->tt == MRB_TT_SCLASS) {
- mrb_value klass;
+ if (cls->tt == MRB_TT_SCLASS) {
+ mrb_value klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
- klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
c = mrb_class_ptr(klass);
if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
given = FALSE;
@@ -645,12 +1109,41 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
return mrb_nil_value();
}
+/*
+ * Retrieves a class variable from a module or class.
+ *
+ * This function is a wrapper around mrb_mod_cv_get.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) from which to retrieve the class variable.
+ * sym: The symbol representing the name of the class variable.
+ *
+ * Returns:
+ * The value of the class variable.
+ *
+ * Raises:
+ * E_NAME_ERROR: If the class variable is not defined.
+ */
MRB_API mrb_value
mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym);
}
+/*
+ * Sets a class variable in a module or class.
+ *
+ * This function searches for the class variable in the superclass chain.
+ * If found, it updates the value in the class where it's defined.
+ * Otherwise, it sets the variable in the given class `c`.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * c: The class or module (struct RClass*) in which to set the class variable.
+ * sym: The symbol representing the name of the class variable.
+ * v: The value to set for the class variable.
+ */
MRB_API void
mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
{
@@ -669,10 +1162,9 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
c = c->super;
}
- if (cls && cls->tt == MRB_TT_SCLASS) {
- mrb_value klass;
+ if (cls->tt == MRB_TT_SCLASS) {
+ mrb_value klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
- klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
switch (mrb_type(klass)) {
case MRB_TT_CLASS:
case MRB_TT_MODULE:
@@ -684,7 +1176,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
break;
}
}
- else if (cls && cls->tt == MRB_TT_ICLASS) {
+ else if (cls->tt == MRB_TT_ICLASS) {
c = cls->c;
}
else {
@@ -700,12 +1192,34 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v);
}
+/*
+ * Sets a class variable in a module or class.
+ *
+ * This function is a wrapper around mrb_mod_cv_set.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) in which to set the class variable.
+ * sym: The symbol representing the name of the class variable.
+ * v: The value to set for the class variable.
+ */
MRB_API void
mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
{
mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v);
}
+/*
+ * Checks if a class variable is defined in a module or class or its ancestors.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * c: The class or module (struct RClass*) to check.
+ * sym: The symbol representing the name of the class variable.
+ *
+ * Returns:
+ * TRUE if the class variable is defined, FALSE otherwise.
+ */
mrb_bool
mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
{
@@ -718,6 +1232,19 @@ mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
return FALSE;
}
+/*
+ * Checks if a class variable is defined in a module or class or its ancestors.
+ *
+ * This function is a wrapper around mrb_mod_cv_defined.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) to check.
+ * sym: The symbol representing the name of the class variable.
+ *
+ * Returns:
+ * TRUE if the class variable is defined, FALSE otherwise.
+ */
MRB_API mrb_bool
mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
@@ -815,6 +1342,26 @@ mrb_exc_const_get(mrb_state *mrb, mrb_sym sym)
return const_get_nohook(mrb, mrb->object_class, sym, FALSE);
}
+/*
+ * Retrieves a constant from a module or class.
+ *
+ * It first checks if `mod` is a class or module, then calls the
+ * internal `const_get` function to retrieve the constant.
+ * This function will also call the `const_missing` hook if the
+ * constant is not found.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) from which to retrieve the constant.
+ * sym: The symbol representing the name of the constant.
+ *
+ * Returns:
+ * The value of the constant.
+ *
+ * Raises:
+ * E_TYPE_ERROR: If `mod` is not a class or module.
+ * E_NAME_ERROR: If the constant is not defined and `const_missing` is not defined or also raises an error.
+ */
MRB_API mrb_value
mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
@@ -825,39 +1372,57 @@ mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
mrb_value
mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
{
- struct RClass *c;
- struct RClass *c2;
- mrb_value v;
const struct RProc *proc = mrb->c->ci->proc;
+ struct RClass *c = MRB_PROC_TARGET_CLASS(proc), *c2;
+ mrb_value v;
- c = MRB_PROC_TARGET_CLASS(proc);
if (!c) c = mrb->object_class;
if (iv_get(mrb, class_iv_ptr(c), sym, &v)) {
return v;
}
- c2 = c;
- while (c2 && c2->tt == MRB_TT_SCLASS) {
- mrb_value klass;
-
- if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) {
- c2 = NULL;
- break;
- }
- c2 = mrb_class_ptr(klass);
- }
- if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
- proc = proc->upper;
- while (proc) {
+ for (proc = proc->upper; proc; proc = proc->upper) {
c2 = MRB_PROC_TARGET_CLASS(proc);
if (!c2) c2 = mrb->object_class;
- if (c2 && iv_get(mrb, class_iv_ptr(c2), sym, &v)) {
+ if (iv_get(mrb, class_iv_ptr(c2), sym, &v)) {
+ return v;
+ }
+ }
+ if (c->tt == MRB_TT_SCLASS) {
+ v = const_get_nohook(mrb, c, sym, TRUE);
+ if (!mrb_undef_p(v)) {
return v;
}
- proc = proc->upper;
+
+ mrb_value klass;
+ for (c2 = c; c2 && c2->tt == MRB_TT_SCLASS; c2 = mrb_class_ptr(klass)) {
+ if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) {
+ c2 = NULL;
+ break;
+ }
+ }
+ if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
}
return const_get(mrb, c, sym, TRUE);
}
+/*
+ * Sets a constant in a module or class.
+ *
+ * It first checks if `mod` is a class or module.
+ * If the value `v` being set is a class or module, it calls
+ * `mrb_class_name_class` to set up the class/module name.
+ * Then, it sets the constant using `mrb_obj_iv_set` and calls
+ * the `const_added` hook.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) in which to set the constant.
+ * sym: The symbol representing the name of the constant.
+ * v: The value to set for the constant.
+ *
+ * Raises:
+ * E_TYPE_ERROR: If `mod` is not a class or module.
+ */
MRB_API void
mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
{
@@ -865,19 +1430,28 @@ mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
if (mrb_type(v) == MRB_TT_CLASS || mrb_type(v) == MRB_TT_MODULE) {
mrb_class_name_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(v), sym);
}
- mrb_iv_set(mrb, mod, sym, v);
-}
-
-void
-mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
-{
- struct RClass *c;
+ mrb_obj_iv_set(mrb, mrb_obj_ptr(mod), sym, v);
- c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
- if (!c) c = mrb->object_class;
- mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v);
+ if (!mrb->bootstrapping) {
+ mrb_value name = mrb_symbol_value(sym);
+ mrb_funcall_argv(mrb, mod, MRB_SYM(const_added), 1, &name);
+ }
}
+/*
+ * Removes a constant from a module or class.
+ *
+ * It first checks if `mod` is a class or module, then removes the
+ * constant using `mrb_iv_remove`.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) from which to remove the constant.
+ * sym: The symbol representing the name of the constant.
+ *
+ * Raises:
+ * E_TYPE_ERROR: If `mod` is not a class or module.
+ */
MRB_API void
mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
@@ -885,18 +1459,52 @@ mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym)
mrb_iv_remove(mrb, mod, sym);
}
+/*
+ * Defines a constant in a module or class using a symbol for the name.
+ *
+ * This is a direct way to set a constant without triggering `const_added` hook.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (struct RClass*) in which to define the constant.
+ * name: The symbol representing the name of the constant.
+ * v: The value to set for the constant.
+ */
MRB_API void
mrb_define_const_id(mrb_state *mrb, struct RClass *mod, mrb_sym name, mrb_value v)
{
mrb_obj_iv_set(mrb, (struct RObject*)mod, name, v);
}
+/*
+ * Defines a constant in a module or class using a C string for the name.
+ *
+ * This is a direct way to set a constant without triggering `const_added` hook.
+ * The C string name is interned into a symbol.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (struct RClass*) in which to define the constant.
+ * name: The C string representing the name of the constant.
+ * v: The value to set for the constant.
+ */
MRB_API void
mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v)
{
mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v);
}
+/*
+ * Defines a global constant.
+ *
+ * Global constants are defined in the `Object` class.
+ * This function is a convenience wrapper around `mrb_define_const`.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * name: The C string representing the name of the global constant.
+ * val: The value to set for the global constant.
+ */
MRB_API void
mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val)
{
@@ -906,12 +1514,10 @@ mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val)
static int
const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
- mrb_value ary;
- const char* s;
+ mrb_value ary = *(mrb_value*)p;
mrb_int len;
+ const char* s = mrb_sym_name_len(mrb, sym, &len);
- ary = *(mrb_value*)p;
- s = mrb_sym_name_len(mrb, sym, &len);
if (len >= 1 && ISUPPER(s[0])) {
mrb_int i, alen = RARRAY_LEN(ary);
@@ -926,6 +1532,13 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
return 0;
}
+mrb_value
+mrb_mod_const_at(mrb_state *mrb, struct RClass *c, mrb_value ary)
+{
+ iv_foreach(mrb, class_iv_ptr(c), const_i, &ary);
+ return ary;
+}
+
/* 15.2.2.4.24 */
/*
* call-seq:
@@ -936,14 +1549,13 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_value
mrb_mod_constants(mrb_state *mrb, mrb_value mod)
{
- mrb_value ary;
mrb_bool inherit = TRUE;
struct RClass *c = mrb_class_ptr(mod);
mrb_get_args(mrb, "|b", &inherit);
- ary = mrb_ary_new(mrb);
+ mrb_value ary = mrb_ary_new(mrb);
while (c) {
- iv_foreach(mrb, class_iv_ptr(c), const_i, &ary);
+ mrb_mod_const_at(mrb, c, ary);
if (!inherit) break;
c = c->super;
if (c == mrb->object_class) break;
@@ -951,6 +1563,17 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod)
return ary;
}
+/*
+ * Retrieves a global variable.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * sym: The symbol representing the name of the global variable.
+ *
+ * Returns:
+ * The value of the global variable, or mrb_nil_value() if the
+ * global variable is not defined.
+ */
MRB_API mrb_value
mrb_gv_get(mrb_state *mrb, mrb_sym sym)
{
@@ -961,6 +1584,16 @@ mrb_gv_get(mrb_state *mrb, mrb_sym sym)
return mrb_nil_value();
}
+/*
+ * Sets a global variable.
+ *
+ * If the global variable table (`mrb->globals`) does not exist, it is created.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * sym: The symbol representing the name of the global variable.
+ * v: The value to set for the global variable.
+ */
MRB_API void
mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
@@ -973,6 +1606,13 @@ mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
iv_put(mrb, t, sym, v);
}
+/*
+ * Removes a global variable.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * sym: The symbol representing the name of the global variable to remove.
+ */
MRB_API void
mrb_gv_remove(mrb_state *mrb, mrb_sym sym)
{
@@ -982,9 +1622,7 @@ mrb_gv_remove(mrb_state *mrb, mrb_sym sym)
static int
gv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
- mrb_value ary;
-
- ary = *(mrb_value*)p;
+ mrb_value ary = *(mrb_value*)p;
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
return 0;
}
@@ -1033,18 +1671,59 @@ retry:
return FALSE;
}
+/*
+ * Checks if a constant is defined in a module or class or its ancestors.
+ *
+ * This function calls `const_defined_0` with `recurse = TRUE`, meaning
+ * it will search the superclass chain.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) to check.
+ * id: The symbol representing the name of the constant.
+ *
+ * Returns:
+ * TRUE if the constant is defined, FALSE otherwise.
+ */
MRB_API mrb_bool
mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id)
{
return const_defined_0(mrb, mod, id, TRUE, TRUE);
}
+/*
+ * Checks if a constant is defined directly in a module or class.
+ *
+ * This function calls `const_defined_0` with `recurse = FALSE`, meaning
+ * it will only search the given module/class, not its ancestors.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * mod: The module or class (mrb_value) to check.
+ * id: The symbol representing the name of the constant.
+ *
+ * Returns:
+ * TRUE if the constant is defined directly in the module/class, FALSE otherwise.
+ */
MRB_API mrb_bool
mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id)
{
return const_defined_0(mrb, mod, id, TRUE, FALSE);
}
+/*
+ * Retrieves an attribute (instance variable) from an object.
+ *
+ * This function is a simple wrapper around `mrb_iv_get`.
+ *
+ * Args:
+ * mrb: The mruby state.
+ * obj: The object (mrb_value) from which to retrieve the attribute.
+ * id: The symbol representing the name of the attribute (instance variable).
+ *
+ * Returns:
+ * The value of the attribute, or mrb_nil_value() if not defined.
+ */
MRB_API mrb_value
mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id)
{
@@ -1082,12 +1761,10 @@ find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c)
return arg.sym;
}
-static struct RClass*
-outer_class(mrb_state *mrb, struct RClass *c)
+MRB_API struct RClass*
+mrb_class_outer(mrb_state *mrb, struct RClass *c)
{
- mrb_value ov;
-
- ov = mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__outer__));
+ mrb_value ov = mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__outer__));
if (mrb_nil_p(ov)) return NULL;
switch (mrb_type(ov)) {
case MRB_TT_CLASS:
@@ -1107,10 +1784,10 @@ detect_outer_loop(mrb_state *mrb, struct RClass *c)
for (;;) {
if (h == NULL) return FALSE;
- h = outer_class(mrb, h);
+ h = mrb_class_outer(mrb, h);
if (h == NULL) return FALSE;
- h = outer_class(mrb, h);
- t = outer_class(mrb, t);
+ h = mrb_class_outer(mrb, h);
+ t = mrb_class_outer(mrb, t);
if (t == h) return TRUE;
}
}
@@ -1118,23 +1795,20 @@ detect_outer_loop(mrb_state *mrb, struct RClass *c)
mrb_value
mrb_class_find_path(mrb_state *mrb, struct RClass *c)
{
- struct RClass *outer;
- mrb_value path;
- mrb_sym name;
- const char *str;
- mrb_int len;
-
if (detect_outer_loop(mrb, c)) return mrb_nil_value();
- outer = outer_class(mrb, c);
+ struct RClass *outer = mrb_class_outer(mrb, c);
if (outer == NULL) return mrb_nil_value();
- name = find_class_sym(mrb, outer, c);
+
+ mrb_sym name = find_class_sym(mrb, outer, c);
if (name == 0) return mrb_nil_value();
- path = mrb_str_new_capa(mrb, 40);
- str = mrb_class_name(mrb, outer);
- mrb_str_cat_cstr(mrb, path, str);
+
+ mrb_value path = mrb_str_new_capa(mrb, 40);
+ const char *cname = mrb_class_name(mrb, outer);
+ mrb_str_cat_cstr(mrb, path, cname);
mrb_str_cat_cstr(mrb, path, "::");
- str = mrb_sym_name_len(mrb, name, &len);
+ mrb_int len;
+ const char *str = mrb_sym_name_len(mrb, name, &len);
mrb_str_cat(mrb, path, str, len);
if (RSTRING_PTR(path)[0] != '#') {
iv_del(mrb, c->iv, MRB_SYM(__outer__), NULL);
@@ -1148,7 +1822,14 @@ mrb_class_find_path(mrb_state *mrb, struct RClass *c)
size_t
mrb_obj_iv_tbl_memsize(mrb_value obj)
{
- iv_tbl *t = mrb_obj_ptr(obj)->iv;
+ struct RObject *o = mrb_obj_ptr(obj);
+ if (MRB_OBJ_SHAPED_P(o)) {
+ mrb_shaped_iv *siv = (mrb_shaped_iv*)o->iv;
+ if (!siv) return 0;
+ return offsetof(mrb_shaped_iv, values) +
+ sizeof(mrb_value) * siv->shape->count;
+ }
+ iv_tbl *t = o->iv;
if (t == NULL) return 0;
return sizeof(iv_tbl) + t->alloc*(sizeof(mrb_value)+sizeof(mrb_sym));
}