diff --git a/ChangeLog b/ChangeLog index 0d837d83c3..a4e0edf611 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Sat Feb 2 07:44:15 2013 Marc-Andre Lafortune + + * proc.c: Add {*}_min_max_arity and refactor. + [Bug #7765] + + * test/ruby/test_proc.rb: Fix wrong test + Fri Feb 2 00:46:00 2013 Charlie Somerville * marshal.c: add security considerations to marshal overview, refer to diff --git a/proc.c b/proc.c index cbb676e8ca..b2c2b70b0e 100644 --- a/proc.c +++ b/proc.c @@ -30,6 +30,7 @@ VALUE rb_cProc; static VALUE bmcall(VALUE, VALUE); static int method_arity(VALUE); +static int method_min_max_arity(VALUE, int *max); static ID attached; /* Proc */ @@ -655,8 +656,23 @@ proc_arity(VALUE self) return INT2FIX(arity); } -int -rb_proc_arity(VALUE self) +static inline int +rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max) +{ + *max = iseq->arg_rest == -1 ? + iseq->argc + iseq->arg_post_len + iseq->arg_opts - (iseq->arg_opts > 0) + : UNLIMITED_ARGUMENTS; + return iseq->argc + iseq->arg_post_len; +} + +/* + * Returns the number of required parameters and stores the maximum + * number of parameters in max, or UNLIMITED_ARGUMENTS if no max. + * For non-lambda procs, the maximum is the number of non-ignored + * parameters even though there is no actual limit to the number of parameters + */ +static int +rb_proc_min_max_arity(VALUE self, int *max) { rb_proc_t *proc; rb_iseq_t *iseq; @@ -664,22 +680,27 @@ rb_proc_arity(VALUE self) iseq = proc->block.iseq; if (iseq) { if (BUILTIN_TYPE(iseq) != T_NODE) { - if (iseq->arg_rest < 0 && (!proc->is_lambda || iseq->arg_opts == 0)) { - return iseq->argc; - } - else { - return -(iseq->argc + 1 + iseq->arg_post_len); - } + return rb_iseq_min_max_arity(iseq, max); } else { NODE *node = (NODE *)iseq; if (IS_METHOD_PROC_NODE(node)) { - /* method(:foo).to_proc.arity */ - return method_arity(node->nd_tval); + /* e.g. method(:foo).to_proc.arity */ + return method_min_max_arity(node->nd_tval, max); } } } - return -1; + *max = UNLIMITED_ARGUMENTS; + return 0; +} + +int +rb_proc_arity(VALUE self) +{ + rb_proc_t *proc; + int max, min = rb_proc_min_max_arity(self, &max); + GetProcPtr(self, proc); + return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; } #define get_proc_iseq rb_proc_get_iseq @@ -1646,54 +1667,66 @@ umethod_bind(VALUE method, VALUE recv) return method; } -int -rb_method_entry_arity(const rb_method_entry_t *me) +/* + * Returns the number of required parameters and stores the maximum + * number of parameters in max, or UNLIMITED_ARGUMENTS + * if there is no maximum. + */ +static int +rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max) { const rb_method_definition_t *def = me->def; - if (!def) return 0; + if (!def) return *max = 0; switch (def->type) { case VM_METHOD_TYPE_CFUNC: - if (def->body.cfunc.argc < 0) - return -1; - return check_argc(def->body.cfunc.argc); + if (def->body.cfunc.argc < 0) { + *max = UNLIMITED_ARGUMENTS; + return 0; + } + return *max = check_argc(def->body.cfunc.argc); case VM_METHOD_TYPE_ZSUPER: - return -1; - case VM_METHOD_TYPE_ATTRSET: - return 1; - case VM_METHOD_TYPE_IVAR: + *max = UNLIMITED_ARGUMENTS; return 0; + case VM_METHOD_TYPE_ATTRSET: + return *max = 1; + case VM_METHOD_TYPE_IVAR: + return *max = 0; case VM_METHOD_TYPE_BMETHOD: - return rb_proc_arity(def->body.proc); + return rb_proc_min_max_arity(def->body.proc, max); case VM_METHOD_TYPE_ISEQ: { rb_iseq_t *iseq = def->body.iseq; - if (iseq->arg_rest == -1 && iseq->arg_opts == 0) { - return iseq->argc; - } - else { - return -(iseq->argc + 1 + iseq->arg_post_len); - } + return rb_iseq_min_max_arity(iseq, max); } case VM_METHOD_TYPE_UNDEF: case VM_METHOD_TYPE_NOTIMPLEMENTED: - return 0; + return *max = 0; case VM_METHOD_TYPE_MISSING: - return -1; + *max = UNLIMITED_ARGUMENTS; + return 0; case VM_METHOD_TYPE_OPTIMIZED: { switch (def->body.optimize_type) { case OPTIMIZED_METHOD_TYPE_SEND: - return -1; + *max = UNLIMITED_ARGUMENTS; + return 0; default: break; } } case VM_METHOD_TYPE_REFINED: - return -1; + *max = UNLIMITED_ARGUMENTS; + return 0; } - rb_bug("rb_method_entry_arity: invalid method entry type (%d)", def->type); - + rb_bug("rb_method_entry_min_max_arity: invalid method entry type (%d)", def->type); UNREACHABLE; } +int +rb_method_entry_arity(const rb_method_entry_t *me) +{ + int max, min = rb_method_entry_min_max_arity(me, &max); + return min == max ? min : -min-1; +} + /* * call-seq: * meth.arity -> fixnum @@ -1758,6 +1791,15 @@ original_method_entry(VALUE mod, ID id) return me; } +static int +method_min_max_arity(VALUE method, int *max) +{ + struct METHOD *data; + + TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); + return rb_method_entry_min_max_arity(data->me, max); +} + int rb_mod_method_arity(VALUE mod, ID id) { diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index f9a0beecd1..ca716a696c 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -64,7 +64,7 @@ class TestProc < Test::Unit::TestCase assert_equal(1, proc{|x|}.arity) assert_equal(0, proc{|x=1|}.arity) assert_equal(2, proc{|x, y|}.arity) - assert_equal(0, proc{|x=0, y|}.arity) + assert_equal(1, proc{|x=0, y|}.arity) assert_equal(0, proc{|x=0, y=0|}.arity) assert_equal(1, proc{|x, y=0|}.arity) assert_equal(-2, proc{|x, *y|}.arity)