From 58daab21cd043e1dc37024a7f99b396788372918 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 9 Mar 2024 14:19:48 +0100 Subject: Merging upstream version 1.44.3. Signed-off-by: Daniel Baumann --- web/server/h2o/libh2o/deps/mruby/src/class.c | 2474 ++++++++++++++++++++++++++ 1 file changed, 2474 insertions(+) create mode 100644 web/server/h2o/libh2o/deps/mruby/src/class.c (limited to 'web/server/h2o/libh2o/deps/mruby/src/class.c') diff --git a/web/server/h2o/libh2o/deps/mruby/src/class.c b/web/server/h2o/libh2o/deps/mruby/src/class.c new file mode 100644 index 000000000..e3888001b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/class.c @@ -0,0 +1,2474 @@ +/* +** class.c - Class class +** +** See Copyright Notice in mruby.h +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal) + +void +mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c) +{ + khiter_t k; + khash_t(mt) *h = c->mt; + + if (!h) return; + for (k = kh_begin(h); k != kh_end(h); k++) { + if (kh_exist(h, k)) { + struct RProc *m = kh_value(h, k); + if (m) { + mrb_gc_mark(mrb, (struct RBasic*)m); + } + } + } +} + +size_t +mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c) +{ + khash_t(mt) *h = c->mt; + + if (!h) return 0; + return kh_size(h); +} + +void +mrb_gc_free_mt(mrb_state *mrb, struct RClass *c) +{ + kh_destroy(mt, 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_intern_lit(mrb, "__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) { + mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"), + mrb_obj_value(outer)); + } + return; + } + mrb_str_cat_cstr(mrb, name, "::"); + mrb_str_cat_cstr(mrb, name, mrb_sym2name(mrb, id)); + } + mrb_obj_iv_set(mrb, (struct RObject*)c, nsym, name); +} + +static void +setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) +{ + mrb_class_name_class(mrb, outer, c, id); + mrb_obj_iv_set(mrb, (struct RObject*)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 *sc, *c; + + if (o->c->tt == MRB_TT_SCLASS) return; + sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); + sc->flags |= MRB_FLAG_IS_INHERITED; + sc->mt = kh_init(mt, mrb); + sc->iv = 0; + 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_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o); + mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o)); +} + +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, "%S is not a class/module", mrb_inspect(mrb, obj)); + } +} + +static struct RClass* +define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer) +{ + struct RClass *m; + + if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { + return module_from_sym(mrb, outer, name); + } + 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); +} + +MRB_API 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_type(old) != MRB_TT_MODULE) { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old)); + } + return mrb_class_ptr(old); + } + return define_module(mrb, id, mrb_class_ptr(outer)); +} + +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 %S (%S not %S)", + mrb_sym2str(mrb, name), + mrb_obj_value(c->super), mrb_obj_value(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 '%S', Object assumed", mrb_sym2str(mrb, 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_bob_init(mrb_state *mrb, mrb_value); +#ifdef MRB_METHOD_CACHE +static void mc_clear_all(mrb_state *mrb); +static void mc_clear_by_class(mrb_state *mrb, struct RClass*); +static void mc_clear_by_id(mrb_state *mrb, struct RClass*, mrb_sym); +#else +#define mc_clear_all(mrb) +#define mc_clear_by_class(mrb,c) +#define mc_clear_by_id(mrb,c,s) +#endif + +static void +mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) +{ + mrb_value s; + mrb_sym mid; + + if (!super) + super = mrb->object_class; + super->flags |= MRB_FLAG_IS_INHERITED; + s = mrb_obj_value(super); + mc_clear_by_class(mrb, klass); + mid = mrb_intern_lit(mrb, "inherited"); + if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) { + mrb_value c = mrb_obj_value(klass); + mrb_funcall_argv(mrb, s, mid, 1, &c); + } +} + +MRB_API 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_type(super) != MRB_TT_CLASS) { + mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", + mrb_inspect(mrb, super)); + } + s = mrb_class_ptr(super); + } + else { + s = 0; + } + 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_type(old) != MRB_TT_CLASS) { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, 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 class %S", 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_value sym = mrb_check_intern_cstr(mrb, name); + if (mrb_nil_p(sym)) { + return FALSE; + } + return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym)); +} + +MRB_API mrb_bool +mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name) +{ + mrb_value sym = mrb_check_intern_cstr(mrb, name); + if (mrb_nil_p(sym)) { + return FALSE; + } + return mrb_const_defined_at(mrb, mrb_obj_value(outer), mrb_symbol(sym)); +} + +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(mrb_state *mrb, const char *name) +{ + return mrb_class_get_under(mrb, mrb->object_class, name); +} + +MRB_API struct RClass* +mrb_exc_get(mrb_state *mrb, const char *name) +{ + struct RClass *exc, *e; + mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), + mrb_intern_cstr(mrb, name)); + + if (mrb_type(c) != MRB_TT_CLASS) { + mrb_raise(mrb, mrb->eException_class, "exception corrupted"); + } + exc = e = mrb_class_ptr(c); + + while (e) { + if (e == mrb->eException_class) + return exc; + e = e->super; + } + return mrb->eException_class; +} + +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(mrb_state *mrb, const char *name) +{ + return mrb_module_get_under(mrb, mrb->object_class, name); +} + +/*! + * Defines a class under the namespace of \a outer. + * \param outer a class which contains the new class. + * \param id 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(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) +{ + mrb_sym id = mrb_intern_cstr(mrb, name); + struct RClass * c; + +#if 0 + if (!super) { + mrb_warn(mrb, "no super class for '%S::%S', Object assumed", + mrb_obj_value(outer), mrb_sym2str(mrb, id)); + } +#endif + c = define_class(mrb, id, super, outer); + setup_class(mrb, outer, c, id); + return c; +} + +MRB_API void +mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) +{ + khash_t(mt) *h; + khiter_t k; + MRB_CLASS_ORIGIN(c); + h = c->mt; + + if (MRB_FROZEN_P(c)) { + if (c->tt == MRB_TT_MODULE) + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen module"); + else + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen class"); + } + if (!h) h = c->mt = kh_init(mt, mrb); + k = kh_put(mt, mrb, h, mid); + kh_value(h, k) = p; + if (p) { + p->c = NULL; + mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); + } + mc_clear_by_id(mrb, c, mid); +} + +MRB_API void +mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec) +{ + struct RProc *p; + int ai = mrb_gc_arena_save(mrb); + + p = mrb_proc_new_cfunc(mrb, func); + p->target_class = c; + mrb_define_method_raw(mrb, c, mid, p); + mrb_gc_arena_restore(mrb, ai); +} + +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); +} + +/* a function to raise NotImplementedError with current method name */ +MRB_API void +mrb_notimplement(mrb_state *mrb) +{ + const char *str; + mrb_int len; + mrb_callinfo *ci = mrb->c->ci; + + if (ci->mid) { + str = mrb_sym2name_len(mrb, ci->mid, &len); + mrb_raisef(mrb, E_NOTIMP_ERROR, + "%S() function is unimplemented on this machine", + mrb_str_new_static(mrb, str, (size_t)len)); + } +} + +/* 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 mrb_value +check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m) +{ + mrb_value tmp; + + tmp = mrb_check_convert_type(mrb, val, t, c, m); + if (mrb_nil_p(tmp)) { + mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c)); + } + return tmp; +} + +static mrb_value +to_str(mrb_state *mrb, mrb_value val) +{ + return check_type(mrb, val, MRB_TT_STRING, "String", "to_str"); +} + +static mrb_value +to_ary(mrb_state *mrb, mrb_value val) +{ + return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); +} + +static mrb_value +to_hash(mrb_state *mrb, mrb_value val) +{ + return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash"); +} + +static mrb_sym +to_sym(mrb_state *mrb, mrb_value ss) +{ + if (mrb_type(ss) == MRB_TT_SYMBOL) { + return mrb_symbol(ss); + } + else if (mrb_string_p(ss)) { + return mrb_intern_str(mrb, to_str(mrb, ss)); + } + else { + mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0); + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj); + /* not reached */ + return 0; + } +} + +/* + 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] + 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 [char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil + z: String [char*] NUL terminated string; z! gives NULL for nil + a: Array [mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil + f: Float [mrb_float] + i: Integer [mrb_int] + b: Boolean [mrb_bool] + n: 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 + I: Inline struct [void*] + &: Block [mrb_value] + *: rest argument [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 + */ +MRB_API mrb_int +mrb_get_args(mrb_state *mrb, const char *format, ...) +{ + const char *fmt = format; + char c; + int i = 0; + va_list ap; + int argc = mrb->c->ci->argc; + int arg_i = 0; + mrb_value *array_argv; + mrb_bool opt = FALSE; + mrb_bool opt_skip = TRUE; + mrb_bool given = TRUE; + + va_start(ap, format); + if (argc < 0) { + struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); + + argc = ARY_LEN(a); + array_argv = ARY_PTR(a); + } + else { + array_argv = NULL; + } + +#define ARGV \ + (array_argv ? array_argv : (mrb->c->stack + 1)) + + while ((c = *fmt++)) { + switch (c) { + case '|': + opt = TRUE; + break; + case '*': + opt_skip = FALSE; + goto check_exit; + case '!': + break; + case '&': case '?': + if (opt) opt_skip = FALSE; + break; + default: + break; + } + } + + check_exit: + opt = FALSE; + i = 0; + while ((c = *format++)) { + switch (c) { + case '|': case '*': case '&': case '?': + break; + default: + if (argc <= i) { + if (opt) { + given = FALSE; + } + else { + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + } + } + break; + } + + switch (c) { + case 'o': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (i < argc) { + *p = ARGV[arg_i++]; + i++; + } + } + break; + case 'C': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (i < argc) { + mrb_value ss; + + ss = ARGV[arg_i++]; + if (!class_ptr_p(ss)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss); + } + *p = ss; + i++; + } + } + break; + case 'S': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *p = ARGV[arg_i++]; + i++; + break; + } + } + if (i < argc) { + *p = to_str(mrb, ARGV[arg_i++]); + i++; + } + } + break; + case 'A': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *p = ARGV[arg_i++]; + i++; + break; + } + } + if (i < argc) { + *p = to_ary(mrb, ARGV[arg_i++]); + i++; + } + } + break; + case 'H': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *p = ARGV[arg_i++]; + i++; + break; + } + } + if (i < argc) { + *p = to_hash(mrb, ARGV[arg_i++]); + i++; + } + } + break; + case 's': + { + mrb_value ss; + char **ps = 0; + mrb_int *pl = 0; + + ps = va_arg(ap, char**); + pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *ps = NULL; + *pl = 0; + i++; arg_i++; + break; + } + } + if (i < argc) { + ss = to_str(mrb, ARGV[arg_i++]); + *ps = RSTRING_PTR(ss); + *pl = RSTRING_LEN(ss); + i++; + } + } + break; + case 'z': + { + mrb_value ss; + const char **ps; + + ps = va_arg(ap, const char**); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *ps = NULL; + i++; arg_i++; + break; + } + } + if (i < argc) { + ss = to_str(mrb, ARGV[arg_i++]); + *ps = mrb_string_value_cstr(mrb, &ss); + i++; + } + } + break; + case 'a': + { + mrb_value aa; + struct RArray *a; + mrb_value **pb; + mrb_int *pl; + + pb = va_arg(ap, mrb_value**); + pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *pb = 0; + *pl = 0; + i++; arg_i++; + break; + } + } + if (i < argc) { + aa = to_ary(mrb, ARGV[arg_i++]); + a = mrb_ary_ptr(aa); + *pb = ARY_PTR(a); + *pl = ARY_LEN(a); + i++; + } + } + break; + case 'I': + { + void* *p; + mrb_value ss; + + p = va_arg(ap, void**); + if (i < argc) { + ss = ARGV[arg_i]; + if (mrb_type(ss) != MRB_TT_ISTRUCT) + { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss); + } + *p = mrb_istruct_ptr(ss); + arg_i++; + i++; + } + } + break; + case 'f': + { + mrb_float *p; + + p = va_arg(ap, mrb_float*); + if (i < argc) { + *p = mrb_to_flo(mrb, ARGV[arg_i]); + arg_i++; + i++; + } + } + break; + case 'i': + { + mrb_int *p; + + p = va_arg(ap, mrb_int*); + if (i < argc) { + switch (mrb_type(ARGV[arg_i])) { + case MRB_TT_FIXNUM: + *p = mrb_fixnum(ARGV[arg_i]); + break; + case MRB_TT_FLOAT: + { + mrb_float f = mrb_float(ARGV[arg_i]); + + if (!FIXABLE_FLOAT(f)) { + mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); + } + *p = (mrb_int)f; + } + break; + case MRB_TT_STRING: + mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer"); + break; + default: + *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i])); + break; + } + arg_i++; + i++; + } + } + break; + case 'b': + { + mrb_bool *boolp = va_arg(ap, mrb_bool*); + + if (i < argc) { + mrb_value b = ARGV[arg_i++]; + *boolp = mrb_test(b); + i++; + } + } + break; + case 'n': + { + mrb_sym *symp; + + symp = va_arg(ap, mrb_sym*); + if (i < argc) { + mrb_value ss; + + ss = ARGV[arg_i++]; + *symp = to_sym(mrb, ss); + i++; + } + } + break; + case 'd': + { + void** datap; + struct mrb_data_type const* type; + + datap = va_arg(ap, void**); + type = va_arg(ap, struct mrb_data_type const*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *datap = 0; + i++; arg_i++; + break; + } + } + if (i < argc) { + *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type); + ++i; + } + } + break; + + case '&': + { + mrb_value *p, *bp; + + p = va_arg(ap, mrb_value*); + if (mrb->c->ci->argc < 0) { + bp = mrb->c->stack + 2; + } + else { + bp = mrb->c->stack + mrb->c->ci->argc + 1; + } + *p = *bp; + } + break; + case '|': + if (opt_skip && i == argc) return argc; + opt = TRUE; + break; + case '?': + { + mrb_bool *p; + + p = va_arg(ap, mrb_bool*); + *p = given; + } + break; + + case '*': + { + mrb_value **var; + mrb_int *pl; + mrb_bool nocopy = array_argv ? TRUE : FALSE; + + if (*format == '!') { + format++; + nocopy = TRUE; + } + var = va_arg(ap, mrb_value**); + pl = va_arg(ap, mrb_int*); + if (argc > i) { + *pl = argc-i; + if (*pl > 0) { + if (nocopy) { + *var = ARGV+arg_i; + } + else { + mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i); + RARRAY(args)->c = NULL; + *var = RARRAY_PTR(args); + } + } + i = argc; + arg_i += *pl; + } + else { + *pl = 0; + *var = NULL; + } + } + break; + default: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1)); + break; + } + } + +#undef ARGV + + if (!c && argc > i) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + } + va_end(ap); + return i; +} + +static struct RClass* +boot_defclass(mrb_state *mrb, struct RClass *super) +{ + struct RClass *c; + + c = (struct RClass*)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); + } + else { + c->super = mrb->object_class; + } + c->mt = kh_init(mt, mrb); + return c; +} + +static void +boot_initmod(mrb_state *mrb, struct RClass *mod) +{ + if (!mod->mt) { + mod->mt = kh_init(mt, mrb); + } +} + +static struct RClass* +include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) +{ + struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); + if (m->tt == MRB_TT_ICLASS) { + m = m->c; + } + MRB_CLASS_ORIGIN(m); + ic->iv = m->iv; + 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 *p, *ic; + void *klass_mt = find_origin(c)->mt; + + while (m) { + int superclass_seen = 0; + + if (m->flags & MRB_FLAG_IS_PREPENDED) + goto skip; + + if (klass_mt && klass_mt == m->mt) + return -1; + + p = c->super; + while (p) { + if (p->tt == MRB_TT_ICLASS) { + if (p->mt == m->mt) { + if (!superclass_seen) { + ins_pos = p; /* move insert point */ + } + goto skip; + } + } else if (p->tt == MRB_TT_CLASS) { + if (!search_super) break; + superclass_seen = 1; + } + p = p->super; + } + + ic = include_class_new(mrb, m, ins_pos->super); + m->flags |= MRB_FLAG_IS_INHERITED; + ins_pos->super = ic; + mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); + mc_clear_by_class(mrb, ins_pos); + ins_pos = ic; + skip: + m = m->super; + } + mc_clear_all(mrb); + return 0; +} + +MRB_API void +mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + int changed = include_module_at(mrb, c, find_origin(c), m, 1); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); + } +} + +MRB_API void +mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + struct RClass *origin; + int changed = 0; + + if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { + origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + origin->flags |= MRB_FLAG_IS_ORIGIN | MRB_FLAG_IS_INHERITED; + origin->super = c->super; + c->super = origin; + origin->mt = c->mt; + c->mt = kh_init(mt, mrb); + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); + c->flags |= MRB_FLAG_IS_PREPENDED; + } + changed = include_module_at(mrb, c, c, m, 0); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); + } +} + +static mrb_value +mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod) +{ + mrb_value klass; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "C", &klass); + mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + return mod; +} + +static mrb_value +mrb_mod_append_features(mrb_state *mrb, mrb_value mod) +{ + mrb_value klass; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "C", &klass); + mrb_include_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + return mod; +} + +/* 15.2.2.4.28 */ +/* + * call-seq: + * mod.include?(module) -> true or false + * + * Returns true if module is included in + * mod or one of mod'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) +{ + mrb_value result; + struct RClass *c = mrb_class_ptr(self); + 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_FLAG_IS_PREPENDED)) { + mrb_ary_push(mrb, result, mrb_obj_value(c)); + } + c = c->super; + } + + return result; +} + +static mrb_value +mrb_mod_extend_object(mrb_state *mrb, mrb_value mod) +{ + mrb_value obj; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "o", &obj); + mrb_include_module(mrb, mrb_class_ptr(mrb_singleton_class(mrb, obj)), mrb_class_ptr(mod)); + return mod; +} + +static mrb_value +mrb_mod_included_modules(mrb_state *mrb, mrb_value self) +{ + mrb_value result; + struct RClass *c = mrb_class_ptr(self); + struct RClass *origin = c; + + MRB_CLASS_ORIGIN(origin); + result = mrb_ary_new(mrb); + while (c) { + if (c != origin && c->tt == MRB_TT_ICLASS) { + if (c->c->tt == MRB_TT_MODULE) { + mrb_ary_push(mrb, result, mrb_obj_value(c->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; +} + +mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int); + +/* 15.2.2.4.33 */ +/* + * call-seq: + * mod.instance_methods(include_super=true) -> array + * + * Returns an array containing the names of the public and protected instance + * methods in the receiver. For a module, these are the public and protected methods; + * for a class, they are the instance (not singleton) methods. With no + * argument, or with an argument that is false, the + * instance methods in mod are returned, otherwise the methods + * in mod and mod's superclasses are returned. + * + * module A + * def method1() end + * end + * class B + * def method2() end + * end + * class C < B + * def method3() end + * end + * + * A.instance_methods #=> [:method1] + * B.instance_methods(false) #=> [:method2] + * C.instance_methods(false) #=> [:method3] + * C.instance_methods(true).length #=> 43 + */ + +static mrb_value +mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod) +{ + struct RClass *c = mrb_class_ptr(mod); + mrb_bool recur = TRUE; + mrb_get_args(mrb, "|b", &recur); + return mrb_class_instance_method_list(mrb, recur, c, 0); +} + +/* implementation of module_eval/class_eval */ +mrb_value mrb_mod_module_eval(mrb_state*, mrb_value); + +static mrb_value +mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod) +{ + return mod; +} + +MRB_API mrb_value +mrb_singleton_class(mrb_state *mrb, mrb_value v) +{ + struct RBasic *obj; + + switch (mrb_type(v)) { + case MRB_TT_FALSE: + if (mrb_nil_p(v)) + return mrb_obj_value(mrb->nil_class); + return mrb_obj_value(mrb->false_class); + case MRB_TT_TRUE: + return mrb_obj_value(mrb->true_class); + case MRB_TT_CPTR: + return mrb_obj_value(mrb->object_class); + case MRB_TT_SYMBOL: + case MRB_TT_FIXNUM: + case MRB_TT_FLOAT: + mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); + return mrb_nil_value(); /* not reached */ + default: + break; + } + obj = mrb_basic_ptr(v); + prepare_singleton_class(mrb, obj); + return mrb_obj_value(obj->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_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_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) +{ + mrb_define_class_method(mrb, c, name, func, aspec); + mrb_define_method(mrb, c, name, func, aspec); +} + +#ifdef MRB_METHOD_CACHE +static void +mc_clear_all(mrb_state *mrb) +{ + struct mrb_cache_entry *mc = mrb->cache; + int i; + + for (i=0; icache; + int i; + + if (c->flags & MRB_FLAG_IS_INHERITED) { + mc_clear_all(mrb); + c->flags &= ~MRB_FLAG_IS_INHERITED; + return; + } + for (i=0; icache; + int i; + + if (c->flags & MRB_FLAG_IS_INHERITED) { + mc_clear_all(mrb); + c->flags &= ~MRB_FLAG_IS_INHERITED; + return; + } + for (i=0; icache[h]; + + if (mc->c == c && mc->mid == mid) { + return mc->m; + } +#endif + + while (c) { + khash_t(mt) *h = c->mt; + + if (h) { + k = kh_get(mt, mrb, h, mid); + if (k != kh_end(h)) { + m = kh_value(h, k); + if (!m) break; + *cp = c; +#ifdef MRB_METHOD_CACHE + mc->c = oc; + mc->mid = mid; + mc->m = m; +#endif + return m; + } + } + c = c->super; + } + return NULL; /* no method */ +} + +MRB_API struct RProc* +mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) +{ + struct RProc *m; + + m = mrb_method_search_vm(mrb, &c, mid); + if (!m) { + mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0); + if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) { + inspect = mrb_any_to_s(mrb, mrb_obj_value(c)); + } + mrb_name_error(mrb, mid, "undefined method '%S' for class %S", + mrb_sym2str(mrb, mid), inspect); + } + return m; +} + +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) +{ + struct RClass *c = mrb_class_ptr(mod); + mrb_value *argv; + mrb_int argc, i; + int ai; + + mrb_get_args(mrb, "*", &argv, &argc); + ai = mrb_gc_arena_save(mrb); + for (i=0; i obj + * + * Creates a new object of class's class, then + * invokes that object's initialize method, + * passing it args. This is the method that ends + * up getting called whenever an object is constructed using + * `.new`. + * + */ + +MRB_API mrb_value +mrb_instance_new(mrb_state *mrb, mrb_value cv) +{ + mrb_value obj, blk; + mrb_value *argv; + mrb_int argc; + mrb_sym init; + + mrb_get_args(mrb, "*&", &argv, &argc, &blk); + obj = mrb_instance_alloc(mrb, cv); + init = mrb_intern_lit(mrb, "initialize"); + if (!mrb_func_basic_p(mrb, obj, init, mrb_bob_init)) { + 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_sym mid; + + obj = mrb_instance_alloc(mrb, mrb_obj_value(c)); + mid = mrb_intern_lit(mrb, "initialize"); + if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) { + mrb_funcall_argv(mrb, obj, mid, argc, argv); + } + return obj; +} + +static mrb_value +mrb_class_initialize(mrb_state *mrb, mrb_value c) +{ + mrb_value a, b; + + mrb_get_args(mrb, "|C&", &a, &b); + if (!mrb_nil_p(b)) { + mrb_yield_with_class(mrb, b, 1, &c, c, mrb_class_ptr(c)); + } + return c; +} + +static mrb_value +mrb_class_new_class(mrb_state *mrb, mrb_value cv) +{ + mrb_int n; + mrb_value super, blk; + mrb_value new_class; + mrb_sym mid; + + n = mrb_get_args(mrb, "|C&", &super, &blk); + if (n == 0) { + super = mrb_obj_value(mrb->object_class); + } + new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); + mid = mrb_intern_lit(mrb, "initialize"); + if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) { + 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; + + 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_bob_init(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 Object level, == returns + * true only if obj and other are the + * same object. Typically, this method is overridden in descendant + * classes to provide class-specific meaning. + * + * Unlike ==, the equal? method should never be + * overridden by subclasses: it is used to determine object identity + * (that is, a.equal?(b) iff a is the same + * object as b). + * + * The eql? method returns true if + * obj and anObject have the same value. Used by + * Hash to test members for equality. For objects of + * class Object, eql? is synonymous with + * ==. Subclasses normally continue this tradition, but + * there are exceptions. Numeric types, for example, + * perform type conversion across ==, but not across + * eql?, 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_args(mrb, "o", &arg); + return mrb_bool_value(mrb_obj_equal(mrb, self, arg)); +} + +static mrb_value +mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self) +{ + mrb_value arg; + + mrb_get_args(mrb, "o", &arg); + return mrb_bool_value(!mrb_equal(mrb, self, arg)); +} + +MRB_API mrb_bool +mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) +{ + struct RProc *m; + + m = mrb_method_search_vm(mrb, &c, mid); + if (!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_value path; + mrb_sym nsym = mrb_intern_lit(mrb, "__classname__"); + + 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)) { + /* topleve class/module */ + const char *str; + mrb_int len; + + str = mrb_sym2name_len(mrb, mrb_symbol(path), &len); + return mrb_str_new(mrb, str, len); + } + 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; + } + return cl; +} + +MRB_API const char* +mrb_class_name(mrb_state *mrb, struct RClass* c) +{ + mrb_value path = mrb_class_path(mrb, c); + if (mrb_nil_p(path)) { + path = mrb_str_new_lit(mrb, "#"); + } + return RSTRING_PTR(path); +} + +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 (%S given)", mrb_obj_value(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) +{ + struct RClass *c; + + if (super) { + mrb_check_inheritable(mrb, super); + } + c = boot_defclass(mrb, super); + if (super) { + MRB_SET_INSTANCE_TT(c, MRB_INSTANCE_TT(super)); + } + make_metaclass(mrb, c); + + return c; +} + +/*! + * Creates a new module. + */ +MRB_API struct RClass* +mrb_module_new(mrb_state *mrb) +{ + struct RClass *m = (struct RClass*)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 obj, now preferred over + * Object#type, 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 class is also a + * reserved word in Ruby. + * + * 1.class #=> Fixnum + * 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) +{ + struct RProc *m = mrb_method_search(mrb, c, b); + + mrb_define_method_raw(mrb, c, a, m); +} + +/*! + * Defines an alias of a method. + * \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)); +} + +/* + * 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. + */ + +static mrb_value +mrb_mod_to_s(mrb_state *mrb, mrb_value klass) +{ + mrb_value str; + + if (mrb_type(klass) == MRB_TT_SCLASS) { + mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__")); + + str = mrb_str_new_lit(mrb, "#"); + } + else { + struct RClass *c; + mrb_value path; + + str = mrb_str_new_capa(mrb, 32); + c = mrb_class_ptr(klass); + path = mrb_class_path(mrb, c); + + if (mrb_nil_p(path)) { + switch (mrb_type(klass)) { + case MRB_TT_CLASS: + mrb_str_cat_lit(mrb, str, "#"); + } + else { + return path; + } + } +} + +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); + return mrb_nil_value(); +} + +static void +undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a) +{ + if (!mrb_obj_respond_to(mrb, c, a)) { + mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c)); + } + else { + mrb_define_method_raw(mrb, c, a, NULL); + } +} + +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(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); +} + +static mrb_value +mrb_mod_undef(mrb_state *mrb, mrb_value mod) +{ + struct RClass *c = mrb_class_ptr(mod); + mrb_int argc; + mrb_value *argv; + + mrb_get_args(mrb, "*", &argv, &argc); + while (argc--) { + undef_method(mrb, c, to_sym(mrb, *argv)); + argv++; + } + return mrb_nil_value(); +} + +static mrb_value +mod_define_method(mrb_state *mrb, mrb_value self) +{ + struct RClass *c = mrb_class_ptr(self); + struct RProc *p; + 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 %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc))); + break; + } + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); + mrb_proc_copy(p, mrb_proc_ptr(blk)); + p->flags |= MRB_PROC_STRICT; + mrb_define_method_raw(mrb, c, mid, p); + return mrb_symbol_value(mid); +} + +static void +check_cv_name_str(mrb_state *mrb, mrb_value str) +{ + const char *s = RSTRING_PTR(str); + mrb_int len = RSTRING_LEN(str); + + if (len < 3 || !(s[0] == '@' && s[1] == '@')) { + mrb_name_error(mrb, mrb_intern_str(mrb, str), "'%S' is not allowed as a class variable name", str); + } +} + +static void +check_cv_name_sym(mrb_state *mrb, mrb_sym id) +{ + check_cv_name_str(mrb, mrb_sym2str(mrb, id)); +} + +/* 15.2.2.4.16 */ +/* + * call-seq: + * obj.class_variable_defined?(symbol) -> true or false + * + * Returns true if the given class variable is defined + * in obj. + * + * class Fred + * @@foo = 99 + * end + * Fred.class_variable_defined?(:@@foo) #=> true + * Fred.class_variable_defined?(:@@bar) #=> false + */ + +static mrb_value +mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod) +{ + mrb_sym id; + + mrb_get_args(mrb, "n", &id); + check_cv_name_sym(mrb, id); + return mrb_bool_value(mrb_cv_defined(mrb, mod, id)); +} + +/* 15.2.2.4.17 */ +/* + * call-seq: + * mod.class_variable_get(symbol) -> obj + * + * Returns the value of the given class variable (or throws a + * NameError exception). The @@ part of the + * variable name should be included for regular class variables + * + * class Fred + * @@foo = 99 + * end + * Fred.class_variable_get(:@@foo) #=> 99 + */ + +static mrb_value +mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod) +{ + mrb_sym id; + + mrb_get_args(mrb, "n", &id); + check_cv_name_sym(mrb, id); + return mrb_cv_get(mrb, mod, id); +} + +/* 15.2.2.4.18 */ +/* + * call-seq: + * obj.class_variable_set(symbol, obj) -> obj + * + * Sets the class variable names by symbol to + * object. + * + * class Fred + * @@foo = 99 + * def foo + * @@foo + * end + * end + * Fred.class_variable_set(:@@foo, 101) #=> 101 + * Fred.new.foo #=> 101 + */ + +static mrb_value +mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod) +{ + mrb_value value; + mrb_sym id; + + mrb_get_args(mrb, "no", &id, &value); + check_cv_name_sym(mrb, id); + mrb_cv_set(mrb, mod, id, value); + return value; +} + +/* 15.2.2.4.39 */ +/* + * call-seq: + * remove_class_variable(sym) -> obj + * + * Removes the definition of the sym, returning that + * constant's value. + * + * class Dummy + * @@var = 99 + * puts @@var + * p class_variables + * remove_class_variable(:@@var) + * p class_variables + * end + * + * produces: + * + * 99 + * [:@@var] + * [] + */ + +static mrb_value +mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod) +{ + mrb_value val; + mrb_sym id; + + mrb_get_args(mrb, "n", &id); + check_cv_name_sym(mrb, id); + + val = mrb_iv_remove(mrb, mod, id); + if (!mrb_undef_p(val)) return val; + + if (mrb_cv_defined(mrb, mod, id)) { + mrb_name_error(mrb, id, "cannot remove %S for %S", + mrb_sym2str(mrb, id), mod); + } + + mrb_name_error(mrb, id, "class variable %S not defined for %S", + mrb_sym2str(mrb, id), mod); + + /* not reached */ + return mrb_nil_value(); +} + +/* 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)); +} + +static void +remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) +{ + struct RClass *c = mrb_class_ptr(mod); + khash_t(mt) *h = find_origin(c)->mt; + khiter_t k; + + if (h) { + k = kh_get(mt, mrb, h, mid); + if (k != kh_end(h)) { + kh_del(mt, mrb, h, k); + mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid)); + return; + } + } + + mrb_name_error(mrb, mid, "method '%S' not defined in %S", + mrb_sym2str(mrb, mid), mod); +} + +/* 15.2.2.4.41 */ +/* + * call-seq: + * remove_method(symbol) -> self + * + * Removes the method identified by _symbol_ from the current + * class. For an example, see Module.undef_method. + */ + +static mrb_value +mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) +{ + mrb_int argc; + mrb_value *argv; + + mrb_get_args(mrb, "*", &argv, &argc); + while (argc--) { + remove_method(mrb, mod, to_sym(mrb, *argv)); + argv++; + } + return mod; +} + + + +static void +check_const_name_str(mrb_state *mrb, mrb_value str) +{ + if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) { + mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str); + } +} + +static void +check_const_name_sym(mrb_state *mrb, mrb_sym id) +{ + check_const_name_str(mrb, mrb_sym2str(mrb, id)); +} + +static mrb_value +const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool inherit) +{ + 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_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); + return const_defined(mrb, mod, id, inherit); +} + +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_sym id; + char *ptr; + mrb_int off, end, len; + + mrb_get_args(mrb, "o", &path); + + if (mrb_symbol_p(path)) { + /* const get with symbol */ + id = mrb_symbol(path); + return mrb_const_get_sym(mrb, mod, id); + } + + /* const get with class path string */ + path = mrb_string_type(mrb, path); + ptr = RSTRING_PTR(path); + len = RSTRING_LEN(path); + off = 0; + + while (off < len) { + end = mrb_str_index_lit(mrb, path, "::", off); + end = (end == -1) ? len : end; + id = mrb_intern(mrb, ptr+off, end-off); + mod = mrb_const_get_sym(mrb, mod, id); + off = (end == len) ? end : end+2; + } + + 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_value val; + + mrb_get_args(mrb, "n", &id); + check_const_name_sym(mrb, id); + val = mrb_iv_remove(mrb, mod, id); + if (mrb_undef_p(val)) { + mrb_name_error(mrb, id, "constant %S not defined", mrb_sym2str(mrb, id)); + } + return val; +} + +static mrb_value +mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) +{ + mrb_sym sym; + + mrb_get_args(mrb, "n", &sym); + + if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) { + mrb_name_error(mrb, sym, "uninitialized constant %S::%S", + mod, + mrb_sym2str(mrb, sym)); + } + else { + mrb_name_error(mrb, sym, "uninitialized constant %S", + mrb_sym2str(mrb, sym)); + } + /* not reached */ + return mrb_nil_value(); +} + +static mrb_value +mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) +{ + mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented"); + return mrb_nil_value(); /* not reached */ +} + +static mrb_value +mrb_mod_eqq(mrb_state *mrb, mrb_value mod) +{ + mrb_value obj; + mrb_bool eqq; + + mrb_get_args(mrb, "o", &obj); + eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); + + return mrb_bool_value(eqq); +} + +MRB_API mrb_value +mrb_mod_module_function(mrb_state *mrb, mrb_value mod) +{ + mrb_value *argv; + mrb_int argc, i; + mrb_sym mid; + struct RProc *method_rproc; + struct RClass *rclass; + int ai; + + 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); */ + + for (i=0; ic, mid, method_rproc); + mrb_gc_arena_restore(mrb, ai); + } + + return mod; +} + +/* implementation of __id__ */ +mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self); +/* implementation of instance_eval */ +mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); +/* implementation of Module.nesting */ +mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value); + +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(mrb, bob, "BasicObject", mrb_obj_value(bob)); + mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob)); + mrb_define_const(mrb, obj, "Object", mrb_obj_value(obj)); + mrb_define_const(mrb, obj, "Module", mrb_obj_value(mod)); + mrb_define_const(mrb, obj, "Class", mrb_obj_value(cls)); + + /* name each classes */ + mrb_class_name_class(mrb, NULL, bob, mrb_intern_lit(mrb, "BasicObject")); + mrb_class_name_class(mrb, NULL, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */ + mrb_class_name_class(mrb, NULL, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ + mrb_class_name_class(mrb, NULL, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */ + + mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */ + MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); + + MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS); + mrb_define_method(mrb, bob, "initialize", mrb_bob_init, MRB_ARGS_NONE()); + mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE()); + mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */ + mrb_define_method(mrb, bob, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */ + mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */ + mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */ + + mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, cls, "superclass", mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */ + mrb_define_method(mrb, cls, "new", mrb_instance_new, MRB_ARGS_ANY()); /* 15.2.3.3.3 */ + mrb_define_method(mrb, cls, "initialize", mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */ + mrb_define_method(mrb, cls, "inherited", mrb_bob_init, MRB_ARGS_REQ(1)); + + MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE); + mrb_define_method(mrb, mod, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */ + mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */ + mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */ + mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */ + mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */ + mrb_define_method(mrb, mod, "prepended", mrb_bob_init, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mod, "prepend_features", mrb_mod_prepend_features, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mod, "include?", mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */ + mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */ + mrb_define_method(mrb, mod, "class_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */ + mrb_define_method(mrb, mod, "included", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */ + mrb_define_method(mrb, mod, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */ + mrb_define_method(mrb, mod, "initialize", mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */ + mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */ + mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */ + mrb_define_method(mrb, mod, "module_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */ + mrb_define_method(mrb, mod, "module_function", mrb_mod_module_function, MRB_ARGS_ANY()); + mrb_define_method(mrb, mod, "private", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.36 */ + mrb_define_method(mrb, mod, "protected", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.37 */ + mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ + mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ + mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ + mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ + mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ + mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, mod, "inspect", mrb_mod_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, mod, "alias_method", mrb_mod_alias, MRB_ARGS_ANY()); /* 15.2.2.4.8 */ + mrb_define_method(mrb, mod, "ancestors", mrb_mod_ancestors, MRB_ARGS_NONE()); /* 15.2.2.4.9 */ + mrb_define_method(mrb, mod, "undef_method", mrb_mod_undef, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ + mrb_define_method(mrb, mod, "const_defined?", mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */ + mrb_define_method(mrb, mod, "const_get", mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */ + mrb_define_method(mrb, mod, "const_set", mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */ + mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */ + mrb_define_method(mrb, mod, "remove_const", mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */ + mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_ARG(1,1)); + mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */ + mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); + mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */ + mrb_define_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */ + + mrb_undef_method(mrb, cls, "append_features"); + mrb_undef_method(mrb, cls, "extend_object"); +} -- cgit v1.2.3