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
This commit is contained in:
dearblue
2024-10-23 23:16:18 +09:00
parent f245943aed
commit f9151e0bd9
5 changed files with 59 additions and 12 deletions
+1
View File
@@ -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);
+2 -12
View File
@@ -3,6 +3,7 @@
#include <mruby/class.h>
#include <mruby/hash.h>
#include <mruby/proc.h>
#include <mruby/internal.h>
#include <mruby/presym.h>
/*
@@ -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
@@ -0,0 +1,17 @@
#include <mruby.h>
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());
}
+14
View File
@@ -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
+25
View File
@@ -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 */
/*