diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | internal.h | 3 | ||||
-rw-r--r-- | marshal.c | 80 | ||||
-rw-r--r-- | vm_eval.c | 90 |
4 files changed, 136 insertions, 45 deletions
@@ -1,3 +1,11 @@ +Sun Dec 23 15:05:48 2012 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * vm_eval.c (rb_check_funcall_with_hook): rb_check_funcall with hook + which is called before calling method_missing or target method. + + * marshal.c (w_object, r_object0): use rb_check_funcall_with_hook + instead of respond_to? and call. + Sun Dec 23 14:52:00 2012 Zachary Scott <zachary@zacharyscott.net> * re.c (rb_reg_eqq): doc: #=== is not a synonym for #=~, added example diff --git a/internal.h b/internal.h index 2c407d52c5..2f05f48595 100644 --- a/internal.h +++ b/internal.h @@ -312,6 +312,9 @@ void rb_vm_bugreport(void); void Init_vm_eval(void); VALUE rb_current_realfilepath(void); VALUE rb_check_block_call(VALUE, ID, int, VALUE *, VALUE (*)(ANYARGS), VALUE); +typedef void rb_check_funcall_hook(int, VALUE, ID, int, VALUE *, VALUE); +VALUE rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, VALUE *argv, + rb_check_funcall_hook *hook, VALUE arg); /* vm_method.c */ void Init_eval_method(void); @@ -587,6 +587,15 @@ w_objivar(VALUE obj, struct dump_call_arg *arg) } static void +push_dump_object(int found, VALUE recv, ID mid, int argc, VALUE *argv, VALUE data) +{ + if (found) { + struct dump_arg *arg = (struct dump_arg *)data; + st_add_direct(arg->data, recv, arg->data->num_entries); + } +} + +static void w_object(VALUE obj, struct dump_arg *arg, int limit) { struct dump_call_arg c_arg; @@ -646,10 +655,8 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) arg->infection |= (int)FL_TEST(obj, MARSHAL_INFECTION); - if (rb_obj_respond_to(obj, s_mdump, TRUE)) { - st_add_direct(arg->data, obj, arg->data->num_entries); - - v = rb_funcall2(obj, s_mdump, 0, 0); + v = rb_check_funcall_with_hook(obj, s_mdump, 0, 0, push_dump_object, (VALUE)arg); + if (v != Qundef) { check_dump_arg(arg, s_mdump); hasiv = has_ivars(obj, ivtbl); if (hasiv) w_byte(TYPE_IVAR, arg); @@ -1026,6 +1033,7 @@ static VALUE r_entry0(VALUE v, st_index_t num, struct load_arg *arg); static VALUE r_object(struct load_arg *arg); static ID r_symbol(struct load_arg *arg); static VALUE path2class(VALUE path); +static VALUE r_object0(struct load_arg *arg, int *ivp, VALUE extmod); NORETURN(static void too_short(void)); static void @@ -1434,6 +1442,42 @@ append_extmod(VALUE obj, VALUE extmod) return obj; } +static void +load_data_hook(int found, VALUE recv, ID mid, int argc, VALUE *argv, VALUE data) +{ + if (found) { + struct load_arg *arg = (struct load_arg *)((VALUE *)data)[0]; + VALUE extmod = ((VALUE *)data)[1]; + *argv = r_object0(arg, 0, extmod); + } +} + +static void +load_userdef_hook(int found, VALUE recv, ID mid, int argc, VALUE *argv, VALUE data) +{ + if (found) { + struct load_arg *arg = (struct load_arg *)((VALUE *)data)[0]; + int *ivp = (int *)((VALUE *)data)[1]; + VALUE r = r_string(arg); + if (ivp) { + r_ivar(r, NULL, arg); + *ivp = FALSE; + } + *argv = r; + } +} + +static void +mload_hook(int found, VALUE recv, ID mid, int argc, VALUE *argv, VALUE data) +{ + if (found) { + struct load_arg *arg = (struct load_arg *)((VALUE *)data)[0]; + VALUE v = ((VALUE *)data)[1]; + r_entry(v, arg); + *argv = r_object(arg); + } +} + static VALUE r_object0(struct load_arg *arg, int *ivp, VALUE extmod) { @@ -1441,6 +1485,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod) int type = r_byte(arg); long id; st_data_t link; + VALUE args[2]; switch (type) { case TYPE_LINK: @@ -1718,16 +1763,14 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod) VALUE klass = path2class(r_unique(arg)); VALUE data; - if (!rb_obj_respond_to(klass, s_load, TRUE)) { + args[0] = (VALUE)arg; + args[1] = (VALUE)ivp; + v = rb_check_funcall_with_hook(klass, s_load, 1, &data, + load_userdef_hook, (VALUE)args); + if (v == Qundef) { rb_raise(rb_eTypeError, "class %s needs to have method `_load'", rb_class2name(klass)); } - data = r_string(arg); - if (ivp) { - r_ivar(data, NULL, arg); - *ivp = FALSE; - } - v = rb_funcall2(klass, s_load, 1, &data); check_load_arg(arg, s_load); v = r_entry(v, arg); v = r_leave(v, arg); @@ -1745,13 +1788,13 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod) /* for the case marshal_load is overridden */ append_extmod(v, extmod); } - if (!rb_obj_respond_to(v, s_mload, TRUE)) { + args[0] = (VALUE)arg; + args[1] = v; + data = rb_check_funcall_with_hook(v, s_mload, 1, &data, mload_hook, (VALUE)args); + if (data == Qundef) { rb_raise(rb_eTypeError, "instance of %s needs to have method `marshal_load'", rb_class2name(klass)); } - v = r_entry(v, arg); - data = r_object(arg); - rb_funcall2(v, s_mload, 1, &data); check_load_arg(arg, s_mload); v = r_leave(v, arg); if (!NIL_P(extmod)) { @@ -1785,13 +1828,14 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod) rb_raise(rb_eArgError, "dump format error"); } v = r_entry(v, arg); - if (!rb_obj_respond_to(v, s_load_data, TRUE)) { + args[0] = (VALUE)arg; + args[1] = extmod; + r = rb_check_funcall_with_hook(v, s_load_data, 1, &r, load_data_hook, (VALUE)args); + if (r == Qundef) { rb_raise(rb_eTypeError, "class %s needs to have instance method `_load_data'", rb_class2name(klass)); } - r = r_object0(arg, 0, extmod); - rb_funcall2(v, s_load_data, 1, &r); check_load_arg(arg, s_load_data); v = r_leave(v, arg); } @@ -338,16 +338,12 @@ check_funcall_failed(struct rescue_funcall_args *args, VALUE e) return Qundef; } -static VALUE -check_funcall(VALUE recv, ID mid, int argc, VALUE *argv) +static int +check_funcall_respond_to(rb_thread_t *th, VALUE klass, VALUE recv, ID mid) { - VALUE klass = CLASS_OF(recv); - const rb_method_entry_t *me; - rb_thread_t *th = GET_THREAD(); - int call_status; VALUE defined_class; + const rb_method_entry_t *me = rb_method_entry(klass, idRespond_to, &defined_class); - me = rb_method_entry(klass, idRespond_to, &defined_class); if (me && !(me->flag & NOEX_BASIC)) { VALUE args[2]; int arity = rb_method_entry_arity(me); @@ -360,37 +356,77 @@ check_funcall(VALUE recv, ID mid, int argc, VALUE *argv) args[0] = ID2SYM(mid); args[1] = Qtrue; if (!RTEST(vm_call0(th, recv, idRespond_to, arity, args, me, defined_class))) { - return Qundef; + return FALSE; } } + return TRUE; +} + +static int +check_funcall_callable(rb_thread_t *th, const rb_method_entry_t *me) +{ + return rb_method_call_status(th, me, CALL_FCALL, th->cfp->self) == NOEX_OK; +} + +static VALUE +check_funcall_missing(rb_thread_t *th, VALUE klass, VALUE recv, ID mid, int argc, VALUE *argv) +{ + if (rb_method_basic_definition_p(klass, idMethodMissing)) { + return Qundef; + } + else { + struct rescue_funcall_args args; + + th->method_missing_reason = 0; + args.recv = recv; + args.sym = ID2SYM(mid); + args.argc = argc; + args.argv = argv; + return rb_rescue2(check_funcall_exec, (VALUE)&args, + check_funcall_failed, (VALUE)&args, + rb_eNoMethodError, (VALUE)0); + } +} + +VALUE +rb_check_funcall(VALUE recv, ID mid, int argc, VALUE *argv) +{ + VALUE klass = CLASS_OF(recv); + const rb_method_entry_t *me; + rb_thread_t *th = GET_THREAD(); + VALUE defined_class; + + if (!check_funcall_respond_to(th, klass, recv, mid)) + return Qundef; me = rb_search_method_entry(recv, mid, &defined_class); - call_status = rb_method_call_status(th, me, CALL_FCALL, th->cfp->self); - if (call_status != NOEX_OK) { - if (rb_method_basic_definition_p(klass, idMethodMissing)) { - return Qundef; - } - else { - struct rescue_funcall_args args; - - th->method_missing_reason = 0; - args.recv = recv; - args.sym = ID2SYM(mid); - args.argc = argc; - args.argv = argv; - return rb_rescue2(check_funcall_exec, (VALUE)&args, - check_funcall_failed, (VALUE)&args, - rb_eNoMethodError, (VALUE)0); - } + if (check_funcall_callable(th, me) != NOEX_OK) { + return check_funcall_missing(th, klass, recv, mid, argc, argv); } stack_check(); return vm_call0(th, recv, mid, argc, argv, me, defined_class); } VALUE -rb_check_funcall(VALUE recv, ID mid, int argc, VALUE *argv) +rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, VALUE *argv, + rb_check_funcall_hook *hook, VALUE arg) { - return check_funcall(recv, mid, argc, argv); + VALUE klass = CLASS_OF(recv); + const rb_method_entry_t *me; + rb_thread_t *th = GET_THREAD(); + VALUE defined_class; + + if (!check_funcall_respond_to(th, klass, recv, mid)) + return Qundef; + + me = rb_search_method_entry(recv, mid, &defined_class); + if (check_funcall_callable(th, me) != NOEX_OK) { + (*hook)(FALSE, recv, mid, argc, argv, arg); + return check_funcall_missing(th, klass, recv, mid, argc, argv); + } + stack_check(); + (*hook)(TRUE, recv, mid, argc, argv, arg); + return vm_call0(th, recv, mid, argc, argv, me, defined_class); } static const char * |