Files
mruby-mruby/src/variable.c
T
dearblue 5c2b11d215 Avoid using FPU with mruby-os-memsize; ref #5032
And, in the calculation of the instance variable size, the fraction was
always rounded down because of division of integers, so fix it.

At the same time, test items that are no longer passed due to this
change are deleted.
2020-07-24 11:42:37 +09:00

1151 lines
24 KiB
C

/*
** variable.c - mruby variables
**
** See Copyright Notice in mruby.h
*/
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/proc.h>
#include <mruby/string.h>
#include <mruby/variable.h>
#ifndef MRB_IV_SEGMENT_SIZE
#define MRB_IV_SEGMENT_SIZE 4
#endif
typedef struct segment {
mrb_sym key[MRB_IV_SEGMENT_SIZE];
mrb_value val[MRB_IV_SEGMENT_SIZE];
struct segment *next;
} segment;
/* Instance variable table structure */
typedef struct iv_tbl {
segment *rootseg;
size_t size;
size_t last_len;
} iv_tbl;
/* Creates the instance variable table. */
static iv_tbl*
iv_new(mrb_state *mrb)
{
iv_tbl *t;
t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
t->size = 0;
t->rootseg = NULL;
t->last_len = 0;
return t;
}
/* Set the value for the symbol in the instance variable table. */
static void
iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
{
segment *seg;
segment *prev = NULL;
segment *matched_seg = NULL;
size_t matched_idx = 0;
size_t i;
if (t == NULL) return;
seg = t->rootseg;
while (seg) {
for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
mrb_sym key = seg->key[i];
/* Found room in last segment after last_len */
if (!seg->next && i >= t->last_len) {
seg->key[i] = sym;
seg->val[i] = val;
t->last_len = i+1;
t->size++;
return;
}
if (!matched_seg && key == 0) {
matched_seg = seg;
matched_idx = i;
}
else if (key == sym) {
seg->val[i] = val;
return;
}
}
prev = seg;
seg = seg->next;
}
/* Not found */
if (matched_seg) {
matched_seg->key[matched_idx] = sym;
matched_seg->val[matched_idx] = val;
t->size++;
return;
}
seg = (segment*)mrb_malloc(mrb, sizeof(segment));
seg->next = NULL;
seg->key[0] = sym;
seg->val[0] = val;
t->last_len = 1;
t->size++;
if (prev) {
prev->next = seg;
}
else {
t->rootseg = seg;
}
}
/* Get a value for a symbol from the instance variable table. */
static mrb_bool
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
segment *seg;
size_t i;
if (t == NULL) return FALSE;
seg = t->rootseg;
while (seg) {
for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
mrb_sym key = seg->key[i];
if (!seg->next && i >= t->last_len) {
return FALSE;
}
if (key == sym) {
if (vp) *vp = seg->val[i];
return TRUE;
}
}
seg = seg->next;
}
return FALSE;
}
/* Deletes the value for the symbol from the instance variable table. */
static mrb_bool
iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
segment *seg;
size_t i;
if (t == NULL) return FALSE;
seg = t->rootseg;
while (seg) {
for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
mrb_sym key = seg->key[i];
if (!seg->next && i >= t->last_len) {
return FALSE;
}
if (key == sym) {
t->size--;
seg->key[i] = 0;
if (vp) *vp = seg->val[i];
return TRUE;
}
}
seg = seg->next;
}
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)
{
segment *seg;
size_t i;
if (t == NULL) return;
seg = t->rootseg;
while (seg) {
for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
mrb_sym key = seg->key[i];
/* no value in last segment after last_len */
if (!seg->next && i >= t->last_len) {
return;
}
if (key != 0) {
if ((*func)(mrb, key, seg->val[i], p) != 0) {
return;
}
}
}
seg = seg->next;
}
return;
}
/* Get the size of the instance variable table. */
static size_t
iv_size(mrb_state *mrb, iv_tbl *t)
{
segment *seg;
size_t size = 0;
if (t == NULL) return 0;
if (t->size > 0) return t->size;
seg = t->rootseg;
while (seg) {
if (seg->next == NULL) {
size += t->last_len;
return size;
}
seg = seg->next;
size += MRB_IV_SEGMENT_SIZE;
}
/* empty iv_tbl */
return 0;
}
/* Copy the instance variable table. */
static iv_tbl*
iv_copy(mrb_state *mrb, iv_tbl *t)
{
segment *seg;
iv_tbl *t2;
size_t i;
seg = t->rootseg;
t2 = iv_new(mrb);
while (seg != NULL) {
for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
mrb_sym key = seg->key[i];
mrb_value val = seg->val[i];
if ((seg->next == NULL) && (i >= t->last_len)) {
return t2;
}
iv_put(mrb, t2, key, val);
}
seg = seg->next;
}
return t2;
}
/* Free memory of the instance variable table. */
static void
iv_free(mrb_state *mrb, iv_tbl *t)
{
segment *seg;
seg = t->rootseg;
while (seg) {
segment *p = seg;
seg = seg->next;
mrb_free(mrb, p);
}
mrb_free(mrb, t);
}
static int
iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
mrb_gc_mark_value(mrb, v);
return 0;
}
static void
mark_tbl(mrb_state *mrb, iv_tbl *t)
{
iv_foreach(mrb, t, iv_mark_i, 0);
}
void
mrb_gc_mark_gv(mrb_state *mrb)
{
mark_tbl(mrb, mrb->globals);
}
void
mrb_gc_free_gv(mrb_state *mrb)
{
if (mrb->globals)
iv_free(mrb, mrb->globals);
}
void
mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj)
{
mark_tbl(mrb, obj->iv);
}
size_t
mrb_gc_mark_iv_size(mrb_state *mrb, struct RObject *obj)
{
return iv_size(mrb, obj->iv);
}
void
mrb_gc_free_iv(mrb_state *mrb, struct RObject *obj)
{
if (obj->iv) {
iv_free(mrb, obj->iv);
}
}
mrb_value
mrb_vm_special_get(mrb_state *mrb, mrb_sym i)
{
return mrb_fixnum_value(0);
}
void
mrb_vm_special_set(mrb_state *mrb, mrb_sym i, mrb_value v)
{
}
static mrb_bool
obj_iv_p(mrb_value obj)
{
switch (mrb_type(obj)) {
case MRB_TT_OBJECT:
case MRB_TT_CLASS:
case MRB_TT_MODULE:
case MRB_TT_SCLASS:
case MRB_TT_HASH:
case MRB_TT_DATA:
case MRB_TT_EXCEPTION:
return TRUE;
default:
return FALSE;
}
}
MRB_API mrb_value
mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
{
mrb_value v;
if (obj->iv && iv_get(mrb, obj->iv, sym, &v))
return v;
return mrb_nil_value();
}
MRB_API mrb_value
mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
if (obj_iv_p(obj)) {
return mrb_obj_iv_get(mrb, mrb_obj_ptr(obj), sym);
}
return mrb_nil_value();
}
static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
void
mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
assign_class_name(mrb, obj, sym, v);
if (!obj->iv) {
obj->iv = iv_new(mrb);
}
iv_put(mrb, obj->iv, sym, v);
mrb_write_barrier(mrb, (struct RBasic*)obj);
}
MRB_API void
mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
mrb_check_frozen(mrb, obj);
mrb_obj_iv_set_force(mrb, obj, sym, v);
}
/* Iterates over the instance variable table. */
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;
iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p);
}
static inline mrb_bool
namespace_p(enum mrb_vtype tt)
{
return tt == MRB_TT_CLASS || tt == MRB_TT_MODULE ? TRUE : FALSE;
}
static inline void
assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
if (namespace_p(obj->tt) && namespace_p(mrb_type(v))) {
struct RObject *c = mrb_obj_ptr(v);
if (obj != c && ISUPPER(mrb_sym_name_len(mrb, sym, NULL)[0])) {
mrb_sym id_classname = mrb_intern_lit(mrb, "__classname__");
mrb_value o = mrb_obj_iv_get(mrb, c, id_classname);
if (mrb_nil_p(o)) {
mrb_sym id_outer = mrb_intern_lit(mrb, "__outer__");
o = mrb_obj_iv_get(mrb, c, id_outer);
if (mrb_nil_p(o)) {
if ((struct RClass *)obj == mrb->object_class) {
mrb_obj_iv_set_force(mrb, c, id_classname, mrb_symbol_value(sym));
}
else {
mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj));
}
}
}
}
}
}
MRB_API void
mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v)
{
if (obj_iv_p(obj)) {
mrb_obj_iv_set(mrb, mrb_obj_ptr(obj), sym, v);
}
else {
mrb_raise(mrb, E_ARGUMENT_ERROR, "cannot set instance variable");
}
}
MRB_API mrb_bool
mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
{
iv_tbl *t;
t = obj->iv;
if (t) {
return iv_get(mrb, t, sym, NULL);
}
return FALSE;
}
MRB_API mrb_bool
mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
if (!obj_iv_p(obj)) return FALSE;
return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym);
}
MRB_API mrb_bool
mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name)
{
const char *s;
mrb_int 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);
}
MRB_API void
mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
{
if (!mrb_iv_name_sym_p(mrb, iv_name)) {
mrb_name_error(mrb, iv_name, "'%n' is not allowed as an instance variable name", iv_name);
}
}
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;
}
if (s->iv) {
mrb_write_barrier(mrb, (struct RBasic*)d);
d->iv = iv_copy(mrb, s->iv);
}
}
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, " ");
}
else {
mrb_str_cat_lit(mrb, str, ", ");
}
s = mrb_sym_name_len(mrb, sym, &len);
mrb_str_cat(mrb, str, s, len);
mrb_str_cat_lit(mrb, str, "=");
if (mrb_object_p(v)) {
ins = mrb_any_to_s(mrb, v);
}
else {
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));
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));
}
MRB_API mrb_value
mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
if (obj_iv_p(obj)) {
iv_tbl *t = mrb_obj_ptr(obj)->iv;
mrb_value val;
mrb_check_frozen(mrb, mrb_obj_ptr(obj));
if (iv_del(mrb, t, sym, &val)) {
return val;
}
}
return mrb_undef_value();
}
static int
iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
mrb_value ary;
const char* s;
mrb_int 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));
}
return 0;
}
/* 15.3.1.3.23 */
/*
* call-seq:
* obj.instance_variables -> array
*
* Returns an array of instance variable names for the receiver. Note
* that simply defining an accessor does not create the corresponding
* instance variable.
*
* class Fred
* attr_accessor :a1
* def initialize
* @iv = 3
* end
* end
* Fred.new.instance_variables #=> [:@iv]
*/
mrb_value
mrb_obj_instance_variables(mrb_state *mrb, mrb_value self)
{
mrb_value ary;
ary = mrb_ary_new(mrb);
if (obj_iv_p(self)) {
iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary);
}
return ary;
}
static int
cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
mrb_value ary;
const char* s;
mrb_int 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));
}
return 0;
}
/* 15.2.2.4.19 */
/*
* call-seq:
* mod.class_variables(inherit=true) -> array
*
* Returns an array of the names of class variables in <i>mod</i>.
*
* class One
* @@var1 = 1
* end
* class Two < One
* @@var2 = 2
* end
* One.class_variables #=> [:@@var1]
* Two.class_variables #=> [:@@var2]
*/
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);
while (c) {
iv_foreach(mrb, c->iv, cv_i, &ary);
if (!inherit) break;
c = c->super;
}
return ary;
}
mrb_value
mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
{
struct RClass * cls = c;
mrb_value v;
int given = FALSE;
while (c) {
if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
given = TRUE;
}
c = c->super;
}
if (given) return v;
if (cls && cls->tt == MRB_TT_SCLASS) {
mrb_value klass;
klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
mrb_intern_lit(mrb, "__attached__"));
c = mrb_class_ptr(klass);
if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
given = FALSE;
while (c) {
if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
given = TRUE;
}
c = c->super;
}
if (given) return v;
}
}
mrb_name_error(mrb, sym, "uninitialized class variable %n in %C", sym, cls);
/* not reached */
return mrb_nil_value();
}
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);
}
MRB_API void
mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
{
struct RClass * cls = c;
while (c) {
iv_tbl *t = c->iv;
if (iv_get(mrb, t, sym, NULL)) {
mrb_check_frozen(mrb, c);
iv_put(mrb, t, sym, v);
mrb_write_barrier(mrb, (struct RBasic*)c);
return;
}
c = c->super;
}
if (cls && cls->tt == MRB_TT_SCLASS) {
mrb_value klass;
klass = mrb_obj_iv_get(mrb, (struct RObject*)cls,
mrb_intern_lit(mrb, "__attached__"));
switch (mrb_type(klass)) {
case MRB_TT_CLASS:
case MRB_TT_MODULE:
case MRB_TT_SCLASS:
c = mrb_class_ptr(klass);
break;
default:
c = cls;
break;
}
}
else{
c = cls;
}
mrb_check_frozen(mrb, c);
if (!c->iv) {
c->iv = iv_new(mrb);
}
iv_put(mrb, c->iv, sym, v);
mrb_write_barrier(mrb, (struct RBasic*)c);
}
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);
}
mrb_bool
mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
{
while (c) {
iv_tbl *t = c->iv;
if (iv_get(mrb, t, sym, NULL)) return TRUE;
c = c->super;
}
return FALSE;
}
MRB_API mrb_bool
mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
return mrb_mod_cv_defined(mrb, mrb_class_ptr(mod), sym);
}
mrb_value
mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym)
{
struct RClass *c;
struct RProc *p = mrb->c->ci->proc;
for (;;) {
c = MRB_PROC_TARGET_CLASS(p);
if (c->tt != MRB_TT_SCLASS) break;
p = p->upper;
}
return mrb_mod_cv_get(mrb, c, sym);
}
void
mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
struct RClass *c;
struct RProc *p = mrb->c->ci->proc;
for (;;) {
c = MRB_PROC_TARGET_CLASS(p);
if (c->tt != MRB_TT_SCLASS) break;
p = p->upper;
}
mrb_mod_cv_set(mrb, c, sym, v);
}
static void
mod_const_check(mrb_state *mrb, mrb_value mod)
{
switch (mrb_type(mod)) {
case MRB_TT_CLASS:
case MRB_TT_MODULE:
case MRB_TT_SCLASS:
break;
default:
mrb_raise(mrb, E_TYPE_ERROR, "constant look-up for non class/module");
break;
}
}
static mrb_value
const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
{
struct RClass *c = base;
mrb_value v;
mrb_bool retry = FALSE;
mrb_value name;
L_RETRY:
while (c) {
if (c->iv) {
if (iv_get(mrb, c->iv, sym, &v))
return v;
}
c = c->super;
}
if (!retry && base->tt == MRB_TT_MODULE) {
c = mrb->object_class;
retry = TRUE;
goto L_RETRY;
}
name = mrb_symbol_value(sym);
return mrb_funcall_argv(mrb, mrb_obj_value(base), mrb_intern_lit(mrb, "const_missing"), 1, &name);
}
MRB_API mrb_value
mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
mod_const_check(mrb, mod);
return const_get(mrb, mrb_class_ptr(mod), sym);
}
mrb_value
mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
{
struct RClass *c;
struct RClass *c2;
mrb_value v;
struct RProc *proc;
c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
if (iv_get(mrb, c->iv, sym, &v)) {
return v;
}
c2 = c;
while (c2 && c2->tt == MRB_TT_SCLASS) {
mrb_value klass;
if (!iv_get(mrb, c2->iv, mrb_intern_lit(mrb, "__attached__"), &klass)) {
c2 = NULL;
break;
}
c2 = mrb_class_ptr(klass);
}
if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
mrb_assert(!MRB_PROC_CFUNC_P(mrb->c->ci->proc));
proc = mrb->c->ci->proc;
while (proc) {
c2 = MRB_PROC_TARGET_CLASS(proc);
if (c2 && iv_get(mrb, c2->iv, sym, &v)) {
return v;
}
proc = proc->upper;
}
return const_get(mrb, c, sym);
}
MRB_API void
mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
{
mod_const_check(mrb, mod);
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;
c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v);
}
MRB_API void
mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
mod_const_check(mrb, mod);
mrb_iv_remove(mrb, mod, sym);
}
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);
}
MRB_API void
mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val)
{
mrb_define_const(mrb, mrb->object_class, name, val);
}
static int
const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
mrb_value ary;
const char* s;
mrb_int 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);
for (i=0; i<alen; i++) {
if (mrb_symbol(RARRAY_PTR(ary)[i]) == sym)
break;
}
if (i==alen) {
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
}
return 0;
}
/* 15.2.2.4.24 */
/*
* call-seq:
* mod.constants -> array
*
* Returns an array of all names of contants defined in the receiver.
*/
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);
while (c) {
iv_foreach(mrb, c->iv, const_i, &ary);
if (!inherit) break;
c = c->super;
if (c == mrb->object_class) break;
}
return ary;
}
MRB_API mrb_value
mrb_gv_get(mrb_state *mrb, mrb_sym sym)
{
mrb_value v;
if (iv_get(mrb, mrb->globals, sym, &v))
return v;
return mrb_nil_value();
}
MRB_API void
mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
iv_tbl *t;
if (!mrb->globals) {
mrb->globals = iv_new(mrb);
}
t = mrb->globals;
iv_put(mrb, t, sym, v);
}
MRB_API void
mrb_gv_remove(mrb_state *mrb, mrb_sym sym)
{
iv_del(mrb, mrb->globals, sym, NULL);
}
static int
gv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
mrb_value ary;
ary = *(mrb_value*)p;
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
return 0;
}
/* 15.3.1.2.4 */
/* 15.3.1.3.14 */
/*
* call-seq:
* global_variables -> array
*
* Returns an array of the names of global variables.
*
* global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr]
*/
mrb_value
mrb_f_global_variables(mrb_state *mrb, mrb_value self)
{
iv_tbl *t = mrb->globals;
mrb_value ary = mrb_ary_new(mrb);
iv_foreach(mrb, t, gv_i, &ary);
return ary;
}
static mrb_bool
mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse)
{
struct RClass *klass = mrb_class_ptr(mod);
struct RClass *tmp;
mrb_bool mod_retry = FALSE;
tmp = klass;
retry:
while (tmp) {
if (iv_get(mrb, tmp->iv, id, NULL)) {
return TRUE;
}
if (!recurse && (klass != mrb->object_class)) break;
tmp = tmp->super;
}
if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) {
mod_retry = TRUE;
tmp = mrb->object_class;
goto retry;
}
return FALSE;
}
MRB_API mrb_bool
mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id)
{
return mrb_const_defined_0(mrb, mod, id, TRUE, TRUE);
}
MRB_API mrb_bool
mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id)
{
return mrb_const_defined_0(mrb, mod, id, TRUE, FALSE);
}
MRB_API mrb_value
mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id)
{
return mrb_iv_get(mrb, obj, id);
}
struct csym_arg {
struct RClass *c;
mrb_sym sym;
};
static int
csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
struct csym_arg *a = (struct csym_arg*)p;
struct RClass *c = a->c;
if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) {
a->sym = sym;
return 1; /* stop iteration */
}
return 0;
}
static mrb_sym
find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c)
{
struct csym_arg arg;
if (!outer) return 0;
if (outer == c) return 0;
arg.c = c;
arg.sym = 0;
iv_foreach(mrb, outer->iv, csym_i, &arg);
return arg.sym;
}
static struct RClass*
outer_class(mrb_state *mrb, struct RClass *c)
{
mrb_value ov;
ov = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"));
if (mrb_nil_p(ov)) return NULL;
switch (mrb_type(ov)) {
case MRB_TT_CLASS:
case MRB_TT_MODULE:
return mrb_class_ptr(ov);
default:
break;
}
return NULL;
}
static mrb_bool
detect_outer_loop(mrb_state *mrb, struct RClass *c)
{
struct RClass *t = c; /* tortoise */
struct RClass *h = c; /* hare */
for (;;) {
if (h == NULL) return FALSE;
h = outer_class(mrb, h);
if (h == NULL) return FALSE;
h = outer_class(mrb, h);
t = outer_class(mrb, t);
if (t == h) return TRUE;
}
}
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);
if (outer == NULL) return mrb_nil_value();
name = find_class_sym(mrb, outer, c);
if (name == 0) return mrb_nil_value();
str = mrb_class_name(mrb, outer);
path = mrb_str_new_capa(mrb, 40);
mrb_str_cat_cstr(mrb, path, str);
mrb_str_cat_cstr(mrb, path, "::");
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_intern_lit(mrb, "__outer__"), NULL);
iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path);
mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
path = mrb_str_dup(mrb, path);
}
return path;
}
mrb_int
mrb_obj_iv_tbl_memsize(mrb_state* mrb, mrb_value obj)
{
size_t ivsize = iv_size(mrb, mrb_obj_ptr(obj)->iv);
size_t ivsegs = (ivsize + MRB_IV_SEGMENT_SIZE - 1) / MRB_IV_SEGMENT_SIZE;
return sizeof(iv_tbl) + (sizeof(segment) * ivsegs);
}
#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
mrb_bool
mrb_ident_p(const char *s, mrb_int len)
{
mrb_int i;
for (i = 0; i < len; i++) {
if (!identchar(s[i])) return FALSE;
}
return TRUE;
}