aboutsummaryrefslogtreecommitdiffstats
path: root/proc.c
diff options
context:
space:
mode:
authorYusuke Endoh <mame@ruby-lang.org>2019-08-30 11:01:25 +0900
committerYusuke Endoh <mame@ruby-lang.org>2019-08-30 11:13:00 +0900
commit83c6a1ef454c51ad1c0ca58e8a95fd67a033f710 (patch)
tree1c54ce712e993cf3ddfe9577345ca0b70b7c993d /proc.c
parent500149709b92ccb36396589a0c39afb3ff77bfb6 (diff)
downloadruby-83c6a1ef454c51ad1c0ca58e8a95fd67a033f710.tar.gz
proc.c: Add UnboundMethod#bind_call
`umethod.bind_call(obj, ...)` is semantically equivalent to `umethod.bind(obj).call(...)`. This idiom is used in some libraries to call a method that is overridden. The added method does the same without allocation of intermediate Method object. [Feature #15955] ``` class Foo def add_1(x) x + 1 end end class Bar < Foo def add_1(x) # override x + 2 end end obj = Bar.new p obj.add_1(1) #=> 3 p Foo.instance_method(:add_1).bind(obj).call(1) #=> 2 p Foo.instance_method(:add_1).bind_call(obj, 1) #=> 2 ```
Diffstat (limited to 'proc.c')
-rw-r--r--proc.c103
1 files changed, 72 insertions, 31 deletions
diff --git a/proc.c b/proc.c
index a99f3c9269..0d8c4cd9fe 100644
--- a/proc.c
+++ b/proc.c
@@ -2318,6 +2318,46 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe
*
*/
+static void
+convert_umethod_to_method_components(VALUE method, VALUE recv, VALUE *methclass_out, VALUE *klass_out, const rb_method_entry_t **me_out)
+{
+ struct METHOD *data;
+
+ TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
+
+ VALUE methclass = data->me->owner;
+ VALUE klass = CLASS_OF(recv);
+
+ if (!RB_TYPE_P(methclass, T_MODULE) &&
+ methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) {
+ if (FL_TEST(methclass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError,
+ "singleton method called for a different object");
+ }
+ else {
+ rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE,
+ methclass);
+ }
+ }
+
+ const rb_method_entry_t *me = rb_method_entry_clone(data->me);
+
+ if (RB_TYPE_P(me->owner, T_MODULE)) {
+ VALUE ic = rb_class_search_ancestor(klass, me->owner);
+ if (ic) {
+ klass = ic;
+ }
+ else {
+ klass = rb_include_class_new(methclass, klass);
+ }
+ me = (const rb_method_entry_t *) rb_method_entry_complement_defined_class(me, me->called_id, klass);
+ }
+
+ *methclass_out = methclass;
+ *klass_out = klass;
+ *me_out = me;
+}
+
/*
* call-seq:
* umeth.bind(obj) -> method
@@ -2356,44 +2396,44 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe
static VALUE
umethod_bind(VALUE method, VALUE recv)
{
- struct METHOD *data, *bound;
VALUE methclass, klass;
+ const rb_method_entry_t *me;
+ convert_umethod_to_method_components(method, recv, &methclass, &klass, &me);
- TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
-
- methclass = data->me->owner;
+ struct METHOD *bound;
+ method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
+ RB_OBJ_WRITE(method, &bound->recv, recv);
+ RB_OBJ_WRITE(method, &bound->klass, klass);
+ RB_OBJ_WRITE(method, &bound->me, me);
- if (!RB_TYPE_P(methclass, T_MODULE) &&
- methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) {
- if (FL_TEST(methclass, FL_SINGLETON)) {
- rb_raise(rb_eTypeError,
- "singleton method called for a different object");
- }
- else {
- rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE,
- methclass);
- }
- }
+ return method;
+}
- klass = CLASS_OF(recv);
+/*
+ * call-seq:
+ * umeth.bind_call(obj, args, ...) -> obj
+ *
+ * Bind <i>umeth</i> to <i>obj</i> and then invokes the method with the
+ * specified arguments.
+ * This is semantically equivalent to <code>umeth.bind(obj).call(args, ...)</code>.
+ */
+static VALUE
+umethod_bind_call(int argc, VALUE *argv, VALUE method)
+{
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ VALUE recv = argv[0];
+ argc--;
+ argv++;
- method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
- RB_OBJ_WRITE(method, &bound->recv, recv);
- RB_OBJ_WRITE(method, &bound->klass, data->klass);
- RB_OBJ_WRITE(method, &bound->me, rb_method_entry_clone(data->me));
+ VALUE methclass, klass;
+ const rb_method_entry_t *me;
+ convert_umethod_to_method_components(method, recv, &methclass, &klass, &me);
+ struct METHOD bound = { recv, klass, 0, me };
- if (RB_TYPE_P(bound->me->owner, T_MODULE)) {
- VALUE ic = rb_class_search_ancestor(klass, bound->me->owner);
- if (ic) {
- klass = ic;
- }
- else {
- klass = rb_include_class_new(methclass, klass);
- }
- RB_OBJ_WRITE(method, &bound->me, rb_method_entry_complement_defined_class(bound->me, bound->me->called_id, klass));
- }
+ VALUE passed_procval = rb_block_given_p() ? rb_block_proc() : Qnil;
- return method;
+ rb_execution_context_t *ec = GET_EC();
+ return call_method_data(ec, &bound, argc, argv, passed_procval);
}
/*
@@ -3683,6 +3723,7 @@ Init_Proc(void)
rb_define_method(rb_cUnboundMethod, "original_name", method_original_name, 0);
rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0);
rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
+ rb_define_method(rb_cUnboundMethod, "bind_call", umethod_bind_call, -1);
rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0);
rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0);
rb_define_method(rb_cUnboundMethod, "super_method", method_super_method, 0);