/*
 *  call-seq:
 *     prc.arity -> fixnum
 *  
 *  Returns the number of arguments that would not be ignored. If the block
 *  is declared to take no arguments, returns 0. If the block is known
 *  to take exactly n arguments, returns n. If the block has optional
 *  arguments, return -n-1, where n is the number of mandatory
 *  arguments. A <code>proc</code> with no argument declarations
 *  is the same a block declaring <code>||</code> as its arguments.
 *     
 *     Proc.new {}.arity          #=>  0
 *     Proc.new {||}.arity        #=>  0
 *     Proc.new {|a|}.arity       #=>  1
 *     Proc.new {|a,b|}.arity     #=>  2
 *     Proc.new {|a,b,c|}.arity   #=>  3
 *     Proc.new {|*a|}.arity      #=> -1
 *     Proc.new {|a,*b|}.arity    #=> -2
 *     Proc.new {|a,*b, c|}.arity    #=> -3
 */

static VALUE
proc_arity(VALUE self)
{
    rb_proc_t *proc;
    rb_iseq_t *iseq;
    GetProcPtr(self, proc);
    iseq = proc->block.iseq;
    if (iseq) {
        if (BUILTIN_TYPE(iseq) != T_NODE) {
            if (iseq->arg_rest < 0) {
                return INT2FIX(iseq->argc);
            }
            else {
                return INT2FIX(-(iseq->argc + 1 + iseq->arg_post_len));
            }
        }
        else {
            NODE *node = (NODE *)iseq;
            if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) {
                /* method(:foo).to_proc.arity */
                return INT2FIX(method_arity(node->nd_tval));
            }
        }
    }
    return INT2FIX(-1);
}