From: Yehuda Katz Date: 2008-11-22T05:31:09+09:00 Subject: [ruby-core:20029] Re: Proposal: Method#get_args ------=_Part_12869_6281907.1227299712083 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline What values does simple_default handle? Assuming it covers the simple cases we discussed is this ready to apply? Are there any objections? -- Yehuda On Sat, Nov 15, 2008 at 12:53 PM, Nobuyoshi Nakada wrote: > Hi, > > At Sun, 16 Nov 2008 00:48:54 +0900, > Yukihiro Matsumoto wrote in [ruby-core:19942]: > > |I guess so. I'd like to make sure two more things: > > | > > | * symbol for block argument should be :block, or anything else? > > | * if you don't have a rest argument name, you will get [:rest], ok? > > > > One more: > > > > > def foo(a, b = 1, *c, d) > > > => [[:req, :a], [:opt, :b, 1], [:rest, :c], [:req, :d]] > > > > what if the default value for an optional argument is more than a simple > > literal? :expr as Austine mentioned in [ruby-core:19882]? Or drop > > default values altogether? > > Since :expr is a valid expression, I don't think it is > reasonable. > > > Index: iseq.c > =================================================================== > --- iseq.c (revision 20237) > +++ iseq.c (working copy) > @@ -1275,4 +1275,67 @@ rb_iseq_clone(VALUE iseqval, VALUE newcb > } > > +static VALUE > +simple_default_value(const VALUE *seq) > +{ > + VALUE val; > + switch (*seq++) { > + case BIN(putnil): > + val = Qnil; > + case BIN(putobject): > + val = *seq++; > + if (*seq == BIN(setlocal)) break; > + default: > + return Qundef; > + } > + return val; > +} > + > +VALUE > +rb_iseq_parameters(const rb_iseq_t *iseq) > +{ > + int i, r, s; > + VALUE a, args = rb_ary_new2(iseq->arg_size); > + ID req, opt, rest, block; > +#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type)) > +#define PARAM_ID(i) iseq->local_table[i] > +#define PARAM(i, type) ( \ > + PARAM_TYPE(type), \ > + rb_id2name(PARAM_ID(i)) ? \ > + rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \ > + a) > + > + CONST_ID(req, "req"); > + for (i = 0; i < iseq->argc; i++) { > + rb_ary_push(args, PARAM(i, req)); > + } > + r = iseq->arg_rest != -1 ? iseq->arg_rest : > + iseq->arg_post_len > 0 ? iseq->arg_post_start : > + iseq->arg_block != -1 ? iseq->arg_block : > + iseq->arg_size; > + CONST_ID(opt, "opt"); > + for (s = i; i < r; i++) { > + PARAM_TYPE(opt); > + if (rb_id2name(PARAM_ID(i))) { > + VALUE defval = simple_default_value(iseq->iseq + > iseq->arg_opt_table[i-s]); > + rb_ary_push(a, ID2SYM(PARAM_ID(i))); > + if (defval != Qundef) rb_ary_push(a, defval); > + } > + rb_ary_push(args, a); > + } > + if (iseq->arg_rest != -1) { > + CONST_ID(rest, "rest"); > + rb_ary_push(args, PARAM(iseq->arg_rest, rest)); > + } > + r = iseq->arg_post_start + iseq->arg_post_len; > + for (i = iseq->arg_post_start; i < r; i++) { > + rb_ary_push(args, PARAM(i, req)); > + } > + if (iseq->arg_block != -1) { > + CONST_ID(block, "block"); > + rb_ary_push(args, PARAM(iseq->arg_block, block)); > + } > + return args; > +} > + > /* ruby2cext */ > > Index: proc.c > =================================================================== > --- proc.c (revision 20237) > +++ proc.c (working copy) > @@ -26,7 +26,10 @@ VALUE rb_cBinding; > VALUE rb_cProc; > > +VALUE rb_iseq_parameters(const rb_iseq_t *iseq); > + > static VALUE bmcall(VALUE, VALUE); > static int method_arity(VALUE); > static VALUE rb_obj_is_method(VALUE m); > +static rb_iseq_t *get_method_iseq(VALUE method); > > /* Proc */ > @@ -616,6 +619,12 @@ get_proc_iseq(VALUE self) > GetProcPtr(self, proc); > iseq = proc->block.iseq; > - if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) > - return 0; > + if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) { > + NODE *node = (NODE *)iseq; > + iseq = 0; > + if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) { > + /* method(:foo).to_proc */ > + iseq = get_method_iseq(node->nd_tval); > + } > + } > return iseq; > } > @@ -651,4 +660,40 @@ rb_proc_location(VALUE self) > } > > +static VALUE > +unnamed_parameters(int arity) > +{ > + VALUE a, param = rb_ary_new2((arity < 0) ? -arity : arity); > + int n = (arity < 0) ? ~arity : arity; > + ID req, rest; > + CONST_ID(req, "req"); > + a = rb_ary_new3(1, ID2SYM(req)); > + OBJ_FREEZE(a); > + for (; n; --n) { > + rb_ary_push(param, a); > + } > + if (arity < 0) { > + CONST_ID(rest, "rest"); > + rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest))); > + } > + return param; > +} > + > +/* > + * call-seq: > + * proc.parameters => array > + * > + * returns the parameter information of this proc > + */ > + > +static VALUE > +rb_proc_parameters(VALUE self) > +{ > + rb_iseq_t *iseq = get_proc_iseq(self); > + if (!iseq) { > + return unnamed_parameters(proc_arity(self)); > + } > + return rb_iseq_parameters(iseq); > +} > + > /* > * call-seq: > @@ -1461,4 +1506,6 @@ get_method_iseq(VALUE method) > body = data->body; > switch (nd_type(body)) { > + case NODE_BMETHOD: > + return get_proc_iseq(body->nd_cval); > case RUBY_VM_METHOD_NODE: > GetISeqPtr((VALUE)body->nd_body, iseq); > @@ -1485,4 +1532,21 @@ rb_method_location(VALUE method) > > /* > + * call-seq: > + * meth.parameters => array > + * > + * returns the parameter information of this method > + */ > + > +static VALUE > +rb_method_parameters(VALUE method) > +{ > + rb_iseq_t *iseq = get_method_iseq(method); > + if (!iseq) { > + return unnamed_parameters(method_arity(method)); > + } > + return rb_iseq_parameters(iseq); > +} > + > +/* > * call-seq: > * meth.to_s => string > @@ -1812,4 +1876,5 @@ Init_Proc(void) > rb_define_method(rb_cProc, "curry", proc_curry, -1); > rb_define_method(rb_cProc, "source_location", rb_proc_location, 0); > + rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0); > > /* Exceptions */ > @@ -1847,4 +1912,5 @@ Init_Proc(void) > rb_define_method(rb_cMethod, "unbind", method_unbind, 0); > rb_define_method(rb_cMethod, "source_location", rb_method_location, 0); > + rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0); > rb_define_method(rb_mKernel, "method", rb_obj_method, 1); > rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1); > @@ -1865,4 +1931,5 @@ Init_Proc(void) > rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); > rb_define_method(rb_cUnboundMethod, "source_location", > rb_method_location, 0); > + rb_define_method(rb_cUnboundMethod, "parameters", > rb_method_parameters, 0); > > /* Module#*_method */ > Index: test/ruby/test_proc.rb > =================================================================== > --- test/ruby/test_proc.rb (revision 20237) > +++ test/ruby/test_proc.rb (working copy) > @@ -674,3 +674,46 @@ class TestProc < Test::Unit::TestCase > assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]") > end > + > + def test_parameters > + assert_equal([], proc {}.parameters) > + assert_equal([], proc {||}.parameters) > + assert_equal([[:req, :a]], proc {|a|}.parameters) > + assert_equal([[:req, :a], [:req, :b]], proc {|a, b|}.parameters) > + assert_equal([[:opt, :a, :a], [:block, :b]], proc {|a=:a, > &b|}.parameters) > + assert_equal([[:req, :a], [:opt, :b, :b]], proc {|a, > b=:b|}.parameters) > + assert_equal([[:rest, :a]], proc {|*a|}.parameters) > + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], proc {|a, *b, > &c|}.parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], proc {|a, *b, > c|}.parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], proc > {|a, *b, c, &d|}.parameters) > + assert_equal([[:req, :a], [:opt, :b, :b], [:rest, :c], [:req, :d], > [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters) > + assert_equal([[:req], [:block, :b]], proc {|(a), &b|}.parameters) > + assert_equal([[:req, :a], [:req, :b], [:opt, :c, :c], [:opt, :d, :d], > [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], proc > {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) > + end > + > + def pm0() end > + def pm1(a) end > + def pm2(a, b) end > + def pmo1(a = :a, &b) end > + def pmo2(a, b = :b) end > + def pmo3(*a) end > + def pmo4(a, *b, &c) end > + def pmo5(a, *b, c) end > + def pmo6(a, *b, c, &d) end > + def pmo7(a, b = :b, *c, d, &e) end > + def pma1((a), &b) end > + > + > + def test_bound_parameters > + assert_equal([], method(:pm0).to_proc.parameters) > + assert_equal([[:req, :a]], method(:pm1).to_proc.parameters) > + assert_equal([[:req, :a], [:req, :b]], > method(:pm2).to_proc.parameters) > + assert_equal([[:opt, :a, :a], [:block, :b]], > method(:pmo1).to_proc.parameters) > + assert_equal([[:req, :a], [:opt, :b, :b]], > method(:pmo2).to_proc.parameters) > + assert_equal([[:rest, :a]], method(:pmo3).to_proc.parameters) > + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], > method(:pmo4).to_proc.parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], > method(:pmo5).to_proc.parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], > method(:pmo6).to_proc.parameters) > + assert_equal([[:req, :a], [:opt, :b, :b], [:rest, :c], [:req, :d], > [:block, :e]], method(:pmo7).to_proc.parameters) > + assert_equal([[:req], [:block, :b]], method(:pma1).to_proc.parameters) > + end > end > Index: test/ruby/test_method.rb > =================================================================== > --- test/ruby/test_method.rb (revision 20237) > +++ test/ruby/test_method.rb (working copy) > @@ -21,4 +21,6 @@ class TestMethod < Test::Unit::TestCase > def mo5(a, *b, c) end > def mo6(a, *b, c, &d) end > + def mo7(a, b = nil, *c, d, &e) end > + def ma1((a), &b) end > > class Base > @@ -222,3 +224,71 @@ class TestMethod < Test::Unit::TestCase > assert_raise(ArgumentError) { o.method(:foo).call(1) } > end > + > + define_method(:pm0) {||} > + define_method(:pm1) {|a|} > + define_method(:pm2) {|a, b|} > + define_method(:pmo1) {|a = nil, &b|} > + define_method(:pmo2) {|a, b = nil|} > + define_method(:pmo3) {|*a|} > + define_method(:pmo4) {|a, *b, &c|} > + define_method(:pmo5) {|a, *b, c|} > + define_method(:pmo6) {|a, *b, c, &d|} > + define_method(:pmo7) {|a, b = nil, *c, d, &e|} > + define_method(:pma1) {|(a), &b|} > + > + def test_bound_parameters > + assert_equal([], method(:m0).parameters) > + assert_equal([[:req, :a]], method(:m1).parameters) > + assert_equal([[:req, :a], [:req, :b]], method(:m2).parameters) > + assert_equal([[:opt, :a, nil], [:block, :b]], method(:mo1).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil]], method(:mo2).parameters) > + assert_equal([[:rest, :a]], method(:mo3).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], > method(:mo4).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], > method(:mo5).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], > method(:mo6).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], > [:block, :e]], method(:mo7).parameters) > + assert_equal([[:req], [:block, :b]], method(:ma1).parameters) > + end > + > + def test_unbound_parameters > + assert_equal([], self.class.instance_method(:m0).parameters) > + assert_equal([[:req, :a]], self.class.instance_method(:m1).parameters) > + assert_equal([[:req, :a], [:req, :b]], > self.class.instance_method(:m2).parameters) > + assert_equal([[:opt, :a, nil], [:block, :b]], > self.class.instance_method(:mo1).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil]], > self.class.instance_method(:mo2).parameters) > + assert_equal([[:rest, :a]], > self.class.instance_method(:mo3).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], > self.class.instance_method(:mo4).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], > self.class.instance_method(:mo5).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], > self.class.instance_method(:mo6).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], > [:block, :e]], self.class.instance_method(:mo7).parameters) > + assert_equal([[:req], [:block, :b]], > self.class.instance_method(:ma1).parameters) > + end > + > + def test_bmethod_bound_parameters > + assert_equal([], method(:pm0).parameters) > + assert_equal([[:req, :a]], method(:pm1).parameters) > + assert_equal([[:req, :a], [:req, :b]], method(:pm2).parameters) > + assert_equal([[:opt, :a, nil], [:block, :b]], > method(:pmo1).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil]], method(:pmo2).parameters) > + assert_equal([[:rest, :a]], method(:pmo3).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], > method(:pmo4).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], > method(:pmo5).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], > method(:pmo6).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], > [:block, :e]], method(:pmo7).parameters) > + assert_equal([[:req], [:block, :b]], method(:pma1).parameters) > + end > + > + def test_bmethod_unbound_parameters > + assert_equal([], self.class.instance_method(:pm0).parameters) > + assert_equal([[:req, :a]], > self.class.instance_method(:pm1).parameters) > + assert_equal([[:req, :a], [:req, :b]], > self.class.instance_method(:pm2).parameters) > + assert_equal([[:opt, :a, nil], [:block, :b]], > self.class.instance_method(:pmo1).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil]], > self.class.instance_method(:pmo2).parameters) > + assert_equal([[:rest, :a]], > self.class.instance_method(:pmo3).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], > self.class.instance_method(:pmo4).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], > self.class.instance_method(:pmo5).parameters) > + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], > self.class.instance_method(:pmo6).parameters) > + assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], > [:block, :e]], self.class.instance_method(:pmo7).parameters) > + assert_equal([[:req], [:block, :b]], > self.class.instance_method(:pma1).parameters) > + end > end > > > -- > Nobu Nakada > > -- Yehuda Katz Developer | Engine Yard (ph) 718.877.1325 ------=_Part_12869_6281907.1227299712083 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline What values does simple_default handle? Assuming it covers the simple cases we discussed is this ready to apply? Are there any objections?

-- Yehuda

On Sat, Nov 15, 2008 at 12:53 PM, Nobuyoshi Nakada <nobu@ruby-lang.org> wrote:
Hi,

At Sun, 16 Nov 2008 00:48:54 +0900,
Yukihiro Matsumoto wrote in [ruby-core:19942]:
> |I guess so.  I'd like to make sure two more things:
> |
> |  * symbol for block argument should be :block, or anything else?
> |  * if you don't have a rest argument name, you will get [:rest], ok?
>
> One more:
>
> > def foo(a, b = 1, *c, d)
> > => [[:req, :a], [:opt, :b, 1], [:rest, :c], [:req, :d]]
>
> what if the default value for an optional argument is more than a simple
> literal?   :expr as Austine mentioned in [ruby-core:19882]?  Or drop
> default values altogether?

Since :expr is a valid expression, I don't think it is
reasonable.


Index: iseq.c
===================================================================
--- iseq.c      (revision 20237)
+++ iseq.c      (working copy)
@@ -1275,4 +1275,67 @@ rb_iseq_clone(VALUE iseqval, VALUE newcb
 }

+static VALUE
+simple_default_value(const VALUE *seq)
+{
+    VALUE val;
+    switch (*seq++) {
+      case BIN(putnil):
+       val = Qnil;
+      case BIN(putobject):
+       val = *seq++;
+       if (*seq == BIN(setlocal)) break;
+      default:
+       return Qundef;
+    }
+    return val;
+}
+
+VALUE
+rb_iseq_parameters(const rb_iseq_t *iseq)
+{
+    int i, r, s;
+    VALUE a, args = rb_ary_new2(iseq->arg_size);
+    ID req, opt, rest, block;
+#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
+#define PARAM_ID(i) iseq->local_table[i]
+#define PARAM(i, type) (                     \
+       PARAM_TYPE(type),                     \
+       rb_id2name(PARAM_ID(i)) ?             \
+       rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
+       a)
+
+    CONST_ID(req, "req");
+    for (i = 0; i < iseq->argc; i++) {
+       rb_ary_push(args, PARAM(i, req));
+    }
+    r = iseq->arg_rest != -1 ? iseq->arg_rest :
+       iseq->arg_post_len > 0 ? iseq->arg_post_start :
+       iseq->arg_block != -1 ? iseq->arg_block :
+       iseq->arg_size;
+    CONST_ID(opt, "opt");
+    for (s = i; i < r; i++) {
+       PARAM_TYPE(opt);
+       if (rb_id2name(PARAM_ID(i))) {
+           VALUE defval = simple_default_value(iseq->iseq + iseq->arg_opt_table[i-s]);
+           rb_ary_push(a, ID2SYM(PARAM_ID(i)));
+           if (defval != Qundef) rb_ary_push(a, defval);
+       }
+       rb_ary_push(args, a);
+    }
+    if (iseq->arg_rest != -1) {
+       CONST_ID(rest, "rest");
+       rb_ary_push(args, PARAM(iseq->arg_rest, rest));
+    }
+    r = iseq->arg_post_start + iseq->arg_post_len;
+    for (i = iseq->arg_post_start; i < r; i++) {
+       rb_ary_push(args, PARAM(i, req));
+    }
+    if (iseq->arg_block != -1) {
+       CONST_ID(block, "block");
+       rb_ary_push(args, PARAM(iseq->arg_block, block));
+    }
+    return args;
+}
+
 /* ruby2cext */

Index: proc.c
===================================================================
--- proc.c      (revision 20237)
+++ proc.c      (working copy)
@@ -26,7 +26,10 @@ VALUE rb_cBinding;
 VALUE rb_cProc;

+VALUE rb_iseq_parameters(const rb_iseq_t *iseq);
+
 static VALUE bmcall(VALUE, VALUE);
 static int method_arity(VALUE);
 static VALUE rb_obj_is_method(VALUE m);
+static rb_iseq_t *get_method_iseq(VALUE method);

 /* Proc */
@@ -616,6 +619,12 @@ get_proc_iseq(VALUE self)
    GetProcPtr(self, proc);
    iseq = proc->block.iseq;
-    if (!RUBY_VM_NORMAL_ISEQ_P(iseq))
-       return 0;
+    if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) {
+       NODE *node = (NODE *)iseq;
+       iseq = 0;
+       if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) {
+           /* method(:foo).to_proc */
+           iseq = get_method_iseq(node->nd_tval);
+       }
+    }
    return iseq;
 }
@@ -651,4 +660,40 @@ rb_proc_location(VALUE self)
 }

+static VALUE
+unnamed_parameters(int arity)
+{
+    VALUE a, param = rb_ary_new2((arity < 0) ? -arity : arity);
+    int n = (arity < 0) ? ~arity : arity;
+    ID req, rest;
+    CONST_ID(req, "req");
+    a = rb_ary_new3(1, ID2SYM(req));
+    OBJ_FREEZE(a);
+    for (; n; --n) {
+       rb_ary_push(param, a);
+    }
+    if (arity < 0) {
+       CONST_ID(rest, "rest");
+       rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest)));
+    }
+    return param;
+}
+
+/*
+ * call-seq:
+ *    proc.parameters  => array
+ *
+ * returns the parameter information of this proc
+ */
+
+static VALUE
+rb_proc_parameters(VALUE self)
+{
+    rb_iseq_t *iseq = get_proc_iseq(self);
+    if (!iseq) {
+       return unnamed_parameters(proc_arity(self));
+    }
+    return rb_iseq_parameters(iseq);
+}
+
 /*
 * call-seq:
@@ -1461,4 +1506,6 @@ get_method_iseq(VALUE method)
    body = data->body;
    switch (nd_type(body)) {
+      case NODE_BMETHOD:
+       return get_proc_iseq(body->nd_cval);
      case RUBY_VM_METHOD_NODE:
       GetISeqPtr((VALUE)body->nd_body, iseq);
@@ -1485,4 +1532,21 @@ rb_method_location(VALUE method)

 /*
+ * call-seq:
+ *    meth.parameters  => array
+ *
+ * returns the parameter information of this method
+ */
+
+static VALUE
+rb_method_parameters(VALUE method)
+{
+    rb_iseq_t *iseq = get_method_iseq(method);
+    if (!iseq) {
+       return unnamed_parameters(method_arity(method));
+    }
+    return rb_iseq_parameters(iseq);
+}
+
+/*
 *  call-seq:
 *   meth.to_s      =>  string
@@ -1812,4 +1876,5 @@ Init_Proc(void)
    rb_define_method(rb_cProc, "curry", proc_curry, -1);
    rb_define_method(rb_cProc, "source_location", rb_proc_location, 0);
+    rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);

    /* Exceptions */
@@ -1847,4 +1912,5 @@ Init_Proc(void)
    rb_define_method(rb_cMethod, "unbind", method_unbind, 0);
    rb_define_method(rb_cMethod, "source_location", rb_method_location, 0);
+    rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0);
    rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
    rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1);
@@ -1865,4 +1931,5 @@ Init_Proc(void)
    rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
    rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0);
+    rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0);

    /* Module#*_method */
Index: test/ruby/test_proc.rb
===================================================================
--- test/ruby/test_proc.rb      (revision 20237)
+++ test/ruby/test_proc.rb      (working copy)
@@ -674,3 +674,46 @@ class TestProc < Test::Unit::TestCase
    assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
  end
+
+  def test_parameters
+    assert_equal([], proc {}.parameters)
+    assert_equal([], proc {||}.parameters)
+    assert_equal([[:req, :a]], proc {|a|}.parameters)
+    assert_equal([[:req, :a], [:req, :b]], proc {|a, b|}.parameters)
+    assert_equal([[:opt, :a, :a], [:block, :b]], proc {|a=:a, &b|}.parameters)
+    assert_equal([[:req, :a], [:opt, :b, :b]], proc {|a, b=:b|}.parameters)
+    assert_equal([[:rest, :a]], proc {|*a|}.parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:block, :c]], proc {|a, *b, &c|}.parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c]], proc {|a, *b, c|}.parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], proc {|a, *b, c, &d|}.parameters)
+    assert_equal([[:req, :a], [:opt, :b, :b], [:rest, :c], [:req, :d], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters)
+    assert_equal([[:req], [:block, :b]], proc {|(a), &b|}.parameters)
+    assert_equal([[:req, :a], [:req, :b], [:opt, :c, :c], [:opt, :d, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
+  end
+
+  def pm0() end
+  def pm1(a) end
+  def pm2(a, b) end
+  def pmo1(a = :a, &b) end
+  def pmo2(a, b = :b) end
+  def pmo3(*a) end
+  def pmo4(a, *b, &c) end
+  def pmo5(a, *b, c) end
+  def pmo6(a, *b, c, &d) end
+  def pmo7(a, b = :b, *c, d, &e) end
+  def pma1((a), &b) end
+
+
+  def test_bound_parameters
+    assert_equal([], method(:pm0).to_proc.parameters)
+    assert_equal([[:req, :a]], method(:pm1).to_proc.parameters)
+    assert_equal([[:req, :a], [:req, :b]], method(:pm2).to_proc.parameters)
+    assert_equal([[:opt, :a, :a], [:block, :b]], method(:pmo1).to_proc.parameters)
+    assert_equal([[:req, :a], [:opt, :b, :b]], method(:pmo2).to_proc.parameters)
+    assert_equal([[:rest, :a]], method(:pmo3).to_proc.parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).to_proc.parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).to_proc.parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).to_proc.parameters)
+    assert_equal([[:req, :a], [:opt, :b, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).to_proc.parameters)
+    assert_equal([[:req], [:block, :b]], method(:pma1).to_proc.parameters)
+  end
 end
Index: test/ruby/test_method.rb
===================================================================
--- test/ruby/test_method.rb    (revision 20237)
+++ test/ruby/test_method.rb    (working copy)
@@ -21,4 +21,6 @@ class TestMethod < Test::Unit::TestCase
  def mo5(a, *b, c) end
  def mo6(a, *b, c, &d) end
+  def mo7(a, b = nil, *c, d, &e) end
+  def ma1((a), &b) end

  class Base
@@ -222,3 +224,71 @@ class TestMethod < Test::Unit::TestCase
    assert_raise(ArgumentError) { o.method(:foo).call(1) }
  end
+
+  define_method(:pm0) {||}
+  define_method(:pm1) {|a|}
+  define_method(:pm2) {|a, b|}
+  define_method(:pmo1) {|a = nil, &b|}
+  define_method(:pmo2) {|a, b = nil|}
+  define_method(:pmo3) {|*a|}
+  define_method(:pmo4) {|a, *b, &c|}
+  define_method(:pmo5) {|a, *b, c|}
+  define_method(:pmo6) {|a, *b, c, &d|}
+  define_method(:pmo7) {|a, b = nil, *c, d, &e|}
+  define_method(:pma1) {|(a), &b|}
+
+  def test_bound_parameters
+    assert_equal([], method(:m0).parameters)
+    assert_equal([[:req, :a]], method(:m1).parameters)
+    assert_equal([[:req, :a], [:req, :b]], method(:m2).parameters)
+    assert_equal([[:opt, :a, nil], [:block, :b]], method(:mo1).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil]], method(:mo2).parameters)
+    assert_equal([[:rest, :a]], method(:mo3).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:mo4).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:mo5).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:mo6).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], method(:mo7).parameters)
+    assert_equal([[:req], [:block, :b]], method(:ma1).parameters)
+  end
+
+  def test_unbound_parameters
+    assert_equal([], self.class.instance_method(:m0).parameters)
+    assert_equal([[:req, :a]], self.class.instance_method(:m1).parameters)
+    assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:m2).parameters)
+    assert_equal([[:opt, :a, nil], [:block, :b]], self.class.instance_method(:mo1).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil]], self.class.instance_method(:mo2).parameters)
+    assert_equal([[:rest, :a]], self.class.instance_method(:mo3).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:mo4).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:mo5).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:mo6).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:mo7).parameters)
+    assert_equal([[:req], [:block, :b]], self.class.instance_method(:ma1).parameters)
+  end
+
+  def test_bmethod_bound_parameters
+    assert_equal([], method(:pm0).parameters)
+    assert_equal([[:req, :a]], method(:pm1).parameters)
+    assert_equal([[:req, :a], [:req, :b]], method(:pm2).parameters)
+    assert_equal([[:opt, :a, nil], [:block, :b]], method(:pmo1).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil]], method(:pmo2).parameters)
+    assert_equal([[:rest, :a]], method(:pmo3).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).parameters)
+    assert_equal([[:req], [:block, :b]], method(:pma1).parameters)
+  end
+
+  def test_bmethod_unbound_parameters
+    assert_equal([], self.class.instance_method(:pm0).parameters)
+    assert_equal([[:req, :a]], self.class.instance_method(:pm1).parameters)
+    assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:pm2).parameters)
+    assert_equal([[:opt, :a, nil], [:block, :b]], self.class.instance_method(:pmo1).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil]], self.class.instance_method(:pmo2).parameters)
+    assert_equal([[:rest, :a]], self.class.instance_method(:pmo3).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:pmo4).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:pmo5).parameters)
+    assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:pmo6).parameters)
+    assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:pmo7).parameters)
+    assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters)
+  end
 end


--
Nobu Nakada




--
Yehuda Katz
Developer | Engine Yard
(ph) 718.877.1325
------=_Part_12869_6281907.1227299712083--