diff --git a/ChangeLog b/ChangeLog index f5a7560d78..53a802607d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Thu Aug 16 22:10:06 2007 Koichi Sasada + + * compile.c (iseq_compile_each): fix next/redo stack consistency. + [ruby-dev:31373] + + * bootstraptest/test_syntax.rb: add tests for above. + + * sample/test.rb: fix to use __FILE__ instead of $0 to know basedir. + Thu Aug 16 21:14:06 2007 WATANABE Hirofumi * configure.in (BASERUBY): need AC_SUBST. [ruby-dev:31438] diff --git a/bootstraptest/test_syntax.rb b/bootstraptest/test_syntax.rb index 1b6d0c8eab..9541d67089 100644 --- a/bootstraptest/test_syntax.rb +++ b/bootstraptest/test_syntax.rb @@ -523,25 +523,88 @@ assert_equal %q{1}, %q{ end until true i } -def assert_syntax_error expected, code +def assert_syntax_error expected, code, message = '' assert_equal "#{expected}", - "begin eval(%q{#{code}}, nil, '', 0)"'; rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end' + "begin eval(%q{#{code}}, nil, '', 0)"'; rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end', message end -assert_syntax_error "unterminated string meets end of file", '().."' # [ruby-dev:29732] -assert_equal %q{[]}, %q{$&;[]} # [ruby-dev:31068] -assert_syntax_error "syntax error, unexpected tSTAR, expecting '}'", %q{{*0}} # [ruby-dev:31072] -assert_syntax_error "empty symbol literal", %q{0..:""} # [ruby-dev:31085] -assert_syntax_error "`@0' is not allowed as an instance variable name", %q{@0..0} # [ruby-dev:31095] -assert_syntax_error "identifier $00 is not valid to get", %q{$00..0} # [ruby-dev:31100] +assert_syntax_error "unterminated string meets end of file", '().."', '[ruby-dev:29732]' +assert_equal %q{[]}, %q{$&;[]}, '[ruby-dev:31068]' +assert_syntax_error "syntax error, unexpected tSTAR, expecting '}'", %q{{*0}}, '[ruby-dev:31072]' +assert_syntax_error "empty symbol literal", %q{0..:""}, '[ruby-dev:31085]' +assert_syntax_error "`@0' is not allowed as an instance variable name", %q{@0..0}, '[ruby-dev:31095]' +assert_syntax_error "identifier $00 is not valid to get", %q{$00..0}, '[ruby-dev:31100]' assert_syntax_error "identifier $00 is not valid to set", %q{0..$00=1} -assert_equal %q{0}, %q{[*0];0} # [ruby-dev:31102] -assert_syntax_error "syntax error, unexpected ')'", %q{v0,(*,v1,) = 0} # [ruby-dev:31104] -assert_equal %q{1}, %q{class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1} # [ruby-dev:31110] -assert_syntax_error "Can't set variable $1", %q{0..$1=1} # [ruby-dev:31118] -assert_syntax_error "void value expression", %q{1.times{1+(1&&next)}} # [ruby-dev:31119] -assert_syntax_error "void value expression", %q{x=-1;loop{x+=1&&redo if (x+=1).zero?}} # [ruby-dev:31119] -assert_syntax_error %q{syntax error, unexpected $end}, %q{!} # [ruby-dev:31243] +assert_equal %q{0}, %q{[*0];0}, '[ruby-dev:31102]' +assert_syntax_error "syntax error, unexpected ')'", %q{v0,(*,v1,) = 0}, '[ruby-dev:31104]' +assert_equal %q{1}, %q{ + class << (ary=[]); def []; 0; end; def []=(x); super(0,x);end;end; ary[]+=1 +}, '[ruby-dev:31110]' +assert_syntax_error "Can't set variable $1", %q{0..$1=1}, '[ruby-dev:31118]' +assert_syntax_error "void value expression", %q{1.times{1+(1&&next)}}, '[ruby-dev:31119]' +assert_syntax_error "void value expression", %q{x=-1;loop{x+=1&&redo if (x+=1).zero?}}, '[ruby-dev:31119]' +assert_syntax_error %q{syntax error, unexpected $end}, %q{!}, '[ruby-dev:31243]' assert_equal %q{[nil]}, %q{[()]}, '[ruby-dev:31252]' assert_equal %q{true}, %q{!_=()}, '[ruby-dev:31263]' assert_equal 'ok', %q{while true; redo; end if 1 == 2; :ok}, '[ruby-dev:31360]' +assert_equal 'ok', %q{ + 1.times { + begin + ensure + next + end + }; :ok +}, '[ruby-dev:31373]' +assert_equal 'ok', %q{ + flag = false + 1.times { + next if flag + flag = true + begin + ensure + redo + end + }; :ok +}, '[ruby-dev:31373]' +assert_equal 'ok', %q{ + 1.times{ + p(1, (next; 2)) + }; :ok +} +assert_equal '3', %q{ + i = 0 + 1 + (while true + break 2 if (i+=1) > 1 + next + end) +} +assert_equal '3', %q{ + i = 0 + 1 + (while true + break 2 if (i+=1) > 1 + p(1, (next; 2)) + end) +} +# redo +assert_equal 'ok', %q{ + i = 0 + 1.times{ + break if i>1 + i+=1 + p(1, (redo; 2)) + }; :ok +} +assert_equal '3', %q{ + i = 0 + 1 + (while true + break 2 if (i+=1) > 1 + redo + end) +} +assert_equal '3', %q{ + i = 0 + 1 + (while true + break 2 if (i+=1) > 1 + p(1, (redo; 2)) + end) +} diff --git a/compile.c b/compile.c index 770e1cf89b..52fae34d59 100644 --- a/compile.c +++ b/compile.c @@ -1151,6 +1151,13 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) list = list->next; } +#if 0 + /* this check need dead code elimination */ + if (sp != 0) { + rb_bug("SP is not 0 on %s (%d)\n", RSTRING_PTR(iseq->name), sp); + } +#endif + iseq->iseq = (void *)generated_iseq; iseq->iseq_size = pos; iseq->insn_info_table = insn_info_table; @@ -2927,13 +2934,17 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } case NODE_NEXT:{ unsigned long level = 0; + int pop_after_throw = 0; if (iseq->compile_data->redo_label != 0) { - add_ensure_iseq(ret, iseq); - ADD_INSNL(ret, nd_line(node), jump, - iseq->compile_data->start_label); + /* next in while loop */ + debugs("next in while\n"); + pop_after_throw = 1; + goto next_by_throw; } else if (iseq->compile_data->end_label) { + debugs("next in block\n"); + ADD_INSN (ret, nd_line(node), emptstack); COMPILE(ret, "next val", node->nd_stts); add_ensure_iseq(ret, iseq); ADD_INSNL(ret, nd_line(node), jump, @@ -2943,7 +2954,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with next")); } else { - rb_iseq_t *ip = iseq->parent_iseq; + rb_iseq_t *ip; + next_by_throw: + ip = iseq; while (ip) { level = 0x8000; if (ip->type == ISEQ_TYPE_BLOCK) { @@ -2957,9 +2970,11 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } if (ip != 0) { COMPILE(ret, "next val", node->nd_stts); - add_ensure_iseq(ret, iseq); ADD_INSN1(ret, nd_line(node), throw, INT2FIX(level | 0x03) /* TAG_NEXT */ ); + if (pop_after_throw) { + ADD_INSN(ret, nd_line(node), pop); + } } else { COMPILE_ERROR((ERROR_ARGS "Illegal next")); @@ -2968,18 +2983,27 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) break; } case NODE_REDO:{ + int pop_after_throw = 0; if (iseq->compile_data->redo_label) { + debugs("redo in while"); +#if 1 + pop_after_throw = 1; + goto redo_by_throw; +#else add_ensure_iseq(ret, iseq); ADD_INSNL(ret, nd_line(node), jump, iseq->compile_data->redo_label); if (!poped) { /* for stack consistency */ ADD_INSN(ret, nd_line(node), putnil); } +#endif } else if (iseq->type == ISEQ_TYPE_EVAL) { COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with redo")); } else if (iseq->compile_data->start_label) { + ADD_INSN (ret, nd_line(node), emptstack); + add_ensure_iseq(ret, iseq); ADD_INSNL(ret, nd_line(node), jump, iseq->compile_data->start_label); if (!poped) { /* for stack consistency */ @@ -2987,24 +3011,30 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } } else { - rb_iseq_t *ip = iseq->parent_iseq; - unsigned long level = 0x8000 | 0x4000; + rb_iseq_t *ip; + unsigned long level; + redo_by_throw: + level = 0x8000 | 0x4000; + ip = iseq; while (ip) { - if (ip->type == ISEQ_TYPE_BLOCK) { + if (ip->compile_data->redo_label != 0) { + break; + } + else if (ip->type == ISEQ_TYPE_BLOCK) { break; } else if (ip->type == ISEQ_TYPE_EVAL) { COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with redo")); } - else if (ip->compile_data->redo_label != 0) { - break; - } ip = ip->parent_iseq; } if (ip != 0) { - add_ensure_iseq(ret, iseq); ADD_INSN1(ret, nd_line(node), throw, INT2FIX(level | 0x05) /* TAG_REDO */ ); + + if (pop_after_throw) { + ADD_INSN(ret, nd_line(node), pop); + } } else { COMPILE_ERROR((ERROR_ARGS "Illegal redo")); @@ -3716,8 +3746,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) ADD_INSN(ret, nd_line(node), emptstack); } - COMPILE(ret, "return nd_stts (return val)", - node->nd_stts); + COMPILE(ret, "return nd_stts (return val)", node->nd_stts); if (is->type == ISEQ_TYPE_METHOD) { add_ensure_iseq(ret, iseq); diff --git a/sample/test.rb b/sample/test.rb index 6dffa609dc..768429d951 100644 --- a/sample/test.rb +++ b/sample/test.rb @@ -1871,13 +1871,14 @@ File.unlink "script_tmp" or `/bin/rm -f "script_tmp"` File.unlink "script_tmp.bak" or `/bin/rm -f "script_tmp.bak"` $bad = false -if (dir = File.dirname(File.dirname($0))) == '.' +if (dir = File.dirname(File.dirname(__FILE__))) == '.' dir = "" else dir << "/" end def valid_syntax?(code, fname) + p fname eval("BEGIN {return true}\n#{code}", nil, fname, 0) rescue Exception STDERR.puts $!.message