diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | vm_eval.c | 46 | ||||
-rw-r--r-- | vm_method.c | 22 |
3 files changed, 58 insertions, 16 deletions
@@ -1,3 +1,9 @@ +Sat Aug 22 15:43:12 2015 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * vm_eval.c (check_funcall_failed, check_funcall_missing): cache + results of respond_to? and respond_to_missing?, and search a + pulibc method only for compatibility with rb_respond_to. + Sat Aug 22 08:23:32 2015 Koichi Sasada <ko1@atdot.net> * ext/thread/thread.c: move definitions of Queue, SizedQueue @@ -352,6 +352,8 @@ struct rescue_funcall_args { VALUE recv; ID mid; const rb_method_entry_t *me; + unsigned int respond: 1; + unsigned int respond_to_missing: 1; int argc; const VALUE *argv; }; @@ -364,10 +366,27 @@ check_funcall_exec(struct rescue_funcall_args *args) args->me, args->argc, args->argv); } +#define PRIV Qfalse /* TODO: for rubyspec now, should be Qtrue */ + static VALUE check_funcall_failed(struct rescue_funcall_args *args, VALUE e) { - if (rb_respond_to(args->recv, args->mid)) { + int ret = args->respond; + if (!ret) { + switch (rb_method_boundp(args->defined_class, args->mid, + BOUND_PRIVATE|BOUND_RESPONDS)) { + case 2: + ret = TRUE; + break; + case 0: + ret = args->respond_to_missing; + break; + default: + ret = FALSE; + break; + } + } + if (ret) { rb_exc_raise(e); } return Qundef; @@ -376,7 +395,7 @@ check_funcall_failed(struct rescue_funcall_args *args, VALUE e) static int check_funcall_respond_to(rb_thread_t *th, VALUE klass, VALUE recv, ID mid) { - return vm_respond_to(th, klass, recv, mid, 1); + return vm_respond_to(th, klass, recv, mid, TRUE); } static int @@ -386,12 +405,19 @@ check_funcall_callable(rb_thread_t *th, const rb_callable_method_entry_t *me) } static VALUE -check_funcall_missing(rb_thread_t *th, VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv) +check_funcall_missing(rb_thread_t *th, VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int respond) { struct rescue_funcall_args args; + const rb_method_entry_t *me; VALUE ret = Qundef; - const rb_method_entry_t *const me = - method_entry_get(klass, idMethodMissing, &args.defined_class); + + ret = basic_obj_respond_to_missing(th, klass, recv, + ID2SYM(mid), PRIV); + if (!RTEST(ret)) return Qundef; + args.respond = respond > 0; + args.respond_to_missing = (ret != Qundef); + ret = Qundef; + me = method_entry_get(klass, idMethodMissing, &args.defined_class); if (me && !METHOD_ENTRY_BASIC(me)) { VALUE argbuf, *new_args = ALLOCV_N(VALUE, argbuf, argc+1); @@ -418,13 +444,14 @@ rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv) VALUE klass = CLASS_OF(recv); const rb_callable_method_entry_t *me; rb_thread_t *th = GET_THREAD(); + int respond = check_funcall_respond_to(th, klass, recv, mid); - if (!check_funcall_respond_to(th, klass, recv, mid)) + if (!respond) return Qundef; me = rb_search_method_entry(recv, mid); if (!check_funcall_callable(th, me)) { - return check_funcall_missing(th, klass, recv, mid, argc, argv); + return check_funcall_missing(th, klass, recv, mid, argc, argv, respond); } stack_check(); return vm_call0(th, recv, mid, argc, argv, me); @@ -437,14 +464,15 @@ rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE klass = CLASS_OF(recv); const rb_callable_method_entry_t *me; rb_thread_t *th = GET_THREAD(); + int respond = check_funcall_respond_to(th, klass, recv, mid); - if (!check_funcall_respond_to(th, klass, recv, mid)) + if (!respond) return Qundef; me = rb_search_method_entry(recv, mid); if (!check_funcall_callable(th, me)) { (*hook)(FALSE, recv, mid, argc, argv, arg); - return check_funcall_missing(th, klass, recv, mid, argc, argv); + return check_funcall_missing(th, klass, recv, mid, argc, argv, respond); } stack_check(); (*hook)(TRUE, recv, mid, argc, argv, arg); diff --git a/vm_method.c b/vm_method.c index 50935ea740..9b9dd13751 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1832,7 +1832,7 @@ basic_obj_respond_to_missing(rb_thread_t *th, VALUE klass, VALUE obj, const rb_method_entry_t *const me = method_entry_get(klass, rtmid, &defined_class); - if (!me || METHOD_ENTRY_BASIC(me)) return Qfalse; + if (!me || METHOD_ENTRY_BASIC(me)) return Qundef; args[0] = mid; args[1] = priv; return call_method_entry(th, defined_class, obj, rtmid, me, 2, args); @@ -1842,13 +1842,15 @@ static inline int basic_obj_respond_to(rb_thread_t *th, VALUE obj, ID id, int pub) { VALUE klass = CLASS_OF(obj); + VALUE ret; switch (rb_method_boundp(klass, id, pub|BOUND_RESPONDS)) { case 2: return FALSE; case 0: - return RTEST(basic_obj_respond_to_missing(th, klass, obj, ID2SYM(id), - pub ? Qfalse : Qtrue)); + ret = basic_obj_respond_to_missing(th, klass, obj, ID2SYM(id), + pub ? Qfalse : Qtrue); + return RTEST(ret) && ret != Qundef; default: return TRUE; } @@ -1864,7 +1866,7 @@ vm_respond_to(rb_thread_t *th, VALUE klass, VALUE obj, ID id, int priv) if (!me) return TRUE; if (METHOD_ENTRY_BASIC(me)) { - return basic_obj_respond_to(th, obj, id, !priv); + return -1; } else { int argc = 1; @@ -1908,7 +1910,11 @@ vm_respond_to(rb_thread_t *th, VALUE klass, VALUE obj, ID id, int priv) int rb_obj_respond_to(VALUE obj, ID id, int priv) { - return vm_respond_to(GET_THREAD(), CLASS_OF(obj), obj, id, priv); + rb_thread_t *th = GET_THREAD(); + VALUE klass = CLASS_OF(obj); + int ret = vm_respond_to(th, klass, obj, id, priv); + if (ret == -1) ret = basic_obj_respond_to(th, obj, id, !priv); + return ret; } int @@ -1947,8 +1953,10 @@ obj_respond_to(int argc, VALUE *argv, VALUE obj) rb_scan_args(argc, argv, "11", &mid, &priv); if (!(id = rb_check_id(&mid))) { - return basic_obj_respond_to_missing(th, CLASS_OF(obj), obj, - rb_to_symbol(mid), priv); + VALUE ret = basic_obj_respond_to_missing(th, CLASS_OF(obj), obj, + rb_to_symbol(mid), priv); + if (ret == Qundef) ret = Qfalse; + return ret; } if (basic_obj_respond_to(th, obj, id, !RTEST(priv))) return Qtrue; |