vm.c (mrb_f_public_send): define #public_send method

The method itself is available when mruby-metaprog is loaded.
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-04-22 21:38:02 +09:00
parent dd96afd5a8
commit a6ef3a45d1
3 changed files with 73 additions and 24 deletions
+1
View File
@@ -198,6 +198,7 @@ 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);
mrb_value mrb_f_public_send(mrb_state *mrb, mrb_value self);
#ifdef MRB_USE_BIGINT
mrb_value mrb_bint_new_int(mrb_state *mrb, mrb_int x);
+1
View File
@@ -712,6 +712,7 @@ mrb_mruby_metaprog_gem_init(mrb_state* mrb)
mrb_define_method_id(mrb, krn, MRB_SYM(singleton_methods), mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */
mrb_define_method_id(mrb, krn, MRB_SYM(define_singleton_method), mod_define_singleton_method, MRB_ARGS_REQ(1)|MRB_ARGS_BLOCK());
mrb_define_method_id(mrb, krn, MRB_SYM(send), mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.44 */
mrb_define_method_id(mrb, krn, MRB_SYM(public_send), mrb_f_public_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK());
mrb_define_method_id(mrb, mod, MRB_SYM(class_variables), mrb_mod_class_variables, MRB_ARGS_OPT(1)); /* 15.2.2.4.19 */
mrb_define_method_id(mrb, mod, MRB_SYM(remove_class_variable), mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */
+71 -24
View File
@@ -858,27 +858,14 @@ mrb_object_exec(mrb_state *mrb, mrb_value self, struct RClass *target_class)
return mrb_exec_irep(mrb, self, mrb_proc_ptr(blk));
}
/* 15.3.1.3.4 */
/* 15.3.1.3.44 */
/*
* call-seq:
* obj.send(symbol [, args...]) -> obj
* obj.__send__(symbol [, args...]) -> obj
*
* Invokes the method identified by _symbol_, passing it any
* arguments specified. You can use <code>__send__</code> if the name
* +send+ clashes with an existing method in _obj_.
*
* class Klass
* def hello(*args)
* "Hello " + args.join(' ')
* end
* end
* k = Klass.new
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
*/
mrb_value
mrb_f_send(mrb_state *mrb, mrb_value self)
static mrb_noreturn void
vis_error(mrb_state *mrb, mrb_sym mid, mrb_value args, mrb_value recv, mrb_bool priv)
{
mrb_no_method_error(mrb, mid, args, "%s method '%n' called for %T", (priv ? "private" : "protected"), mid, recv);
}
static mrb_value
send_method(mrb_state *mrb, mrb_value self, mrb_bool public)
{
mrb_callinfo *ci = mrb->c->ci;
int n = ci->n;
@@ -914,6 +901,22 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
goto funcall;
}
if (public) {
mrb_bool priv = TRUE;
if (m.flags & MRB_METHOD_PRIVATE_FL) {
vis_err:;
if (n == 15) {
n = RARRAY_LEN(regs[0]) - 1;
regs = RARRAY_PTR(regs[0]);
}
vis_error(mrb, name, mrb_ary_new_from_values(mrb, n, regs+1), self, priv);
}
else if ((m.flags & MRB_METHOD_PROTECTED_FL) && mrb_obj_is_kind_of(mrb, self, ci->u.target_class)) {
priv = FALSE;
goto vis_err;
}
}
ci->mid = name;
ci->u.target_class = c;
/* remove first symbol from arguments */
@@ -950,6 +953,48 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
return exec_irep(mrb, self, p);
}
/* 15.3.1.3.4 */
/* 15.3.1.3.44 */
/*
* call-seq:
* obj.send(symbol [, args...]) -> obj
* obj.__send__(symbol [, args...]) -> obj
*
* Invokes the method identified by _symbol_, passing it any
* arguments specified. You can use <code>__send__</code> if the name
* +send+ clashes with an existing method in _obj_.
*
* class Klass
* def hello(*args)
* "Hello " + args.join(' ')
* end
* end
* k = Klass.new
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
*/
mrb_value
mrb_f_send(mrb_state *mrb, mrb_value self)
{
return send_method(mrb, self, FALSE);
}
/*
* call-seq:
* obj.public_send(symbol [, args...]) -> obj
*
* Invokes the method identified by symbol, passing it any
* arguments specified. Unlike send, public_send calls public methods only.
* When the method is identified by a string, the string is converted to a
* symbol.
*
* 1.public_send(:puts, "hello") # causes NoMethodError
*/
mrb_value
mrb_f_public_send(mrb_state *mrb, mrb_value self)
{
return send_method(mrb, self, TRUE);
}
static void
check_block(mrb_state *mrb, mrb_value blk)
{
@@ -1911,13 +1956,15 @@ RETRY_TRY_BLOCK:
ci->mid = mid;
}
if (insn == OP_SEND || insn == OP_SENDB) {
mrb_bool priv = TRUE;
if (m.flags & MRB_METHOD_PRIVATE_FL) {
vis_err:;
mrb_value args = (ci->n == 15) ? regs[1] : mrb_ary_new_from_values(mrb, ci->n, regs+1);
mrb_no_method_error(mrb, mid, args, "private method '%n' called for %T", mid, recv);
vis_error(mrb, mid, args, recv, priv);
}
else if ((m.flags & MRB_METHOD_PROTECTED_FL) && mrb_obj_is_kind_of(mrb, recv, ci->u.target_class)) {
mrb_value args = (ci->n == 15) ? regs[1] : mrb_ary_new_from_values(mrb, ci->n, regs+1);
mrb_no_method_error(mrb, mid, args, "protected method '%n' called for %T", mid, recv);
priv = FALSE;
goto vis_err;
}
}
ci->cci = CINFO_NONE;