mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
6766fcb905
Since mrb_calloc() (along with mrb_calloc) takes two arguments: nmemb which is number of array elements, and size which is size of the array. Of course, revsersing does not change the behavior, but we'd like to respect the original design intention of calloc(3).
3309 lines
88 KiB
C
3309 lines
88 KiB
C
/*
|
|
** class.c - Class class
|
|
**
|
|
** See Copyright Notice in mruby.h
|
|
*/
|
|
|
|
#include <mruby.h>
|
|
#include <mruby/array.h>
|
|
#include <mruby/hash.h>
|
|
#include <mruby/class.h>
|
|
#include <mruby/numeric.h>
|
|
#include <mruby/proc.h>
|
|
#include <mruby/string.h>
|
|
#include <mruby/variable.h>
|
|
#include <mruby/error.h>
|
|
#include <mruby/data.h>
|
|
#include <mruby/istruct.h>
|
|
#include <mruby/opcode.h>
|
|
#include <mruby/internal.h>
|
|
#include <mruby/presym.h>
|
|
|
|
#define METHOD_MID(m) MT_KEY_SYM((m).flags)
|
|
|
|
union mt_ptr {
|
|
const struct RProc *proc;
|
|
mrb_func_t func;
|
|
};
|
|
|
|
#define MT_KEY_SHIFT 4
|
|
#define MT_KEY_MASK ((1<<MT_KEY_SHIFT)-1)
|
|
#define MT_KEY_P(k) (((k)>>MT_KEY_SHIFT) != 0)
|
|
#define MT_FUNC MRB_METHOD_FUNC_FL
|
|
#define MT_NOARG MRB_METHOD_NOARG_FL
|
|
#define MT_PUBLIC MRB_METHOD_PUBLIC_FL
|
|
#define MT_PRIVATE MRB_METHOD_PRIVATE_FL
|
|
#define MT_PROTECTED MRB_METHOD_PROTECTED_FL
|
|
#define MT_VDEFAULT MRB_METHOD_VDEFAULT_FL
|
|
#define MT_VMASK MRB_METHOD_VISIBILITY_MASK
|
|
#define MT_EMPTY 0
|
|
#define MT_DELETED 1
|
|
|
|
#define MT_KEY(sym, flags) ((sym)<<MT_KEY_SHIFT|(flags))
|
|
#define MT_KEY_SYM(k) ((k)>>MT_KEY_SHIFT)
|
|
#define MT_KEY_FLG(k) ((k)&MT_KEY_MASK)
|
|
|
|
/* method table structure */
|
|
typedef struct mt_tbl {
|
|
int size;
|
|
int alloc;
|
|
union mt_ptr *ptr;
|
|
} mt_tbl;
|
|
|
|
#ifdef MRB_USE_INLINE_METHOD_CACHE
|
|
#define MT_INLINE_CACHE_SIZE 256
|
|
static uint8_t mt_cache[MT_INLINE_CACHE_SIZE];
|
|
#endif
|
|
|
|
/* Creates the method table. */
|
|
static mt_tbl*
|
|
mt_new(mrb_state *mrb)
|
|
{
|
|
mt_tbl *t;
|
|
|
|
t = (mt_tbl*)mrb_malloc(mrb, sizeof(mt_tbl));
|
|
t->size = 0;
|
|
t->alloc = 0;
|
|
t->ptr = NULL;
|
|
|
|
return t;
|
|
}
|
|
|
|
static void mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, mrb_sym flags, union mt_ptr ptr);
|
|
|
|
static void
|
|
mt_rehash(mrb_state *mrb, mt_tbl *t)
|
|
{
|
|
int old_alloc = t->alloc;
|
|
int new_alloc = old_alloc > 0 ? old_alloc << 1 : 8;
|
|
union mt_ptr *old_ptr = t->ptr;
|
|
|
|
t->ptr = (union mt_ptr*)mrb_calloc(mrb, new_alloc, sizeof(union mt_ptr)+sizeof(mrb_sym));
|
|
t->alloc = new_alloc;
|
|
t->size = 0;
|
|
if (old_alloc == 0) return;
|
|
|
|
mrb_sym *keys = (mrb_sym*)&old_ptr[old_alloc];
|
|
union mt_ptr *vals = old_ptr;
|
|
for (int i = 0; i < old_alloc; i++) {
|
|
mrb_sym key = keys[i];
|
|
if (MT_KEY_P(key)) {
|
|
mt_put(mrb, t, MT_KEY_SYM(key), MT_KEY_FLG(key), vals[i]);
|
|
}
|
|
}
|
|
mrb_free(mrb, old_ptr);
|
|
}
|
|
|
|
#define slot_empty_p(slot) ((slot)->key == 0 && (slot)->func_p == 0)
|
|
|
|
/* Set the value for the symbol in the method table. */
|
|
static void
|
|
mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, mrb_sym flags, union mt_ptr ptr)
|
|
{
|
|
int pos, start, dpos = -1;
|
|
|
|
if (t->alloc == 0) {
|
|
mt_rehash(mrb, t);
|
|
}
|
|
|
|
mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
|
|
union mt_ptr *vals = t->ptr;
|
|
int hash = mrb_int_hash_func(mrb, sym);
|
|
start = pos = hash & (t->alloc-1);
|
|
for (;;) {
|
|
mrb_sym key = keys[pos];
|
|
if (MT_KEY_SYM(key) == sym) {
|
|
value_set:
|
|
keys[pos] = MT_KEY(sym, flags);
|
|
vals[pos] = ptr;
|
|
return;
|
|
}
|
|
else if (key == MT_EMPTY) {
|
|
t->size++;
|
|
goto value_set;
|
|
}
|
|
else if (key == MT_DELETED && dpos < 0) {
|
|
dpos = pos;
|
|
}
|
|
pos = (pos+1) & (t->alloc-1);
|
|
if (pos == start) { /* not found */
|
|
if (dpos > 0) {
|
|
t->size++;
|
|
pos = dpos;
|
|
goto value_set;
|
|
}
|
|
/* no room */
|
|
mt_rehash(mrb, t);
|
|
start = pos = hash & (t->alloc-1);
|
|
keys = (mrb_sym*)&t->ptr[t->alloc];
|
|
vals = t->ptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get a value for a symbol from the method table. */
|
|
static mrb_sym
|
|
mt_get(mrb_state *mrb, mt_tbl *t, mrb_sym sym, union mt_ptr *pp)
|
|
{
|
|
int pos;
|
|
|
|
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];
|
|
union mt_ptr *vals = t->ptr;
|
|
int hash = mrb_int_hash_func(mrb, sym);
|
|
#ifdef MRB_USE_INLINE_METHOD_CACHE
|
|
int cpos = (hash^(uintptr_t)t) % MT_INLINE_CACHE_SIZE;
|
|
pos = mt_cache[cpos];
|
|
if (pos < t->alloc) {
|
|
mrb_sym key = keys[pos];
|
|
if (key) {
|
|
if (MT_KEY_SYM(key) == sym) {
|
|
*pp = vals[pos];
|
|
return key;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
int start = pos = hash & (t->alloc-1);
|
|
for (;;) {
|
|
mrb_sym key = keys[pos];
|
|
if (MT_KEY_SYM(key) == sym) {
|
|
*pp = vals[pos];
|
|
#ifdef MRB_USE_INLINE_METHOD_CACHE
|
|
if (pos < 0x100) {
|
|
mt_cache[cpos] = pos;
|
|
}
|
|
#endif
|
|
return key;
|
|
}
|
|
else if (key == MT_EMPTY) {
|
|
return 0;
|
|
}
|
|
pos = (pos+1) & (t->alloc-1);
|
|
if (pos == start) { /* not found */
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Deletes the value for the symbol from the method table. */
|
|
static mrb_bool
|
|
mt_del(mrb_state *mrb, mt_tbl *t, mrb_sym sym)
|
|
{
|
|
int pos, start;
|
|
|
|
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];
|
|
int hash = mrb_int_hash_func(mrb, sym);
|
|
start = pos = hash & (t->alloc-1);
|
|
for (;;) {
|
|
mrb_sym key = keys[pos];
|
|
if (MT_KEY_SYM(key) == sym) {
|
|
t->size--;
|
|
keys[pos] = MT_DELETED;
|
|
return TRUE;
|
|
}
|
|
else if (key == MT_EMPTY) {
|
|
return FALSE;
|
|
}
|
|
pos = (pos+1) & (t->alloc-1);
|
|
if (pos == start) { /* not found */
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Copy the method table. */
|
|
static struct mt_tbl*
|
|
mt_copy(mrb_state *mrb, mt_tbl *t)
|
|
{
|
|
mt_tbl *t2;
|
|
|
|
if (t == NULL) return NULL;
|
|
if (t->alloc == 0) return NULL;
|
|
if (t->size == 0) return NULL;
|
|
|
|
t2 = mt_new(mrb);
|
|
mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
|
|
union mt_ptr *vals = t->ptr;
|
|
for (int i=0; i<t->alloc; i++) {
|
|
if (MT_KEY_P(keys[i])) {
|
|
mt_put(mrb, t2, MT_KEY_SYM(keys[i]), MT_KEY_FLG(keys[i]), vals[i]);
|
|
}
|
|
}
|
|
return t2;
|
|
}
|
|
|
|
/* Free memory of the method table. */
|
|
static void
|
|
mt_free(mrb_state *mrb, mt_tbl *t)
|
|
{
|
|
mrb_free(mrb, t->ptr);
|
|
mrb_free(mrb, t);
|
|
}
|
|
|
|
static inline mrb_method_t
|
|
create_method_value(mrb_state *mrb, mrb_sym key, union mt_ptr val)
|
|
{
|
|
mrb_method_t m = { key, { val.proc } };
|
|
return m;
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_mt_foreach(mrb_state *mrb, struct RClass *c, mrb_mt_foreach_func *fn, void *p)
|
|
{
|
|
mt_tbl *t = c->mt;
|
|
|
|
if (t == NULL) return;
|
|
if (t->alloc == 0) return;
|
|
if (t->size == 0) return;
|
|
|
|
mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
|
|
union mt_ptr *vals = t->ptr;
|
|
for (int i=0; i<t->alloc; i++) {
|
|
mrb_sym key = keys[i];
|
|
if (MT_KEY_SYM(key)) {
|
|
if (fn(mrb, MT_KEY_SYM(key), create_method_value(mrb, key, vals[i]), p) != 0)
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
size_t
|
|
mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c)
|
|
{
|
|
mt_tbl *t = c->mt;
|
|
|
|
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];
|
|
union mt_ptr *vals = t->ptr;
|
|
for (int i=0; i<t->alloc; i++) {
|
|
if (MT_KEY_P(keys[i]) && (keys[i] & MT_FUNC) == 0) { /* Proc pointer */
|
|
const struct RProc *p = vals[i].proc;
|
|
mrb_gc_mark(mrb, (struct RBasic*)p);
|
|
}
|
|
}
|
|
if (!t) return 0;
|
|
return (size_t)t->size;
|
|
}
|
|
|
|
size_t
|
|
mrb_class_mt_memsize(mrb_state *mrb, struct RClass *c)
|
|
{
|
|
struct mt_tbl *h = c->mt;
|
|
|
|
if (!h) return 0;
|
|
return sizeof(struct mt_tbl) + (size_t)h->size * sizeof(mrb_method_t);
|
|
}
|
|
|
|
void
|
|
mrb_gc_free_mt(mrb_state *mrb, struct RClass *c)
|
|
{
|
|
if (c->mt) mt_free(mrb, c->mt);
|
|
}
|
|
|
|
void
|
|
mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id)
|
|
{
|
|
mrb_value name;
|
|
mrb_sym nsym = MRB_SYM(__classname__);
|
|
|
|
if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return;
|
|
if (outer == NULL || outer == mrb->object_class) {
|
|
name = mrb_symbol_value(id);
|
|
}
|
|
else {
|
|
name = mrb_class_path(mrb, outer);
|
|
if (mrb_nil_p(name)) { /* unnamed outer class */
|
|
if (outer != mrb->object_class && outer != c) {
|
|
mrb_obj_iv_set_force(mrb, (struct RObject*)c, MRB_SYM(__outer__),
|
|
mrb_obj_value(outer));
|
|
}
|
|
return;
|
|
}
|
|
else {
|
|
mrb_int len;
|
|
const char *n = mrb_sym_name_len(mrb, id, &len);
|
|
|
|
mrb_str_cat_lit(mrb, name, "::");
|
|
mrb_str_cat(mrb, name, n, len);
|
|
}
|
|
}
|
|
mrb_obj_iv_set_force(mrb, (struct RObject*)c, nsym, name);
|
|
}
|
|
|
|
mrb_bool
|
|
mrb_const_name_p(mrb_state *mrb, const char *name, mrb_int len)
|
|
{
|
|
return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1);
|
|
}
|
|
|
|
static void
|
|
setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id)
|
|
{
|
|
mrb_const_set(mrb, mrb_obj_value(outer), id, mrb_obj_value(c));
|
|
}
|
|
|
|
#define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c))
|
|
|
|
static void
|
|
prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
|
|
{
|
|
struct RClass *c;
|
|
|
|
mrb_assert(o->c);
|
|
if (o->c->tt == MRB_TT_SCLASS) return;
|
|
struct RClass *sc = MRB_OBJ_ALLOC(mrb, MRB_TT_SCLASS, mrb->class_class);
|
|
sc->flags |= MRB_FL_CLASS_IS_INHERITED;
|
|
sc->mt = NULL;
|
|
sc->iv = NULL;
|
|
if (o->tt == MRB_TT_CLASS) {
|
|
c = (struct RClass*)o;
|
|
if (!c->super) {
|
|
sc->super = mrb->class_class;
|
|
}
|
|
else {
|
|
sc->super = c->super->c;
|
|
}
|
|
}
|
|
else if (o->tt == MRB_TT_SCLASS) {
|
|
c = (struct RClass*)o;
|
|
while (c->super->tt == MRB_TT_ICLASS)
|
|
c = c->super;
|
|
make_metaclass(mrb, c->super);
|
|
sc->super = c->super->c;
|
|
}
|
|
else {
|
|
sc->super = o->c;
|
|
prepare_singleton_class(mrb, (struct RBasic*)sc);
|
|
}
|
|
o->c = sc;
|
|
mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc);
|
|
mrb_obj_iv_set(mrb, (struct RObject*)sc, MRB_SYM(__attached__), mrb_obj_value(o));
|
|
sc->frozen = o->frozen;
|
|
}
|
|
|
|
static mrb_value
|
|
class_name_str(mrb_state *mrb, struct RClass* c)
|
|
{
|
|
mrb_value path = mrb_class_path(mrb, c);
|
|
if (mrb_nil_p(path)) {
|
|
path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#<Module:") :
|
|
mrb_str_new_lit(mrb, "#<Class:");
|
|
mrb_str_cat_str(mrb, path, mrb_ptr_to_str(mrb, c));
|
|
mrb_str_cat_lit(mrb, path, ">");
|
|
}
|
|
return path;
|
|
}
|
|
|
|
static struct RClass*
|
|
class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
|
|
{
|
|
mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
|
|
|
|
mrb_check_type(mrb, c, MRB_TT_CLASS);
|
|
return mrb_class_ptr(c);
|
|
}
|
|
|
|
static struct RClass*
|
|
module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
|
|
{
|
|
mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
|
|
|
|
mrb_check_type(mrb, c, MRB_TT_MODULE);
|
|
return mrb_class_ptr(c);
|
|
}
|
|
|
|
static mrb_bool
|
|
class_ptr_p(mrb_value obj)
|
|
{
|
|
switch (mrb_type(obj)) {
|
|
case MRB_TT_CLASS:
|
|
case MRB_TT_SCLASS:
|
|
case MRB_TT_MODULE:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_if_class_or_module(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
if (!class_ptr_p(obj)) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class/module", obj);
|
|
}
|
|
}
|
|
|
|
static struct RClass*
|
|
define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer)
|
|
{
|
|
if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) {
|
|
return module_from_sym(mrb, outer, name);
|
|
}
|
|
struct RClass *m = mrb_module_new(mrb);
|
|
setup_class(mrb, outer, m, name);
|
|
|
|
return m;
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_define_module_id(mrb_state *mrb, mrb_sym name)
|
|
{
|
|
return define_module(mrb, name, mrb->object_class);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_define_module(mrb_state *mrb, const char *name)
|
|
{
|
|
return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class);
|
|
}
|
|
|
|
struct RClass*
|
|
mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
|
|
{
|
|
check_if_class_or_module(mrb, outer);
|
|
if (mrb_const_defined_at(mrb, outer, id)) {
|
|
mrb_value old = mrb_const_get(mrb, outer, id);
|
|
|
|
if (!mrb_module_p(old)) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a module", old);
|
|
}
|
|
return mrb_class_ptr(old);
|
|
}
|
|
return define_module(mrb, id, mrb_class_ptr(outer));
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_define_module_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
|
|
{
|
|
struct RClass * c = define_module(mrb, name, outer);
|
|
|
|
setup_class(mrb, outer, c, name);
|
|
return c;
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name)
|
|
{
|
|
mrb_sym id = mrb_intern_cstr(mrb, name);
|
|
struct RClass * c = define_module(mrb, id, outer);
|
|
|
|
setup_class(mrb, outer, c, id);
|
|
return c;
|
|
}
|
|
|
|
static struct RClass*
|
|
find_origin(struct RClass *c)
|
|
{
|
|
MRB_CLASS_ORIGIN(c);
|
|
return c;
|
|
}
|
|
|
|
static struct RClass*
|
|
define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer)
|
|
{
|
|
struct RClass * c;
|
|
|
|
if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) {
|
|
c = class_from_sym(mrb, outer, name);
|
|
MRB_CLASS_ORIGIN(c);
|
|
if (super && mrb_class_real(c->super) != super) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %n (%C not %C)",
|
|
name, c->super, super);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
c = mrb_class_new(mrb, super);
|
|
setup_class(mrb, outer, c, name);
|
|
|
|
return c;
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super)
|
|
{
|
|
if (!super) {
|
|
mrb_warn(mrb, "no super class for '%n', Object assumed", name);
|
|
}
|
|
return define_class(mrb, name, super, mrb->object_class);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
|
|
{
|
|
return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super);
|
|
}
|
|
|
|
static mrb_value mrb_do_nothing(mrb_state *mrb, mrb_value);
|
|
#ifndef MRB_NO_METHOD_CACHE
|
|
static void mc_clear(mrb_state *mrb);
|
|
static void mc_clear_by_id(mrb_state *mrb, mrb_sym mid);
|
|
#else
|
|
#define mc_clear(mrb)
|
|
#define mc_clear_by_id(mrb,mid)
|
|
#endif
|
|
|
|
static void
|
|
mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
|
|
{
|
|
|
|
if (!super)
|
|
super = mrb->object_class;
|
|
super->flags |= MRB_FL_CLASS_IS_INHERITED;
|
|
|
|
mrb_value s = mrb_obj_value(super);
|
|
mrb_sym mid = MRB_SYM(inherited);
|
|
|
|
if (!mrb_func_basic_p(mrb, s, mid, mrb_do_nothing)) {
|
|
mrb_value c = mrb_obj_value(klass);
|
|
mrb_funcall_argv(mrb, s, mid, 1, &c);
|
|
}
|
|
}
|
|
|
|
struct RClass*
|
|
mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id)
|
|
{
|
|
struct RClass *s;
|
|
struct RClass *c;
|
|
|
|
if (!mrb_nil_p(super)) {
|
|
if (!mrb_class_p(super)) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%!v given)", super);
|
|
}
|
|
s = mrb_class_ptr(super);
|
|
}
|
|
else {
|
|
s = NULL;
|
|
}
|
|
check_if_class_or_module(mrb, outer);
|
|
if (mrb_const_defined_at(mrb, outer, id)) {
|
|
mrb_value old = mrb_const_get(mrb, outer, id);
|
|
|
|
if (!mrb_class_p(old)) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class", old);
|
|
}
|
|
c = mrb_class_ptr(old);
|
|
if (s) {
|
|
/* check super class */
|
|
if (mrb_class_real(c->super) != s) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for %v", old);
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
c = define_class(mrb, id, s, mrb_class_ptr(outer));
|
|
mrb_class_inherited(mrb, mrb_class_real(c->super), c);
|
|
|
|
return c;
|
|
}
|
|
|
|
MRB_API mrb_bool
|
|
mrb_class_defined(mrb_state *mrb, const char *name)
|
|
{
|
|
mrb_sym sym = mrb_intern_check_cstr(mrb, name);
|
|
if (!sym) return FALSE;
|
|
return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), sym);
|
|
}
|
|
|
|
MRB_API mrb_bool
|
|
mrb_class_defined_id(mrb_state *mrb, mrb_sym name)
|
|
{
|
|
return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), name);
|
|
}
|
|
|
|
MRB_API mrb_bool
|
|
mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name)
|
|
{
|
|
mrb_sym sym = mrb_intern_check_cstr(mrb, name);
|
|
if (!sym) return FALSE;
|
|
return mrb_const_defined_at(mrb, mrb_obj_value(outer), sym);
|
|
}
|
|
|
|
MRB_API mrb_bool
|
|
mrb_class_defined_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
|
|
{
|
|
return mrb_const_defined_at(mrb, mrb_obj_value(outer), name);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
|
|
{
|
|
return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_class_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
|
|
{
|
|
return class_from_sym(mrb, outer, name);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_class_get(mrb_state *mrb, const char *name)
|
|
{
|
|
return mrb_class_get_under(mrb, mrb->object_class, name);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_class_get_id(mrb_state *mrb, mrb_sym name)
|
|
{
|
|
return mrb_class_get_under_id(mrb, mrb->object_class, name);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_exc_get_id(mrb_state *mrb, mrb_sym name)
|
|
{
|
|
mrb_value c = mrb_exc_const_get(mrb, name);
|
|
|
|
if (!mrb_class_p(c)) {
|
|
mrb_raise(mrb, E_EXCEPTION, "exception corrupted");
|
|
}
|
|
|
|
struct RClass *exc = mrb_class_ptr(c);
|
|
for (struct RClass *e = exc; e; e = e->super) {
|
|
if (e == E_EXCEPTION)
|
|
return exc;
|
|
}
|
|
mrb_raise(mrb, E_EXCEPTION, "non-exception raised");
|
|
/* not reached */
|
|
return NULL;
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
|
|
{
|
|
return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_module_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
|
|
{
|
|
return module_from_sym(mrb, outer, name);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_module_get(mrb_state *mrb, const char *name)
|
|
{
|
|
return mrb_module_get_under(mrb, mrb->object_class, name);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_module_get_id(mrb_state *mrb, mrb_sym name)
|
|
{
|
|
return mrb_module_get_under_id(mrb, mrb->object_class, name);
|
|
}
|
|
|
|
/*!
|
|
* Defines a class under the namespace of \a outer.
|
|
* \param outer a class which contains the new class.
|
|
* \param name name of the new class
|
|
* \param super a class from which the new class will derive.
|
|
* NULL means \c Object class.
|
|
* \return the created class
|
|
* \throw TypeError if the constant name \a name is already taken but
|
|
* the constant is not a \c Class.
|
|
* \throw NameError if the class is already defined but the class can not
|
|
* be reopened because its superclass is not \a super.
|
|
* \post top-level constant named \a name refers the returned class.
|
|
*
|
|
* \note if a class named \a name is already defined and its superclass is
|
|
* \a super, the function just returns the defined class.
|
|
*/
|
|
MRB_API struct RClass*
|
|
mrb_define_class_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name, struct RClass *super)
|
|
{
|
|
struct RClass * c;
|
|
|
|
#if 0
|
|
if (!super) {
|
|
mrb_warn(mrb, "no super class for '%C::%n', Object assumed", outer, id);
|
|
}
|
|
#endif
|
|
c = define_class(mrb, name, super, outer);
|
|
setup_class(mrb, outer, c, name);
|
|
return c;
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super)
|
|
{
|
|
return mrb_define_class_under_id(mrb, outer, mrb_intern_cstr(mrb, name), super);
|
|
}
|
|
|
|
static mrb_bool
|
|
check_visibility_break(const struct RProc *p, const struct RClass *c, mrb_callinfo *ci, struct REnv *env)
|
|
{
|
|
if (!p || p->upper == NULL || MRB_PROC_SCOPE_P(p) || p->e.env == NULL || !MRB_PROC_ENV_P(p)) {
|
|
return TRUE;
|
|
}
|
|
if (env) {
|
|
return p->e.env->c != c || MRB_ENV_VISIBILITY_BREAK_P(env);
|
|
}
|
|
return mrb_vm_ci_target_class(ci) != c || MRB_CI_VISIBILITY_BREAK_P(ci);
|
|
}
|
|
|
|
static void
|
|
find_visibility_scope(mrb_state *mrb, const struct RClass *c, int n, mrb_callinfo **cp, struct REnv **ep)
|
|
{
|
|
const struct mrb_context *ec = mrb->c;
|
|
mrb_callinfo *ci = ec->ci - n;
|
|
const struct RProc *p = ci->proc;
|
|
|
|
if (c == NULL) c = mrb_vm_ci_target_class(ci);
|
|
|
|
if (check_visibility_break(p, c, ci, NULL)) {
|
|
mrb_assert(ci->u.env);
|
|
*ep = NULL;
|
|
*cp = ci;
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
struct REnv *env = p->e.env;
|
|
p = p->upper;
|
|
if (check_visibility_break(p, c, ci, env)) {
|
|
*ep = env;
|
|
*cp = NULL;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_t m)
|
|
{
|
|
union mt_ptr ptr;
|
|
|
|
MRB_CLASS_ORIGIN(c);
|
|
|
|
mt_tbl *h = c->mt;
|
|
if (c->tt == MRB_TT_SCLASS && mrb_frozen_p(c)) {
|
|
mrb_value v = mrb_iv_get(mrb, mrb_obj_value(c), MRB_SYM(__attached__));
|
|
mrb_check_frozen_value(mrb, v);
|
|
}
|
|
else {
|
|
mrb_check_frozen(mrb, c);
|
|
}
|
|
if (!h) h = c->mt = mt_new(mrb);
|
|
if (MRB_METHOD_PROC_P(m)) {
|
|
struct RProc *p = (struct RProc*)MRB_METHOD_PROC(m);
|
|
|
|
ptr.proc = p;
|
|
if (p) {
|
|
if (p->gc_color != MRB_GC_RED) {
|
|
p->flags |= MRB_PROC_SCOPE;
|
|
p->c = NULL;
|
|
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)p);
|
|
if (!MRB_PROC_ENV_P(p)) {
|
|
MRB_PROC_SET_TARGET_CLASS(p, c);
|
|
}
|
|
}
|
|
else {
|
|
mrb_assert(mrb_frozen_p(p) && MRB_PROC_SCOPE_P(p));
|
|
mrb_assert(p->c == NULL && p->upper == NULL && p->e.target_class == NULL);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ptr.func = MRB_METHOD_FUNC(m);
|
|
}
|
|
|
|
int flags = MT_KEY_FLG(m.flags);
|
|
if (mid == MRB_SYM(initialize)) {
|
|
MRB_SET_VISIBILITY_FLAGS(flags, MT_PRIVATE);
|
|
}
|
|
else if ((flags & MT_VMASK) == MT_VDEFAULT) {
|
|
mrb_callinfo *ci;
|
|
struct REnv *e;
|
|
find_visibility_scope(mrb, c, 0, &ci, &e);
|
|
mrb_assert(ci || e);
|
|
MRB_SET_VISIBILITY_FLAGS(flags, (e ? MRB_ENV_VISIBILITY(e) : MRB_CI_VISIBILITY(ci)));
|
|
}
|
|
mt_put(mrb, h, mid, flags, ptr);
|
|
mc_clear_by_id(mrb, mid);
|
|
}
|
|
|
|
static void
|
|
define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec, int vis)
|
|
{
|
|
mrb_method_t m;
|
|
int ai = mrb_gc_arena_save(mrb);
|
|
|
|
MRB_METHOD_FROM_FUNC(m, func);
|
|
if (aspec == MRB_ARGS_NONE()) {
|
|
MRB_METHOD_NOARG_SET(m);
|
|
}
|
|
MRB_METHOD_SET_VISIBILITY(m, vis);
|
|
mrb_define_method_raw(mrb, c, mid, m);
|
|
mrb_gc_arena_restore(mrb, ai);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
define_method_id(mrb, c, mid, func, aspec, MT_PUBLIC);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_private_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
define_method_id(mrb, c, mid, func, aspec, MT_PRIVATE);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_private_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
mrb_define_private_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
|
|
}
|
|
|
|
/* a function to raise NotImplementedError with current method name */
|
|
MRB_API void
|
|
mrb_notimplement(mrb_state *mrb)
|
|
{
|
|
mrb_callinfo *ci = mrb->c->ci;
|
|
|
|
if (ci->mid) {
|
|
mrb_raisef(mrb, E_NOTIMP_ERROR, "%n() function is unimplemented on this machine", ci->mid);
|
|
}
|
|
}
|
|
|
|
/* a function to be replacement of unimplemented method */
|
|
MRB_API mrb_value
|
|
mrb_notimplement_m(mrb_state *mrb, mrb_value self)
|
|
{
|
|
mrb_notimplement(mrb);
|
|
/* not reached */
|
|
return mrb_nil_value();
|
|
}
|
|
|
|
static void
|
|
ensure_class_type(mrb_state *mrb, mrb_value val)
|
|
{
|
|
if (!class_ptr_p(val)) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", val);
|
|
}
|
|
}
|
|
|
|
#define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss)
|
|
|
|
MRB_API mrb_int
|
|
mrb_get_argc(mrb_state *mrb)
|
|
{
|
|
mrb_int argc = mrb->c->ci->n;
|
|
|
|
if (argc == 15) {
|
|
struct RArray *a = mrb_ary_ptr(mrb->c->ci->stack[1]);
|
|
|
|
a->c = NULL; /* hide from ObjectSpace.each_object */
|
|
argc = ARY_LEN(a);
|
|
}
|
|
return argc;
|
|
}
|
|
|
|
MRB_API const mrb_value*
|
|
mrb_get_argv(mrb_state *mrb)
|
|
{
|
|
mrb_int argc = mrb->c->ci->n;
|
|
mrb_value *array_argv = mrb->c->ci->stack + 1;
|
|
if (argc == 15) {
|
|
struct RArray *a = mrb_ary_ptr(*array_argv);
|
|
|
|
a->c = NULL; /* hide from ObjectSpace.each_object */
|
|
array_argv = ARY_PTR(a);
|
|
}
|
|
return array_argv;
|
|
}
|
|
|
|
MRB_API mrb_value
|
|
mrb_get_arg1(mrb_state *mrb)
|
|
{
|
|
mrb_callinfo *ci = mrb->c->ci;
|
|
mrb_int argc = ci->n;
|
|
mrb_value *array_argv = ci->stack + 1;
|
|
if (argc == 15) {
|
|
struct RArray *a = mrb_ary_ptr(*array_argv);
|
|
|
|
argc = ARY_LEN(a);
|
|
array_argv = ARY_PTR(a);
|
|
}
|
|
if (argc == 0 && ci->nk == 15) {
|
|
mrb_int n = ci->n;
|
|
if (n == 15) n = 1;
|
|
return ci->stack[n+1]; /* kwhash next to positional arguments */
|
|
}
|
|
if (argc != 1) {
|
|
mrb_argnum_error(mrb, argc, 1, 1);
|
|
}
|
|
return array_argv[0];
|
|
}
|
|
|
|
MRB_API mrb_bool
|
|
mrb_block_given_p(mrb_state *mrb)
|
|
{
|
|
mrb_callinfo *ci = mrb->c->ci;
|
|
mrb_value b = ci->stack[mrb_ci_bidx(ci)];
|
|
|
|
return !mrb_nil_p(b);
|
|
}
|
|
|
|
#define GET_ARG(_type) (ptr ? ((_type)(*ptr++)) : va_arg((*ap), _type))
|
|
|
|
static mrb_int
|
|
get_args_v(mrb_state *mrb, mrb_args_format format, void** ptr, va_list *ap)
|
|
{
|
|
const char *fmt = format;
|
|
char c;
|
|
mrb_int i = 0;
|
|
mrb_callinfo *ci = mrb->c->ci;
|
|
mrb_int argc = ci->n;
|
|
const mrb_value *argv = ci->stack+1;
|
|
mrb_bool argv_on_stack;
|
|
mrb_bool opt = FALSE;
|
|
mrb_bool opt_skip = TRUE;
|
|
const mrb_value *pickarg = NULL; /* arguments currently being processed */
|
|
mrb_value kdict = mrb_nil_value();
|
|
mrb_bool reqkarg = FALSE;
|
|
int argc_min = 0, argc_max = 0;
|
|
|
|
while ((c = *fmt++)) {
|
|
switch (c) {
|
|
case '|':
|
|
opt = TRUE;
|
|
break;
|
|
case '*':
|
|
opt_skip = FALSE;
|
|
argc_max = -1;
|
|
if (!reqkarg) reqkarg = strchr(fmt, ':') ? TRUE : FALSE;
|
|
goto check_exit;
|
|
case '!':
|
|
case '+':
|
|
break;
|
|
case ':':
|
|
reqkarg = TRUE;
|
|
/* fall through */
|
|
case '&': case '?':
|
|
if (opt) opt_skip = FALSE;
|
|
break;
|
|
default:
|
|
if (!opt) argc_min++;
|
|
argc_max++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
check_exit:
|
|
if (!reqkarg && ci->nk > 0) {
|
|
mrb_assert(ci->nk == 15);
|
|
kdict = ci->stack[mrb_ci_bidx(ci)-1];
|
|
if (mrb_hash_p(kdict) && mrb_hash_size(mrb, kdict) > 0) {
|
|
if (argc < 14) {
|
|
ci->n++;
|
|
argc++; /* include kdict in normal arguments */
|
|
}
|
|
else {
|
|
/* 14+1 == 15 so pack first */
|
|
if (argc == 14) {
|
|
/* pack arguments and kdict */
|
|
ci->stack[1] = mrb_ary_new_from_values(mrb, argc+1, &ci->stack[1]);
|
|
argc = ci->n = 15;
|
|
}
|
|
else {
|
|
/* push kdict to packed arguments */
|
|
mrb_ary_push(mrb, ci->stack[1], kdict);
|
|
}
|
|
ci->stack[2] = ci->stack[mrb_ci_bidx(ci)];
|
|
}
|
|
ci->nk = 0;
|
|
}
|
|
}
|
|
if (reqkarg && ci->nk > 0) {
|
|
kdict = ci->stack[mrb_ci_bidx(ci)-1];
|
|
mrb_assert(ci->nk == 15);
|
|
mrb_assert(mrb_hash_p(kdict));
|
|
}
|
|
|
|
argv_on_stack = argc < 15;
|
|
if (!argv_on_stack) {
|
|
struct RArray *a = mrb_ary_ptr(*argv);
|
|
argv = ARY_PTR(a);
|
|
argc = ARY_LEN(a);
|
|
a->c = NULL; /* hide from ObjectSpace.each_object */
|
|
}
|
|
|
|
opt = FALSE;
|
|
i = 0;
|
|
while ((c = *format++)) {
|
|
mrb_bool altmode = FALSE;
|
|
mrb_bool needmodify = FALSE;
|
|
|
|
for (; *format; format++) {
|
|
switch (*format) {
|
|
case '!':
|
|
if (altmode) goto modifier_exit; /* not accept for multiple '!' */
|
|
altmode = TRUE;
|
|
break;
|
|
case '+':
|
|
if (needmodify) goto modifier_exit; /* not accept for multiple '+' */
|
|
needmodify = TRUE;
|
|
break;
|
|
default:
|
|
goto modifier_exit;
|
|
}
|
|
}
|
|
|
|
modifier_exit:
|
|
switch (c) {
|
|
case '|': case '*': case '&': case '?': case ':':
|
|
if (needmodify) {
|
|
bad_needmodify:
|
|
mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong `%c+` modified specifier`", c);
|
|
}
|
|
break;
|
|
default:
|
|
if (i < argc) {
|
|
pickarg = &argv[i++];
|
|
if (needmodify && !mrb_nil_p(*pickarg)) {
|
|
mrb_check_frozen_value(mrb, *pickarg);
|
|
}
|
|
}
|
|
else {
|
|
if (opt) {
|
|
pickarg = NULL;
|
|
}
|
|
else {
|
|
mrb_argnum_error(mrb, argc, argc_min, argc_max);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (c) {
|
|
case 'o':
|
|
case 'C':
|
|
case 'S':
|
|
case 'A':
|
|
case 'H':
|
|
{
|
|
mrb_value *p;
|
|
|
|
p = GET_ARG(mrb_value*);
|
|
if (pickarg) {
|
|
if (!(altmode && mrb_nil_p(*pickarg))) {
|
|
switch (c) {
|
|
case 'C': ensure_class_type(mrb, *pickarg); break;
|
|
case 'S': mrb_ensure_string_type(mrb, *pickarg); break;
|
|
case 'A': mrb_ensure_array_type(mrb, *pickarg); break;
|
|
case 'H': mrb_ensure_hash_type(mrb, *pickarg); break;
|
|
}
|
|
}
|
|
*p = *pickarg;
|
|
}
|
|
}
|
|
break;
|
|
case 'c':
|
|
{
|
|
struct RClass **p;
|
|
|
|
p = GET_ARG(struct RClass**);
|
|
if (pickarg) {
|
|
if (altmode && mrb_nil_p(*pickarg)) {
|
|
*p = NULL;
|
|
}
|
|
else {
|
|
ensure_class_type(mrb, *pickarg);
|
|
*p = mrb_class_ptr(*pickarg);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 's':
|
|
{
|
|
const char **ps = NULL;
|
|
mrb_int *pl = NULL;
|
|
|
|
ps = GET_ARG(const char**);
|
|
pl = GET_ARG(mrb_int*);
|
|
if (needmodify) goto bad_needmodify;
|
|
if (pickarg) {
|
|
if (altmode && mrb_nil_p(*pickarg)) {
|
|
*ps = NULL;
|
|
*pl = 0;
|
|
}
|
|
else {
|
|
mrb_ensure_string_type(mrb, *pickarg);
|
|
*ps = RSTRING_PTR(*pickarg);
|
|
*pl = RSTRING_LEN(*pickarg);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'z':
|
|
{
|
|
const char **ps;
|
|
|
|
ps = GET_ARG(const char**);
|
|
if (needmodify) goto bad_needmodify;
|
|
if (pickarg) {
|
|
if (altmode && mrb_nil_p(*pickarg)) {
|
|
*ps = NULL;
|
|
}
|
|
else {
|
|
mrb_ensure_string_type(mrb, *pickarg);
|
|
*ps = RSTRING_CSTR(mrb, *pickarg);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'a':
|
|
{
|
|
struct RArray *a;
|
|
const mrb_value **pb;
|
|
mrb_int *pl;
|
|
|
|
pb = GET_ARG(const mrb_value**);
|
|
pl = GET_ARG(mrb_int*);
|
|
if (needmodify) goto bad_needmodify;
|
|
if (pickarg) {
|
|
if (altmode && mrb_nil_p(*pickarg)) {
|
|
*pb = NULL;
|
|
*pl = 0;
|
|
}
|
|
else {
|
|
mrb_ensure_array_type(mrb, *pickarg);
|
|
a = mrb_ary_ptr(*pickarg);
|
|
*pb = ARY_PTR(a);
|
|
*pl = ARY_LEN(a);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#ifndef MRB_NO_FLOAT
|
|
case 'f':
|
|
{
|
|
mrb_float *p;
|
|
|
|
p = GET_ARG(mrb_float*);
|
|
if (pickarg) {
|
|
*p = mrb_as_float(mrb, *pickarg);
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case 'i':
|
|
{
|
|
mrb_int *p;
|
|
|
|
p = GET_ARG(mrb_int*);
|
|
if (pickarg) {
|
|
*p = mrb_as_int(mrb, *pickarg);
|
|
}
|
|
}
|
|
break;
|
|
case 'b':
|
|
{
|
|
mrb_bool *boolp = GET_ARG(mrb_bool*);
|
|
|
|
if (pickarg) {
|
|
*boolp = mrb_test(*pickarg);
|
|
}
|
|
}
|
|
break;
|
|
case 'n':
|
|
{
|
|
mrb_sym *symp;
|
|
|
|
symp = GET_ARG(mrb_sym*);
|
|
if (pickarg) {
|
|
*symp = to_sym(mrb, *pickarg);
|
|
}
|
|
}
|
|
break;
|
|
case 'd':
|
|
{
|
|
void** datap;
|
|
struct mrb_data_type const* type;
|
|
|
|
datap = GET_ARG(void**);
|
|
type = GET_ARG(struct mrb_data_type const*);
|
|
if (pickarg) {
|
|
if (altmode && mrb_nil_p(*pickarg)) {
|
|
*datap = NULL;
|
|
}
|
|
else {
|
|
*datap = mrb_data_get_ptr(mrb, *pickarg, type);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '&':
|
|
{
|
|
mrb_value *p, *bp;
|
|
|
|
p = GET_ARG(mrb_value*);
|
|
bp = ci->stack + mrb_ci_bidx(ci);
|
|
if (altmode && mrb_nil_p(*bp)) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
|
|
}
|
|
*p = *bp;
|
|
}
|
|
break;
|
|
case '|':
|
|
if (opt_skip && i == argc) goto finish;
|
|
opt = TRUE;
|
|
break;
|
|
case '?':
|
|
{
|
|
mrb_bool *p;
|
|
|
|
p = GET_ARG(mrb_bool*);
|
|
*p = pickarg ? TRUE : FALSE;
|
|
}
|
|
break;
|
|
|
|
case '*':
|
|
{
|
|
const mrb_value **var;
|
|
mrb_int *pl;
|
|
mrb_bool nocopy = (altmode || !argv_on_stack) ? TRUE : FALSE;
|
|
|
|
var = GET_ARG(const mrb_value**);
|
|
pl = GET_ARG(mrb_int*);
|
|
if (argc > i) {
|
|
*pl = argc-i;
|
|
if (*pl > 0) {
|
|
if (nocopy) {
|
|
*var = argv+i;
|
|
}
|
|
else {
|
|
mrb_value args = mrb_ary_new_from_values(mrb, *pl, argv+i);
|
|
RARRAY(args)->c = NULL;
|
|
*var = RARRAY_PTR(args);
|
|
}
|
|
}
|
|
i = argc;
|
|
}
|
|
else {
|
|
*pl = 0;
|
|
*var = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ':':
|
|
{
|
|
mrb_value ksrc = mrb_hash_p(kdict) ? mrb_hash_dup(mrb, kdict) : mrb_hash_new(mrb);
|
|
const mrb_kwargs *kwargs = GET_ARG(const mrb_kwargs*);
|
|
mrb_value *rest;
|
|
|
|
if (kwargs == NULL) {
|
|
rest = NULL;
|
|
}
|
|
else {
|
|
mrb_int kwnum = kwargs->num;
|
|
mrb_int required = kwargs->required;
|
|
const mrb_sym *kname = kwargs->table;
|
|
mrb_value *values = kwargs->values;
|
|
mrb_int j;
|
|
const mrb_int keyword_max = 40;
|
|
|
|
mrb_assert(kwnum >= 0);
|
|
mrb_assert(required >= 0);
|
|
if (kwnum > keyword_max || required > kwnum) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword number is too large");
|
|
}
|
|
|
|
for (j = required; j > 0; j--, kname++, values++) {
|
|
mrb_value k = mrb_symbol_value(*kname);
|
|
if (!mrb_hash_key_p(mrb, ksrc, k)) {
|
|
mrb_raisef(mrb, E_ARGUMENT_ERROR, "missing keyword: %n", *kname);
|
|
}
|
|
*values = mrb_hash_delete_key(mrb, ksrc, k);
|
|
mrb_gc_protect(mrb, *values);
|
|
}
|
|
|
|
for (j = kwnum - required; j > 0; j--, kname++, values++) {
|
|
mrb_value k = mrb_symbol_value(*kname);
|
|
if (mrb_hash_key_p(mrb, ksrc, k)) {
|
|
*values = mrb_hash_delete_key(mrb, ksrc, k);
|
|
mrb_gc_protect(mrb, *values);
|
|
}
|
|
else {
|
|
*values = mrb_undef_value();
|
|
}
|
|
}
|
|
|
|
rest = kwargs->rest;
|
|
}
|
|
|
|
if (rest) {
|
|
*rest = ksrc;
|
|
}
|
|
else if (!mrb_hash_empty_p(mrb, ksrc)) {
|
|
ksrc = mrb_hash_first_key(mrb, ksrc);
|
|
mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown keyword: %v", ksrc);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %c", c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!c && argc > i) {
|
|
mrb_argnum_error(mrb, argc, argc_min, argc_max);
|
|
}
|
|
|
|
finish:
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
retrieve arguments from mrb_state.
|
|
|
|
mrb_get_args(mrb, format, ...)
|
|
|
|
returns number of arguments parsed.
|
|
|
|
format specifiers:
|
|
|
|
string mruby type C type note
|
|
----------------------------------------------------------------------------------------------
|
|
o: Object [mrb_value]
|
|
C: Class/Module [mrb_value] when ! follows, the value may be nil
|
|
S: String [mrb_value] when ! follows, the value may be nil
|
|
A: Array [mrb_value] when ! follows, the value may be nil
|
|
H: Hash [mrb_value] when ! follows, the value may be nil
|
|
s: String [const char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil
|
|
z: String [const char*] NUL terminated string; z! gives NULL for nil
|
|
a: Array [const mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil
|
|
c: Class/Module [struct RClass*] c! gives NULL for nil
|
|
f: Integer/Float [mrb_float]
|
|
i: Integer/Float [mrb_int]
|
|
b: boolean [mrb_bool]
|
|
n: String/Symbol [mrb_sym]
|
|
d: data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified; when ! follows, the value may be nil
|
|
&: block [mrb_value] &! raises exception if no block given
|
|
*: rest argument [const mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack
|
|
|: optional Following arguments are optional
|
|
?: optional given [mrb_bool] true if preceding argument (optional) is given
|
|
':': keyword args [mrb_kwargs const] Get keyword arguments
|
|
|
|
format modifiers:
|
|
|
|
string note
|
|
----------------------------------------------------------------------------------------------
|
|
!: Switch to the alternate mode; The behaviour changes depending on the specifier
|
|
+: Request a not frozen object; However, except nil value
|
|
*/
|
|
MRB_API mrb_int
|
|
mrb_get_args(mrb_state *mrb, mrb_args_format format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
mrb_int rc = get_args_v(mrb, format, NULL, &ap);
|
|
va_end(ap);
|
|
return rc;
|
|
}
|
|
|
|
MRB_API mrb_int
|
|
mrb_get_args_a(mrb_state *mrb, mrb_args_format format, void **args)
|
|
{
|
|
return get_args_v(mrb, format, args, NULL);
|
|
}
|
|
|
|
static struct RClass*
|
|
boot_defclass(mrb_state *mrb, struct RClass *super)
|
|
{
|
|
struct RClass *c = MRB_OBJ_ALLOC(mrb, MRB_TT_CLASS, mrb->class_class);
|
|
|
|
if (super) {
|
|
c->super = super;
|
|
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super);
|
|
c->flags |= MRB_FL_CLASS_IS_INHERITED;
|
|
}
|
|
else {
|
|
c->super = mrb->object_class;
|
|
}
|
|
c->mt = mt_new(mrb);
|
|
return c;
|
|
}
|
|
|
|
static void
|
|
boot_initmod(mrb_state *mrb, struct RClass *mod)
|
|
{
|
|
if (!mod->mt) {
|
|
mod->mt = mt_new(mrb);
|
|
}
|
|
}
|
|
|
|
static struct RClass*
|
|
include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super)
|
|
{
|
|
struct RClass *ic = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, mrb->class_class);
|
|
if (m->tt == MRB_TT_ICLASS) {
|
|
m = m->c;
|
|
}
|
|
MRB_CLASS_ORIGIN(m);
|
|
ic->mt = m->mt;
|
|
ic->super = super;
|
|
if (m->tt == MRB_TT_ICLASS) {
|
|
ic->c = m->c;
|
|
}
|
|
else {
|
|
ic->c = m;
|
|
}
|
|
return ic;
|
|
}
|
|
|
|
static int
|
|
include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super)
|
|
{
|
|
struct RClass *ic;
|
|
void *klass_mt = find_origin(c)->mt;
|
|
|
|
while (m) {
|
|
struct RClass *p = c->super;
|
|
int original_seen = FALSE;
|
|
int superclass_seen = FALSE;
|
|
|
|
if (c == ins_pos) original_seen = TRUE;
|
|
if (m->flags & MRB_FL_CLASS_IS_PREPENDED)
|
|
goto skip;
|
|
if (klass_mt && klass_mt == m->mt)
|
|
return -1;
|
|
|
|
while (p) {
|
|
if (c == p) original_seen = TRUE;
|
|
if (p->tt == MRB_TT_ICLASS) {
|
|
if (p->mt == m->mt) {
|
|
if (!superclass_seen && original_seen) {
|
|
ins_pos = p; /* move insert point */
|
|
}
|
|
goto skip;
|
|
}
|
|
}
|
|
else if (p->tt == MRB_TT_CLASS) {
|
|
if (!search_super) break;
|
|
superclass_seen = TRUE;
|
|
}
|
|
p = p->super;
|
|
}
|
|
|
|
ic = include_class_new(mrb, m, ins_pos->super);
|
|
m->flags |= MRB_FL_CLASS_IS_INHERITED;
|
|
ins_pos->super = ic;
|
|
mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
|
|
ins_pos = ic;
|
|
skip:
|
|
m = m->super;
|
|
}
|
|
mc_clear(mrb);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fix_include_module(mrb_state *mrb, struct RBasic *obj, void *data)
|
|
{
|
|
struct RClass **m = (struct RClass**)data;
|
|
|
|
if (obj->tt == MRB_TT_ICLASS && obj->c == m[0] && !MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) {
|
|
struct RClass *ic = (struct RClass*)obj;
|
|
include_module_at(mrb, ic, ic, m[1], 1);
|
|
}
|
|
return MRB_EACH_OBJ_OK;
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
|
|
{
|
|
mrb_check_frozen(mrb, c);
|
|
if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
|
|
}
|
|
if (c->tt == MRB_TT_MODULE && (c->flags & MRB_FL_CLASS_IS_INHERITED)) {
|
|
struct RClass *data[2];
|
|
data[0] = c;
|
|
data[1] = m;
|
|
mrb_objspace_each_objects(mrb, fix_include_module, data);
|
|
}
|
|
}
|
|
|
|
static int
|
|
fix_prepend_module(mrb_state *mrb, struct RBasic *obj, void *data)
|
|
{
|
|
struct RClass **m = (struct RClass**)data;
|
|
struct RClass *c = (struct RClass*)obj;
|
|
|
|
if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
|
|
struct RClass *p = c->super;
|
|
struct RClass *ins_pos = c;
|
|
while (p) {
|
|
if (c == m[0]) break;
|
|
if (p == m[0]->super->c) {
|
|
ins_pos = c;
|
|
}
|
|
if (p->tt == MRB_TT_CLASS) break;
|
|
if (p->c == m[0]) {
|
|
include_module_at(mrb, ins_pos, ins_pos, m[1], 0);
|
|
break;
|
|
}
|
|
c = p;
|
|
p = p->super;
|
|
}
|
|
}
|
|
return MRB_EACH_OBJ_OK;
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
|
|
{
|
|
mrb_check_frozen(mrb, c);
|
|
if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
|
|
struct RClass *origin = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, c);
|
|
origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED;
|
|
origin->super = c->super;
|
|
c->super = origin;
|
|
origin->mt = c->mt;
|
|
c->mt = NULL;
|
|
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin);
|
|
c->flags |= MRB_FL_CLASS_IS_PREPENDED;
|
|
}
|
|
if (include_module_at(mrb, c, c, m, 0) < 0) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected");
|
|
}
|
|
if (c->tt == MRB_TT_MODULE &&
|
|
(c->flags & (MRB_FL_CLASS_IS_INHERITED|MRB_FL_CLASS_IS_PREPENDED))) {
|
|
struct RClass *data[2];
|
|
data[0] = c;
|
|
data[1] = m;
|
|
mrb_objspace_each_objects(mrb, fix_prepend_module, data);
|
|
}
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_prepend(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(mod);
|
|
mrb_int argc;
|
|
mrb_value *argv;
|
|
mrb_sym prepended = MRB_SYM(prepended);
|
|
|
|
mrb_get_args(mrb, "*", &argv, &argc);
|
|
while (argc--) {
|
|
mrb_value m = argv[argc];
|
|
mrb_check_type(mrb, m, MRB_TT_MODULE);
|
|
mrb_prepend_module(mrb, c, mrb_class_ptr(m));
|
|
if (!mrb_func_basic_p(mrb, m, prepended, mrb_do_nothing)) {
|
|
mrb_funcall_argv(mrb, m, prepended, 1, &mod);
|
|
}
|
|
}
|
|
return mod;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_include(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(mod);
|
|
mrb_int argc;
|
|
mrb_value *argv;
|
|
mrb_sym included = MRB_SYM(included);
|
|
|
|
mrb_get_args(mrb, "*", &argv, &argc);
|
|
while (argc--) {
|
|
mrb_value m = argv[argc];
|
|
mrb_check_type(mrb, m, MRB_TT_MODULE);
|
|
mrb_include_module(mrb, c, mrb_class_ptr(m));
|
|
if (!mrb_func_basic_p(mrb, m, included, mrb_do_nothing)) {
|
|
mrb_funcall_argv(mrb, m, included, 1, &mod);
|
|
}
|
|
}
|
|
return mod;
|
|
}
|
|
|
|
/* 15.3.1.3.13 */
|
|
/*
|
|
* call-seq:
|
|
* obj.extend(module, ...) -> obj
|
|
*
|
|
* Adds to _obj_ the instance methods from each module given as a
|
|
* parameter.
|
|
*
|
|
* module Mod
|
|
* def hello
|
|
* "Hello from Mod.\n"
|
|
* end
|
|
* end
|
|
*
|
|
* class Klass
|
|
* def hello
|
|
* "Hello from Klass.\n"
|
|
* end
|
|
* end
|
|
*
|
|
* k = Klass.new
|
|
* k.hello #=> "Hello from Klass.\n"
|
|
* k.extend(Mod) #=> #<Klass:0x401b3bc8>
|
|
* k.hello #=> "Hello from Mod.\n"
|
|
*
|
|
*/
|
|
mrb_value
|
|
mrb_obj_extend(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
mrb_int argc;
|
|
mrb_value *argv;
|
|
mrb_sym extended = MRB_SYM(extended);
|
|
|
|
mrb_get_args(mrb, "*", &argv, &argc);
|
|
|
|
mrb_value cc = mrb_singleton_class(mrb, obj);
|
|
while (argc--) {
|
|
mrb_value mod = argv[argc];
|
|
mrb_check_type(mrb, mod, MRB_TT_MODULE);
|
|
mrb_include_module(mrb, mrb_class_ptr(cc), mrb_class_ptr(mod));
|
|
if (!mrb_func_basic_p(mrb, cc, extended, mrb_do_nothing)) {
|
|
mrb_funcall_argv(mrb, cc, extended, 1, &mod);
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/* 15.2.2.4.28 */
|
|
/*
|
|
* call-seq:
|
|
* mod.include?(module) -> true or false
|
|
*
|
|
* Returns <code>true</code> if <i>module</i> is included in
|
|
* <i>mod</i> or one of <i>mod</i>'s ancestors.
|
|
*
|
|
* module A
|
|
* end
|
|
* class B
|
|
* include A
|
|
* end
|
|
* class C < B
|
|
* end
|
|
* B.include?(A) #=> true
|
|
* C.include?(A) #=> true
|
|
* A.include?(A) #=> false
|
|
*/
|
|
static mrb_value
|
|
mrb_mod_include_p(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_value mod2;
|
|
struct RClass *c = mrb_class_ptr(mod);
|
|
|
|
mrb_get_args(mrb, "C", &mod2);
|
|
mrb_check_type(mrb, mod2, MRB_TT_MODULE);
|
|
|
|
while (c) {
|
|
if (c->tt == MRB_TT_ICLASS) {
|
|
if (c->c == mrb_class_ptr(mod2)) return mrb_true_value();
|
|
}
|
|
c = c->super;
|
|
}
|
|
return mrb_false_value();
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(self);
|
|
mrb_value result = mrb_ary_new(mrb);
|
|
|
|
while (c) {
|
|
if (c->tt == MRB_TT_ICLASS) {
|
|
mrb_ary_push(mrb, result, mrb_obj_value(c->c));
|
|
}
|
|
else if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
|
|
mrb_ary_push(mrb, result, mrb_obj_value(c));
|
|
}
|
|
c = c->super;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_value b;
|
|
struct RClass *m = mrb_class_ptr(mod);
|
|
boot_initmod(mrb, m); /* bootstrap a newly initialized module */
|
|
mrb_get_args(mrb, "|&", &b);
|
|
if (!mrb_nil_p(b)) {
|
|
mrb_yield_with_class(mrb, b, 1, &mod, mod, m);
|
|
}
|
|
return mod;
|
|
}
|
|
|
|
static void
|
|
mrb_mod_visibility(mrb_state *mrb, mrb_value mod, int vis)
|
|
{
|
|
mrb_assert((vis&MT_VMASK)==vis);
|
|
mrb_int argc;
|
|
mrb_value *argv;
|
|
struct RClass *c = mrb_class_ptr(mod);
|
|
|
|
mrb_get_args(mrb, "*!", &argv, &argc);
|
|
if (argc == 0) {
|
|
mrb_callinfo *ci;
|
|
struct REnv *e;
|
|
find_visibility_scope(mrb, NULL, 1, &ci, &e);
|
|
if (e) {
|
|
MRB_ENV_SET_VISIBILITY(e, vis);
|
|
}
|
|
else {
|
|
MRB_CI_SET_VISIBILITY(ci, vis);
|
|
}
|
|
}
|
|
else {
|
|
mt_tbl *h = c->mt;
|
|
for (int i=0; i<argc; i++) {
|
|
mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL);
|
|
mrb_sym mid = mrb_symbol(argv[i]);
|
|
mrb_method_t m = mrb_method_search(mrb, c, mid);
|
|
MRB_METHOD_SET_VISIBILITY(m, vis);
|
|
union mt_ptr ptr;
|
|
if (MRB_METHOD_PROC_P(m)) {
|
|
ptr.proc = MRB_METHOD_PROC(m);
|
|
}
|
|
else {
|
|
ptr.func = MRB_METHOD_FUNC(m);
|
|
}
|
|
mt_put(mrb, h, mid, MT_KEY_FLG(m.flags), ptr);
|
|
mc_clear_by_id(mrb, mid);
|
|
}
|
|
}
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_public(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_mod_visibility(mrb, mod, MT_PUBLIC);
|
|
return mod;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_private(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_mod_visibility(mrb, mod, MT_PRIVATE);
|
|
return mod;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_protected(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_mod_visibility(mrb, mod, MT_PROTECTED);
|
|
return mod;
|
|
}
|
|
|
|
static mrb_value
|
|
top_public(mrb_state *mrb, mrb_value self)
|
|
{
|
|
self = mrb_obj_value(mrb->object_class);
|
|
mrb_mod_visibility(mrb, self, MT_PUBLIC);
|
|
return self;
|
|
}
|
|
|
|
static mrb_value
|
|
top_private(mrb_state *mrb, mrb_value self)
|
|
{
|
|
self = mrb_obj_value(mrb->object_class);
|
|
mrb_mod_visibility(mrb, self, MT_PRIVATE);
|
|
return self;
|
|
}
|
|
|
|
static mrb_value
|
|
top_protected(mrb_state *mrb, mrb_value self)
|
|
{
|
|
self = mrb_obj_value(mrb->object_class);
|
|
mrb_mod_visibility(mrb, self, MT_PROTECTED);
|
|
return self;
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_singleton_class_ptr(mrb_state *mrb, mrb_value v)
|
|
{
|
|
struct RBasic *obj;
|
|
|
|
switch (mrb_type(v)) {
|
|
case MRB_TT_FALSE:
|
|
if (mrb_nil_p(v))
|
|
return mrb->nil_class;
|
|
return mrb->false_class;
|
|
case MRB_TT_TRUE:
|
|
return mrb->true_class;
|
|
case MRB_TT_CPTR:
|
|
case MRB_TT_SYMBOL:
|
|
case MRB_TT_INTEGER:
|
|
#ifndef MRB_NO_FLOAT
|
|
case MRB_TT_FLOAT:
|
|
#endif
|
|
return NULL;
|
|
default:
|
|
break;
|
|
}
|
|
obj = mrb_basic_ptr(v);
|
|
if (obj->c == NULL) return NULL;
|
|
prepare_singleton_class(mrb, obj);
|
|
return obj->c;
|
|
}
|
|
|
|
MRB_API mrb_value
|
|
mrb_singleton_class(mrb_state *mrb, mrb_value v)
|
|
{
|
|
struct RClass *c = mrb_singleton_class_ptr(mrb, v);
|
|
|
|
if (c == NULL) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton");
|
|
}
|
|
return mrb_obj_value(c);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
prepare_singleton_class(mrb, (struct RBasic*)o);
|
|
mrb_define_method_id(mrb, o->c, mrb_intern_cstr(mrb, name), func, aspec);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_singleton_method_id(mrb_state *mrb, struct RObject *o, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
prepare_singleton_class(mrb, (struct RBasic*)o);
|
|
mrb_define_method_id(mrb, o->c, name, func, aspec);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
mrb_define_singleton_method_id(mrb, (struct RObject*)c, name, func, aspec);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_module_function_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
mrb_define_class_method_id(mrb, c, name, func, aspec);
|
|
mrb_define_private_method_id(mrb, c, name, func, aspec);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
|
|
{
|
|
mrb_define_module_function_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
|
|
}
|
|
|
|
#ifndef MRB_NO_METHOD_CACHE
|
|
/* clear whole method cache table */
|
|
static void
|
|
mc_clear(mrb_state *mrb)
|
|
{
|
|
static const struct mrb_cache_entry ce_zero ={0};
|
|
|
|
for (int i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
|
|
mrb->cache[i] = ce_zero;
|
|
}
|
|
}
|
|
|
|
/* clear method cache for a class */
|
|
void
|
|
mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c)
|
|
{
|
|
struct mrb_cache_entry *mc = mrb->cache;
|
|
|
|
for (int i=0; i<MRB_METHOD_CACHE_SIZE; mc++,i++) {
|
|
if (mc->c == c || mc->c0 == c) mc->c = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mc_clear_by_id(mrb_state *mrb, mrb_sym id)
|
|
{
|
|
struct mrb_cache_entry *mc = mrb->cache;
|
|
|
|
for (int i=0; i<MRB_METHOD_CACHE_SIZE; mc++,i++) {
|
|
if (METHOD_MID(mc->m) == id) mc->c = NULL;
|
|
}
|
|
}
|
|
#endif // MRB_NO_METHOD_CACHE
|
|
|
|
mrb_method_t
|
|
mrb_vm_find_method(mrb_state *mrb, struct RClass *c, struct RClass **cp, mrb_sym mid)
|
|
{
|
|
mrb_method_t m;
|
|
#ifndef MRB_NO_METHOD_CACHE
|
|
struct RClass *oc = c;
|
|
int h = mrb_int_hash_func(mrb, ((intptr_t)oc) ^ mid) & (MRB_METHOD_CACHE_SIZE-1);
|
|
struct mrb_cache_entry *mc = &mrb->cache[h];
|
|
|
|
if (mc->c == c && METHOD_MID(mc->m) == mid) {
|
|
*cp = mc->c0;
|
|
return mc->m;
|
|
}
|
|
#endif
|
|
|
|
while (c) {
|
|
mt_tbl *h = c->mt;
|
|
|
|
if (h) {
|
|
union mt_ptr ptr;
|
|
mrb_sym ret = mt_get(mrb, h, mid, &ptr);
|
|
if (ret) {
|
|
if (ptr.proc == 0) break;
|
|
*cp = c;
|
|
m = create_method_value(mrb, ret, ptr);
|
|
#ifndef MRB_NO_METHOD_CACHE
|
|
mc->c = oc;
|
|
mc->c0 = c;
|
|
mc->m = m;
|
|
#endif
|
|
return m;
|
|
}
|
|
}
|
|
c = c->super;
|
|
}
|
|
MRB_METHOD_FROM_PROC(m, NULL);
|
|
return m; /* no method */
|
|
}
|
|
|
|
MRB_API mrb_method_t
|
|
mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
|
|
{
|
|
return mrb_vm_find_method(mrb, *cp, cp, mid);
|
|
}
|
|
|
|
MRB_API mrb_method_t
|
|
mrb_method_search(mrb_state *mrb, struct RClass *c, mrb_sym mid)
|
|
{
|
|
mrb_method_t m;
|
|
|
|
m = mrb_method_search_vm(mrb, &c, mid);
|
|
if (MRB_METHOD_UNDEF_P(m)) {
|
|
mrb_name_error(mrb, mid, "undefined method '%n' for class %C", mid, c);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
#define ONSTACK_ALLOC_MAX 32
|
|
|
|
static mrb_sym
|
|
prepare_name_common(mrb_state *mrb, mrb_sym sym, const char *prefix, const char *suffix)
|
|
{
|
|
char onstack[ONSTACK_ALLOC_MAX];
|
|
mrb_int sym_len;
|
|
const char *sym_str = mrb_sym_name_len(mrb, sym, &sym_len);
|
|
size_t prefix_len = prefix ? strlen(prefix) : 0;
|
|
size_t suffix_len = suffix ? strlen(suffix) : 0;
|
|
size_t name_len = sym_len + prefix_len + suffix_len;
|
|
char *buf = name_len > sizeof(onstack) ? (char*)mrb_alloca(mrb, name_len) : onstack;
|
|
char *p = buf;
|
|
|
|
if (prefix_len > 0) {
|
|
memcpy(p, prefix, prefix_len);
|
|
p += prefix_len;
|
|
}
|
|
|
|
memcpy(p, sym_str, sym_len);
|
|
p += sym_len;
|
|
|
|
if (suffix_len > 0) {
|
|
memcpy(p, suffix, suffix_len);
|
|
}
|
|
|
|
return mrb_intern(mrb, buf, name_len);
|
|
}
|
|
|
|
static mrb_value
|
|
prepare_ivar_name(mrb_state *mrb, mrb_sym sym)
|
|
{
|
|
sym = prepare_name_common(mrb, sym, "@", NULL);
|
|
mrb_iv_name_sym_check(mrb, sym);
|
|
return mrb_symbol_value(sym);
|
|
}
|
|
|
|
static mrb_sym
|
|
prepare_writer_name(mrb_state *mrb, mrb_sym sym)
|
|
{
|
|
return prepare_name_common(mrb, sym, NULL, "=");
|
|
}
|
|
|
|
static mrb_value
|
|
mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state*, mrb_value), mrb_sym (*access_name)(mrb_state*, mrb_sym))
|
|
{
|
|
struct RClass *c = mrb_class_ptr(mod);
|
|
const mrb_value *argv;
|
|
mrb_int argc;
|
|
|
|
mrb_get_args(mrb, "*", &argv, &argc);
|
|
|
|
int ai = mrb_gc_arena_save(mrb);
|
|
for (int i=0; i<argc; i++) {
|
|
mrb_value name;
|
|
mrb_sym method;
|
|
struct RProc *p;
|
|
mrb_method_t m;
|
|
|
|
method = to_sym(mrb, argv[i]);
|
|
name = prepare_ivar_name(mrb, method);
|
|
if (access_name) {
|
|
method = access_name(mrb, method);
|
|
}
|
|
|
|
p = mrb_proc_new_cfunc_with_env(mrb, accessor, 1, &name);
|
|
MRB_METHOD_FROM_PROC(m, p);
|
|
mrb_define_method_raw(mrb, c, method, m);
|
|
mrb_gc_arena_restore(mrb, ai);
|
|
}
|
|
return mrb_nil_value();
|
|
}
|
|
|
|
static mrb_value
|
|
attr_reader(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
|
|
return mrb_iv_get(mrb, obj, to_sym(mrb, name));
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
return mod_attr_define(mrb, mod, attr_reader, NULL);
|
|
}
|
|
|
|
static mrb_value
|
|
attr_writer(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
|
|
mrb_value val = mrb_get_arg1(mrb);
|
|
|
|
mrb_iv_set(mrb, obj, to_sym(mrb, name), val);
|
|
return val;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name);
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_attr_accessor(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_mod_attr_reader(mrb, mod);
|
|
return mrb_mod_attr_writer(mrb, mod);
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(cv);
|
|
enum mrb_vtype ttype = MRB_INSTANCE_TT(c);
|
|
|
|
if (c->tt == MRB_TT_SCLASS)
|
|
mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class");
|
|
|
|
if (c == mrb->nil_class || c == mrb->false_class) {
|
|
mrb_assert(ttype == 0);
|
|
}
|
|
else if (ttype == 0) {
|
|
ttype = MRB_TT_OBJECT;
|
|
}
|
|
if (MRB_UNDEF_ALLOCATOR_P(c)) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "allocator undefined for %v", cv);
|
|
}
|
|
if (ttype <= MRB_TT_CPTR) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %v", cv);
|
|
}
|
|
|
|
struct RObject *o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c);
|
|
return mrb_obj_value(o);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* class.new(args, ...) -> obj
|
|
*
|
|
* Creates a new object of <i>class</i>'s class, then
|
|
* invokes that object's <code>initialize</code> method,
|
|
* passing it <i>args</i>. This is the method that ends
|
|
* up getting called whenever an object is constructed using
|
|
* `.new`.
|
|
*
|
|
*/
|
|
|
|
mrb_value
|
|
mrb_instance_new(mrb_state *mrb, mrb_value cv)
|
|
{
|
|
const mrb_value *argv;
|
|
mrb_int argc;
|
|
mrb_value blk;
|
|
mrb_sym init;
|
|
|
|
mrb_get_args(mrb, "*!&", &argv, &argc, &blk);
|
|
mrb_value obj = mrb_instance_alloc(mrb, cv);
|
|
init = MRB_SYM(initialize);
|
|
if (!mrb_func_basic_p(mrb, obj, init, mrb_do_nothing)) {
|
|
mrb_funcall_with_block(mrb, obj, init, argc, argv, blk);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
MRB_API mrb_value
|
|
mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv)
|
|
{
|
|
mrb_value obj = mrb_instance_alloc(mrb, mrb_obj_value(c));
|
|
mrb_sym mid = MRB_SYM(initialize);
|
|
|
|
if (!mrb_func_basic_p(mrb, obj, mid, mrb_do_nothing)) {
|
|
mrb_funcall_argv(mrb, obj, mid, argc, argv);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_class_initialize(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(obj);
|
|
|
|
if (c->iv) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "already initialized class");
|
|
}
|
|
|
|
mrb_value a, b;
|
|
mrb_get_args(mrb, "|C&", &a, &b);
|
|
if (!mrb_nil_p(b)) {
|
|
mrb_yield_with_class(mrb, b, 1, &obj, obj, c);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_class_new_class(mrb_state *mrb, mrb_value cv)
|
|
{
|
|
mrb_value super, blk;
|
|
mrb_int n = mrb_get_args(mrb, "|C&", &super, &blk);
|
|
|
|
if (n == 0) {
|
|
super = mrb_obj_value(mrb->object_class);
|
|
}
|
|
mrb_value new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super)));
|
|
mrb_sym mid = MRB_SYM(initialize);
|
|
if (mrb_func_basic_p(mrb, new_class, mid, mrb_class_initialize)) {
|
|
mrb_class_initialize(mrb, new_class);
|
|
}
|
|
else {
|
|
mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk);
|
|
}
|
|
mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class));
|
|
return new_class;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_class_superclass(mrb_state *mrb, mrb_value klass)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(klass);
|
|
|
|
c = find_origin(c)->super;
|
|
while (c && c->tt == MRB_TT_ICLASS) {
|
|
c = find_origin(c)->super;
|
|
}
|
|
if (!c) return mrb_nil_value();
|
|
return mrb_obj_value(c);
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_do_nothing(mrb_state *mrb, mrb_value cv)
|
|
{
|
|
return mrb_nil_value();
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_bob_not(mrb_state *mrb, mrb_value cv)
|
|
{
|
|
return mrb_bool_value(!mrb_test(cv));
|
|
}
|
|
|
|
/* 15.3.1.3.1 */
|
|
/* 15.3.1.3.10 */
|
|
/* 15.3.1.3.11 */
|
|
/*
|
|
* call-seq:
|
|
* obj == other -> true or false
|
|
* obj.equal?(other) -> true or false
|
|
* obj.eql?(other) -> true or false
|
|
*
|
|
* Equality---At the <code>Object</code> level, <code>==</code> returns
|
|
* <code>true</code> only if <i>obj</i> and <i>other</i> are the
|
|
* same object. Typically, this method is overridden in descendant
|
|
* classes to provide class-specific meaning.
|
|
*
|
|
* Unlike <code>==</code>, the <code>equal?</code> method should never be
|
|
* overridden by subclasses: it is used to determine object identity
|
|
* (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
|
|
* object as <code>b</code>).
|
|
*
|
|
* The <code>eql?</code> method returns <code>true</code> if
|
|
* <i>obj</i> and <i>anObject</i> have the same value. Used by
|
|
* <code>Hash</code> to test members for equality. For objects of
|
|
* class <code>Object</code>, <code>eql?</code> is synonymous with
|
|
* <code>==</code>. Subclasses normally continue this tradition, but
|
|
* there are exceptions. <code>Numeric</code> types, for example,
|
|
* perform type conversion across <code>==</code>, but not across
|
|
* <code>eql?</code>, so:
|
|
*
|
|
* 1 == 1.0 #=> true
|
|
* 1.eql? 1.0 #=> false
|
|
*/
|
|
mrb_value
|
|
mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
|
|
{
|
|
mrb_value arg = mrb_get_arg1(mrb);
|
|
|
|
return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
|
|
}
|
|
|
|
MRB_API mrb_bool
|
|
mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid)
|
|
{
|
|
mrb_method_t m = mrb_method_search_vm(mrb, &c, mid);
|
|
|
|
if (MRB_METHOD_UNDEF_P(m)) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
MRB_API mrb_bool
|
|
mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid)
|
|
{
|
|
return mrb_obj_respond_to(mrb, mrb_class(mrb, obj), mid);
|
|
}
|
|
|
|
MRB_API mrb_value
|
|
mrb_class_path(mrb_state *mrb, struct RClass *c)
|
|
{
|
|
mrb_sym nsym = MRB_SYM(__classname__);
|
|
mrb_value path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym);
|
|
|
|
if (mrb_nil_p(path)) {
|
|
/* no name (yet) */
|
|
return mrb_class_find_path(mrb, c);
|
|
}
|
|
else if (mrb_symbol_p(path)) {
|
|
/* toplevel class/module */
|
|
return mrb_sym_str(mrb, mrb_symbol(path));
|
|
}
|
|
return mrb_str_dup(mrb, path);
|
|
}
|
|
|
|
MRB_API struct RClass*
|
|
mrb_class_real(struct RClass* cl)
|
|
{
|
|
if (cl == 0) return NULL;
|
|
while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) {
|
|
cl = cl->super;
|
|
if (cl == 0) return NULL;
|
|
}
|
|
return cl;
|
|
}
|
|
|
|
MRB_API const char*
|
|
mrb_class_name(mrb_state *mrb, struct RClass* c)
|
|
{
|
|
if (c == NULL) return NULL;
|
|
|
|
mrb_value name = class_name_str(mrb, c);
|
|
return RSTRING_PTR(name);
|
|
}
|
|
|
|
MRB_API const char*
|
|
mrb_obj_classname(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
return mrb_class_name(mrb, mrb_obj_class(mrb, obj));
|
|
}
|
|
|
|
/*!
|
|
* Ensures a class can be derived from super.
|
|
*
|
|
* \param super a reference to an object.
|
|
* \exception TypeError if \a super is not a Class or \a super is a singleton class.
|
|
*/
|
|
static void
|
|
mrb_check_inheritable(mrb_state *mrb, struct RClass *super)
|
|
{
|
|
if (super->tt != MRB_TT_CLASS) {
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%C given)", super);
|
|
}
|
|
if (super->tt == MRB_TT_SCLASS) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class");
|
|
}
|
|
if (super == mrb->class_class) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of Class");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Creates a new class.
|
|
* \param super a class from which the new class derives.
|
|
* \exception TypeError \a super is not inheritable.
|
|
* \exception TypeError \a super is the Class class.
|
|
*/
|
|
MRB_API struct RClass*
|
|
mrb_class_new(mrb_state *mrb, struct RClass *super)
|
|
{
|
|
if (super) {
|
|
mrb_check_inheritable(mrb, super);
|
|
}
|
|
|
|
struct RClass *c = boot_defclass(mrb, super);
|
|
if (super) {
|
|
MRB_SET_INSTANCE_TT(c, MRB_INSTANCE_TT(super));
|
|
c->flags |= super->flags & MRB_FL_UNDEF_ALLOCATE;
|
|
}
|
|
make_metaclass(mrb, c);
|
|
|
|
return c;
|
|
}
|
|
|
|
/*!
|
|
* Creates a new module.
|
|
*/
|
|
MRB_API struct RClass*
|
|
mrb_module_new(mrb_state *mrb)
|
|
{
|
|
struct RClass *m = MRB_OBJ_ALLOC(mrb, MRB_TT_MODULE, mrb->module_class);
|
|
boot_initmod(mrb, m);
|
|
return m;
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* obj.class => class
|
|
*
|
|
* Returns the class of <i>obj</i>, now preferred over
|
|
* <code>Object#type</code>, as an object's type in Ruby is only
|
|
* loosely tied to that object's class. This method must always be
|
|
* called with an explicit receiver, as <code>class</code> is also a
|
|
* reserved word in Ruby.
|
|
*
|
|
* 1.class #=> Integer
|
|
* self.class #=> Object
|
|
*/
|
|
|
|
MRB_API struct RClass*
|
|
mrb_obj_class(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
return mrb_class_real(mrb_class(mrb, obj));
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
|
|
{
|
|
if (a == b) return;
|
|
mrb_method_t m = mrb_method_search(mrb, c, b);
|
|
|
|
if (!MRB_METHOD_CFUNC_P(m)) {
|
|
const struct RProc *p = MRB_METHOD_PROC(m);
|
|
if (!MRB_PROC_CFUNC_P(p) && !MRB_PROC_ALIAS_P(p)) {
|
|
struct RProc *pnew = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
|
|
int vis = MRB_METHOD_VISIBILITY(m);
|
|
|
|
pnew->body.mid = b;
|
|
pnew->upper = p;
|
|
pnew->e.env = NULL;
|
|
pnew->flags |= MRB_PROC_ALIAS;
|
|
MRB_METHOD_FROM_PROC(m, pnew);
|
|
MRB_METHOD_SET_VISIBILITY(m, vis);
|
|
}
|
|
}
|
|
mrb_define_method_raw(mrb, c, a, m);
|
|
}
|
|
|
|
/*!
|
|
* Defines an alias of a method.
|
|
* \param mrb the mruby state
|
|
* \param klass the class which the original method belongs to
|
|
* \param name1 a new name for the method
|
|
* \param name2 the original name of the method
|
|
*/
|
|
MRB_API void
|
|
mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2)
|
|
{
|
|
mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2));
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_define_alias_id(mrb_state *mrb, struct RClass *klass, mrb_sym a, mrb_sym b)
|
|
{
|
|
mrb_alias_method(mrb, klass, a, b);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* mod.to_s -> string
|
|
*
|
|
* Return a string representing this module or class. For basic
|
|
* classes and modules, this is the name. For singletons, we
|
|
* show information on the thing we're attached to as well.
|
|
*/
|
|
|
|
mrb_value
|
|
mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
|
|
{
|
|
if (mrb_sclass_p(klass)) {
|
|
mrb_value v = mrb_iv_get(mrb, klass, MRB_SYM(__attached__));
|
|
mrb_value str = mrb_str_new_lit(mrb, "#<Class:");
|
|
|
|
if (class_ptr_p(v)) {
|
|
mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
|
|
}
|
|
else {
|
|
mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v));
|
|
}
|
|
return mrb_str_cat_lit(mrb, str, ">");
|
|
}
|
|
else {
|
|
return class_name_str(mrb, mrb_class_ptr(klass));
|
|
}
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_alias(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(mod);
|
|
mrb_sym new_name, old_name;
|
|
|
|
mrb_get_args(mrb, "nn", &new_name, &old_name);
|
|
mrb_alias_method(mrb, c, new_name, old_name);
|
|
mrb_method_added(mrb, c, new_name);
|
|
return mod;
|
|
}
|
|
|
|
static void
|
|
undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
|
|
{
|
|
mrb_method_t m;
|
|
mrb_sym undefined;
|
|
mrb_value recv;
|
|
|
|
MRB_METHOD_FROM_PROC(m, NULL);
|
|
mrb_define_method_raw(mrb, c, a, m);
|
|
if (c->tt == MRB_TT_SCLASS) {
|
|
undefined = MRB_SYM(singleton_method_undefined);
|
|
recv = mrb_iv_get(mrb, mrb_obj_value(c), MRB_SYM(__attached__));
|
|
}
|
|
else {
|
|
undefined = MRB_SYM(method_undefined);
|
|
recv = mrb_obj_value(c);
|
|
}
|
|
if (!mrb_func_basic_p(mrb, recv, undefined, mrb_do_nothing)) {
|
|
mrb_value sym = mrb_symbol_value(a);
|
|
mrb_funcall_argv(mrb, recv, undefined, 1, &sym);
|
|
}
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_undef_method_id(mrb_state *mrb, struct RClass *c, mrb_sym a)
|
|
{
|
|
if (!mrb_obj_respond_to(mrb, c, a)) {
|
|
mrb_name_error(mrb, a, "undefined method '%n' for class '%C'", a, c);
|
|
}
|
|
undef_method(mrb, c, a);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name)
|
|
{
|
|
undef_method(mrb, c, mrb_intern_cstr(mrb, name));
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_undef_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name)
|
|
{
|
|
mrb_undef_method_id(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name)
|
|
{
|
|
mrb_undef_method(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name);
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_remove_method(mrb_state *mrb, struct RClass *c0, mrb_sym mid)
|
|
{
|
|
struct RClass *c = c0;
|
|
MRB_CLASS_ORIGIN(c);
|
|
mt_tbl *h = c->mt;
|
|
|
|
if (h && mt_del(mrb, h, mid)) {
|
|
mrb_sym removed;
|
|
mrb_value recv;
|
|
|
|
mc_clear_by_id(mrb, mid);
|
|
if (c0->tt == MRB_TT_SCLASS) {
|
|
removed = MRB_SYM(singleton_method_removed);
|
|
recv = mrb_iv_get(mrb, mrb_obj_value(c0), MRB_SYM(__attached__));
|
|
}
|
|
else {
|
|
removed = MRB_SYM(method_removed);
|
|
recv = mrb_obj_value(c0);
|
|
}
|
|
if (!mrb_func_basic_p(mrb, recv, removed, mrb_do_nothing)) {
|
|
mrb_value sym = mrb_symbol_value(mid);
|
|
mrb_funcall_argv(mrb, recv, removed, 1, &sym);
|
|
}
|
|
return;
|
|
}
|
|
mrb_name_error(mrb, mid, "method '%n' not defined in %C", mid, c);
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_undef(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
struct RClass *c = mrb_class_ptr(mod);
|
|
mrb_int argc;
|
|
const mrb_value *argv;
|
|
|
|
mrb_get_args(mrb, "*", &argv, &argc);
|
|
while (argc--) {
|
|
mrb_undef_method_id(mrb, c, to_sym(mrb, *argv));
|
|
argv++;
|
|
}
|
|
return mrb_nil_value();
|
|
}
|
|
|
|
static void
|
|
check_const_name_sym(mrb_state *mrb, mrb_sym id)
|
|
{
|
|
mrb_int len;
|
|
const char *name = mrb_sym_name_len(mrb, id, &len);
|
|
if (!mrb_const_name_p(mrb, name, len)) {
|
|
mrb_name_error(mrb, id, "wrong constant name %n", id);
|
|
}
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_sym id;
|
|
mrb_bool inherit = TRUE;
|
|
|
|
mrb_get_args(mrb, "n|b", &id, &inherit);
|
|
check_const_name_sym(mrb, id);
|
|
if (inherit) {
|
|
return mrb_bool_value(mrb_const_defined(mrb, mod, id));
|
|
}
|
|
return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id)
|
|
{
|
|
check_const_name_sym(mrb, id);
|
|
return mrb_const_get(mrb, mod, id);
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_value path = mrb_get_arg1(mrb);
|
|
|
|
if (mrb_symbol_p(path)) {
|
|
/* const get with symbol */
|
|
return mrb_const_get_sym(mrb, mod, mrb_symbol(path));
|
|
}
|
|
|
|
/* const get with class path string */
|
|
mrb_ensure_string_type(mrb, path);
|
|
|
|
char *ptr = RSTRING_PTR(path);
|
|
mrb_int len = RSTRING_LEN(path);
|
|
mrb_int off = 0;
|
|
|
|
while (off < len) {
|
|
mrb_int end = mrb_str_index_lit(mrb, path, "::", off);
|
|
if (end == -1) end = len;
|
|
mrb_sym id = mrb_intern(mrb, ptr+off, end-off);
|
|
mod = mrb_const_get_sym(mrb, mod, id);
|
|
if (end == len)
|
|
off = end;
|
|
else {
|
|
off = end + 2;
|
|
if (off == len) { /* trailing "::" */
|
|
mrb_name_error(mrb, id, "wrong constant name '%v'", path);
|
|
}
|
|
}
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_const_set(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_sym id;
|
|
mrb_value value;
|
|
|
|
mrb_get_args(mrb, "no", &id, &value);
|
|
check_const_name_sym(mrb, id);
|
|
mrb_const_set(mrb, mod, id, value);
|
|
return value;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_remove_const(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_sym id;
|
|
|
|
mrb_get_args(mrb, "n", &id);
|
|
check_const_name_sym(mrb, id);
|
|
|
|
mrb_value val = mrb_iv_remove(mrb, mod, id);
|
|
if (mrb_undef_p(val)) {
|
|
mrb_name_error(mrb, id, "constant %n not defined", id);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
mrb_value
|
|
mrb_const_missing(mrb_state *mrb, mrb_value mod, mrb_sym sym)
|
|
{
|
|
if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) {
|
|
mrb_name_error(mrb, sym, "uninitialized constant %v::%n", mod, sym);
|
|
}
|
|
else {
|
|
mrb_name_error(mrb, sym, "uninitialized constant %n", sym);
|
|
}
|
|
/* not reached */
|
|
return mrb_nil_value();
|
|
}
|
|
|
|
mrb_value
|
|
mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_sym sym;
|
|
|
|
mrb_get_args(mrb, "n", &sym);
|
|
mrb->c->ci->mid = 0;
|
|
return mrb_const_missing(mrb, mod, sym);
|
|
}
|
|
|
|
/* 15.2.2.4.34 */
|
|
/*
|
|
* call-seq:
|
|
* mod.method_defined?(symbol) -> true or false
|
|
*
|
|
* Returns +true+ if the named method is defined by
|
|
* _mod_ (or its included modules and, if _mod_ is a class,
|
|
* its ancestors). Public and protected methods are matched.
|
|
*
|
|
* module A
|
|
* def method1() end
|
|
* end
|
|
* class B
|
|
* def method2() end
|
|
* end
|
|
* class C < B
|
|
* include A
|
|
* def method3() end
|
|
* end
|
|
*
|
|
* A.method_defined? :method1 #=> true
|
|
* C.method_defined? "method1" #=> true
|
|
* C.method_defined? "method2" #=> true
|
|
* C.method_defined? "method3" #=> true
|
|
* C.method_defined? "method4" #=> false
|
|
*/
|
|
|
|
static mrb_value
|
|
mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_sym id;
|
|
|
|
mrb_get_args(mrb, "n", &id);
|
|
return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id));
|
|
}
|
|
|
|
void
|
|
mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid)
|
|
{
|
|
mrb_sym added;
|
|
mrb_value recv = mrb_obj_value(c);
|
|
|
|
if (c->tt == MRB_TT_SCLASS) {
|
|
added = MRB_SYM(singleton_method_added);
|
|
recv = mrb_iv_get(mrb, recv, MRB_SYM(__attached__));
|
|
}
|
|
else {
|
|
added = MRB_SYM(method_added);
|
|
}
|
|
if (!mrb_func_basic_p(mrb, recv, added, mrb_do_nothing)) {
|
|
mrb_value sym = mrb_symbol_value(mid);
|
|
mrb_funcall_argv(mrb, recv, added, 1, &sym);
|
|
}
|
|
}
|
|
|
|
mrb_value
|
|
define_method_m(mrb_state *mrb, struct RClass *c, int vis)
|
|
{
|
|
mrb_sym mid;
|
|
mrb_value proc = mrb_undef_value();
|
|
mrb_value blk;
|
|
|
|
mrb_get_args(mrb, "n|o&", &mid, &proc, &blk);
|
|
switch (mrb_type(proc)) {
|
|
case MRB_TT_PROC:
|
|
blk = proc;
|
|
break;
|
|
case MRB_TT_UNDEF:
|
|
/* ignored */
|
|
break;
|
|
default:
|
|
mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %T (expected Proc)", proc);
|
|
break;
|
|
}
|
|
if (mrb_nil_p(blk)) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
|
|
}
|
|
struct RProc *p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
|
|
mrb_proc_copy(mrb, p, mrb_proc_ptr(blk));
|
|
p->flags |= MRB_PROC_STRICT;
|
|
|
|
mrb_method_t m;
|
|
MRB_METHOD_FROM_PROC(m, p);
|
|
MRB_METHOD_SET_VISIBILITY(m, vis);
|
|
mrb_define_method_raw(mrb, c, mid, m);
|
|
mrb_method_added(mrb, c, mid);
|
|
return mrb_symbol_value(mid);
|
|
}
|
|
|
|
mrb_value
|
|
mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c)
|
|
{
|
|
return define_method_m(mrb, c, MT_PUBLIC);
|
|
}
|
|
|
|
static mrb_value
|
|
mod_define_method(mrb_state *mrb, mrb_value self)
|
|
{
|
|
return mrb_mod_define_method_m(mrb, mrb_class_ptr(self));
|
|
}
|
|
|
|
static mrb_value
|
|
top_define_method(mrb_state *mrb, mrb_value self)
|
|
{
|
|
return define_method_m(mrb, mrb->object_class, MT_PRIVATE);
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_value obj = mrb_get_arg1(mrb);
|
|
mrb_bool eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod));
|
|
|
|
return mrb_bool_value(eqq);
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_dup(mrb_state *mrb, mrb_value self)
|
|
{
|
|
mrb_value mod = mrb_obj_clone(mrb, self);
|
|
mrb_obj_ptr(mod)->frozen = 0;
|
|
return mod;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
const mrb_value *argv;
|
|
mrb_int argc;
|
|
|
|
mrb_check_type(mrb, mod, MRB_TT_MODULE);
|
|
|
|
mrb_get_args(mrb, "*", &argv, &argc);
|
|
if (argc == 0) {
|
|
/* set MODFUNC SCOPE if implemented */
|
|
return mod;
|
|
}
|
|
|
|
/* set PRIVATE method visibility if implemented */
|
|
/* mrb_mod_dummy_visibility(mrb, mod); */
|
|
|
|
struct RClass *rclass = mrb_class_ptr(mod);
|
|
int ai = mrb_gc_arena_save(mrb);
|
|
for (int i=0; i<argc; i++) {
|
|
mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL);
|
|
|
|
mrb_sym mid = mrb_symbol(argv[i]);
|
|
mrb_method_t m = mrb_method_search(mrb, rclass, mid);
|
|
|
|
prepare_singleton_class(mrb, (struct RBasic*)rclass);
|
|
MRB_METHOD_SET_VISIBILITY(m, MT_PUBLIC);
|
|
mrb_define_method_raw(mrb, rclass->c, mid, m);
|
|
mrb_gc_arena_restore(mrb, ai);
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
static struct RClass*
|
|
mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
struct RClass *klass = mrb_basic_ptr(obj)->c;
|
|
|
|
if (klass->tt != MRB_TT_SCLASS)
|
|
return klass;
|
|
else {
|
|
/* copy singleton(unnamed) class */
|
|
struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class);
|
|
|
|
switch (mrb_type(obj)) {
|
|
case MRB_TT_CLASS:
|
|
case MRB_TT_SCLASS:
|
|
break;
|
|
default:
|
|
clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass));
|
|
break;
|
|
}
|
|
clone->super = klass->super;
|
|
if (klass->iv) {
|
|
mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass));
|
|
mrb_obj_iv_set(mrb, (struct RObject*)clone, MRB_SYM(__attached__), obj);
|
|
}
|
|
if (klass->mt) {
|
|
clone->mt = mt_copy(mrb, klass->mt);
|
|
}
|
|
else {
|
|
clone->mt = mt_new(mrb);
|
|
}
|
|
clone->tt = MRB_TT_SCLASS;
|
|
return clone;
|
|
}
|
|
}
|
|
|
|
static void
|
|
copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
|
|
{
|
|
struct RClass *dc = mrb_class_ptr(dst);
|
|
struct RClass *sc = mrb_class_ptr(src);
|
|
/* if the origin is not the same as the class, then the origin and
|
|
the current class need to be copied */
|
|
if (sc->flags & MRB_FL_CLASS_IS_PREPENDED) {
|
|
struct RClass *c0 = sc->super;
|
|
struct RClass *c1 = dc;
|
|
|
|
/* copy prepended iclasses */
|
|
while (!(c0->flags & MRB_FL_CLASS_IS_ORIGIN)) {
|
|
c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
|
|
c1 = c1->super;
|
|
c0 = c0->super;
|
|
}
|
|
c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
|
|
c1->super->flags |= MRB_FL_CLASS_IS_ORIGIN;
|
|
}
|
|
if (sc->mt) {
|
|
if (sc->tt == MRB_TT_ICLASS && !(sc->flags & MRB_FL_CLASS_IS_ORIGIN)) {
|
|
dc->mt = sc->mt;
|
|
}
|
|
else {
|
|
dc->mt = mt_copy(mrb, sc->mt);
|
|
}
|
|
}
|
|
dc->super = sc->super;
|
|
dc->flags = sc->flags;
|
|
dc->frozen = 0;
|
|
}
|
|
|
|
/* 15.3.1.3.16 */
|
|
mrb_value mrb_obj_init_copy(mrb_state *mrb, mrb_value self);
|
|
|
|
static void
|
|
init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
|
|
{
|
|
mrb_assert((mrb_type(dest) == mrb_type(obj)));
|
|
switch (mrb_unboxed_type(obj)) {
|
|
case MRB_TT_ICLASS:
|
|
copy_class(mrb, dest, obj);
|
|
return;
|
|
case MRB_TT_CLASS:
|
|
case MRB_TT_MODULE:
|
|
copy_class(mrb, dest, obj);
|
|
mrb_iv_copy(mrb, dest, obj);
|
|
mrb_iv_remove(mrb, dest, MRB_SYM(__classname__));
|
|
break;
|
|
case MRB_TT_OBJECT:
|
|
case MRB_TT_SCLASS:
|
|
case MRB_TT_HASH:
|
|
case MRB_TT_CDATA:
|
|
case MRB_TT_EXCEPTION:
|
|
mrb_iv_copy(mrb, dest, obj);
|
|
break;
|
|
case MRB_TT_ISTRUCT:
|
|
mrb_istruct_copy(dest, obj);
|
|
break;
|
|
#if !defined(MRB_NO_FLOAT) && defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE)
|
|
case MRB_TT_FLOAT:
|
|
{
|
|
struct RFloat *f = (struct RFloat*)mrb_obj_ptr(dest);
|
|
f->f = mrb_float(obj);
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef MRB_USE_BIGINT
|
|
case MRB_TT_BIGINT:
|
|
mrb_bint_copy(mrb, dest, obj);
|
|
break;
|
|
#endif
|
|
#ifdef MRB_USE_RATIONAL
|
|
case MRB_TT_RATIONAL:
|
|
mrb_rational_copy(mrb, dest, obj);
|
|
break;
|
|
#endif
|
|
#ifdef MRB_USE_COMPLEX
|
|
case MRB_TT_COMPLEX:
|
|
mrb_complex_copy(mrb, dest, obj);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (!mrb_func_basic_p(mrb, dest, MRB_SYM(initialize_copy), mrb_obj_init_copy)) {
|
|
mrb_funcall_argv(mrb, dest, MRB_SYM(initialize_copy), 1, &obj);
|
|
}
|
|
}
|
|
|
|
/* 15.3.1.3.8 */
|
|
/*
|
|
* call-seq:
|
|
* obj.clone -> an_object
|
|
*
|
|
* Produces a shallow copy of <i>obj</i>---the instance variables of
|
|
* <i>obj</i> are copied, but not the objects they reference. Copies
|
|
* the frozen state of <i>obj</i>. See also the discussion
|
|
* under <code>Object#dup</code>.
|
|
*
|
|
* class Klass
|
|
* attr_accessor :str
|
|
* end
|
|
* s1 = Klass.new #=> #<Klass:0x401b3a38>
|
|
* s1.str = "Hello" #=> "Hello"
|
|
* s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
|
|
* s2.str[1,4] = "i" #=> "i"
|
|
* s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
|
|
* s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
|
|
*
|
|
* This method may have class-specific behavior. If so, that
|
|
* behavior will be documented under the #+initialize_copy+ method of
|
|
* the class.
|
|
*
|
|
* Some Class(True False Nil Symbol Integer Float) Object cannot clone.
|
|
*/
|
|
MRB_API mrb_value
|
|
mrb_obj_clone(mrb_state *mrb, mrb_value self)
|
|
{
|
|
if (mrb_immediate_p(self)) {
|
|
return self;
|
|
}
|
|
if (mrb_sclass_p(self)) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class");
|
|
}
|
|
struct RObject *p = (struct RObject*)mrb_obj_alloc(mrb, mrb_unboxed_type(self), mrb_obj_class(mrb, self));
|
|
p->c = mrb_singleton_class_clone(mrb, self);
|
|
mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c);
|
|
|
|
mrb_value clone = mrb_obj_value(p);
|
|
init_copy(mrb, clone, self);
|
|
p->frozen = mrb_obj_ptr(self)->frozen;
|
|
|
|
return clone;
|
|
}
|
|
|
|
/* 15.3.1.3.9 */
|
|
/*
|
|
* call-seq:
|
|
* obj.dup -> an_object
|
|
*
|
|
* Produces a shallow copy of <i>obj</i>---the instance variables of
|
|
* <i>obj</i> are copied, but not the objects they reference.
|
|
* <code>dup</code> copies the frozen state of <i>obj</i>. See also
|
|
* the discussion under <code>Object#clone</code>. In general,
|
|
* <code>clone</code> and <code>dup</code> may have different semantics
|
|
* in descendant classes. While <code>clone</code> is used to duplicate
|
|
* an object, including its internal state, <code>dup</code> typically
|
|
* uses the class of the descendant object to create the new instance.
|
|
*
|
|
* This method may have class-specific behavior. If so, that
|
|
* behavior will be documented under the #+initialize_copy+ method of
|
|
* the class.
|
|
*/
|
|
|
|
MRB_API mrb_value
|
|
mrb_obj_dup(mrb_state *mrb, mrb_value obj)
|
|
{
|
|
if (mrb_immediate_p(obj)) {
|
|
return obj;
|
|
}
|
|
if (mrb_sclass_p(obj)) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class");
|
|
}
|
|
|
|
struct RBasic *p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj));
|
|
mrb_value dup = mrb_obj_value(p);
|
|
init_copy(mrb, dup, obj);
|
|
|
|
return dup;
|
|
}
|
|
|
|
/* implementation of __id__ */
|
|
mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
|
|
|
|
mrb_noreturn void
|
|
mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
|
|
{
|
|
mrb_no_method_error(mrb, name, args, "undefined method '%n' for %T", name, self);
|
|
}
|
|
|
|
/* 15.3.1.3.30 */
|
|
/*
|
|
* call-seq:
|
|
* obj.method_missing(symbol [, *args] ) -> result
|
|
*
|
|
* Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
|
|
* <i>symbol</i> is the symbol for the method called, and <i>args</i>
|
|
* are any arguments that were passed to it. By default, the interpreter
|
|
* raises an error when this method is called. However, it is possible
|
|
* to override the method to provide more dynamic behavior.
|
|
* If it is decided that a particular method should not be handled, then
|
|
* <i>super</i> should be called, so that ancestors can pick up the
|
|
* missing method.
|
|
* The example below creates
|
|
* a class <code>Roman</code>, which responds to methods with names
|
|
* consisting of roman numerals, returning the corresponding integer
|
|
* values.
|
|
*
|
|
* class Roman
|
|
* def romanToInt(str)
|
|
* # ...
|
|
* end
|
|
* def method_missing(sym)
|
|
* str = sym.to_s
|
|
* romanToInt(str)
|
|
* end
|
|
* end
|
|
*
|
|
* r = Roman.new
|
|
* r.iv #=> 4
|
|
* r.xxiii #=> 23
|
|
* r.mm #=> 2000
|
|
*/
|
|
mrb_value
|
|
mrb_obj_missing(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
mrb_sym name;
|
|
const mrb_value *a;
|
|
mrb_int alen;
|
|
|
|
mrb->c->ci->mid = 0;
|
|
mrb_get_args(mrb, "n*!", &name, &a, &alen);
|
|
mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
|
|
/* not reached */
|
|
return mrb_nil_value();
|
|
}
|
|
|
|
static mrb_value
|
|
inspect_main(mrb_state *mrb, mrb_value mod)
|
|
{
|
|
return mrb_str_new_lit(mrb, "main");
|
|
}
|
|
|
|
static const mrb_code new_iseq[] = {
|
|
OP_ENTER, 0x0, 0x10, 0x3, // OP_ENTER 0:0:1:0:0:1:1
|
|
OP_SSEND, 4, 0, 0, // OP_SSEND R4 :allocate n=0
|
|
OP_MOVE, 0, 4, // OP_MOVE R0 R4
|
|
OP_MOVE, 4, 3, // OP_MOVE R4 R3 (&)
|
|
OP_MOVE, 3, 2, // OP_MOVE R3 R2 (**)
|
|
OP_MOVE, 2, 1, // OP_MOVE R2 R1 (*)
|
|
OP_SSENDB, 1, 1, 255, // OP_SSENDB R1 :initialize n=*|nk=*
|
|
OP_RETURN, 0 // OP_RETURN R0
|
|
};
|
|
|
|
MRB_PRESYM_DEFINE_VAR_AND_INITER(new_syms, 2, MRB_SYM(allocate), MRB_SYM(initialize))
|
|
|
|
static const mrb_irep new_irep = {
|
|
4, 6, 0, MRB_IREP_STATIC,
|
|
new_iseq, NULL, new_syms, NULL, NULL, NULL,
|
|
sizeof(new_iseq), 0, 2, 0, 0,
|
|
};
|
|
|
|
mrb_alignas(8)
|
|
static const struct RProc new_proc = {
|
|
NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_OBJ_IS_FROZEN, MRB_PROC_SCOPE | MRB_PROC_STRICT,
|
|
{ &new_irep }, NULL, { NULL }
|
|
};
|
|
|
|
static void
|
|
init_class_new(mrb_state *mrb, struct RClass *cls)
|
|
{
|
|
mrb_method_t m;
|
|
|
|
MRB_PRESYM_INIT_SYMBOLS(mrb, new_syms);
|
|
MRB_METHOD_FROM_PROC(m, &new_proc);
|
|
mrb_define_method_raw(mrb, cls, MRB_SYM(new), m);
|
|
}
|
|
|
|
static const mrb_code neq_iseq[] = {
|
|
OP_ENTER, 0x4, 0, 0, // OP_ENTER 1:0:0:0:0:0:0
|
|
OP_EQ, 0, // OP_EQ R0 (R1)
|
|
OP_JMPNOT, 0, 0, 5, // OP_JMPNOT R3 016
|
|
OP_LOADF, 0, // OP_LOADF R0 (true)
|
|
OP_JMP, 0, 2, // OP_JMP R1 018
|
|
OP_LOADT, 0, // OP_LOADT R3 (true)
|
|
OP_RETURN, 0 // OP_RETURN R0
|
|
};
|
|
|
|
static const mrb_irep neq_irep = {
|
|
4, 6, 0, MRB_IREP_STATIC,
|
|
neq_iseq, NULL, NULL, NULL, NULL, NULL,
|
|
sizeof(neq_iseq), 0, 2, 0, 0,
|
|
};
|
|
|
|
mrb_alignas(8)
|
|
static const struct RProc neq_proc = {
|
|
NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_OBJ_IS_FROZEN, MRB_PROC_SCOPE | MRB_PROC_STRICT,
|
|
{ &neq_irep }, NULL, { NULL }
|
|
};
|
|
|
|
void
|
|
mrb_init_class(mrb_state *mrb)
|
|
{
|
|
struct RClass *bob; /* BasicObject */
|
|
struct RClass *obj; /* Object */
|
|
struct RClass *mod; /* Module */
|
|
struct RClass *cls; /* Class */
|
|
|
|
/* boot class hierarchy */
|
|
bob = boot_defclass(mrb, 0);
|
|
obj = boot_defclass(mrb, bob); mrb->object_class = obj;
|
|
mod = boot_defclass(mrb, obj); mrb->module_class = mod;/* obj -> mod */
|
|
cls = boot_defclass(mrb, mod); mrb->class_class = cls; /* obj -> cls */
|
|
/* fix-up loose ends */
|
|
bob->c = obj->c = mod->c = cls->c = cls;
|
|
make_metaclass(mrb, bob);
|
|
make_metaclass(mrb, obj);
|
|
make_metaclass(mrb, mod);
|
|
make_metaclass(mrb, cls);
|
|
|
|
/* name basic classes */
|
|
mrb_define_const_id(mrb, bob, MRB_SYM(BasicObject), mrb_obj_value(bob));
|
|
mrb_define_const_id(mrb, obj, MRB_SYM(Object), mrb_obj_value(obj));
|
|
mrb_define_const_id(mrb, obj, MRB_SYM(Module), mrb_obj_value(mod));
|
|
mrb_define_const_id(mrb, obj, MRB_SYM(Class), mrb_obj_value(cls));
|
|
|
|
/* name each classes */
|
|
mrb_class_name_class(mrb, NULL, bob, MRB_SYM(BasicObject));
|
|
mrb_class_name_class(mrb, NULL, obj, MRB_SYM(Object)); /* 15.2.1 */
|
|
mrb_class_name_class(mrb, NULL, mod, MRB_SYM(Module)); /* 15.2.2 */
|
|
mrb_class_name_class(mrb, NULL, cls, MRB_SYM(Class)); /* 15.2.3 */
|
|
|
|
MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS);
|
|
mrb_define_method_id(mrb, bob, MRB_SYM(initialize), mrb_do_nothing, MRB_ARGS_NONE());
|
|
mrb_define_method_id(mrb, bob, MRB_OPSYM(not), mrb_bob_not, MRB_ARGS_NONE());
|
|
mrb_define_method_id(mrb, bob, MRB_OPSYM(eq), mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */
|
|
mrb_define_method_id(mrb, bob, MRB_SYM(__id__), mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.4 */
|
|
mrb_define_method_id(mrb, bob, MRB_SYM(__send__), mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.5 */
|
|
mrb_define_method_id(mrb, bob, MRB_SYM_Q(equal), mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
|
|
mrb_define_method_id(mrb, bob, MRB_SYM(instance_eval), mrb_obj_instance_eval, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.3.1.3.18 */
|
|
mrb_define_private_method_id(mrb, bob, MRB_SYM(singleton_method_added), mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
mrb_define_private_method_id(mrb, bob, MRB_SYM(singleton_method_removed),mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
mrb_define_private_method_id(mrb, bob, MRB_SYM(singleton_method_undefined),mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
mrb_define_private_method_id(mrb, bob, MRB_SYM(method_missing), mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */
|
|
|
|
mrb_method_t m;
|
|
MRB_METHOD_FROM_PROC(m, &neq_proc);
|
|
mrb_define_method_raw(mrb, bob, MRB_OPSYM(neq), m);
|
|
|
|
mrb_define_class_method_id(mrb, cls, MRB_SYM(new), mrb_class_new_class, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK());
|
|
mrb_define_method_id(mrb, cls, MRB_SYM(allocate), mrb_instance_alloc, MRB_ARGS_NONE());
|
|
mrb_define_method_id(mrb, cls, MRB_SYM(superclass), mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */
|
|
mrb_define_method_id(mrb, cls, MRB_SYM(initialize), mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */
|
|
mrb_define_private_method_id(mrb, cls, MRB_SYM(inherited), mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
|
|
init_class_new(mrb, cls);
|
|
|
|
MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE);
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(extended), mrb_do_nothing, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(prepended), mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
mrb_define_method_id(mrb, mod, MRB_SYM_Q(include), mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */
|
|
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(include), mrb_mod_include, MRB_ARGS_REQ(1)); /* 15.2.2.4.27 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(prepend), mrb_mod_prepend, MRB_ARGS_REQ(1));
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(class_eval), mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(included), mrb_do_nothing, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(initialize), mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(module_eval), mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(module_function), mrb_mod_module_function, MRB_ARGS_ANY());
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(private), mrb_mod_private, MRB_ARGS_ANY()); /* 15.2.2.4.36 */
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(protected), mrb_mod_protected, MRB_ARGS_ANY()); /* 15.2.2.4.37 */
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(public), mrb_mod_public, MRB_ARGS_ANY()); /* 15.2.2.4.38 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(attr_accessor), mrb_mod_attr_accessor, MRB_ARGS_ANY()); /* 15.2.2.4.12 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(attr_reader), mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(attr_writer), mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */
|
|
mrb_define_alias_id(mrb, mod, MRB_SYM(attr), MRB_SYM(attr_reader)); /* 15.2.2.4.11 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(to_s), mrb_mod_to_s, MRB_ARGS_NONE());
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(inspect), mrb_mod_to_s, MRB_ARGS_NONE());
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(alias_method), mrb_mod_alias, MRB_ARGS_ANY()); /* 15.2.2.4.8 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(ancestors), mrb_mod_ancestors, MRB_ARGS_NONE()); /* 15.2.2.4.9 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(undef_method), mrb_mod_undef, MRB_ARGS_ANY()); /* 15.2.2.4.41 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM_Q(const_defined), mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(const_get), mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(const_set), mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(remove_const), mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(const_missing), mrb_mod_const_missing, MRB_ARGS_REQ(1));
|
|
mrb_define_method_id(mrb, mod, MRB_SYM_Q(method_defined), mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(define_method), mod_define_method, MRB_ARGS_ARG(1,1));
|
|
mrb_define_method_id(mrb, mod, MRB_OPSYM(eqq), mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */
|
|
mrb_define_method_id(mrb, mod, MRB_SYM(dup), mrb_mod_dup, MRB_ARGS_NONE());
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(method_added), mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(method_removed), mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(method_undefined), mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
mrb_define_private_method_id(mrb, mod, MRB_SYM(const_added), mrb_do_nothing, MRB_ARGS_REQ(1));
|
|
|
|
mrb_undef_method_id(mrb, cls, MRB_SYM(module_function));
|
|
|
|
mrb->top_self = MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mrb->object_class);
|
|
mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(inspect), inspect_main, MRB_ARGS_NONE());
|
|
mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(to_s), inspect_main, MRB_ARGS_NONE());
|
|
mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(define_method), top_define_method, MRB_ARGS_ARG(1,1));
|
|
mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(public), top_public, MRB_ARGS_ANY());
|
|
mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(private), top_private, MRB_ARGS_ANY());
|
|
mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(protected), top_protected, MRB_ARGS_ANY());
|
|
}
|