* eval.c (proc_invoke): intercept break and return from lambda

Proc objects.  [ruby-dev:28742]

* eval.c (proc_invoke): remove unnecessary YIELD_PROC_CALL flag.

* eval.c (YIELD_EXACT_ARGS): renamed from YIELD_LAMBDA_CALL, which
  is no longer related to the behavior turned on by this flag.

* eval.c (return_jump): no need to care about PROT_YIELD.

* eval.c (break_jump): no jump to toplevel PROT_TREAD tag.

* eval.c (rb_yield_0): fix confusion between lambda (which is a
  property of a proc) and pcall (which depends on whether it's
  called via yield or call).

* eval.c (rb_thread_yield): no need to specify YIELD_LAMBDA_CALL.

* eval.c (rb_block_pass): update blkid in prot_tag.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@10338 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
matz 2006-06-20 06:09:04 +00:00
parent 765c807219
commit 3be20019c1
3 changed files with 83 additions and 72 deletions

View File

@ -1,3 +1,25 @@
Tue Jun 20 11:07:55 2006 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (proc_invoke): intercept break and return from lambda
Proc objects. [ruby-dev:28742]
* eval.c (proc_invoke): remove unnecessary YIELD_PROC_CALL flag.
* eval.c (YIELD_EXACT_ARGS): renamed from YIELD_LAMBDA_CALL, which
is no longer related to the behavior turned on by this flag.
* eval.c (return_jump): no need to care about PROT_YIELD.
* eval.c (break_jump): no jump to toplevel PROT_TREAD tag.
* eval.c (rb_yield_0): fix confusion between lambda (which is a
property of a proc) and pcall (which depends on whether it's
called via yield or call).
* eval.c (rb_thread_yield): no need to specify YIELD_LAMBDA_CALL.
* eval.c (rb_block_pass): update blkid in prot_tag.
Mon Jun 19 23:40:59 2006 NARUSE, Yui <naruse@ruby-lang.org> Mon Jun 19 23:40:59 2006 NARUSE, Yui <naruse@ruby-lang.org>
* ext/nkf/lib/kconv.rb: remove default -m0 and fix document. * ext/nkf/lib/kconv.rb: remove default -m0 and fix document.

83
eval.c
View File

@ -747,7 +747,7 @@ struct SCOPE *ruby_scope;
static struct FRAME *top_frame; static struct FRAME *top_frame;
static struct SCOPE *top_scope; static struct SCOPE *top_scope;
static unsigned long frame_unique = 0; static unsigned long frame_unique = 1;
#define PUSH_FRAME(link) do { \ #define PUSH_FRAME(link) do { \
struct FRAME _frame; \ struct FRAME _frame; \
@ -942,7 +942,7 @@ static struct tag *prot_tag;
_tag.prev = prot_tag; \ _tag.prev = prot_tag; \
_tag.scope = ruby_scope; \ _tag.scope = ruby_scope; \
_tag.tag = ptag; \ _tag.tag = ptag; \
_tag.dst = 0; \ _tag.dst = -1; \
_tag.blkid = 0; \ _tag.blkid = 0; \
prot_tag = &_tag prot_tag = &_tag
@ -1047,8 +1047,7 @@ static NODE *compile(VALUE, const char*, int);
static VALUE rb_yield_0(VALUE, VALUE, VALUE, int); static VALUE rb_yield_0(VALUE, VALUE, VALUE, int);
#define YIELD_LAMBDA_CALL 1 #define YIELD_EXACT_ARGS 1
#define YIELD_PROC_CALL 2
#define YIELD_PUBLIC_DEF 4 #define YIELD_PUBLIC_DEF 4
#define YIELD_FUNC_AVALUE 1 #define YIELD_FUNC_AVALUE 1
#define YIELD_FUNC_SVALUE 2 #define YIELD_FUNC_SVALUE 2
@ -2608,7 +2607,7 @@ call_trace_func(rb_event_t event, NODE *node, VALUE self, ID id, VALUE klass /*
static VALUE static VALUE
svalue_to_avalue(VALUE v) svalue_to_avalue(VALUE v)
{ {
VALUE tmp, top; VALUE tmp;
if (v == Qundef) return rb_ary_new2(0); if (v == Qundef) return rb_ary_new2(0);
tmp = rb_check_array_type(v); tmp = rb_check_array_type(v);
@ -2682,7 +2681,6 @@ rb_eval(VALUE self, NODE *n)
NODE * volatile node = n; NODE * volatile node = n;
int state; int state;
volatile VALUE result = Qnil; volatile VALUE result = Qnil;
VALUE pushed_block = 0;
#define RETURN(v) do { \ #define RETURN(v) do { \
result = (v); \ result = (v); \
@ -4598,16 +4596,11 @@ static void
return_jump(VALUE retval) return_jump(VALUE retval)
{ {
struct tag *tt = prot_tag; struct tag *tt = prot_tag;
int yield = Qfalse;
if (retval == Qundef) retval = Qnil; if (retval == Qundef) retval = Qnil;
while (tt) { while (tt) {
if (tt->tag == PROT_YIELD) {
yield = Qtrue;
tt = tt->prev;
}
if ((tt->tag == PROT_FUNC && tt->frame->uniq == ruby_frame->uniq) || if ((tt->tag == PROT_FUNC && tt->frame->uniq == ruby_frame->uniq) ||
(tt->tag == PROT_LAMBDA && !yield)) (tt->tag == PROT_LAMBDA))
{ {
tt->dst = (VALUE)tt->frame->uniq; tt->dst = (VALUE)tt->frame->uniq;
tt->retval = retval; tt->retval = retval;
@ -4625,14 +4618,17 @@ static void
break_jump(VALUE retval) break_jump(VALUE retval)
{ {
struct tag *tt = prot_tag; struct tag *tt = prot_tag;
int yield = 0;
if (retval == Qundef) retval = Qnil; if (retval == Qundef) retval = Qnil;
while (tt) { while (tt) {
switch (tt->tag) { switch (tt->tag) {
case PROT_THREAD: case PROT_THREAD:
/* skip toplevel tag */
if (!tt->prev) break;
case PROT_YIELD: case PROT_YIELD:
case PROT_LOOP:
case PROT_LAMBDA: case PROT_LAMBDA:
case PROT_LOOP:
tt->dst = (VALUE)tt->frame->uniq; tt->dst = (VALUE)tt->frame->uniq;
tt->retval = retval; tt->retval = retval;
JUMP_TAG(TAG_BREAK); JUMP_TAG(TAG_BREAK);
@ -4671,8 +4667,8 @@ rb_yield_0(VALUE val, VALUE self, VALUE klass /* OK */, int flags)
int old_vmode; int old_vmode;
struct FRAME frame; struct FRAME frame;
NODE *cnode = ruby_current_node; NODE *cnode = ruby_current_node;
int lambda = flags & YIELD_LAMBDA_CALL; int pcall = flags & YIELD_EXACT_ARGS, lambda;
int state; int state, broken = 0;
rb_need_block(); rb_need_block();
@ -4704,6 +4700,7 @@ rb_yield_0(VALUE val, VALUE self, VALUE klass /* OK */, int flags)
} }
node = block->body; node = block->body;
var = block->var; var = block->var;
lambda = block->flags & BLOCK_LAMBDA;
if (var) { if (var) {
PUSH_TAG(PROT_NONE); PUSH_TAG(PROT_NONE);
@ -4711,7 +4708,7 @@ rb_yield_0(VALUE val, VALUE self, VALUE klass /* OK */, int flags)
NODE *bvar = NULL; NODE *bvar = NULL;
block_var: block_var:
if (var == (NODE*)1) { /* no parameter || */ if (var == (NODE*)1) { /* no parameter || */
if (lambda && RARRAY(val)->len != 0) { if (pcall && RARRAY(val)->len != 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)", rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
RARRAY(val)->len); RARRAY(val)->len);
} }
@ -4739,24 +4736,24 @@ rb_yield_0(VALUE val, VALUE self, VALUE klass /* OK */, int flags)
goto block_var; goto block_var;
} }
else if (nd_type(var) == NODE_MASGN) { else if (nd_type(var) == NODE_MASGN) {
massign(self, var, val, lambda); massign(self, var, val, pcall);
} }
else { else {
assign(self, var, val, lambda); assign(self, var, val, pcall);
} }
if (bvar) { if (bvar) {
VALUE blk; VALUE blk;
if (flags & YIELD_PROC_CALL) if (lambda)
blk = block->block_obj;
else
blk = rb_block_proc(); blk = rb_block_proc();
else
blk = block->block_obj;
assign(self, bvar, blk, 0); assign(self, bvar, blk, 0);
} }
} }
POP_TAG(); POP_TAG();
if (state) goto pop_state; if (state) goto pop_state;
} }
else if (lambda && RARRAY(val)->len != 0 && else if (pcall && RARRAY(val)->len != 0 &&
(!node || nd_type(node) != NODE_IFUNC || (!node || nd_type(node) != NODE_IFUNC ||
node->nd_cfnc != bmcall)) { node->nd_cfnc != bmcall)) {
rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)", rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
@ -4808,9 +4805,7 @@ rb_yield_0(VALUE val, VALUE self, VALUE klass /* OK */, int flags)
case TAG_BREAK: case TAG_BREAK:
if (TAG_DST()) { if (TAG_DST()) {
result = prot_tag->retval; result = prot_tag->retval;
} broken = 1;
else {
lambda = Qtrue; /* just pass TAG_BREAK */
} }
break; break;
default: default:
@ -4845,11 +4840,12 @@ rb_yield_0(VALUE val, VALUE self, VALUE klass /* OK */, int flags)
case 0: case 0:
break; break;
case TAG_BREAK: case TAG_BREAK:
if (!lambda) { if (broken) {
struct tag *tt = prot_tag; struct tag *tt = prot_tag;
while (tt) { while (tt) {
if (tt->tag == PROT_LOOP && tt->blkid == block->uniq) { if ((tt->tag == PROT_LOOP && tt->blkid == block->uniq) ||
(lambda && tt->tag == PROT_LAMBDA)) {
tt->dst = (VALUE)tt->frame->uniq; tt->dst = (VALUE)tt->frame->uniq;
tt->retval = result; tt->retval = result;
JUMP_TAG(TAG_BREAK); JUMP_TAG(TAG_BREAK);
@ -4857,8 +4853,7 @@ rb_yield_0(VALUE val, VALUE self, VALUE klass /* OK */, int flags)
tt = tt->prev; tt = tt->prev;
} }
proc_jump_error(TAG_BREAK, result); proc_jump_error(TAG_BREAK, result);
} }
/* fall through */
default: default:
JUMP_TAG(state); JUMP_TAG(state);
break; break;
@ -5649,7 +5644,6 @@ rb_call0(VALUE klass, VALUE recv, ID id, ID oid,
NODE *b2; /* OK */ NODE *b2; /* OK */
volatile VALUE result = Qnil; volatile VALUE result = Qnil;
static int tick; static int tick;
volatile VALUE args;
volatile int safe = -1; volatile int safe = -1;
TMP_PROTECT; TMP_PROTECT;
@ -5897,7 +5891,6 @@ rb_call(VALUE klass, VALUE recv, ID mid,
prot_tag->blkid = block->uniq; prot_tag->blkid = block->uniq;
state = EXEC_TAG(); state = EXEC_TAG();
if (state == 0) { if (state == 0) {
retry:
result = rb_call0(klass, recv, mid, id, argc, argv, block, body, noex); result = rb_call0(klass, recv, mid, id, argc, argv, block, body, noex);
} }
else if (state == TAG_BREAK && TAG_DST()) { else if (state == TAG_BREAK && TAG_DST()) {
@ -7927,8 +7920,6 @@ rb_mod_autoload_p(VALUE mod, VALUE sym)
static VALUE static VALUE
rb_f_autoload(VALUE obj, VALUE sym, VALUE file) rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
{ {
VALUE klass = ruby_cbase;
if (NIL_P(ruby_cbase)) { if (NIL_P(ruby_cbase)) {
rb_raise(rb_eTypeError, "no class/module for autoload target"); rb_raise(rb_eTypeError, "no class/module for autoload target");
} }
@ -8356,11 +8347,12 @@ proc_invoke(VALUE proc, VALUE args /* OK */, VALUE self, VALUE klass, int call)
int state; int state;
volatile int safe = ruby_safe_level; volatile int safe = ruby_safe_level;
volatile VALUE old_wrapper = ruby_wrapper; volatile VALUE old_wrapper = ruby_wrapper;
volatile int pcall; volatile int pcall, lambda;
VALUE bvar = Qnil; VALUE bvar = Qnil;
Data_Get_Struct(proc, struct BLOCK, data); Data_Get_Struct(proc, struct BLOCK, data);
pcall = call ? YIELD_LAMBDA_CALL : 0; pcall = call ? YIELD_EXACT_ARGS : 0;
lambda = data->flags & BLOCK_LAMBDA;
if (rb_block_given_p() && ruby_frame->callee) { if (rb_block_given_p() && ruby_frame->callee) {
if (klass != ruby_frame->this_class) if (klass != ruby_frame->this_class)
klass = rb_obj_class(proc); klass = rb_obj_class(proc);
@ -8387,12 +8379,11 @@ proc_invoke(VALUE proc, VALUE args /* OK */, VALUE self, VALUE klass, int call)
/* modify current frame */ /* modify current frame */
old_block = ruby_frame->block; old_block = ruby_frame->block;
ruby_frame->block = &_block; ruby_frame->block = &_block;
PUSH_TAG((data->flags&BLOCK_LAMBDA) ? PROT_LAMBDA : PROT_NONE); PUSH_TAG(lambda ? PROT_LAMBDA : PROT_NONE);
state = EXEC_TAG(); state = EXEC_TAG();
if (state == 0) { if (state == 0) {
proc_set_safe_level(proc); proc_set_safe_level(proc);
result = rb_yield_0(args, self, (self!=Qundef)?CLASS_OF(self):0, result = rb_yield_0(args, self, (self!=Qundef)?CLASS_OF(self):0, pcall);
pcall | YIELD_PROC_CALL);
} }
else if (TAG_DST()) { else if (TAG_DST()) {
result = prot_tag->retval; result = prot_tag->retval;
@ -8412,9 +8403,8 @@ proc_invoke(VALUE proc, VALUE args /* OK */, VALUE self, VALUE klass, int call)
JUMP_TAG(state); JUMP_TAG(state);
break; break;
case TAG_BREAK: case TAG_BREAK:
if (!pcall && result != Qundef) { if (lambda && result != Qundef) break;
proc_jump_error(state, result); JUMP_TAG(state);
}
case TAG_RETURN: case TAG_RETURN:
if (result != Qundef) { if (result != Qundef) {
if (pcall) break; if (pcall) break;
@ -8731,10 +8721,13 @@ rb_block_pass(VALUE (*func) (VALUE), VALUE arg, VALUE proc)
orphan = block_orphan(data); orphan = block_orphan(data);
PUSH_FRAME(Qtrue); PUSH_FRAME(Qtrue);
_block = *data;
if (orphan) _block.uniq = block_unique++;
ruby_frame->block = &_block;
PUSH_TAG(PROT_LOOP); PUSH_TAG(PROT_LOOP);
_block = *data;
if (orphan) {
_block.uniq = block_unique++;
prot_tag->blkid = _block.uniq;
}
ruby_frame->block = &_block;
state = EXEC_TAG(); state = EXEC_TAG();
if (state == 0) { if (state == 0) {
retry: retry:
@ -12054,7 +12047,7 @@ rb_thread_yield(VALUE arg, rb_thread_t th)
rb_dvar_push('~', Qnil); rb_dvar_push('~', Qnil);
ruby_frame->block->dyna_vars = ruby_dyna_vars; ruby_frame->block->dyna_vars = ruby_dyna_vars;
return rb_yield_0(arg, 0, 0, YIELD_LAMBDA_CALL); return rb_yield_0(arg, 0, 0, 0);
} }
/* /*

View File

@ -1045,33 +1045,33 @@ yield_argument_test(true, lambda{|a,|}, 1)
yield_argument_test(true, lambda{|a,|}) yield_argument_test(true, lambda{|a,|})
yield_argument_test(true, lambda{|a,|}, 1,2) yield_argument_test(true, lambda{|a,|}, 1,2)
def get_block(&block) def block_get(&block)
block block
end end
test_ok(Proc == get_block{}.class) test_ok(Proc == block_get{}.class)
yield_argument_test(true, get_block{||}) yield_argument_test(true, block_get{||})
yield_argument_test(true, get_block{||}, 1) yield_argument_test(true, block_get{||}, 1)
yield_argument_test(true, get_block{|a,|}, 1) yield_argument_test(true, block_get{|a,|}, 1)
yield_argument_test(true, get_block{|a,|}) yield_argument_test(true, block_get{|a,|})
yield_argument_test(true, get_block{|a,|}, 1,2) yield_argument_test(true, block_get{|a,|}, 1,2)
call_argument_test(true, get_block(&lambda{||})) call_argument_test(true, block_get(&lambda{||}))
call_argument_test(false, get_block(&lambda{||}),1) call_argument_test(false, block_get(&lambda{||}),1)
call_argument_test(true, get_block(&lambda{|a,|}),1) call_argument_test(true, block_get(&lambda{|a,|}),1)
call_argument_test(false, get_block(&lambda{|a,|}),1,2) call_argument_test(false, block_get(&lambda{|a,|}),1,2)
blk = get_block{11} blk = block_get{11}
test_ok(blk.class == Proc) test_ok(blk.class == Proc)
test_ok(blk.to_proc.class == Proc) test_ok(blk.to_proc.class == Proc)
test_ok(blk.clone.call == 11) test_ok(blk.clone.call == 11)
test_ok(get_block(&blk).class == Proc) test_ok(block_get(&blk).class == Proc)
lmd = lambda{44} lmd = lambda{44}
test_ok(lmd.class == Proc) test_ok(lmd.class == Proc)
test_ok(lmd.to_proc.class == Proc) test_ok(lmd.to_proc.class == Proc)
test_ok(lmd.clone.call == 44) test_ok(lmd.clone.call == 44)
test_ok(get_block(&lmd).class == Proc) test_ok(block_get(&lmd).class == Proc)
test_ok(Proc.new{|a,| a}.yield(1,2,3) == 1) test_ok(Proc.new{|a,| a}.yield(1,2,3) == 1)
yield_argument_test(true, Proc.new{|a,|}, 1,2) yield_argument_test(true, Proc.new{|a,|}, 1,2)
@ -1125,7 +1125,7 @@ def ljump_test(state, proc, *args)
test_ok(x,2) test_ok(x,2)
end end
ljump_test(true, get_block{break}) ljump_test(false, block_get{break})
ljump_test(true, lambda{break}) ljump_test(true, lambda{break})
def exit_value_test(&block) def exit_value_test(&block)
@ -1134,10 +1134,10 @@ rescue LocalJumpError
$!.exit_value $!.exit_value
end end
test_ok(45, exit_value_test{break 45}) test_ok(45 == exit_value_test{break 45})
test_ok(55, begin test_ok(55 == begin
get_block{break 55}.call block_get{break 55}.call
rescue LocalJumpError rescue LocalJumpError
$!.exit_value $!.exit_value
end) end)
@ -1146,10 +1146,6 @@ def block_call(&block)
block.call block.call
end end
def block_get(&block)
block
end
def test_b1 def test_b1
block_call{break 11} block_call{break 11}
end end
@ -1168,7 +1164,7 @@ def test_b2
block_get{break 21}.call block_get{break 21}.call
end end
end end
test_ok(test_b2() == 21) test_ok(test_b2() == 22)
def test_b3 def test_b3
ljump_rescue(33) do ljump_rescue(33) do
@ -1207,7 +1203,7 @@ def test_b7
block_call(&b) block_call(&b)
end end
end end
test_ok(test_b7() == 78) test_ok(test_b7() == 77)
def util_b8(&block) def util_b8(&block)
block_call(&block) block_call(&block)
@ -1219,7 +1215,7 @@ end
test_ok(test_b8() == 88) test_ok(test_b8() == 88)
def util_b9(&block) def util_b9(&block)
lambda{block.call}.call lambda{block.call; 98}.call
end end
def test_b9 def test_b9
@ -1287,7 +1283,7 @@ marity_test(:marity_test)
marity_test(:p) marity_test(:p)
lambda(&method(:test_ok)).call(true) lambda(&method(:test_ok)).call(true)
lambda(&get_block{|a,n| test_ok(a,n)}).call(true, 2) lambda(&block_get{|a,n| test_ok(a,n)}).call(true, 2)
class ITER_TEST1 class ITER_TEST1
def a def a