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

ruby-changes:8437

From: yugui <ko1@a...>
Date: Mon, 27 Oct 2008 22:36:41 +0900 (JST)
Subject: [ruby-changes:8437] Ruby:r19968 (trunk): * vm_insnhelper.c (vm_yield_setup_args): supports optional parameters.

yugui	2008-10-27 22:35:46 +0900 (Mon, 27 Oct 2008)

  New Revision: 19968

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

  Log:
    * vm_insnhelper.c (vm_yield_setup_args): supports optional parameters.
      Fixed [ruby-core:19503].
    
    * vm_insnhelper.c (vm_yield_setup_block_args): a new function. extracted
      from vm_yield_setup_args.
    
    * vm_insnhelper.c (vm_yield_setup_block_args_complex): ditto.
    
    * test/ruby/test_proc.rb: added tests for arguments on a Proc from
      Kernel#proc called.

  Modified files:
    trunk/ChangeLog
    trunk/test/ruby/test_proc.rb
    trunk/vm_insnhelper.c

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 19967)
+++ ChangeLog	(revision 19968)
@@ -1,3 +1,16 @@
+Mon Oct 27 22:29:11 2008  Yuki Sonoda (Yugui)  <yugui@y...>
+
+	* vm_insnhelper.c (vm_yield_setup_args): supports optional parameters.
+	  Fixed [ruby-core:19503].
+
+	* vm_insnhelper.c (vm_yield_setup_block_args): a new function. extracted
+	  from vm_yield_setup_args.
+
+	* vm_insnhelper.c (vm_yield_setup_block_args_complex): ditto.
+
+	* test/ruby/test_proc.rb: added tests for arguments on a Proc from
+	  Kernel#proc called.
+
 Mon Oct 27 20:03:05 2008  NAKAMURA Usaku  <usa@r...>
 
 	* ext/mathn/complex/complex.c: no need to define rb_cComplex because
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 19967)
+++ vm_insnhelper.c	(revision 19968)
@@ -678,126 +678,175 @@
     return val;
 }
 
+
+/*--
+ * @brief on supplied all of optional, rest and post parameters.
+ * @pre iseq is block style (not lambda style)
+ */
 static inline int
-vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq,
-		    int orig_argc, VALUE *argv,
-		    const rb_block_t *blockptr, int lambda)
+vm_yield_setup_block_args_complex(rb_thread_t *th, const rb_iseq_t * iseq,
+	int argc, VALUE * argv)
 {
-    if (0) { /* for debug */
-	printf("     argc: %d\n", orig_argc);
-	printf("iseq argc: %d\n", iseq->argc);
-	printf("iseq opts: %d\n", iseq->arg_opts);
-	printf("iseq rest: %d\n", iseq->arg_rest);
-	printf("iseq post: %d\n", iseq->arg_post_len);
-	printf("iseq blck: %d\n", iseq->arg_block);
-	printf("iseq smpl: %d\n", iseq->arg_simple);
-	printf("   lambda: %s\n", lambda ? "true" : "false");
+    int opt_pc = 0;
+    int i;
+    const int m = iseq->argc;
+    const int r = iseq->arg_rest;
+    int len = iseq->arg_post_len;
+    int start = iseq->arg_post_start;
+    int rsize = argc > m ? argc - m : 0;    /* # of arguments which did not consumed yet */
+    int psize = rsize > len ? len : rsize;  /* # of post arguments */
+    int osize = 0;  /* # of opt arguments */
+    VALUE ary;
+
+    /* reserves arguments for post parameters */
+    rsize -= psize;
+
+    if (iseq->arg_opts) {
+	const int opts = iseq->arg_opts - 1;
+	if (rsize > opts) {
+            osize = opts;
+	    opt_pc = iseq->arg_opt_table[opts];
+	}
+	else {
+            osize = rsize;
+	    opt_pc = iseq->arg_opt_table[rsize];
+	}
     }
+    rsize -= osize;
 
-    if (lambda) {
-	/* call as method */
-	int opt_pc;
-	VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, orig_argc, argv, &blockptr);
-	return opt_pc;
+    if (0) {
+	printf(" argc: %d\n", argc);
+	printf("  len: %d\n", len);
+	printf("start: %d\n", start);
+	printf("rsize: %d\n", rsize);
     }
+
+    if (r == -1) {
+        /* copy post argument */
+        MEMMOVE(&argv[start], &argv[m+osize], VALUE, psize);
+    }
     else {
-	int i;
-	int argc = orig_argc;
-	const int m = iseq->argc;
-	VALUE ary;
+        ary = rb_ary_new4(rsize, &argv[r]);
 
-	th->mark_stack_len = argc;
+        /* copy post argument */
+        MEMMOVE(&argv[start], &argv[m+rsize+osize], VALUE, psize);
+        argv[r] = ary;
+    }
 
-	/*
-	 * yield [1, 2]
-	 *  => {|a|} => a = [1, 2]
-	 *  => {|a, b|} => a, b = [1, 2]
-	 */
-	if (!(iseq->arg_simple & 0x02) &&
-	    (m + iseq->arg_post_len) > 0 &&
-	    argc == 1 && !NIL_P(ary = rb_check_array_type(argv[0]))) {
-	    th->mark_stack_len = argc = RARRAY_LEN(ary);
+    for (i=psize; i<len; i++) {
+	argv[start + i] = Qnil;
+    }
 
-	    CHECK_STACK_OVERFLOW(th->cfp, argc);
+    return opt_pc;
+}
 
-	    MEMCPY(argv, RARRAY_PTR(ary), VALUE, argc);
-	}
+static inline int
+vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq,
+	int orig_argc, VALUE * argv,
+	const rb_block_t *blockptr)
+{
+    int i;
+    int argc = orig_argc;
+    const int m = iseq->argc;
+    VALUE ary;
+    int opt_pc = 0;
 
-	for (i=argc; i<m; i++) {
-	    argv[i] = Qnil;
-	}
+    th->mark_stack_len = argc;
 
-	if (iseq->arg_rest == -1) {
-	    const int arg_size = iseq->arg_size;
-	    if (arg_size < argc) {
-		/*
-		 * yield 1, 2
-		 * => {|a|} # truncate
-		 */
-		th->mark_stack_len = argc = arg_size;
-	    }
-	}
-	else {
-	    int r = iseq->arg_rest;
+    /*
+     * yield [1, 2]
+     *  => {|a|} => a = [1, 2]
+     *  => {|a, b|} => a, b = [1, 2]
+     */
+    if (!(iseq->arg_simple & 0x02) &&          /* exclude {|a|} */
+            (m + iseq->arg_post_len) > 0 &&    /* this process is meaningful */
+            argc == 1 && !NIL_P(ary = rb_check_array_type(argv[0]))) { /* rhs is only an array */
+        th->mark_stack_len = argc = RARRAY_LEN(ary);
 
-	    if (iseq->arg_post_len) {
-		int len = iseq->arg_post_len;
-		int start = iseq->arg_post_start;
-		int rsize = argc > m ? argc - m : 0;
-		int psize = rsize;
-		VALUE ary;
+        CHECK_STACK_OVERFLOW(th->cfp, argc);
 
-		if (psize > len) psize = len;
+        MEMCPY(argv, RARRAY_PTR(ary), VALUE, argc);
+    }
 
-		ary = rb_ary_new4(rsize - psize, &argv[r]);
+    for (i=argc; i<m; i++) {
+        argv[i] = Qnil;
+    }
 
-		if (0) {
-		    printf(" argc: %d\n", argc);
-		    printf("  len: %d\n", len);
-		    printf("start: %d\n", start);
-		    printf("rsize: %d\n", rsize);
-		}
+    if (iseq->arg_rest == -1 && iseq->arg_opts == 0) {
+        const int arg_size = iseq->arg_size;
+        if (arg_size < argc) {
+            /*
+             * yield 1, 2
+             * => {|a|} # truncate
+             */
+            th->mark_stack_len = argc = arg_size;
+        }
+    }
+    else {
+        int r = iseq->arg_rest;
 
-		/* copy post argument */
-		MEMMOVE(&argv[start], &argv[r + rsize - psize], VALUE, psize);
+        if (iseq->arg_post_len || 
+                iseq->arg_opts) { /* TODO: implement simple version for (iseq->arg_post_len==0 && iseq->arg_opts > 0) */
+	    opt_pc = vm_yield_setup_block_args_complex(th, iseq, argc, argv);
+        }
+        else {
+            if (argc < r) {
+                /* yield 1
+                 * => {|a, b, *r|}
+                 */
+                for (i=argc; i<r; i++) {
+                    argv[i] = Qnil;
+                }
+                argv[r] = rb_ary_new();
+            }
+            else {
+                argv[r] = rb_ary_new4(argc-r, &argv[r]);
+            }
+        }
 
-		for (i=psize; i<len; i++) {
-		    argv[start + i] = Qnil;
-		}
-		argv[r] = ary;
-	    }
-	    else {
-		if (argc < r) {
-		    /* yield 1
-		     * => {|a, b, *r|}
-		     */
-		    for (i=argc; i<r; i++) {
-			argv[i] = Qnil;
-		    }
-		    argv[r] = rb_ary_new();
-		}
-		else {
-		    argv[r] = rb_ary_new4(argc-r, &argv[r]);
-		}
-	    }
+        th->mark_stack_len = iseq->arg_size;
+    }
 
-	    th->mark_stack_len = iseq->arg_size;
-	}
+    /* {|&b|} */
+    if (iseq->arg_block != -1) {
+        VALUE procval = Qnil;
 
-	/* {|&b|} */
-	if (iseq->arg_block != -1) {
-	    VALUE procval = Qnil;
+        if (blockptr) {
+            procval = blockptr->proc;
+        }
 
-	    if (blockptr) {
-		procval = blockptr->proc;
-	    }
+        argv[iseq->arg_block] = procval;
+    }
 
-	    argv[iseq->arg_block] = procval;
-	}
+    th->mark_stack_len = 0;
+    return opt_pc;
+}
 
-	th->mark_stack_len = 0;
-	return 0;
+static inline int
+vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq,
+		    int argc, VALUE *argv,
+		    const rb_block_t *blockptr, int lambda)
+{
+    if (0) { /* for debug */
+	printf("     argc: %d\n", argc);
+	printf("iseq argc: %d\n", iseq->argc);
+	printf("iseq opts: %d\n", iseq->arg_opts);
+	printf("iseq rest: %d\n", iseq->arg_rest);
+	printf("iseq post: %d\n", iseq->arg_post_len);
+	printf("iseq blck: %d\n", iseq->arg_block);
+	printf("iseq smpl: %d\n", iseq->arg_simple);
+	printf("   lambda: %s\n", lambda ? "true" : "false");
     }
+
+    if (lambda) {
+	/* call as method */
+	int opt_pc;
+	VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr);
+	return opt_pc;
+    }
+    else {
+	return vm_yield_setup_block_args(th, iseq, argc, argv, blockptr);
+    }
 }
 
 static VALUE
Index: test/ruby/test_proc.rb
===================================================================
--- test/ruby/test_proc.rb	(revision 19967)
+++ test/ruby/test_proc.rb	(revision 19968)
@@ -310,6 +310,166 @@
     assert_raise(ArgumentError) { proc {}.curry.binding }
   end
 
+  def test_proc_args_plain
+    pr = proc {|a,b,c,d,e|
+      [a,b,c,d,e]
+    }
+    assert_equal [nil,nil,nil,nil,nil],  pr.call()
+    assert_equal [1,nil,nil,nil,nil],  pr.call(1)
+    assert_equal [1,2,nil,nil,nil],  pr.call(1,2)
+    assert_equal [1,2,3,nil,nil],  pr.call(1,2,3)
+    assert_equal [1,2,3,4,nil],  pr.call(1,2,3,4)
+    assert_equal [1,2,3,4,5],  pr.call(1,2,3,4,5)
+    assert_equal [1,2,3,4,5],  pr.call(1,2,3,4,5,6)
+
+    assert_equal [nil,nil,nil,nil,nil],  pr.call([])
+    assert_equal [1,nil,nil,nil,nil],  pr.call([1])
+    assert_equal [1,2,nil,nil,nil],  pr.call([1,2])
+    assert_equal [1,2,3,nil,nil],  pr.call([1,2,3])
+    assert_equal [1,2,3,4,nil],  pr.call([1,2,3,4])
+    assert_equal [1,2,3,4,5],  pr.call([1,2,3,4,5])
+    assert_equal [1,2,3,4,5],  pr.call([1,2,3,4,5,6])
+
+    r = proc{|a| a}.call([1,2,3])
+    assert_equal [1,2,3], r
+
+    r = proc{|a,| a}.call([1,2,3])
+    assert_equal 1, r
+
+    r = proc{|a,| a}.call([])
+    assert_equal nil, r
+  end
+
+
+  def test_proc_args_rest
+    pr = proc {|a,b,c,*d|
+      [a,b,c,d]
+    }
+    assert_equal [nil,nil,nil,[]],  pr.call()
+    assert_equal [1,nil,nil,[]],  pr.call(1)
+    assert_equal [1,2,nil,[]],  pr.call(1,2)
+    assert_equal [1,2,3,[]],  pr.call(1,2,3)
+    assert_equal [1,2,3,[4]], pr.call(1,2,3,4)
+    assert_equal [1,2,3,[4,5]], pr.call(1,2,3,4,5)
+    assert_equal [1,2,3,[4,5,6]], pr.call(1,2,3,4,5,6)
+
+    assert_equal [nil,nil,nil,[]],  pr.call([])
+    assert_equal [1,nil,nil,[]],  pr.call([1])
+    assert_equal [1,2,nil,[]],  pr.call([1,2])
+    assert_equal [1,2,3,[]],  pr.call([1,2,3])
+    assert_equal [1,2,3,[4]], pr.call([1,2,3,4])
+    assert_equal [1,2,3,[4,5]], pr.call([1,2,3,4,5])
+    assert_equal [1,2,3,[4,5,6]], pr.call([1,2,3,4,5,6])
+
+    r = proc{|*a| a}.call([1,2,3])
+    assert [1,2,3], r
+  end
+
+  def test_proc_args_rest_and_post
+    pr = proc {|a,b,*c,d,e|
+      [a,b,c,d,e]
+    }
+    assert_equal [nil, nil, [], nil, nil], pr.call()
+    assert_equal [1, nil, [], nil, nil], pr.call(1)
+    assert_equal [1, 2, [], nil, nil], pr.call(1,2)
+    assert_equal [1, 2, [], 3, nil], pr.call(1,2,3)
+    assert_equal [1, 2, [], 3, 4], pr.call(1,2,3,4)
+    assert_equal [1, 2, [3], 4, 5], pr.call(1,2,3,4,5)
+    assert_equal [1, 2, [3, 4], 5, 6], pr.call(1,2,3,4,5,6)
+    assert_equal [1, 2, [3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7)
+
+    assert_equal [nil, nil, [], nil, nil], pr.call([])
+    assert_equal [1, nil, [], nil, nil], pr.call([1])
+    assert_equal [1, 2, [], nil, nil], pr.call([1,2])
+    assert_equal [1, 2, [], 3, nil], pr.call([1,2,3])
+    assert_equal [1, 2, [], 3, 4], pr.call([1,2,3,4])
+    assert_equal [1, 2, [3], 4, 5], pr.call([1,2,3,4,5])
+    assert_equal [1, 2, [3, 4], 5, 6], pr.call([1,2,3,4,5,6])
+    assert_equal [1, 2, [3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7])
+  end
+
+  def test_proc_args_opt
+    pr = proc {|a,b,c=:c|
+      [a,b,c]
+    }
+    assert_equal [nil, nil, :c], pr.call()
+    assert_equal [1, nil, :c], pr.call(1)
+    assert_equal [1, 2, :c], pr.call(1,2)
+    assert_equal [1, 2, 3], pr.call(1,2,3)
+    assert_equal [1, 2, 3], pr.call(1,2,3,4)
+    assert_equal [1, 2, 3], pr.call(1,2,3,4,5)
+    assert_equal [1, 2, 3], pr.call(1,2,3,4,5,6)
+
+    assert_equal [nil, nil, :c], pr.call([])
+    assert_equal [1, nil, :c], pr.call([1])
+    assert_equal [1, 2, :c], pr.call([1,2])
+    assert_equal [1, 2, 3], pr.call([1,2,3])
+    assert_equal [1, 2, 3], pr.call([1,2,3,4])
+    assert_equal [1, 2, 3], pr.call([1,2,3,4,5])
+    assert_equal [1, 2, 3], pr.call([1,2,3,4,5,6])
+  end
+
+  def test_proc_args_opt_and_post
+    pr = proc {|a,b,c=:c,d,e|
+      [a,b,c,d,e]
+    }
+    assert_equal [nil, nil, :c, nil, nil], pr.call()
+    assert_equal [1, nil, :c, nil, nil], pr.call(1)
+    assert_equal [1, 2, :c, nil, nil], pr.call(1,2)
+    assert_equal [1, 2, :c, 3, nil], pr.call(1,2,3)
+    assert_equal [1, 2, :c, 3, 4], pr.call(1,2,3,4)
+    assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5)
+    assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5,6)
+
+    assert_equal [nil, nil, :c, nil, nil], pr.call([])
+    assert_equal [1, nil, :c, nil, nil], pr.call([1])
+    assert_equal [1, 2, :c, nil, nil], pr.call([1,2])
+    assert_equal [1, 2, :c, 3, nil], pr.call([1,2,3])
+    assert_equal [1, 2, :c, 3, 4], pr.call([1,2,3,4])
+    assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5])
+    assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5,6])
+  end
+
+  def test_proc_args_opt_and_rest
+    pr = proc {|a,b,c=:c,*d|
+      [a,b,c,d]
+    }
+    assert_equal [nil, nil, :c, []], pr.call()
+    assert_equal [1, nil, :c, []], pr.call(1)
+    assert_equal [1, 2, :c, []], pr.call(1,2)
+    assert_equal [1, 2, 3, []], pr.call(1,2,3)
+    assert_equal [1, 2, 3, [4]], pr.call(1,2,3,4)
+    assert_equal [1, 2, 3, [4, 5]], pr.call(1,2,3,4,5)
+
+    assert_equal [nil, nil, :c, []], pr.call([])
+    assert_equal [1, nil, :c, []], pr.call([1])
+    assert_equal [1, 2, :c, []], pr.call([1,2])
+    assert_equal [1, 2, 3, []], pr.call([1,2,3])
+    assert_equal [1, 2, 3, [4]], pr.call([1,2,3,4])
+    assert_equal [1, 2, 3, [4, 5]], pr.call([1,2,3,4,5])
+  end
+
+  def test_proc_args_opt_and_rest_and_post
+    pr = proc {|a,b,c=:c,*d,e|
+      [a,b,c,d,e]
+    }
+    assert_equal [nil, nil, :c, [], nil], pr.call()
+    assert_equal [1, nil, :c, [], nil], pr.call(1)
+    assert_equal [1, 2, :c, [], nil], pr.call(1,2)
+    assert_equal [1, 2, :c, [], 3], pr.call(1,2,3)
+    assert_equal [1, 2, 3, [], 4], pr.call(1,2,3,4)
+    assert_equal [1, 2, 3, [4], 5], pr.call(1,2,3,4,5)
+    assert_equal [1, 2, 3, [4,5], 6], pr.call(1,2,3,4,5,6)
+
+    assert_equal [nil, nil, :c, [], nil], pr.call([])
+    assert_equal [1, nil, :c, [], nil], pr.call([1])
+    assert_equal [1, 2, :c, [], nil], pr.call([1,2])
+    assert_equal [1, 2, :c, [], 3], pr.call([1,2,3])
+    assert_equal [1, 2, 3, [], 4], pr.call([1,2,3,4])
+    assert_equal [1, 2, 3, [4], 5], pr.call([1,2,3,4,5])
+    assert_equal [1, 2, 3, [4,5], 6], pr.call([1,2,3,4,5,6])
+  end
+
   def test_proc_args_unleashed
     r = proc {|a,b=1,*c,d,e|
       [a,b,c,d,e]

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

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