Hi. noreply / rubyforge.org wrote: (2006/01/01 19:04) >Bugs item #3160, was opened at 2006-01-01 09:53 >You can respond by visiting: >http://rubyforge.org/tracker/?func=detail&atid=1698&aid=3160&group_id=426 > >Category: Core >Group: None >Status: Open >Resolution: None >Priority: 3 >Submitted By: Dan Griffin (dangriffin) >Assigned to: Nobody (None) >Summary: Call to NoMethodError#message hangs ruby > >Initial Comment: >The attached program demonstrates what I think is some kind of stack overflow in Ruby. A deliberate bug in the Cell#cause_hang method causes the interpreter to freeze when the Test::Unit framework attempts to get the message for the NoMethodError exception. > >This has been tested on 1.8.2 and 1.8.4 on Windows. > >---------------------------------------------------------------------- > >You can respond by visiting: >http://rubyforge.org/tracker/?func=detail&atid=1698&aid=3160&group_id=426 Minimum reproducable code is this. ///////////////////////////////////////// Cell = Struct.new(:row, :col) n = 9 # increase this, it will take longer time cols = Array.new(n) { [] } rows = Array.new(n) { [] } boxes = Array.new(n) { Array.new(n) { Cell.new } } n.times {|x| n.times {|y| cell = boxes[x][y] cols[x] << cell rows[y] << cell cell.col = cols[x] cell.row = rows[y] } } boxes[0][0].dummy ///////////////////////////////////////// This is because name_err_mesg_to_str (error.c) calls rb_inspect and checks length of messege after all recursion is done. So some kind of this patch will prevent the unnessesary recursion. (this is adhok patch! I think commitable patch must be more cleaner) Index: error.c =================================================================== RCS file: /src/ruby/error.c,v retrieving revision 1.119 diff -u -p -r1.119 error.c --- error.c 28 Sep 2005 03:51:52 -0000 1.119 +++ error.c 2 Jan 2006 06:14:40 -0000 @@ -698,7 +698,9 @@ name_err_mesg_to_str(VALUE obj) desc = "false"; break; default: + rb_thread_local_aset(rb_thread_current(), rb_intern("__abort_if_too_long__"), Qtrue); d = rb_protect(rb_inspect, obj, 0); + rb_thread_local_aset(rb_thread_current(), rb_intern("__abort_if_too_long__"), Qfalse); if (NIL_P(d) || RSTRING(d)->len > 65) { d = rb_any_to_s(obj); } Index: eval.c =================================================================== RCS file: /src/ruby/eval.c,v retrieving revision 1.863 diff -u -p -r1.863 eval.c --- eval.c 31 Dec 2005 13:57:20 -0000 1.863 +++ eval.c 2 Jan 2006 06:12:24 -0000 @@ -13080,11 +13080,12 @@ recursive_pop(void) VALUE rb_exec_recursive(VALUE (*func)(VALUE, VALUE, int), VALUE obj, VALUE arg) { + VALUE result; + if (recursive_check(obj)) { - return (*func)(obj, arg, Qtrue); + result = (*func)(obj, arg, Qtrue); } else { - VALUE result; int state; recursive_push(obj); @@ -13095,6 +13096,12 @@ rb_exec_recursive(VALUE (*func)(VALUE, V POP_TAG(); recursive_pop(); if (state) JUMP_TAG(state); - return result; } + + if (RTEST(rb_thread_local_aref(rb_thread_current(), rb_intern("__abort_if_too_long__")))) { + if (RSTRING(result)->len > 65) { + rb_raise(rb_eRuntimeError, "foo"); + } + } + return result; }