[前][次][番号順一覧][スレッド一覧]

ruby-changes:8848

From: nobu <ko1@a...>
Date: Fri, 28 Nov 2008 13:19:59 +0900 (JST)
Subject: [ruby-changes:8848] Ruby:r20384 (trunk): * iseq.c (simple_default_value): extracts simplest default

nobu	2008-11-28 13:19:37 +0900 (Fri, 28 Nov 2008)

  New Revision: 20384

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=20384

  Log:
    * iseq.c (simple_default_value): extracts simplest default
      argument value.
    * iseq.c (rb_iseq_parameters): returns parameter list.
    
    * proc.c (get_proc_iseq, get_method_iseq): handles ifunc and
      bmethod.
    
    * proc.c (rb_proc_parameters, rb_method_parameters): added
      Proc#parameters and Method#parameters.  [ruby-core:19759]

  Modified files:
    trunk/ChangeLog
    trunk/iseq.c
    trunk/proc.c
    trunk/test/ruby/test_method.rb
    trunk/test/ruby/test_proc.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 20383)
+++ ChangeLog	(revision 20384)
@@ -1,3 +1,16 @@
+Fri Nov 28 13:19:34 2008  Nobuyoshi Nakada  <nobu@r...>
+
+	* iseq.c (simple_default_value): extracts simplest default
+	  argument value.
+
+	* iseq.c (rb_iseq_parameters): returns parameter list.
+
+	* proc.c (get_proc_iseq, get_method_iseq): handles ifunc and
+	  bmethod.
+
+	* proc.c (rb_proc_parameters, rb_method_parameters): added
+	  Proc#parameters and Method#parameters.  [ruby-core:19759]
+
 Fri Nov 28 02:18:47 2008  Yukihiro Matsumoto  <matz@r...>
 
 	* ext/bigdecimal/bigdecimal.c (BigDecimal_DoDivmod): bigdecimal
Index: iseq.c
===================================================================
--- iseq.c	(revision 20383)
+++ iseq.c	(revision 20384)
@@ -1274,6 +1274,84 @@
     return newiseq;
 }
 
+static VALUE
+simple_default_value(const VALUE *seq, const VALUE *eseq)
+{
+    VALUE val;
+
+  again:
+    switch (*seq++) {
+      case BIN(trace):
+	if (++seq >= eseq) return Qundef;
+	goto again;
+      case BIN(putnil):
+	val = Qnil;
+	goto got;
+      case BIN(putobject):
+	val = *seq++;
+      got:
+	switch (*seq++) {
+	  case BIN(setlocal):
+	    if ((seq+=1) != eseq) return Qundef;
+	    break;
+	  case BIN(setdynamic):
+	    if ((seq+=2) != eseq) return Qundef;
+	    break;
+	}
+	return val;
+      default:
+	return Qundef;
+    }
+}
+
+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],
+						iseq->iseq + iseq->arg_opt_table[i-s+1]);
+	    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 */
 
 VALUE
Index: proc.c
===================================================================
--- proc.c	(revision 20383)
+++ proc.c	(revision 20384)
@@ -25,9 +25,12 @@
 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 */
 
@@ -615,8 +618,14 @@
 
     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;
 }
 
@@ -650,8 +659,44 @@
     return iseq_location(get_proc_iseq(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:
  *   prc == other_proc   =>  true or false
  *
  * Return <code>true</code> if <i>prc</i> is the same object as
@@ -1463,6 +1508,8 @@
     Data_Get_Struct(method, struct METHOD, data);
     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);
 	if (RUBY_VM_NORMAL_ISEQ_P(iseq)) break;
@@ -1487,6 +1534,23 @@
 }
 
 /*
+ * 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
  *   meth.inspect   =>  string
@@ -1814,6 +1878,7 @@
     rb_define_method(rb_cProc, "binding", proc_binding, 0);
     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 */
     rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
@@ -1849,6 +1914,7 @@
     rb_define_method(rb_cMethod, "owner", method_owner, 0);
     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);
 
@@ -1867,6 +1933,7 @@
     rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0);
     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 */
     rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1);
Index: test/ruby/test_proc.rb
===================================================================
--- test/ruby/test_proc.rb	(revision 20383)
+++ test/ruby/test_proc.rb	(revision 20384)
@@ -673,4 +673,47 @@
     }.call(1,2,3,4,5)
     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 20383)
+++ test/ruby/test_method.rb	(revision 20384)
@@ -20,6 +20,8 @@
   def mo4(a, *b, &c) end
   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
     def foo() :base end
@@ -237,4 +239,72 @@
     assert !M.public_instance_methods.include?(:func), 'module methods are private by default'
     assert M.public_instance_methods.include?(:meth), 'normal methods are public by default'
   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

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]