diff --git a/ChangeLog b/ChangeLog index f3de6635d0..07853e6d9c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Mon Oct 27 22:29:11 2008 Yuki Sonoda (Yugui) + + * 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 * ext/mathn/complex/complex.c: no need to define rb_cComplex because diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 8d2521b02f..294b12abd2 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -310,6 +310,166 @@ class TestProc < Test::Unit::TestCase 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] diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 2e862492f3..5c4930499c 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -678,13 +678,157 @@ vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block, 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_block_args_complex(rb_thread_t *th, const rb_iseq_t * iseq, + int argc, VALUE * argv) +{ + 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 (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 { + ary = rb_ary_new4(rsize, &argv[r]); + + /* copy post argument */ + MEMMOVE(&argv[start], &argv[m+rsize+osize], VALUE, psize); + argv[r] = ary; + } + + for (i=psize; iargc; + VALUE ary; + int opt_pc = 0; + + th->mark_stack_len = argc; + + /* + * 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); + + CHECK_STACK_OVERFLOW(th->cfp, argc); + + MEMCPY(argv, RARRAY_PTR(ary), VALUE, argc); + } + + for (i=argc; iarg_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; + + 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; imark_stack_len = iseq->arg_size; + } + + /* {|&b|} */ + if (iseq->arg_block != -1) { + VALUE procval = Qnil; + + if (blockptr) { + procval = blockptr->proc; + } + + argv[iseq->arg_block] = procval; + } + + th->mark_stack_len = 0; + return opt_pc; +} + static inline int vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, - int orig_argc, VALUE *argv, + int argc, VALUE *argv, const rb_block_t *blockptr, int lambda) { if (0) { /* for debug */ - printf(" argc: %d\n", orig_argc); + 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); @@ -697,106 +841,11 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, if (lambda) { /* call as method */ int opt_pc; - VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, orig_argc, argv, &blockptr); + VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr); return opt_pc; } else { - int i; - int argc = orig_argc; - const int m = iseq->argc; - VALUE ary; - - th->mark_stack_len = argc; - - /* - * 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); - - CHECK_STACK_OVERFLOW(th->cfp, argc); - - MEMCPY(argv, RARRAY_PTR(ary), VALUE, argc); - } - - for (i=argc; iarg_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; - - 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; - - if (psize > len) psize = len; - - ary = rb_ary_new4(rsize - psize, &argv[r]); - - if (0) { - printf(" argc: %d\n", argc); - printf(" len: %d\n", len); - printf("start: %d\n", start); - printf("rsize: %d\n", rsize); - } - - /* copy post argument */ - MEMMOVE(&argv[start], &argv[r + rsize - psize], VALUE, psize); - - for (i=psize; i {|a, b, *r|} - */ - for (i=argc; imark_stack_len = iseq->arg_size; - } - - /* {|&b|} */ - if (iseq->arg_block != -1) { - VALUE procval = Qnil; - - if (blockptr) { - procval = blockptr->proc; - } - - argv[iseq->arg_block] = procval; - } - - th->mark_stack_len = 0; - return 0; + return vm_yield_setup_block_args(th, iseq, argc, argv, blockptr); } }