mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
vm.c: generalize pre-dispatch argument count check for C methods
Replace check_method_noarg() with check_argument_count() that validates min <= argc <= max using the full aspec stored in mrb_method_t.flags. This catches ArgumentError earlier at dispatch time, before entering the C function. The old check only handled the special case of aspec==0 (NOARG). The new check extracts REQ, OPT, REST, POST, KEY, and KDICT from the aspec and validates accordingly. Keyword hash is counted as a positional arg only when the method doesn't accept keywords. Remove MRB_METHOD_NOARG_P macro from proc.h (subsumed by aspec check). Fix 15 incorrect aspec declarations across the codebase that were exposed by the stricter enforcement. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -2264,7 +2264,7 @@ static const mrb_mt_entry array_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_ary_eq, MRB_OPSYM(eq), MRB_ARGS_REQ(1)),
|
||||
MRB_MT_ENTRY(mrb_ary_eql, MRB_SYM_Q(eql), MRB_ARGS_REQ(1)),
|
||||
MRB_MT_ENTRY(mrb_ary_first, MRB_SYM(first), MRB_ARGS_OPT(1)), /* 15.2.12.5.13 */
|
||||
MRB_MT_ENTRY(mrb_ary_index_m, MRB_SYM(index), MRB_ARGS_REQ(1)), /* 15.2.12.5.14 */
|
||||
MRB_MT_ENTRY(mrb_ary_index_m, MRB_SYM(index), MRB_ARGS_OPT(1)), /* 15.2.12.5.14 */
|
||||
MRB_MT_ENTRY(mrb_ary_init, MRB_SYM(initialize), MRB_ARGS_OPT(2) | MRB_MT_PRIVATE), /* 15.2.12.5.15 */
|
||||
MRB_MT_ENTRY(mrb_ary_replace_m, MRB_SYM(initialize_copy), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE), /* 15.2.12.5.16 */
|
||||
MRB_MT_ENTRY(mrb_ary_join_m, MRB_SYM(join), MRB_ARGS_OPT(1)), /* 15.2.12.5.17 */
|
||||
@@ -2275,7 +2275,7 @@ static const mrb_mt_entry array_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_ary_replace_m, MRB_SYM(replace), MRB_ARGS_REQ(1)), /* 15.2.12.5.23 */
|
||||
MRB_MT_ENTRY(mrb_ary_reverse, MRB_SYM(reverse), MRB_ARGS_NONE()), /* 15.2.12.5.24 */
|
||||
MRB_MT_ENTRY(mrb_ary_reverse_bang, MRB_SYM_B(reverse), MRB_ARGS_NONE()), /* 15.2.12.5.25 */
|
||||
MRB_MT_ENTRY(mrb_ary_rindex_m, MRB_SYM(rindex), MRB_ARGS_REQ(1)), /* 15.2.12.5.26 */
|
||||
MRB_MT_ENTRY(mrb_ary_rindex_m, MRB_SYM(rindex), MRB_ARGS_OPT(1)), /* 15.2.12.5.26 */
|
||||
MRB_MT_ENTRY(mrb_ary_shift_m, MRB_SYM(shift), MRB_ARGS_OPT(1)), /* 15.2.12.5.27 */
|
||||
MRB_MT_ENTRY(mrb_ary_size, MRB_SYM(size), MRB_ARGS_NONE()), /* 15.2.12.5.28 */
|
||||
MRB_MT_ENTRY(mrb_ary_aget, MRB_SYM(slice), MRB_ARGS_ARG(1,1)), /* 15.2.12.5.29 */
|
||||
|
||||
+2
-2
@@ -4296,7 +4296,7 @@ static const mrb_mt_entry mod_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mod_define_method, MRB_SYM(define_method), MRB_ARGS_ARG(1,1)),
|
||||
MRB_MT_ENTRY(mrb_mod_dup, MRB_SYM(dup), MRB_ARGS_NONE()),
|
||||
MRB_MT_ENTRY(mrb_do_nothing, MRB_SYM(extended), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE), /* 15.2.2.4.26 */
|
||||
MRB_MT_ENTRY(mrb_mod_include, MRB_SYM(include), MRB_ARGS_REQ(1)), /* 15.2.2.4.27 */
|
||||
MRB_MT_ENTRY(mrb_mod_include, MRB_SYM(include), MRB_ARGS_ANY()), /* 15.2.2.4.27 */
|
||||
MRB_MT_ENTRY(mrb_mod_include_p, MRB_SYM_Q(include), MRB_ARGS_REQ(1)), /* 15.2.2.4.28 */
|
||||
MRB_MT_ENTRY(mrb_do_nothing, MRB_SYM(included), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE), /* 15.2.2.4.29 */
|
||||
MRB_MT_ENTRY(mrb_mod_initialize, MRB_SYM(initialize), MRB_ARGS_NONE() | MRB_MT_PRIVATE), /* 15.2.2.4.31 */
|
||||
@@ -4307,7 +4307,7 @@ static const mrb_mt_entry mod_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_do_nothing, MRB_SYM(method_undefined), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE),
|
||||
MRB_MT_ENTRY(mrb_mod_module_eval, MRB_SYM(module_eval), MRB_ARGS_ANY()), /* 15.2.2.4.35 */
|
||||
MRB_MT_ENTRY(mrb_mod_module_function, MRB_SYM(module_function), MRB_ARGS_ANY() | MRB_MT_PRIVATE),
|
||||
MRB_MT_ENTRY(mrb_mod_prepend, MRB_SYM(prepend), MRB_ARGS_REQ(1)),
|
||||
MRB_MT_ENTRY(mrb_mod_prepend, MRB_SYM(prepend), MRB_ARGS_ANY()),
|
||||
MRB_MT_ENTRY(mrb_do_nothing, MRB_SYM(prepended), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE),
|
||||
MRB_MT_ENTRY(mrb_mod_private, MRB_SYM(private), MRB_ARGS_ANY() | MRB_MT_PRIVATE), /* 15.2.2.4.36 */
|
||||
MRB_MT_ENTRY(mrb_mod_protected, MRB_SYM(protected), MRB_ARGS_ANY() | MRB_MT_PRIVATE), /* 15.2.2.4.37 */
|
||||
|
||||
+1
-1
@@ -2336,7 +2336,7 @@ static const mrb_mt_entry hash_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_hash_to_hash, MRB_SYM(to_hash), MRB_ARGS_NONE()),
|
||||
MRB_MT_ENTRY(mrb_hash_assoc, MRB_SYM(assoc), MRB_ARGS_REQ(1)),
|
||||
MRB_MT_ENTRY(mrb_hash_rassoc, MRB_SYM(rassoc), MRB_ARGS_REQ(1)),
|
||||
MRB_MT_ENTRY(mrb_hash_merge_m, MRB_SYM(__merge), MRB_ARGS_REQ(1)),
|
||||
MRB_MT_ENTRY(mrb_hash_merge_m, MRB_SYM(__merge), MRB_ARGS_ANY()),
|
||||
MRB_MT_ENTRY(mrb_hash_compact, MRB_SYM(__compact), MRB_ARGS_NONE()), /* implementation of Hash#compact! */
|
||||
MRB_MT_ENTRY(mrb_hash_pat_values, MRB_SYM(__pat_values), MRB_ARGS_REQ(1)), /* for pattern matching keys */
|
||||
MRB_MT_ENTRY(mrb_hash_except_keys, MRB_SYM(__except), MRB_ARGS_REQ(1)), /* for pattern matching **rest */
|
||||
|
||||
+1
-1
@@ -3529,7 +3529,7 @@ static const mrb_mt_entry string_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_str_hash_m, MRB_SYM(hash), MRB_ARGS_NONE()), /* 15.2.10.5.20 */
|
||||
MRB_MT_ENTRY(mrb_str_include, MRB_SYM_Q(include), MRB_ARGS_REQ(1)), /* 15.2.10.5.21 */
|
||||
MRB_MT_ENTRY(mrb_str_index_m, MRB_SYM(index), MRB_ARGS_ARG(1,1)), /* 15.2.10.5.22 */
|
||||
MRB_MT_ENTRY(mrb_str_init, MRB_SYM(initialize), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE), /* 15.2.10.5.23 */
|
||||
MRB_MT_ENTRY(mrb_str_init, MRB_SYM(initialize), MRB_ARGS_OPT(1) | MRB_MT_PRIVATE), /* 15.2.10.5.23 */
|
||||
MRB_MT_ENTRY(mrb_str_replace, MRB_SYM(initialize_copy), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE), /* 15.2.10.5.24 */
|
||||
MRB_MT_ENTRY(mrb_str_intern, MRB_SYM(intern), MRB_ARGS_NONE()), /* 15.2.10.5.25 */
|
||||
MRB_MT_ENTRY(mrb_str_size, MRB_SYM(length), MRB_ARGS_NONE()), /* 15.2.10.5.26 */
|
||||
|
||||
@@ -870,17 +870,23 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
|
||||
}
|
||||
|
||||
static void
|
||||
check_method_noarg(mrb_state *mrb, const mrb_callinfo *ci)
|
||||
check_argument_count(mrb_state *mrb, const mrb_callinfo *ci, mrb_aspec aspec)
|
||||
{
|
||||
mrb_int argc = ci->n == CALL_MAXARGS ? RARRAY_LEN(ci->stack[1]) : ci->n;
|
||||
if (ci->nk > 0) {
|
||||
mrb_int argc = ci->n;
|
||||
if (mrb_unlikely(argc == CALL_MAXARGS)) {
|
||||
argc = RARRAY_LEN(ci->stack[1]);
|
||||
}
|
||||
/* keyword hash counts as positional if method doesn't accept keywords */
|
||||
if (ci->nk > 0 && MRB_ASPEC_KEY(aspec) == 0 && !MRB_ASPEC_KDICT(aspec)) {
|
||||
mrb_value kdict = ci->stack[mrb_ci_kidx(ci)];
|
||||
if (!(mrb_hash_p(kdict) && mrb_hash_empty_p(mrb, kdict))) {
|
||||
if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) {
|
||||
argc++;
|
||||
}
|
||||
}
|
||||
if (argc > 0) {
|
||||
mrb_argnum_error(mrb, argc, 0, 0);
|
||||
int min = MRB_ASPEC_REQ(aspec) + MRB_ASPEC_POST(aspec);
|
||||
int max = MRB_ASPEC_REST(aspec) ? -1 : min + MRB_ASPEC_OPT(aspec);
|
||||
if (mrb_unlikely(argc < min || (max >= 0 && argc > max))) {
|
||||
mrb_argnum_error(mrb, argc, min, max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -895,7 +901,7 @@ exec_irep(mrb_state *mrb, mrb_value self, const struct RProc *p)
|
||||
CI_PROC_SET(ci, p);
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_method_noarg(mrb, ci);
|
||||
check_argument_count(mrb, ci, 0);
|
||||
}
|
||||
return MRB_PROC_CFUNC(p)(mrb, self);
|
||||
}
|
||||
@@ -925,7 +931,7 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, const struct RProc *p)
|
||||
mrb_value ret;
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_method_noarg(mrb, ci);
|
||||
check_argument_count(mrb, ci, 0);
|
||||
}
|
||||
ci = cipush(mrb, 0, CINFO_DIRECT, CI_TARGET_CLASS(ci), p, NULL, ci->mid, ci->n|(ci->nk<<4));
|
||||
mrb->exc = NULL;
|
||||
@@ -1037,18 +1043,18 @@ send_method(mrb_state *mrb, mrb_value self, mrb_bool pub)
|
||||
ci->n--;
|
||||
}
|
||||
|
||||
const struct RProc *p;
|
||||
if (MRB_METHOD_PROC_P(m)) {
|
||||
p = MRB_METHOD_PROC(m);
|
||||
/* handle alias */
|
||||
MRB_PROC_RESOLVE_ALIAS(ci, p);
|
||||
CI_PROC_SET(ci, p);
|
||||
if (MRB_METHOD_FUNC_P(m)) {
|
||||
check_argument_count(mrb, ci, MRB_MT_ASPEC(m.flags));
|
||||
return MRB_METHOD_FUNC(m)(mrb, self);
|
||||
}
|
||||
if (MRB_METHOD_CFUNC_P(m)) {
|
||||
if (MRB_METHOD_NOARG_P(m) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_method_noarg(mrb, ci);
|
||||
const struct RProc *p = MRB_METHOD_PROC(m);
|
||||
MRB_PROC_RESOLVE_ALIAS(ci, p);
|
||||
CI_PROC_SET(ci, p);
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_argument_count(mrb, ci, 0);
|
||||
}
|
||||
return MRB_METHOD_CFUNC(m)(mrb, self);
|
||||
return MRB_PROC_CFUNC(p)(mrb, self);
|
||||
}
|
||||
return exec_irep(mrb, self, p);
|
||||
}
|
||||
@@ -2319,15 +2325,13 @@ RETRY_TRY_BLOCK:
|
||||
}
|
||||
else {
|
||||
if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_method_noarg(mrb, ci);
|
||||
check_argument_count(mrb, ci, 0);
|
||||
}
|
||||
recv = MRB_PROC_CFUNC(p)(mrb, recv);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (MRB_METHOD_NOARG_P(m) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_method_noarg(mrb, ci);
|
||||
}
|
||||
check_argument_count(mrb, ci, MRB_MT_ASPEC(m.flags));
|
||||
recv = MRB_METHOD_FUNC(m)(mrb, recv);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user