From: Adiel Mittmann Date: 2011-03-24T00:17:37+09:00 Subject: [ruby-core:35543] Re: NoMethodError#message may take very long to execute Hi, Should I report the bug I described a few days ago somewhere else? Or is it considered a non-issue that should be left behind? On Fri, Mar 18, 2011 at 07:53:14AM +0900, Adiel Mittmann wrote: > Hello, > > When a non-existing method is called on an object, NoMethodError is risen. If > you call #message, however, your code may use up all CPU for a very long time > (in my case, up to a few minutes). > > I narrowed the problem down to this code in error.c (SVN snapshot) in the function > name_err_mesg_to_str(): > > d = rb_protect(rb_inspect, obj, 0); > if (NIL_P(d) || RSTRING_LEN(d) > 65) { > d = rb_any_to_s(obj); > } > > The problem is that, for a complex object, #inspect may take very long to > execute, only to have its results thrown away because they will be larger than > 65 characters. > > Of course I can write a #to_s for all my objects, but the point is that I didn't > call #to_s or #inspect, I called #message on an exception object, which then > takes a few minutes just to return a short string. > > Needless to say, this might be easy to spot in a simple example, but once you're > writing a web application that suddenly freezes for one minute with no apparent > reason, you're all but clueless as to what's going on. (The first time this > happened, I didn't even know that something would eventually show up on the > screen -- I thought it was an infinite loop). > > Here's an example code that shows this behavior: > > > require 'nokogiri' > class A > def x > @xml = Nokogiri::XML(File.new('baz.xml', 'rb').read()) > foo() > end > end > A.new().x() > a.x > > > Here, the time it takes for Ruby to print out the message that #foo doesn't > exist is proportional to the size of baz.xml. > > As a comparison, Python doesn't seem to do this. Take the following code: > > > class Test: > def __str__(self): > return "hello" > a = Test() > print a > print a.x() > > > If you execute it, this is the result: > > > hello > Traceback (most recent call last): > File "test.py", line 6, in > print a.x() > AttributeError: Test instance has no attribute 'x' > > > It uses the method __str__ to convert the object to a string when necessary, but > doesn't use it when printing out the message stating that the attribute doesn't > exist. > > One obvious way to fix this would be to always print out the simpler > representation given by rb_any_to_s. > > -- > Adiel Mittmann