diff --git a/hash.c b/hash.c index 9b4c315a09..c3a512b8e9 100644 --- a/hash.c +++ b/hash.c @@ -142,7 +142,11 @@ hash_recursive(VALUE obj, VALUE arg, int recurse) VALUE rb_hash(VALUE obj) { - VALUE hval = rb_exec_recursive_outer(hash_recursive, obj, 0); + VALUE hval = rb_check_funcall_basic_kw(obj, id_hash, rb_mKernel, 0, 0, 0); + + if (hval == Qundef) { + hval = rb_exec_recursive_outer(hash_recursive, obj, 0); + } while (!FIXNUM_P(hval)) { if (RB_TYPE_P(hval, T_BIGNUM)) { diff --git a/internal/vm.h b/internal/vm.h index eb193a23dc..d36ed3d0c8 100644 --- a/internal/vm.h +++ b/internal/vm.h @@ -74,6 +74,7 @@ VALUE rb_check_funcall_with_hook_kw(VALUE recv, ID mid, int argc, const VALUE *a rb_check_funcall_hook *hook, VALUE arg, int kw_splat); const char *rb_type_str(enum ruby_value_type type); VALUE rb_check_funcall_default(VALUE, ID, int, const VALUE *, VALUE); +VALUE rb_check_funcall_basic_kw(VALUE, ID, VALUE, int, const VALUE*, int); VALUE rb_yield_1(VALUE val); VALUE rb_yield_force_blockarg(VALUE values); VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv, diff --git a/vm_eval.c b/vm_eval.c index b5ee1d26ec..94f3baceab 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -936,6 +936,34 @@ rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat) return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL); } +/*! + * Calls a method only if it is the basic method of `ancestor` + * otherwise returns Qundef; + * \param recv receiver of the method + * \param mid an ID that represents the name of the method + * \param ancestor the Class that defined the basic method + * \param argc the number of arguments + * \param argv pointer to an array of method arguments + * \param kw_splat bool + */ +VALUE +rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VALUE *argv, int kw_splat) +{ + const rb_callable_method_entry_t *cme; + rb_execution_context_t *ec; + VALUE klass = CLASS_OF(recv); + if (!klass) return Qundef; /* hidden object */ + + cme = rb_callable_method_entry(klass, mid); + if (cme && METHOD_ENTRY_BASIC(cme) && RBASIC_CLASS(cme->defined_class) == ancestor) { + ec = GET_EC(); + return rb_vm_call0(ec, recv, mid, argc, argv, cme, kw_splat); + } + + return Qundef; +} + + /*! * Calls a method. *