Files
mruby-mruby/src/class.c
T
Yukihiro "Matz" Matsumoto 6766fcb905 class.c (mt_rehash): Fix mrb_calloc argument order
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).
2025-05-11 20:11:56 +09:00

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());
}