Issue #9725 has been updated by Josh Cheek.


> Solutions:
> 
> * NameError should not capture the target object.
> * NameError should build a message based on the target object *at creation time*, and only include information useful to indicate the type of object.
> * (Optional) If verbose mode is set, NameError can just do what it does now.

+1 for creation time.

I would also like to see it not hidden in the NameError::message method, but to just be a normal class that is easier to find and reason about

e.g. it causes this issue https://bugs.ruby-lang.org/issues/8303 I've seen a few others that resulted from it. Here's a fun article about it http://web.archive.org/web/20120125075139/http://www.carboni.ca/blog/p/Ruby-Did-You-Know-That-5-NameErrormessage

Because its so confusing, it causes what appear to be bugs in basically anything that hits this case. E.g. I thought RSpec was broken for quite a while, because half my students were hitting this, and it means that it blows not on the test that had the issue, but when the message is accessed at the very end of the test suite. This is very misleading and difficult to debug https://github.com/rspec/rspec-core/issues/1631


~~~ruby
# Here is an example showing how confusing it is that you can raise and rescue the NameError,
# and continue on your merry way, not bitten until you access the message.

def self.inspect
  sleep 5 # mimics an expensive inspect like the one below
  "#<expensive inspect result>"
end

puts "BEFORE EXCEPTION"
ex = whoops! rescue $!
puts "AFTER EXCEPTION"
 
puts "...some time later, we decide to look at the message"
puts ex.message
puts "WTF?"
~~~



~~~ruby
# Here is an example of how graphs inspect methods will grow incredibly quickly,
# making them use up all the resources available when asking for the inspect method,
# which will happen at the time that NameError.message is called.

class Graph
  def self.size(n)
    nodes = n.times.map { new }
    nodes.each { |n| n.associations = nodes - [n] }
    nodes
  end
  attr_accessor :associations
end

a, * = Graph.size(2)
a.inspect      # => "#<Graph:0x007fbc21803d08 @associations=[#<Graph:0x007fbc21803ce0 @associations=[#<Graph:0x007fbc21803d08 ...>]>]>"
a.inspect.size # => 113

a, * = Graph.size(3)
a.inspect.size # => 394

a, * = Graph.size(4)
a.inspect.size # => 1693

a, * = Graph.size(5)
a.inspect.size # => 8804

a, * = Graph.size(6)
a.inspect.size # => 54145

a, * = Graph.size(7)
a.inspect.size # => 385558

a, * = Graph.size(8) # quite a pause when generating this one
a.inspect.size # => 3123629
~~~

----------------------------------------
Feature #9725: Do not inspect NameError target object unless verbose
https://bugs.ruby-lang.org/issues/9725#change-48169

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* Category: 
* Target version: 
----------------------------------------
=begin
At least once every few months, we get an error report of JRuby raising a memory error where MRI does not due to NameError's Message object holding a reference to an object that's too large to inspect. I propose that this inspection of the target object should only be done in verbose mode.

Background:

NameError is raised when a variable-like method call fails to find a defined method. The resulting exception is created with a hidden NameError::Message that holds the object in which the method could not be found.

When name error needs to render its message, such as when it bubbles out or when #message is called, it does to_str on the NameError::Message, which ends up inspecting the target object. If this object's inspect output is large (or infinite) it can end up consuming a large amount of memory.

Problems:

* If the amount of memory required to render a NameError exceeds available memory, a very confusing and misleading memory error can be raised instead.
* If the target object is considered sensitive data, it will end up bubbling out through potentially untrustworthy code. It is an encapsulation flaw, basically.
* A NameError that gets held in memory will also prevent GC of the object it references.

Solutions:

* NameError should not capture the target object.
* NameError should build a message based on the target object *at creation time*, and only include information useful to indicate the type of object.
* (Optional) If verbose mode is set, NameError can just do what it does now.



-- 
https://bugs.ruby-lang.org/