diff options
Diffstat (limited to 'mrbgems/mruby-method/src/method.c')
| -rw-r--r-- | mrbgems/mruby-method/src/method.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/mrbgems/mruby-method/src/method.c b/mrbgems/mruby-method/src/method.c new file mode 100644 index 000000000..763bc5f7d --- /dev/null +++ b/mrbgems/mruby-method/src/method.c @@ -0,0 +1,549 @@ +#include "mruby.h" +#include "mruby/array.h" +#include "mruby/class.h" +#include "mruby/variable.h" +#include "mruby/proc.h" +#include "mruby/string.h" +#include "mruby/presym.h" + +mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); +mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p); + +static mrb_value +args_shift(mrb_state *mrb) +{ + mrb_callinfo *ci = mrb->c->ci; + mrb_value *argv = ci->stack + 1; + + if (ci->n < 15) { + mrb_value obj = argv[0]; + memmove(argv, argv + 1, (ci->n + 1 /* block */ - 1 /* first value */) * sizeof(mrb_value)); + ci->n--; + return obj; + } + else if (ci->n == 15 && RARRAY_LEN(*argv) > 0) { + return mrb_ary_shift(mrb, *argv); + } + else { + mrb_argnum_error(mrb, 0, 1, -1); + return mrb_undef_value(); /* not reached */ + } +} + +static void +args_unshift(mrb_state *mrb, mrb_value obj) +{ + mrb_callinfo *ci = mrb->c->ci; + mrb_value *argv = ci->stack + 1; + + if (ci->n < 15) { + mrb_value block = argv[ci->n]; + argv[0] = mrb_ary_new_from_values(mrb, ci->n, argv); + argv[1] = block; + ci->n = 15; + } + + mrb_ary_unshift(mrb, *argv, obj); +} + +static struct RProc* +method_missing_prepare(mrb_state *mrb, mrb_sym *mid, mrb_value recv, struct RClass **tc) +{ + const mrb_sym id_method_missing = MRB_SYM(method_missing); + mrb_callinfo *ci = mrb->c->ci; + + if (*mid == id_method_missing) { + method_missing: ; + int n = ci->n; + mrb_value *argv = ci->stack + 1; + mrb_value args = (n == 15) ? argv[0] : mrb_ary_new_from_values(mrb, n, argv); + mrb_method_missing(mrb, id_method_missing, recv, args); + } + + *tc = mrb_class(mrb, recv); + mrb_method_t m = mrb_method_search_vm(mrb, tc, id_method_missing); + if (MRB_METHOD_UNDEF_P(m)) { + goto method_missing; + } + + struct RProc *proc; + if (MRB_METHOD_FUNC_P(m)) { + proc = mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m)); + MRB_PROC_SET_TARGET_CLASS(proc, *tc); + } + else { + proc = MRB_METHOD_PROC(m); + } + + args_unshift(mrb, mrb_symbol_value(*mid)); + *mid = id_method_missing; + + return proc; +} + +static struct RObject * +method_object_alloc(mrb_state *mrb, struct RClass *mclass) +{ + return MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mclass); +} + +static struct RProc* +method_extract_proc(mrb_state *mrb, mrb_value self) +{ + mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_proc)); + if (mrb_nil_p(obj)) { + return NULL; + } + else { + mrb_check_type(mrb, obj, MRB_TT_PROC); + return mrb_proc_ptr(obj); + } +} + +static mrb_value +method_extract_receiver(mrb_state *mrb, mrb_value self) +{ + return mrb_iv_get(mrb, self, MRB_SYM(_recv)); +} + +static mrb_sym +method_extract_mid(mrb_state *mrb, mrb_value self) +{ + mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_name)); + mrb_check_type(mrb, obj, MRB_TT_SYMBOL); + return mrb_symbol(obj); +} + +static struct RClass* +method_extract_owner(mrb_state *mrb, mrb_value self) +{ + mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_owner)); + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + break; + default: + mrb_raise(mrb, E_TYPE_ERROR, "not class/module as owner of method object"); + } + return mrb_class_ptr(obj); +} + +static void +bind_check(mrb_state *mrb, mrb_value recv, mrb_value owner) +{ + if (!mrb_module_p(owner) && + mrb_class_ptr(owner) != mrb_obj_class(mrb, recv) && + !mrb_obj_is_kind_of(mrb, recv, mrb_class_ptr(owner))) { + if (mrb_sclass_p(owner)) { + mrb_raise(mrb, E_TYPE_ERROR, "singleton method called for a different object"); + } else { + mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %v", owner); + } + } +} + +static mrb_value +unbound_method_bind(mrb_state *mrb, mrb_value self) +{ + struct RObject *me; + mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); + mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); + mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); + mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); + mrb_value recv = mrb_get_arg1(mrb); + + bind_check(mrb, recv, owner); + me = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(Method))); + mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), owner); + mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), recv); + mrb_obj_iv_set(mrb, me, MRB_SYM(_name), name); + mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), proc); + mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), klass); + + return mrb_obj_value(me); +} + +#define IV_GET(value, name) mrb_iv_get(mrb, value, name) +static mrb_value +method_eql(mrb_state *mrb, mrb_value self) +{ + mrb_value other = mrb_get_arg1(mrb); + mrb_value receiver, orig_proc, other_proc; + struct RClass *owner, *klass; + struct RProc *orig_rproc, *other_rproc; + + if (!mrb_obj_is_instance_of(mrb, other, mrb_class(mrb, self))) + return mrb_false_value(); + + if (mrb_class(mrb, self) != mrb_class(mrb, other)) + return mrb_false_value(); + + klass = mrb_class_ptr(IV_GET(self, MRB_SYM(_klass))); + if (klass != mrb_class_ptr(IV_GET(other, MRB_SYM(_klass)))) + return mrb_false_value(); + + owner = mrb_class_ptr(IV_GET(self, MRB_SYM(_owner))); + if (owner != mrb_class_ptr(IV_GET(other, MRB_SYM(_owner)))) + return mrb_false_value(); + + receiver = IV_GET(self, MRB_SYM(_recv)); + if (!mrb_obj_equal(mrb, receiver, IV_GET(other, MRB_SYM(_recv)))) + return mrb_false_value(); + + orig_proc = IV_GET(self, MRB_SYM(_proc)); + other_proc = IV_GET(other, MRB_SYM(_proc)); + if (mrb_nil_p(orig_proc) && mrb_nil_p(other_proc)) { + if (mrb_symbol(IV_GET(self, MRB_SYM(_name))) == mrb_symbol(IV_GET(other, MRB_SYM(_name)))) + return mrb_true_value(); + else + return mrb_false_value(); + } + + if (mrb_nil_p(orig_proc)) + return mrb_false_value(); + if (mrb_nil_p(other_proc)) + return mrb_false_value(); + + orig_rproc = mrb_proc_ptr(orig_proc); + other_rproc = mrb_proc_ptr(other_proc); + if (MRB_PROC_CFUNC_P(orig_rproc)) { + if (!MRB_PROC_CFUNC_P(other_rproc)) + return mrb_false_value(); + if (orig_rproc->body.func != other_rproc->body.func) + return mrb_false_value(); + } + else { + if (MRB_PROC_CFUNC_P(other_rproc)) + return mrb_false_value(); + if (orig_rproc->body.irep != other_rproc->body.irep) + return mrb_false_value(); + } + + return mrb_true_value(); +} + +#undef IV_GET + +static mrb_value +mcall(mrb_state *mrb, mrb_value self, mrb_value recv) +{ + struct RProc *proc = method_extract_proc(mrb, self); + mrb_sym mid = method_extract_mid(mrb, self); + struct RClass *tc = method_extract_owner(mrb, self); + + if (mrb_undef_p(recv)) { + recv = method_extract_receiver(mrb, self); + } + else { + bind_check(mrb, recv, mrb_obj_value(tc)); + } + + if (!proc) { + proc = method_missing_prepare(mrb, &mid, recv, &tc); + } + mrb->c->ci->mid = mid; + mrb->c->ci->u.target_class = tc; + + return mrb_exec_irep(mrb, recv, proc); +} + +static mrb_value +method_call(mrb_state *mrb, mrb_value self) +{ + return mcall(mrb, self, mrb_undef_value()); +} + +static mrb_value +method_bcall(mrb_state *mrb, mrb_value self) +{ + mrb_value recv = args_shift(mrb); + mrb_gc_protect(mrb, recv); + return mcall(mrb, self, recv); +} + +static mrb_value +method_unbind(mrb_state *mrb, mrb_value self) +{ + struct RObject *ume; + mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); + mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); + mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); + mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); + + ume = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(UnboundMethod))); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_owner), owner); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_recv), mrb_nil_value()); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_name), name); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_proc), proc); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_klass), klass); + + return mrb_obj_value(ume); +} + +static struct RProc * +method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) +{ + mrb_method_t m = mrb_method_search_vm(mrb, cp, mid); + if (MRB_METHOD_UNDEF_P(m)) + return NULL; + if (MRB_METHOD_PROC_P(m)) + return MRB_METHOD_PROC(m); + return mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m)); +} + +static mrb_value +method_super_method(mrb_state *mrb, mrb_value self) +{ + mrb_value recv = mrb_iv_get(mrb, self, MRB_SYM(_recv)); + mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); + mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); + mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); + struct RClass *super, *rklass; + struct RProc *proc; + struct RObject *me; + + switch (mrb_type(klass)) { + case MRB_TT_SCLASS: + super = mrb_class_ptr(klass)->super->super; + break; + case MRB_TT_ICLASS: + super = mrb_class_ptr(klass)->super; + break; + default: + super = mrb_class_ptr(owner)->super; + break; + } + + proc = method_search_vm(mrb, &super, mrb_symbol(name)); + if (!proc) + return mrb_nil_value(); + + rklass = super; + while (super->tt == MRB_TT_ICLASS) + super = super->c; + + me = method_object_alloc(mrb, mrb_obj_class(mrb, self)); + mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), mrb_obj_value(super)); + mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), recv); + mrb_obj_iv_set(mrb, me, MRB_SYM(_name), name); + mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), mrb_obj_value(proc)); + mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), mrb_obj_value(rklass)); + + return mrb_obj_value(me); +} + +static mrb_value +method_arity(mrb_state *mrb, mrb_value self) +{ + mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); + mrb_int arity = mrb_nil_p(proc) ? -1 : mrb_proc_arity(mrb_proc_ptr(proc)); + return mrb_fixnum_value(arity); +} + +static mrb_value +method_source_location(mrb_state *mrb, mrb_value self) +{ + mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); + struct RProc *rproc; + struct RClass *orig; + mrb_value ret; + + if (mrb_nil_p(proc)) + return mrb_nil_value(); + + rproc = mrb_proc_ptr(proc); + orig = rproc->c; + rproc->c = mrb->proc_class; + ret = mrb_funcall_id(mrb, proc, MRB_SYM(source_location), 0); + rproc->c = orig; + return ret; +} + +static mrb_value +method_parameters(mrb_state *mrb, mrb_value self) +{ + mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); + struct RProc *rproc; + struct RClass *orig; + mrb_value ret; + + if (mrb_nil_p(proc)) { + mrb_value rest = mrb_symbol_value(MRB_SYM(rest)); + mrb_value arest = mrb_ary_new_from_values(mrb, 1, &rest); + return mrb_ary_new_from_values(mrb, 1, &arest); + } + + rproc = mrb_proc_ptr(proc); + orig = rproc->c; + rproc->c = mrb->proc_class; + ret = mrb_funcall_id(mrb, proc, MRB_SYM(parameters), 0); + rproc->c = orig; + return ret; +} + +static mrb_value +method_to_s(mrb_state *mrb, mrb_value self) +{ + mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); + mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); + mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); + mrb_value str = mrb_str_new_lit(mrb, "#<"); + struct RClass *rklass; + + mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, self)); + mrb_str_cat_lit(mrb, str, ": "); + rklass = mrb_class_ptr(klass); + if (mrb_class_ptr(owner) == rklass) { + mrb_str_concat(mrb, str, owner); + mrb_str_cat_lit(mrb, str, "#"); + mrb_str_concat(mrb, str, name); + } + else { + mrb_str_cat_cstr(mrb, str, mrb_class_name(mrb, rklass)); + mrb_str_cat_lit(mrb, str, "("); + mrb_str_concat(mrb, str, owner); + mrb_str_cat_lit(mrb, str, ")#"); + mrb_str_concat(mrb, str, name); + } + mrb_str_cat_lit(mrb, str, ">"); + return str; +} + +static void +mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym name, struct RClass **owner, struct RProc **proc, mrb_bool unbound) +{ + mrb_value ret; + + *owner = c; + *proc = method_search_vm(mrb, owner, name); + if (!*proc) { + if (unbound) { + goto name_error; + } + if (!mrb_respond_to(mrb, obj, MRB_SYM_Q(respond_to_missing))) { + goto name_error; + } + ret = mrb_funcall_id(mrb, obj, MRB_SYM_Q(respond_to_missing), 2, mrb_symbol_value(name), mrb_true_value()); + if (!mrb_test(ret)) { + goto name_error; + } + *owner = c; + } + + while ((*owner)->tt == MRB_TT_ICLASS) + *owner = (*owner)->c; + + return; + +name_error: + mrb_raisef(mrb, E_NAME_ERROR, "undefined method '%n' for class '%C'", name, c); +} + +static mrb_value +mrb_kernel_method(mrb_state *mrb, mrb_value self) +{ + struct RClass *owner; + struct RProc *proc; + struct RObject *me; + mrb_sym name; + + mrb_get_args(mrb, "n", &name); + + mrb_search_method_owner(mrb, mrb_class(mrb, self), self, name, &owner, &proc, FALSE); + + me = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(Method))); + mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), mrb_obj_value(owner)); + mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), self); + mrb_obj_iv_set(mrb, me, MRB_SYM(_name), mrb_symbol_value(name)); + mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), proc ? mrb_obj_value(proc) : mrb_nil_value()); + mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), mrb_obj_value(mrb_class(mrb, self))); + + return mrb_obj_value(me); +} + +static mrb_value +mrb_module_instance_method(mrb_state *mrb, mrb_value self) +{ + struct RClass *owner; + struct RProc *proc; + struct RObject *ume; + mrb_sym name; + + mrb_get_args(mrb, "n", &name); + + mrb_search_method_owner(mrb, mrb_class_ptr(self), self, name, &owner, &proc, TRUE); + + ume = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(UnboundMethod))); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_owner), mrb_obj_value(owner)); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_recv), mrb_nil_value()); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_name), mrb_symbol_value(name)); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_proc), proc ? mrb_obj_value(proc) : mrb_nil_value()); + mrb_obj_iv_set(mrb, ume, MRB_SYM(_klass), self); + + return mrb_obj_value(ume); +} + +static mrb_value +method_owner(mrb_state *mrb, mrb_value self) +{ + return mrb_iv_get(mrb, self, MRB_SYM(_owner)); +} + +static mrb_value +method_receiver(mrb_state *mrb, mrb_value self) +{ + return mrb_iv_get(mrb, self, MRB_SYM(_recv)); +} + +static mrb_value +method_name(mrb_state *mrb, mrb_value self) +{ + return mrb_iv_get(mrb, self, MRB_SYM(_name)); +} + +void +mrb_mruby_method_gem_init(mrb_state* mrb) +{ + struct RClass *unbound_method = mrb_define_class_id(mrb, MRB_SYM(UnboundMethod), mrb->object_class); + struct RClass *method = mrb_define_class_id(mrb, MRB_SYM(Method), mrb->object_class); + + mrb_undef_class_method(mrb, unbound_method, "new"); + mrb_define_method(mrb, unbound_method, "bind", unbound_method_bind, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, unbound_method, "super_method", method_super_method, MRB_ARGS_NONE()); + mrb_define_method(mrb, unbound_method, "==", method_eql, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, unbound_method, "eql?", method_eql, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, unbound_method, "to_s", method_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, unbound_method, "inspect", method_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, unbound_method, "arity", method_arity, MRB_ARGS_NONE()); + mrb_define_method(mrb, unbound_method, "source_location", method_source_location, MRB_ARGS_NONE()); + mrb_define_method(mrb, unbound_method, "parameters", method_parameters, MRB_ARGS_NONE()); + mrb_define_method(mrb, unbound_method, "bind_call", method_bcall, MRB_ARGS_REQ(1)|MRB_ARGS_ANY()); + mrb_define_method(mrb, unbound_method, "owner", method_owner, MRB_ARGS_NONE()); + mrb_define_method(mrb, unbound_method, "name", method_name, MRB_ARGS_NONE()); + + mrb_undef_class_method(mrb, method, "new"); + mrb_define_method(mrb, method, "==", method_eql, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, method, "eql?", method_eql, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, method, "to_s", method_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "inspect", method_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "call", method_call, MRB_ARGS_ANY()); + mrb_define_method(mrb, method, "[]", method_call, MRB_ARGS_ANY()); + mrb_define_method(mrb, method, "unbind", method_unbind, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "super_method", method_super_method, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "arity", method_arity, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "source_location", method_source_location, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "parameters", method_parameters, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "owner", method_owner, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "receiver", method_receiver, MRB_ARGS_NONE()); + mrb_define_method(mrb, method, "name", method_name, MRB_ARGS_NONE()); + + mrb_define_method(mrb, mrb->kernel_module, "method", mrb_kernel_method, MRB_ARGS_REQ(1)); + + mrb_define_method(mrb, mrb->module_class, "instance_method", mrb_module_instance_method, MRB_ARGS_REQ(1)); +} + +void +mrb_mruby_method_gem_final(mrb_state* mrb) +{ +} |
