aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-André Lafortune <github@marc-andre.ca>2020-12-24 12:08:12 -0500
committerGitHub <noreply@github.com>2020-12-25 02:08:12 +0900
commitdb2ebbd71b746734b88832b1e70db8afed3d68ed (patch)
treeab48ce3cc5083b59ef7858ec9ecadc2ce018fc70
parent8981a63f12f9c30e9c6f893d292d01fd5df89991 (diff)
downloadruby-db2ebbd71b746734b88832b1e70db8afed3d68ed.tar.gz
Optimize calls to `Kernel#hash` (#3987)
This avoids recursive checks when the `hash` method of an object isn't specialized.
-rw-r--r--hash.c6
-rw-r--r--internal/vm.h1
-rw-r--r--vm_eval.c28
3 files changed, 34 insertions, 1 deletions
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
@@ -937,6 +937,34 @@ rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
}
/*!
+ * 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.
*
* Same as rb_funcallv but this function can call only public methods.