Feature #3447
Updated by nobu (Nobuyoshi Nakada) almost 11 years ago
=begin なかだです。 http://www.rubyist.net/~matz/20100615.html#p01 を実装してみました。 ((<URL:http://www.rubyist.net/~matz/20100615.html#p01>))を実装してみました。 `foo(...)`でブロックまでコミ、`foo(..)`はブロック抜きにしてあります。 ``` (({foo(...)}))でブロックまでコミ、(({foo(..)}))はブロック抜きにしてあります。 diff --git i/compile.c w/compile.c index 4621cd9..d769c56 100644 --- i/compile.c +++ w/compile.c @@ -2729,7 +2729,6 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret, return 1; case NODE_SUPER: - case NODE_ZSUPER: ADD_INSN(ret, nd_line(node), putnil); ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_ZSUPER), 0, needstr); @@ -2919,6 +2918,67 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned long *flag) POP_ELEMENT(args); break; } + case NODE_DELEGATE: { + int i; + rb_iseq_t *liseq = iseq->local_iseq; + + if (argn->nd_state) *flag |= VM_CALL_SUPER_BIT; + argc = INT2FIX(liseq->argc); + + /* normal arguments */ + for (i = 0; i < liseq->argc; i++) { + int idx = liseq->local_size - i; + ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx)); + } + + if (!liseq->arg_simple) { + if (liseq->arg_opts) { + /* optional arguments */ + int j; + for (j = 0; j < liseq->arg_opts - 1; j++) { + int idx = liseq->local_size - (i + j); + ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx)); + } + i += j; + argc = INT2FIX(i); + } + + if (liseq->arg_rest != -1) { + /* rest argument */ + int idx = liseq->local_size - liseq->arg_rest; + ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx)); + argc = INT2FIX(liseq->arg_rest + 1); + *flag |= VM_CALL_ARGS_SPLAT_BIT; + } + + if (liseq->arg_post_len) { + /* post arguments */ + int post_len = liseq->arg_post_len; + int post_start = liseq->arg_post_start; + + if (liseq->arg_rest != -1) { + int j; + for (j=0; j<post_len; j++) { + int idx = liseq->local_size - (post_start + j); + ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx)); + } + ADD_INSN1(args, nd_line(argn), newarray, INT2FIX(j)); + ADD_INSN (args, nd_line(argn), concatarray); + /* argc is setteled at above */ + } + else { + int j; + for (j=0; j<post_len; j++) { + int idx = liseq->local_size - (post_start + j); + ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx)); + } + argc = INT2FIX(post_len + post_start); + } + } + } + + break; + } default: { rb_bug("setup_arg: unknown node: %s\n", ruby_node_name(nd_type(argn))); } @@ -4115,8 +4175,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } break; } - case NODE_SUPER: - case NODE_ZSUPER:{ + case NODE_SUPER: { DECL_ANCHOR(args); VALUE argc; unsigned long flag = 0; @@ -4124,72 +4183,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) INIT_ANCHOR(args); iseq->compile_data->current_block = Qfalse; - if (nd_type(node) == NODE_SUPER) { - argc = setup_args(iseq, args, node->nd_args, &flag); - } - else { - /* NODE_ZSUPER */ - int i; - rb_iseq_t *liseq = iseq->local_iseq; - - argc = INT2FIX(liseq->argc); - - /* normal arguments */ - for (i = 0; i < liseq->argc; i++) { - int idx = liseq->local_size - i; - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); - } - - if (!liseq->arg_simple) { - if (liseq->arg_opts) { - /* optional arguments */ - int j; - for (j = 0; j < liseq->arg_opts - 1; j++) { - int idx = liseq->local_size - (i + j); - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); - } - i += j; - argc = INT2FIX(i); - } - - if (liseq->arg_rest != -1) { - /* rest argument */ - int idx = liseq->local_size - liseq->arg_rest; - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); - argc = INT2FIX(liseq->arg_rest + 1); - flag |= VM_CALL_ARGS_SPLAT_BIT; - } - - if (liseq->arg_post_len) { - /* post arguments */ - int post_len = liseq->arg_post_len; - int post_start = liseq->arg_post_start; - - if (liseq->arg_rest != -1) { - int j; - for (j=0; j<post_len; j++) { - int idx = liseq->local_size - (post_start + j); - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); - } - ADD_INSN1(args, nd_line(node), newarray, INT2FIX(j)); - ADD_INSN (args, nd_line(node), concatarray); - /* argc is setteled at above */ - } - else { - int j; - for (j=0; j<post_len; j++) { - int idx = liseq->local_size - (post_start + j); - ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx)); - } - argc = INT2FIX(post_len + post_start); - } - } - } - } + argc = setup_args(iseq, args, node->nd_args, &flag); /* dummy receiver */ ADD_INSN1(ret, nd_line(node), putobject, - nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue); + (flag & VM_CALL_SUPER_BIT) ? Qfalse : Qtrue); + flag &= ~VM_CALL_SUPER_BIT; ADD_SEQ(ret, args); ADD_INSN3(ret, nd_line(node), invokesuper, argc, parent_block, LONG2FIX(flag)); diff --git i/gc.c w/gc.c index 58e4550..0d5fbad 100644 --- i/gc.c +++ w/gc.c @@ -1671,7 +1671,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) goto again; case NODE_ZARRAY: /* - */ - case NODE_ZSUPER: + case NODE_DELEGATE: case NODE_VCALL: case NODE_GVAR: case NODE_LVAR: diff --git i/insns.def w/insns.def index f75007d..6c1efdc 100644 --- i/insns.def +++ w/insns.def @@ -993,10 +993,10 @@ send { const rb_method_entry_t *me; VALUE recv, klass; - rb_block_t *blockptr = 0; + rb_block_t *blockptr = (op_flag & VM_CALL_SUPER_BIT) ? GET_BLOCK_PTR() : 0; int num = caller_setup_args(th, GET_CFP(), op_flag, (int)op_argc, (rb_iseq_t *)blockiseq, &blockptr); - rb_num_t flag = op_flag; + rb_num_t flag = op_flag & ~VM_CALL_SUPER_BIT; ID id = op_id; /* get receiver */ diff --git i/node.c w/node.c index 65bc541..f2900d3 100644 --- i/node.c +++ w/node.c @@ -408,10 +408,17 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node) F_NODE(nd_args, "arguments"); break; - case NODE_ZSUPER: - ANN("super invocation with no argument"); - ANN("format: super"); - ANN("example: super"); + case NODE_DELEGATE: + if (node->nd_state) { + ANN("argument delegation with block"); + ANN("format: ..."); + ANN("example: foo(...)"); + } + else { + ANN("argument delegation without block"); + ANN("format: .."); + ANN("example: foo(..)"); + } break; case NODE_ARRAY: diff --git i/node.h w/node.h index f8cf7de..74320c0 100644 --- i/node.h +++ w/node.h @@ -96,8 +96,8 @@ enum node_type { #define NODE_VCALL NODE_VCALL NODE_SUPER, #define NODE_SUPER NODE_SUPER - NODE_ZSUPER, -#define NODE_ZSUPER NODE_ZSUPER + NODE_DELEGATE, +#define NODE_DELEGATE NODE_DELEGATE NODE_ARRAY, #define NODE_ARRAY NODE_ARRAY NODE_ZARRAY, @@ -414,7 +414,7 @@ typedef struct RNode { #define NEW_FCALL(m,a) NEW_NODE(NODE_FCALL,0,m,a) #define NEW_VCALL(m) NEW_NODE(NODE_VCALL,0,m,0) #define NEW_SUPER(a) NEW_NODE(NODE_SUPER,0,0,a) -#define NEW_ZSUPER() NEW_NODE(NODE_ZSUPER,0,0,0) +#define NEW_DELEGATE(b) NEW_NODE(NODE_DELEGATE,0,0,b) #define NEW_ARGS(m,o) NEW_NODE(NODE_ARGS,o,m,0) #define NEW_ARGS_AUX(r,b) NEW_NODE(NODE_ARGS_AUX,r,b,0) #define NEW_OPT_ARG(i,v) NEW_NODE(NODE_OPT_ARG,i,v,0) diff --git i/parse.y w/parse.y index 9fac5bd..735d1bf 100644 --- i/parse.y +++ w/parse.y @@ -2399,6 +2399,20 @@ opt_paren_args : none opt_call_args : none | call_args + | tDOT2 + { + /*%%%*/ + $$ = NEW_DELEGATE(0); + /*% + %*/ + } + | tDOT3 + { + /*%%%*/ + $$ = NEW_DELEGATE(1); + /*% + %*/ + } ; call_args : command @@ -3647,7 +3661,7 @@ method_call : operation paren_args | keyword_super { /*%%%*/ - $$ = NEW_ZSUPER(); + $$ = NEW_SUPER(NEW_DELEGATE(1)); /*% $$ = dispatch0(zsuper); %*/ diff --git i/test/ruby/test_method.rb w/test/ruby/test_method.rb index 7be70b0..a04a285 100644 --- i/test/ruby/test_method.rb +++ w/test/ruby/test_method.rb @@ -345,4 +345,38 @@ class TestMethod < Test::Unit::TestCase obj.extend(m) assert_equal([:m1, :a], obj.public_methods(false), bug) end + + def test_argument_delegate + class << (o = Object.new) + def foo(*args) + yield(*args) + end + def foo1(*) + foo(...) + end + def foo2(*) + foo1(...) + end + def bar(*args, &block) + [args, block] + end + def bar1(*) + bar(..) + end + def bar2(*) + bar1(..) + end + end + called = nil + assert_equal([42], o.foo1(42) {|*x| called = x}) + assert_equal([42], called) + called = nil + assert_equal([42], o.foo2(42) {|*x| called = x}) + assert_equal([42], called) + called = :not_called + assert_equal([[42], nil], o.bar1(42) {|*x| called = true}) + assert_equal(:not_called, called) + assert_equal([[42], nil], o.bar2(42) {|*x| called = true}) + assert_equal(:not_called, called) + end end ``` -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦 =end