diff --git a/ChangeLog b/ChangeLog index 4251d89598..51eedc90da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Tue May 1 13:12:49 2007 Koichi Sasada + + * yarvcore.h, compile.c (set_arguments): support post arguments. + + * test/ruby/test_method.rb: add tests for above. + + * test/ruby/test_proc.rb: ditto. + + * proc.c: fix an arity bug ([ruby-core:11029]). + + * vm.c, vm.h, insns.def, vm_dump.h: fix bmethod process. + + * vm.c: support block argument on block parameter. + Fri Apr 27 17:05:41 2007 Nobuyoshi Nakada * numeric.c (int_pow): bugfix of overflow detection. diff --git a/compile.c b/compile.c index 41c2c11b20..7f702b93a7 100644 --- a/compile.c +++ b/compile.c @@ -849,6 +849,83 @@ set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args) if (iseq->arg_rest == -1) { rb_bug("arg_rest: -1"); } + + if (post_len) { + /* + * if rest.length < post_len + * raise ArgumentError + * end + * post1 = rest.shift + * post2 = rest.shift + * ... + * + * #=> yarv insns + * push rest + * call :length + * put 1 + * call :< + * branchunless :success + * put ArgumentsError + * put "wrong number of arugments (%d of %d)" + * call :new, 1 + * call :raise + * success: + * push rest + * call :shift + * set post1 + * push rest + * call :shift + * set post2 + * ... + */ + LABEL *lsuccess = NEW_LABEL(nd_line(node_args)); + int i; + +#define GET_LOCAL(idx) do { \ + if (iseq->type == ISEQ_TYPE_METHOD) { \ + ADD_INSN1(optargs, nd_line(node_args), getlocal, INT2FIX(iseq->local_size - (idx) + 1)); \ + } \ + else { \ + ADD_INSN2(optargs, nd_line(node_args), getdynamic, INT2FIX(iseq->local_size - (idx)), INT2FIX(0)); \ + } \ +} while (0) + +#define SET_LOCAL(idx) do { \ + if (iseq->type == ISEQ_TYPE_METHOD) { \ + ADD_INSN1(optargs, nd_line(node_args), setlocal, INT2FIX(iseq->local_size - (idx) + 1)); \ + } \ + else { \ + ADD_INSN2(optargs, nd_line(node_args), setdynamic, INT2FIX(iseq->local_size - (idx)), INT2FIX(0)); \ + } \ +} while (0) + + GET_LOCAL(iseq->arg_rest); + ADD_SEND (optargs, nd_line(node_args), ID2SYM(idLength), INT2FIX(0)); + ADD_INSN1(optargs, nd_line(node_args), putobject, INT2FIX(1)); + ADD_SEND (optargs, nd_line(node_args), ID2SYM(idLT), INT2FIX(1)); + ADD_INSNL(optargs, nd_line(node_args), branchunless, lsuccess); + ADD_CALL_RECEIVER(optargs, nd_line(node_args)); + + /* error */ + ADD_INSN1(optargs, nd_line(node_args), putobject, rb_eArgError); + ADD_INSN1(optargs, nd_line(node_args), putstring, rb_str_new2("wrong number of arguments")); + ADD_SEND (optargs, nd_line(node_args), ID2SYM(rb_intern("new")), INT2FIX(1)); + ADD_CALL (optargs, nd_line(node_args), ID2SYM(rb_intern("raise")), INT2FIX(1)); + ADD_INSN (optargs, nd_line(node_args), pop); /* dummy */ + + ADD_LABEL(optargs, lsuccess); + + for (i=0; iarg_rest); + ADD_SEND (optargs, nd_line(node_args), ID2SYM(rb_intern("pop")), + INT2FIX(0)); + SET_LOCAL(iseq->arg_rest + i + 1); + } + + iseq->arg_post_len = post_len; + } +#undef GET_LOCAL +#undef SET_LOCAL } if (block_id) { diff --git a/insns.def b/insns.def index d616508b8c..b311199228 100644 --- a/insns.def +++ b/insns.def @@ -1251,9 +1251,8 @@ send } } - /* dirty hack */ - id = (ID) ((VALUE *)(lcfp+1)->block_iseq)[0]; - klass = ((VALUE *)(lcfp+1)->block_iseq)[1]; + id = lcfp->method_id; + klass = search_super_klass(lcfp->method_klass, recv); if (TOPN(num) == Qfalse) { /* for ZSUPER */ @@ -1265,7 +1264,10 @@ send } } } + else { klass = search_super_klass(ip->klass, recv); + } + flag = VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT; blockptr = tmp_blockptr; mn = rb_method_node(klass, id); diff --git a/proc.c b/proc.c index 98d62e2a35..66eebec8ab 100644 --- a/proc.c +++ b/proc.c @@ -247,14 +247,23 @@ proc_new(VALUE klass, int is_lambda) if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0 && !RUBY_VM_CLASS_SPECIAL_P(cfp->lfp[0])) { + block = GC_GUARDED_PTR_REF(cfp->lfp[0]); + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); } else { cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0 && !RUBY_VM_CLASS_SPECIAL_P(cfp->lfp[0])) { + block = GC_GUARDED_PTR_REF(cfp->lfp[0]); + /* TODO: check more (cfp limit, called via cfunc, etc) */ + while (cfp->dfp != block->dfp) { + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + } + if (is_lambda) { rb_warn("tried to create Proc object without a block"); } @@ -265,7 +274,6 @@ proc_new(VALUE klass, int is_lambda) } } - cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); procval = th_make_proc(th, cfp, block); if (is_lambda) { @@ -389,6 +397,7 @@ proc_call(int argc, VALUE *argv, VALUE procval) { rb_proc_t *proc; GetProcPtr(procval, proc); + return th_invoke_proc(GET_THREAD(), proc, proc->block.self, argc, argv); } @@ -424,6 +433,7 @@ rb_proc_call(VALUE proc, VALUE args) * 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 @@ -434,11 +444,11 @@ proc_arity(VALUE self) GetProcPtr(self, proc); iseq = proc->block.iseq; if (iseq && BUILTIN_TYPE(iseq) != T_NODE) { - if (iseq->arg_rest == 0 && iseq->arg_opts == 0) { + if (iseq->arg_rest < 0) { return INT2FIX(iseq->argc); } else { - return INT2FIX(-iseq->argc - 1); + return INT2FIX(-(iseq->argc + 1 + iseq->arg_post_len)); } } else { @@ -1150,16 +1160,6 @@ rb_node_arity(NODE* body) return 0; case NODE_BMETHOD: return rb_proc_arity(body->nd_cval); - case NODE_SCOPE: - body = body->nd_next; /* skip NODE_SCOPE */ - if (nd_type(body) == NODE_BLOCK) - body = body->nd_head; - if (!body) - return 0; - n = body->nd_frml ? RARRAY_LEN(body->nd_frml) : 0; - if (body->nd_opt || body->nd_rest) - n = -n - 1; - return n; case RUBY_VM_METHOD_NODE:{ rb_iseq_t *iseq; GetISeqPtr((VALUE)body->nd_body, iseq); @@ -1167,7 +1167,7 @@ rb_node_arity(NODE* body) return iseq->argc; } else { - return -iseq->argc - 1; + return -(iseq->argc + 1 + iseq->arg_post_len); } } default: diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index c30705cb15..5f436406a5 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -8,6 +8,8 @@ class TestMethod < Test::Unit::TestCase def mo2(a, b = nil) end def mo3(*a) end def mo4(a, *b, &c) end + def mo5(a, *b, c) end + def mo6(a, *b, c, &d) end class Base def foo() :base end @@ -24,6 +26,8 @@ class TestMethod < Test::Unit::TestCase assert_equal(-2, method(:mo2).arity) assert_equal(-1, method(:mo3).arity) assert_equal(-2, method(:mo4).arity) + assert_equal(-3, method(:mo5).arity) + assert_equal(-3, method(:mo6).arity) end def test_unbind diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index f293bbc1a7..49e9d9284b 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -57,12 +57,15 @@ class TestProc < Test::Unit::TestCase assert_equal(-2, proc{|x, *y|}.arity) assert_equal(-1, proc{|*x|}.arity) assert_equal(-1, proc{|*|}.arity) + assert_equal(-3, proc{|x, *y, z|}.arity) + assert_equal(-4, proc{|x, *y, z, a|}.arity) assert_arity(0) {} assert_arity(0) {||} assert_arity(1) {|x|} assert_arity(2) {|x, y|} assert_arity(-2) {|x, *y|} + assert_arity(-3) {|x, *y, z|} assert_arity(-1) {|*x|} assert_arity(-1) {|*|} end @@ -89,9 +92,8 @@ class TestProc < Test::Unit::TestCase end def test_block_par - assert false, "TODO: block parameter |&b| not supported" - # assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x}) - # assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x}) + assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x}) + assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x}) end def test_safe diff --git a/version.h b/version.h index 36eb51d122..d383b3303f 100644 --- a/version.h +++ b/version.h @@ -1,15 +1,15 @@ #define RUBY_VERSION "1.9.0" -#define RUBY_RELEASE_DATE "2007-04-27" +#define RUBY_RELEASE_DATE "2007-05-01" #define RUBY_VERSION_CODE 190 -#define RUBY_RELEASE_CODE 20070427 +#define RUBY_RELEASE_CODE 20070501 #define RUBY_PATCHLEVEL 0 #define RUBY_VERSION_MAJOR 1 #define RUBY_VERSION_MINOR 9 #define RUBY_VERSION_TEENY 0 #define RUBY_RELEASE_YEAR 2007 -#define RUBY_RELEASE_MONTH 4 -#define RUBY_RELEASE_DAY 27 +#define RUBY_RELEASE_MONTH 5 +#define RUBY_RELEASE_DAY 1 RUBY_EXTERN const char ruby_version[]; RUBY_EXTERN const char ruby_release_date[]; diff --git a/vm.c b/vm.c index c1793ae760..fb70e29b12 100644 --- a/vm.c +++ b/vm.c @@ -105,8 +105,6 @@ push_frame(rb_thread_t *th, rb_iseq_t *iseq, VALUE magic, cfp->lfp = lfp; cfp->dfp = dfp; cfp->proc = 0; - cfp->method_id = 0; - cfp->method_klass = 0; #define COLLECT_PROFILE 0 #if COLLECT_PROFILE @@ -436,8 +434,8 @@ th_make_proc_from_block(rb_thread_t *th, rb_control_frame_t *cfp, struct RObject *rb; VALUE -th_make_proc(rb_thread_t *th, rb_control_frame_t *cfp, - rb_block_t *block) +th_make_proc(rb_thread_t *th, + rb_control_frame_t *cfp, rb_block_t *block) { VALUE procval, envval, blockprocval = 0; rb_proc_t *proc; @@ -448,8 +446,7 @@ th_make_proc(rb_thread_t *th, rb_control_frame_t *cfp, blockprocval = th_make_proc_from_block(th, cfp, - (rb_block_t *)GC_GUARDED_PTR_REF(*cfp-> - lfp)); + (rb_block_t *)GC_GUARDED_PTR_REF(*cfp->lfp)); GetProcPtr(blockprocval, p); *cfp->lfp = GC_GUARDED_PTR(&p->block); } @@ -489,12 +486,11 @@ th_invoke_bmethod(rb_thread_t *th, ID id, VALUE procval, VALUE recv, rb_control_frame_t *cfp = th->cfp; rb_proc_t *proc; VALUE val; - VALUE values[2] = { - id, RCLASS(klass)->super, - }; - /* dirty hack */ - (cfp-1)->block_iseq = (void *)values; + /* control block frame */ + (cfp-2)->method_id = id; + (cfp-2)->method_klass = klass; + GetProcPtr(procval, proc); val = th_invoke_proc(th, proc, recv, argc, argv); return val; @@ -741,6 +737,17 @@ th_yield_setup_args(rb_iseq_t *iseq, int argc, VALUE *argv) argc = iseq->arg_rest + 1; } + if (iseq->arg_block != -1) { + VALUE proc = Qnil; + + if (rb_block_given_p()) { + proc = rb_block_proc(); + } + + argv[iseq->arg_block] = proc; + argc = iseq->arg_block + 1; + } + return argc; } @@ -982,7 +989,7 @@ th_backtrace_each(rb_thread_t *th, rb_ary_push(ary, str); } } - else if (cfp->method_id) { + else if (RUBYVM_CFUNC_FRAME_P(cfp)) { str = rb_sprintf("%s:%d:in `%s'", file, line_no, rb_id2name(cfp->method_id)); diff --git a/vm.h b/vm.h index cccd4af365..930947086d 100644 --- a/vm.h +++ b/vm.h @@ -245,6 +245,9 @@ default: \ } \ } +#define RUBYVM_CFUNC_FRAME_P(cfp) \ + ((cfp)->magic == FRAME_MAGIC_CFUNC) + /* * Excception */ diff --git a/vm_dump.c b/vm_dump.c index 235a516c40..7c03c26d6f 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -237,7 +237,7 @@ stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp) rb_iseq_t *iseq = cfp->iseq; if (iseq == 0) { - if (cfp->method_id) { + if (RUBYVM_CFUNC_FRAME_P(cfp)) { argc = 0; local_size = 0; name = rb_id2name(cfp->method_id); diff --git a/vm_macro.def b/vm_macro.def index 7d95b13ba7..dff6b98c9c 100644 --- a/vm_macro.def +++ b/vm_macro.def @@ -61,6 +61,7 @@ MACRO macro_eval_invoke_cfunc(num, id, recv, klass, mn, blockptr) rb_control_frame_t *cfp = push_frame(th, 0, FRAME_MAGIC_CFUNC, recv, (VALUE) blockptr, 0, GET_SP(), 0, 1); + cfp->method_id = id; cfp->method_klass = klass; @@ -312,13 +313,6 @@ MACRO macro_eval_invoke_method(recv, klass, id, num, mn, blockptr) goto LABEL_IS_SC(start_method_dispatch); } } - case NODE_SCOPE:{ - dpi(id); - SDR(); - rb_bug("eval_invoke_method: NODE_SCOPE should not be appear"); - /* unreachable */ - break; - } default:{ printf("node: %s\n", ruby_node_name(nd_type(node))); rb_bug("eval_invoke_method: unreachable"); diff --git a/yarvcore.h b/yarvcore.h index 532b613c47..2257bee509 100644 --- a/yarvcore.h +++ b/yarvcore.h @@ -273,6 +273,7 @@ struct rb_iseq_struct { int arg_rest; int arg_block; int arg_opts; + int arg_post_len; VALUE *arg_opt_tbl; /* for stack overflow check */