yarv-diff:62
From: ko1 atdot.net
Date: 30 Jul 2005 18:27:58 -0000
Subject: [yarv-diff:62] r217 - in trunk: . test
Author: ko1
Date: 2005-07-31 03:27:58 +0900 (Sun, 31 Jul 2005)
New Revision: 217
Modified:
trunk/ChangeLog
trunk/compile.c
trunk/insns.def
trunk/test.rb
trunk/test/test_flow.rb
trunk/test/yarvtest.rb
trunk/vm.c
trunk/yarvcore.h
Log:
* vm.c, compile.c, yarvcore.h, insns.def :
support jump from rescue/ensure/class/module
* test/test_flow.rb : add tests for above fix
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/ChangeLog 2005-07-30 18:27:58 UTC (rev 217)
@@ -4,6 +4,14 @@
# from Mon, 03 May 2004 01:24:19 +0900
#
+2005-07-31(Sun) 03:25:05 +0900 Koichi Sasada <ko1 atdot.net>
+
+ * vm.c, compile.c, yarvcore.h, insns.def :
+ support jump from rescue/ensure/class/module
+
+ * test/test_flow.rb : add tests for above fix
+
+
2005-07-30(Sat) 04:44:33 +0900 Koichi Sasada <ko1 atdot.net>
* yarvcore.h : struct iseq_compile_data_ensure_node_stack is added
Modified: trunk/compile.c
===================================================================
--- trunk/compile.c 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/compile.c 2005-07-30 18:27:58 UTC (rev 217)
@@ -192,12 +192,15 @@
}
else{
if(iseqobj->type == ISEQ_TYPE_BLOCK){
- LABEL* endl;
+ VALUE tmp;
+ LABEL *start = iseqobj->compile_data->start_label = NEW_LABEL(0);
+ LABEL *end = iseqobj->compile_data->end_label = NEW_LABEL(0);
- endl = iseqobj->compile_data->end_label = NEW_LABEL(0);
- iseqobj->compile_data->start_label = NEW_LABEL(0);
iseqobj->rewind_frame_size = iseqobj->local_size + REWIND_DSIZE();
-
+
+ ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, 0, 0, start);
+ ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, 0, end);
+
ADD_LABEL(list_anchor, iseqobj->compile_data->start_label);
COMPILE(list_anchor, "block body", node);
ADD_LABEL(list_anchor, iseqobj->compile_data->end_label);
@@ -1234,8 +1237,8 @@
* => in this case, first jump instruction should jump tp
* LABEL2 directly
*/
-
list = FIRST_ELEMENT(anchor);
+ // dump_disasm_list(list);
while(list){
if(list->type == ISEQ_ELEMENT_INSN){
INSN *niobj, *diobj, *piobj, *iobj = (INSN *)list;
@@ -2114,7 +2117,12 @@
ADD_INSN(ret, nd_line(node), putnil);
}
ADD_LABEL(ret, break_label); /* braek */
-
+
+ ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, 0, 0, break_label);
+ ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, 0, 0, next_label);
+ ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, 0, 0, redo_label);
+
+
iseqobj->compile_data->start_label = prev_start_label;
iseqobj->compile_data->end_label = prev_end_label;
iseqobj->compile_data->redo_label = prev_redo_label;
@@ -2151,8 +2159,9 @@
break;
}
case NODE_BREAK:{
+ unsigned long level = 0;
+
if(iseqobj->compile_data->redo_label != 0){
- break_in_loop:
/* while/until */
COMPILE_(ret, "break val(while/until)", node->nd_stts,
iseqobj->compile_data->loopval_popped);
@@ -2160,21 +2169,26 @@
ADD_INSNL(ret, nd_line(node), jump, iseqobj->compile_data->end_label);
}
else if(iseqobj->type == ISEQ_TYPE_BLOCK){
- break_in_block:
+ break_by_jump:
/* escape from block */
COMPILE(ret, "break val(block)", node->nd_stts);
- ADD_INSN1(ret, nd_line(node), throw, I2F(0x02) /* TAG_BREAK */);
+ ADD_INSN1(ret, nd_line(node), throw, I2F(level | 0x02) /* TAG_BREAK */);
}
else{
yarv_iseq_t *ip = iseqobj->parent_iseqobj;
+
while(ip){
+ level++;
if(ip->type == ISEQ_TYPE_BLOCK){
- rb_bug("break in rescue/ensure is not supported");
- goto break_in_block;
+ level <<= 16;
+ goto break_by_jump;
}
else if(ip->compile_data->redo_label != 0){
- rb_bug("break in while/until is not supported");
- goto break_in_loop;
+ level = 0x8000;
+ if(ip->compile_data->loopval_popped == 0){
+ level |= 0x4000;
+ }
+ goto break_by_jump;
}
ip = iseqobj->parent_iseqobj;
}
@@ -2184,6 +2198,8 @@
break;
}
case NODE_NEXT:{
+ unsigned long level = 0;
+
if(iseqobj->compile_data->redo_label != 0){
add_ensure_iseq(ret, iseqobj, self);
ADD_INSNL(ret, nd_line(node), jump, iseqobj->compile_data->start_label);
@@ -2196,15 +2212,26 @@
else{
yarv_iseq_t *ip = iseqobj->parent_iseqobj;
while(ip){
+ level = 0x8000;
if(ip->type == ISEQ_TYPE_BLOCK){
- rb_bug("next in rescue/ensure is not supported");
+ break;
}
else if(ip->compile_data->redo_label != 0){
- rb_bug("next in while/until is not supported");
+ if(ip->compile_data->loopval_popped == 0){
+ level |= 0x4000;
+ }
+ break;
}
ip = iseqobj->parent_iseqobj;
}
- COMPILE_ERROR(("can't next"));
+ if(ip != 0){
+ COMPILE(ret, "next val", node->nd_stts);
+ add_ensure_iseq(ret, iseqobj, self);
+ ADD_INSN1(ret, nd_line(node), throw, I2F(level | 0x03) /* TAG_NEXT */);
+ }
+ else{
+ COMPILE_ERROR(("can't next"));
+ }
}
break;
}
@@ -2218,16 +2245,23 @@
}
else{
yarv_iseq_t *ip = iseqobj->parent_iseqobj;
+ unsigned long level = 0x8000 | 0x4000;
while(ip){
if(ip->type == ISEQ_TYPE_BLOCK){
- rb_bug("redo in rescue/ensure is not supported");
+ break;
}
else if(ip->compile_data->redo_label != 0){
- rb_bug("redo in while/until is not supported");
+ break;
}
ip = iseqobj->parent_iseqobj;
}
- COMPILE_ERROR(("can't redo"));
+ if(ip != 0){
+ add_ensure_iseq(ret, iseqobj, self);
+ ADD_INSN1(ret, nd_line(node), throw, I2F(level | 0x05) /* TAG_REDO */);
+ }
+ else{
+ COMPILE_ERROR(("can't redo"));
+ }
}
break;
}
@@ -2317,9 +2351,9 @@
};
iseqobj->compile_data->in_ensure = Qtrue;
- iseqobj->compile_data->ensure_node_stack = &enl;
COMPILE_POPED(ensr, "ensure ensr", node->nd_ensr);
+ iseqobj->compile_data->ensure_node_stack = &enl;
ADD_LABEL(ret, lstart);
COMPILE_(ret, "ensure head", node->nd_head, poped);
@@ -3303,9 +3337,9 @@
}
case NODE_CLASS:{
VALUE iseq =
- NEW_ISEQOBJ(node->nd_body,
- make_name_with_str("<class:%s>", rb_id2name(node->nd_cpath->nd_mid)),
- ISEQ_TYPE_CLASS);
+ NEW_CHILD_ISEQOBJ(node->nd_body,
+ make_name_with_str("<class:%s>", rb_id2name(node->nd_cpath->nd_mid)),
+ self, ISEQ_TYPE_CLASS);
COMPILE(ret, "cbase", node->nd_cpath->nd_head);
COMPILE(ret, "super", node->nd_super);
@@ -3319,9 +3353,9 @@
}
case NODE_MODULE:{
VALUE iseq =
- NEW_ISEQOBJ(node->nd_body,
- make_name_with_str("<module:%s>", rb_id2name(node->nd_cpath->nd_mid)),
- ISEQ_TYPE_CLASS);
+ NEW_CHILD_ISEQOBJ(node->nd_body,
+ make_name_with_str("<module:%s>", rb_id2name(node->nd_cpath->nd_mid)),
+ self, ISEQ_TYPE_CLASS);
COMPILE (ret, "mbase", node->nd_cpath->nd_head);
ADD_INSN2(ret, nd_line(node), moduledef, ID2SYM(node->nd_cpath->nd_mid), iseq);
Modified: trunk/insns.def
===================================================================
--- trunk/insns.def 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/insns.def 2005-07-30 18:27:58 UTC (rev 217)
@@ -950,7 +950,7 @@
/* enter scope */
GetISeqVal(klass_iseqval, klass_iseq);
th_set_env(th, klass_iseq,
- FRAME_MAGIC_CLASS, klass, 0,
+ FRAME_MAGIC_CLASS, klass, (VALUE)GET_DFP(),
klass_iseq->ISEQ_MEMBER, GET_SP(), 0,
klass_iseq->local_size, 0, 0);
RESTORE_REGS();
@@ -1012,7 +1012,7 @@
/* enter scope */
GetISeqVal(module_iseqval, module_iseq);
th_set_env(th, module_iseq,
- FRAME_MAGIC_CLASS, module, 0,
+ FRAME_MAGIC_CLASS, module, (VALUE)GET_DFP(),
module_iseq->ISEQ_MEMBER, GET_SP(), 0,
module_iseq->local_size, 0, 0);
RESTORE_REGS();
@@ -1273,23 +1273,38 @@
(VALUE throwobj)
()
{
- if(throw_state != 0){
- ulong pt;
- /* new throw */
-
- if(throw_state == TAG_BREAK || throw_state == TAG_RETRY){
- /* set current dfp */
- pt = (VALUE)GC_GUARDED_PTR_REF(*GET_DFP());
+ ulong state = throw_state & 0xff;
+ ulong flag = throw_state & 0x8000;
+ ulong level = throw_state >> 16;
+
+ if(state != 0){
+ VALUE *pt;
+ int i;
+ if(flag != 0){
+ if(throw_state & 0x4000){
+ pt = (void *)1;
+ }
+ else{
+ pt = 0;
+ }
}
- else if(throw_state == TAG_RETURN){
- /* set current lfp */
- pt = (ulong)GET_LFP();
- }
else{
- rb_bug("isns(throw): unsupport thorw type");
+ if(state == TAG_BREAK || throw_state == TAG_RETRY){
+ pt = GC_GUARDED_PTR_REF((VALUE*)*GET_DFP());
+ for(i=0; i<level; i++){
+ pt = GC_GUARDED_PTR_REF((VALUE*)*pt);
+ }
+ }
+ else if(state == TAG_RETURN){
+ /* set current lfp */
+ pt = GET_LFP();
+ }
+ else{
+ rb_bug("isns(throw): unsupport thorw type");
+ }
}
- th->state = throw_state;
- return (VALUE)NEW_THROW_OBJECT(throwobj, pt, throw_state);
+ th->state = state;
+ return (VALUE)NEW_THROW_OBJECT(throwobj, (VALUE)pt, state);
}
else{
/* continue throw */
Modified: trunk/test/test_flow.rb
===================================================================
--- trunk/test/test_flow.rb 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/test/test_flow.rb 2005-07-30 18:27:58 UTC (rev 217)
@@ -6,7 +6,38 @@
require 'test/yarvtest'
class TestFlow < YarvTestBase
-
+ def ae_flow(src, for_value=true)
+ # Tracks flow through the code
+ # A test like
+ # begin
+ # ensure
+ # end
+ # gets transformed into
+ # a = []
+ # begin
+ # begin; a << 1
+ # ensure; a << 2
+ # end; a << 3
+ # rescue Exception
+ # a << 99
+ # end
+ # a
+ # before being run. This tracks control flow through the code.
+
+ cnt = 0
+ src = src.gsub(/(\n|$)/) { "; $a << #{cnt+=1}\n" }
+ src = "$a = []; begin; #{src}; rescue Exception; $a << 99; end; $a"
+
+ if false#true
+ puts
+ puts '#----'
+ puts src
+ puts '#----'
+ end
+
+ ae(src)
+ end
+
def test_while_with_ensure
ae %q{
a = []
@@ -187,8 +218,27 @@
}
end
-if false
- # unsupported
+ def test_break_overrides_exception
+ ae_flow %{
+ [1,2].each do
+ begin
+ raise StandardError
+ ensure
+ break
+ end
+ end
+ }
+ ae_flow %{
+ [1,2].each do
+ begin
+ raise StandardError
+ rescue
+ break
+ end
+ end
+ }
+ end
+
def test_break_in_exception
ae_flow %q{
i=0
@@ -196,6 +246,42 @@
i+=1
begin
ensure
+ break
+ end
+ end
+ }
+ ae_flow %q{
+ i=0
+ while i<3
+ i+=1
+ begin
+ raise
+ ensure
+ break
+ end
+ end
+ }
+ ae_flow %q{
+ i=0
+ while i<3
+ i+=1
+ begin
+ raise
+ rescue
+ break
+ end
+ end
+ }
+ end
+
+ def test_next_in_exception
+ return
+ ae_flow %q{
+ i=0
+ while i<3
+ i+=1
+ begin
+ ensure
next
end
end
@@ -206,24 +292,113 @@
i+=1
begin
raise
+ ensure
+ next
+ end
+ end
+ }
+ ae_flow %q{
+ i=0
+ while i<3
+ i+=1
+ begin
+ raise
rescue
next
end
end
}
end
-
- def test_break_overrides_exception
- ae_flow %{
- [1,2].each do
- begin
- raise StandardError
- ensure
+
+ def test_complex_break
+ ae_flow %q{
+ i = 0
+ while i<3
+ i+=1
+ j = 0
+ while j<3
+ j+=1
+ begin
+ raise
+ rescue
+ break
+ end
+ end
+ end
+ }
+ ae_flow %q{
+ i = 0
+ while i<3
+ i+=1
+ j = 0
+ while j<3
+ j+=1
+ 1.times{
+ begin
+ raise
+ rescue
+ break
+ end
+ }
+ end
+ end
+ }
+ ae_flow %q{
+ i = 0
+ while i<3
+ i+=1
+ j = 0
+ while j<3
+ j+=1
+ begin
+ raise
+ ensure
+ break
+ end
+ end
+ end
+ }
+ ae_flow %q{
+ i = 0
+ while i<3
+ i+=1
+ j = 0
+ while j<3
+ j+=1
+ 1.times{
+ begin
+ raise
+ ensure
+ break
+ end
+ }
+ end
+ end
+ }
+ end
+
+ def test_jump_from_class
+ ae_flow %q{
+ 3.times{
+ class C
break
end
+ }
+ }
+ ae_flow %q{
+ 3.times{
+ class C
+ next
+ end
+ }
+ }
+ ae_flow %q{
+ while true
+ class C
+ break
+ end
end
}
end
+
end
-end
-
Modified: trunk/test/yarvtest.rb
===================================================================
--- trunk/test/yarvtest.rb 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/test/yarvtest.rb 2005-07-30 18:27:58 UTC (rev 217)
@@ -27,39 +27,6 @@
assert_equal(ruby, yarv)
end
- def ae_flow(src, for_value=true)
- # Tracks flow through the code
- # A test like
- # begin
- # ensure
- # end
- # gets transformed into
- # a = []
- # begin
- # begin; a << 1
- # ensure; a << 2
- # end; a << 3
- # rescue Exception
- # a << 99
- # end
- # a
- # before being run. This tracks control flow through the code.
-
- cnt = 0
- src = src.gsub(/(\n|$)/) { "; a << #{cnt+=1}\n" }
- src = "a = []; begin; #{src}; rescue Exception; a << 99; end; a"
-
- if false#true
- puts
- puts '#----'
- puts src
- puts '#----'
- end
-
- ae(src)
- end
-
-
def test_
end
Modified: trunk/test.rb
===================================================================
--- trunk/test.rb 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/test.rb 2005-07-30 18:27:58 UTC (rev 217)
@@ -8,68 +8,15 @@
###########################################################
$prog =<<'__EOP__'
-def m1 a
- if a == 0
- else
- m1 a-1
- end
-end
-
-m1 3
-
-p 1
-
-__END__
-def m n
- if n == 0
- #
- else
- m n-1
- end
-end
-
-m 5
-p 1
-
-__END__
-
-def iter
- yield
-end
-
-begin
- iter{
- iter{
- iter{
- raise
- }
- }
- }
-rescue => e
- puts e.backtrace
-end
-
-__END__
-
-def m enum
- enum.map{
- yield
- }
-end
-
-begin
- m(1..3){
- raise
- }
-rescue => e
- puts e.backtrace
-end
-__END__
-while true
+4.times{|e|
+ p e
class C
- break
+ p 1
+ next
+ p 2
end
-end
+ p 3
+}
__EOP__
###########################################################
Modified: trunk/vm.c
===================================================================
--- trunk/vm.c 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/vm.c 2005-07-30 18:27:58 UTC (rev 217)
@@ -1001,6 +1001,9 @@
ulong epc, cont_pc, cont_sp;
VALUE catch_iseqval;
yarv_control_frame_t *cfp;
+ VALUE *escape_dfp;
+ VALUE type;
+
err = ruby_errinfo;
if(state == TAG_RAISE){
rb_ivar_set(err, idThrowState, state);
@@ -1016,7 +1019,6 @@
epc = cfp->pc - cfp->iseq->ISEQ_MEMBER;
if(state == TAG_BREAK || state == TAG_RETURN){
- static VALUE *escape_dfp;
escape_dfp = GET_THROWOBJ_CATCH_POINT(err);
// printf("%p, %p\n", escape_dfp, cfp->dfp);
@@ -1039,38 +1041,104 @@
}
}
}
-
- for(i=0; i<cfp->iseq->catch_table_size; i++){
- entry = &cfp->iseq->catch_table[i];
- if(entry->start < epc &&
- entry->end >= epc){
- if(state == TAG_RAISE &&
- entry->type == CATCH_TYPE_RESCUE){
- catch_iseqval = entry->iseq;
- cont_pc = entry->cont;
- cont_sp = entry->sp;
- break;
+ if(state == TAG_RAISE){
+ for(i=0; i<cfp->iseq->catch_table_size; i++){
+ entry = &cfp->iseq->catch_table[i];
+ if(entry->start < epc &&
+ entry->end >= epc){
+
+ if(entry->type == CATCH_TYPE_RESCUE ||
+ entry->type == CATCH_TYPE_ENSURE){
+ catch_iseqval = entry->iseq;
+ cont_pc = entry->cont;
+ cont_sp = entry->sp;
+ break;
+ }
}
- else if(entry->type == CATCH_TYPE_ENSURE){
- catch_iseqval = entry->iseq;
- cont_pc = entry->cont;
- cont_sp = entry->sp;
- break;
+ }
+ }
+ else if(state == TAG_RETRY){
+ for(i=0; i<cfp->iseq->catch_table_size; i++){
+ entry = &cfp->iseq->catch_table[i];
+ if(entry->start < epc &&
+ entry->end >= epc){
+
+ if(entry->type == CATCH_TYPE_ENSURE){
+ catch_iseqval = entry->iseq;
+ cont_pc = entry->cont;
+ cont_sp = entry->sp;
+ break;
+ }
+ else if(entry->type == CATCH_TYPE_RETRY){
+ VALUE *escape_dfp;
+ escape_dfp = GET_THROWOBJ_CATCH_POINT(err);
+
+ if(cfp->dfp == escape_dfp){
+ cfp->pc = cfp->iseq->ISEQ_MEMBER + entry->cont;
+ goto vm_loop_start;
+ }
+ }
}
- else if(state == TAG_RETRY &&
- entry->type == CATCH_TYPE_RETRY){
- VALUE *escape_dfp;
- escape_dfp = GET_THROWOBJ_CATCH_POINT(err);
+ }
+ }
+ else if(state == TAG_BREAK &&
+ ((VALUE)escape_dfp & ~0x03) == 0){
+ type = CATCH_TYPE_BREAK;
+
+ search_restart_point:
+ for(i=0; i<cfp->iseq->catch_table_size; i++){
+ entry = &cfp->iseq->catch_table[i];
+ if(entry->start < epc &&
+ entry->end >= epc){
- if(cfp->dfp == escape_dfp){
+ if(entry->type == CATCH_TYPE_ENSURE){
+ catch_iseqval = entry->iseq;
+ cont_pc = entry->cont;
+ cont_sp = entry->sp;
+ break;
+ }
+ else if(entry->type == type){
cfp->pc = cfp->iseq->ISEQ_MEMBER + entry->cont;
+ cfp->sp = cfp->bp + entry->sp;
+
+ if(state != TAG_REDO && escape_dfp){
+#if OPT_STACK_CACHING
+ initial = (GET_THROWOBJ_VAL(err));
+#else
+ *th->cfp->sp++ = (GET_THROWOBJ_VAL(err));
+#endif
+ }
+ ruby_errinfo = Qnil;
goto vm_loop_start;
}
}
}
}
-
+ else if(state == TAG_REDO){
+ type = CATCH_TYPE_REDO;
+ goto search_restart_point;
+ }
+ else if(state == TAG_NEXT){
+ type = CATCH_TYPE_NEXT;
+ goto search_restart_point;
+ }
+ else{
+ for(i=0; i<cfp->iseq->catch_table_size; i++){
+ entry = &cfp->iseq->catch_table[i];
+ if(entry->start < epc &&
+ entry->end >= epc){
+
+ if(entry->type == CATCH_TYPE_ENSURE){
+ catch_iseqval = entry->iseq;
+ cont_pc = entry->cont;
+ cont_sp = entry->sp;
+ break;
+ }
+ }
+ }
+ }
+
if(catch_iseqval != 0){
/* found catch table */
yarv_iseq_t* catch_iseq;
Modified: trunk/yarvcore.h
===================================================================
--- trunk/yarvcore.h 2005-07-30 12:26:02 UTC (rev 216)
+++ trunk/yarvcore.h 2005-07-30 18:27:58 UTC (rev 217)
@@ -60,6 +60,9 @@
#define CATCH_TYPE_RESCUE INT2FIX(1)
#define CATCH_TYPE_ENSURE INT2FIX(2)
#define CATCH_TYPE_RETRY INT2FIX(3)
+#define CATCH_TYPE_BREAK INT2FIX(4)
+#define CATCH_TYPE_REDO INT2FIX(5)
+#define CATCH_TYPE_NEXT INT2FIX(6)
struct catch_table_entry{
VALUE type;
--
ML: yarv-diff quickml.atdot.net
Info: http://www.atdot.net/~ko1/quickml