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:

<snip>
require 'nokogiri'
class A
  def x
    @xml = Nokogiri::XML(File.new('baz.xml', 'rb').read())
    foo()
  end
end
A.new().x()
a.x
</snip>

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:

<snip>
class Test:
    def __str__(self):
        return "hello"
a = Test()
print a
print a.x()
</snip>

If you execute it, this is the result:

<output>
hello
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print a.x()
AttributeError: Test instance has no attribute 'x'
</output>

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