Feature #3447

Updated by Shyouhei Urabe over 3 years ago

=begin

なかだです。



http://www.rubyist.net/~matz/20100615.html#p01 を実装してみました。

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

Back