If you enable PARROT_ERRORS_PARAM_COUNT_FLAG, parrot_pass_args
miscounts a :flat array as a single argument, leading to inappropriate
errors. This patch fixes arg counting, adds tests to cover, and
improves the error messages. If parrot_pass_args_fromc supports :flat,
then it may have similar problems . . .
-- Bob Rogers
http://rgrjr.dyndns.org/
Index: src/inter_call.c
===================================================================
--- src/inter_call.c (revision 10513)
+++ src/inter_call.c (working copy)
@@ -238,6 +238,8 @@
st->src.slurp = p_arg;
st->src.slurp_i = 0;
st->src.slurp_n = VTABLE_elements(interpreter, p_arg);
+ /* the -1 is because the :flat PMC itself doesn't count. */
+ st->n_actual_args += st->src.slurp_n-1;
return Parrot_fetch_arg(interpreter, st);
}
@@ -672,6 +674,7 @@
todo = Parrot_init_arg_op(interpreter, dest_ctx, dst_pc, &st.dest);
Parrot_init_arg_op(interpreter, src_ctx, src_pc, &st.src);
st.opt_so_far = 0; /* XXX */
+ st.n_actual_args = st.src.n; /* initial guess, adjusted for :flat args */
while (todo) {
Parrot_fetch_arg(interpreter, &st);
Parrot_convert_arg(interpreter, &st);
@@ -702,24 +705,43 @@
* we are returning 1 retval to caller on behalf
* of the NCI (a PIR method had already returned
* all and doesn't run anything after the
- * tailcall - ignore/fix missing arg_count
+ * tailcall - ignore arg_count
*/
- st.src.i = st.src.n;
}
+ else {
+ /*
+ * compute the range of expected arguments. we do this here when we
+ * know we need to check it. on the other hand, we must compute
+ * st.n_actual_args as we go, as it's harder to get :flat array lengths
+ * after the fact.
+ */
+ int slurpy_p = (st.dest.sig
+ & (PARROT_ARG_SLURPY_ARRAY|PARROT_ARG_OPT_FLAG));
+ int max_expected_args = (slurpy_p ? st.dest.n-1 : st.dest.n);
+ int min_expected_args = max_expected_args;
+ int i;
+ /* allow for optionals. */
+ for (i = 0; i < st.dest.n; i++) {
+ if (st.dest.sig & PARROT_ARG_OPTIONAL)
+ min_expected_args--;
+ }
- if (st.src.i > st.src.n) {
- if (!(st.dest.sig & (PARROT_ARG_OPTIONAL|
- PARROT_ARG_SLURPY_ARRAY|PARROT_ARG_OPT_FLAG))) {
+ /* arg checks. */
+ if (st.n_actual_args < min_expected_args) {
real_exception(interpreter, NULL, E_ValueError,
- "too few arguments passed (%d) - %d %s expected",
- st.src.n, st.dest.n, action);
+ "too few arguments passed (%d) - %s%d %s expected",
+ st.n_actual_args,
+ (min_expected_args < max_expected_args ? "at least " : ""),
+ min_expected_args, action);
}
+ else if (! slurpy_p && st.n_actual_args > max_expected_args) {
+ real_exception(interpreter, NULL, E_ValueError,
+ "too many arguments passed (%d) - %s%d %s expected",
+ st.n_actual_args,
+ (min_expected_args < max_expected_args ? "at most " : ""),
+ max_expected_args, action);
+ }
}
- else if (st.src.n && st.src.i < st.src.n) {
- real_exception(interpreter, NULL, E_ValueError,
- "too many arguments passed (%d) - %d %s expected",
- st.src.n, st.dest.n, action);
- }
/* skip the get_params opcode - all done here */
return dst_pc + st.dest.n + 2;
Index: include/parrot/inter_call.h
===================================================================
--- include/parrot/inter_call.h (revision 10513)
+++ include/parrot/inter_call.h (working copy)
@@ -50,6 +50,7 @@
struct call_state_1 dest;
UnionVal val;
int opt_so_far;
+ int n_actual_args;
};
int Parrot_init_arg_sig(Interp *, parrot_context_t *ctx,
Index: t/op/calling.t
===================================================================
--- t/op/calling.t (revision 10513)
+++ t/op/calling.t (working copy)
@@ -1155,6 +1155,105 @@
1 2 3 4
OUTPUT
+pir_output_is(<<'CODE', <<'OUTPUT', "right number of args via :flat");
+.sub _fn1
+ .param int arg1
+ .param int arg2
+ .param int arg3
+ .param int arg4
+ print arg1
+ print ' '
+ print arg2
+ print ' '
+ print arg3
+ print ' '
+ print arg4
+ print "\n"
+.end
+.sub main :main
+ .include "errors.pasm"
+ errorson .PARROT_ERRORS_PARAM_COUNT_FLAG
+ $P30 = new Integer
+ $P30 = 2
+ $P31 = new Integer
+ $P31 = 3
+ $P34 = new Array
+ $P34 = 3
+ $P34[0] = $P30
+ $P34[1] = $P31
+ $P34[2] = $P30
+ $P35 = _fn1(1, $P34 :flat)
+.end
+CODE
+1 2 3 2
+OUTPUT
+
+pir_output_like(<<'CODE', <<'OUTPUT', "too many args via :flat");
+.sub _fn1
+ .param int arg1
+ .param int arg2
+ .param int arg3
+ .param int arg4
+ print arg1
+ print ' '
+ print arg2
+ print ' '
+ print arg3
+ print ' '
+ print arg4
+ print "\n"
+.end
+.sub main :main
+ .include "errors.pasm"
+ errorson .PARROT_ERRORS_PARAM_COUNT_FLAG
+ $P30 = new Integer
+ $P30 = 2
+ $P31 = new Integer
+ $P31 = 3
+ $P34 = new Array
+ $P34 = 4
+ $P34[0] = $P30
+ $P34[1] = $P31
+ $P34[2] = $P30
+ $P34[3] = $P31
+ $P35 = _fn1(1, $P34 :flat)
+.end
+CODE
+/too many arguments passed \(5\) - 4 params expected/
+OUTPUT
+
+pir_output_like(<<'CODE', <<'OUTPUT', "too few args via :flat");
+.sub _fn1
+ .param int arg1
+ .param int arg2
+ .param int arg3
+ .param int arg4
+ print arg1
+ print ' '
+ print arg2
+ print ' '
+ print arg3
+ print ' '
+ print arg4
+ print "\n"
+.end
+.sub main :main
+ .include "errors.pasm"
+ errorson .PARROT_ERRORS_PARAM_COUNT_FLAG
+ $P30 = new Integer
+ $P30 = 2
+ $P31 = new Integer
+ $P31 = 3
+ $P34 = new Array
+ $P34 = 2
+ $P34[0] = $P30
+ $P34[1] = $P31
+ $P35 = _fn1(1, $P34 :flat)
+.end
+CODE
+/too few arguments passed \(3\) - 4 params expected/
+OUTPUT
+
pir_output_is(<<'CODE', <<'OUTPUT', "tailcall to NCI");
.sub main :main
.local pmc s
@@ -1474,5 +1573,5 @@
## remember to change the number of tests :-)
-BEGIN { plan tests => 54; }
+BEGIN { plan tests => 57; }