From f9151e0bd96699bf9b112240534cc0a399a49cac Mon Sep 17 00:00:00 2001 From: dearblue Date: Wed, 23 Oct 2024 23:16:18 +0900 Subject: [PATCH] Fixed argument forwarding in `instance_exec` However, on C, there is no easy way to pass keyword arguments. Therefore, when called `Kernel#instance_exec` on C, keyword arguments are converted to positional arguments. This is a limitation of current mruby. fixed #6389 --- include/mruby/internal.h | 1 + mrbgems/mruby-object-ext/src/object.c | 14 ++---------- mrbgems/mruby-object-ext/test/object-ext.c | 17 +++++++++++++++ mrbgems/mruby-object-ext/test/object.rb | 14 ++++++++++++ src/vm.c | 25 ++++++++++++++++++++++ 5 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 mrbgems/mruby-object-ext/test/object-ext.c diff --git a/include/mruby/internal.h b/include/mruby/internal.h index f2e8ade6e..5d21d1942 100644 --- a/include/mruby/internal.h +++ b/include/mruby/internal.h @@ -195,6 +195,7 @@ mrb_int mrb_ci_bidx(mrb_callinfo *ci); mrb_int mrb_ci_nregs(mrb_callinfo *ci); mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p); mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); +mrb_value mrb_object_exec(mrb_state *mrb, mrb_value self, struct RClass *target_class); mrb_value mrb_mod_module_eval(mrb_state*, mrb_value); mrb_value mrb_f_send(mrb_state *mrb, mrb_value self); diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c index d20e64784..4efb96edf 100644 --- a/mrbgems/mruby-object-ext/src/object.c +++ b/mrbgems/mruby-object-ext/src/object.c @@ -3,6 +3,7 @@ #include #include #include +#include #include /* @@ -93,18 +94,7 @@ nil_to_i(mrb_state *mrb, mrb_value obj) static mrb_value obj_instance_exec(mrb_state *mrb, mrb_value self) { - const mrb_value *argv; - mrb_int argc; - mrb_value blk; - struct RClass *c; - - mrb_get_args(mrb, "*&!", &argv, &argc, &blk); - c = mrb_singleton_class_ptr(mrb, self); - if (mrb->c->ci->cci > 0) { - return mrb_yield_with_class(mrb, blk, argc, argv, self, c); - } - mrb_vm_ci_target_class_set(mrb->c->ci, c); - return mrb_yield_cont(mrb, blk, self, argc, argv); + return mrb_object_exec(mrb, self, mrb_singleton_class_ptr(mrb, self)); } void diff --git a/mrbgems/mruby-object-ext/test/object-ext.c b/mrbgems/mruby-object-ext/test/object-ext.c new file mode 100644 index 000000000..46274541d --- /dev/null +++ b/mrbgems/mruby-object-ext/test/object-ext.c @@ -0,0 +1,17 @@ +#include + +static mrb_value +obj_instance_exec_from_c(mrb_state *mrb, mrb_value self) +{ + mrb_int argc; + const mrb_value *argv; + mrb_value blk; + mrb_get_args(mrb, "*&!", &argv, &argc, &blk); + return mrb_funcall_with_block(mrb, self, mrb_intern_lit(mrb, "instance_exec"), argc, argv, blk); +} + +void +mrb_mruby_object_ext_gem_test(mrb_state *mrb) +{ + mrb_define_method(mrb, mrb->kernel_module, "instance_exec_from_c", obj_instance_exec_from_c, MRB_ARGS_ANY()); +} diff --git a/mrbgems/mruby-object-ext/test/object.rb b/mrbgems/mruby-object-ext/test/object.rb index f0742f8ce..1718f5cb5 100644 --- a/mrbgems/mruby-object-ext/test/object.rb +++ b/mrbgems/mruby-object-ext/test/object.rb @@ -51,3 +51,17 @@ assert('instance_exec on primitives with class and module definition') do Object.remove_const :A end end + +assert('argument forwarding via instance_exec') do + assert_equal [[], {}, nil], instance_exec { |*args, **kw, &blk| [args, kw, blk] } + assert_equal [[1, 2, 3], {}, nil], instance_exec(1, 2, 3) { |*args, **kw, &blk| [args, kw, blk] } + assert_equal [[], { a: 1 }, nil], instance_exec(a: 1) { |*args, **kw, &blk| [args, kw, blk] } +end + +assert('argument forwarding via instance_exec from c') do + assert_equal [[], {}, nil], instance_exec_from_c { |*args, **kw, &blk| [args, kw, blk] } + assert_equal [[1, 2, 3], {}, nil], instance_exec_from_c(1, 2, 3) { |*args, **kw, &blk| [args, kw, blk] } + + # currently there is no easy way to call a method from C passing keyword arguments + #assert_equal [[], { a: 1 }, nil], instance_exec_from_c(a: 1) { |*args, **kw, &blk| [args, kw, blk] } +end diff --git a/src/vm.c b/src/vm.c index 2d0f9ee98..87ba1fefd 100644 --- a/src/vm.c +++ b/src/vm.c @@ -837,6 +837,31 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) } } +mrb_value +mrb_object_exec(mrb_state *mrb, mrb_value self, struct RClass *target_class) +{ + mrb_callinfo *ci = mrb->c->ci; + if (ci->cci > 0) { + const mrb_value *argv; + mrb_int argc; + mrb_value blk; + mrb_get_args(mrb, "*!&!", &argv, &argc, &blk); + return mrb_yield_with_class(mrb, blk, argc, argv, self, target_class); + } + + int bidx = mrb_ci_bidx(ci); + mrb_value blk = ci->stack[bidx]; + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + + mrb_assert(mrb_proc_p(blk)); + mrb_gc_protect(mrb, blk); + ci->stack[bidx] = mrb_nil_value(); + ci->u.target_class = target_class; + return mrb_exec_irep(mrb, self, mrb_proc_ptr(blk)); +} + /* 15.3.1.3.4 */ /* 15.3.1.3.44 */ /*