Moved from http://jira.codehaus.org/browse/JRUBY-7128, reported by @nirvdrum.
This might be a bug in rspec-expectations, but I'm unable to force JIT my app due to a bad :remove_method call. Specifically, in my rspec (2.13.0) setup, I've tried to disable the old should syntax with something like the following:
RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
This causes rspec to execute this method:
return unless deprecated_should_enabled?
self.deprecated_should_enabled = false
And when that's executed things blow up like so:
~/d/w/mogotest /master> rspec spec/models/user_spec.rb
NameError: method 'should' not defined in RSpec::Expectations::ExpectationTarget
remove_method at org/jruby/RubyModule.java:2331
disable_deprecated_should at /home/nirvdrum/.rbenv/versions/jruby-1.7.2/lib/ruby/gems/shared/gems/rspec-expectations-2.13.0/lib/rspec/expectations/expectation_target.rb:71
disable_should at /home/nirvdrum/.rbenv/versions/jruby-1.7.2/lib/ruby/gems/shared/gems/rspec-expectations-2.13.0/lib/rspec/expectations/syntax.rb:74
syntax= at /home/nirvdrum/.rbenv/versions/jruby-1.7.2/lib/ruby/gems/shared/gems/rspec-expectations-2.13.0/lib/rspec/matchers/configuration.rb:29
I'm raising the issue because I don't see this behavior when I don't force JIT. I don't know if it has to do with loading semantics or if force JIT should be resilient to errors like this. It could also just be an rspec bug that I can take up with them.
Reproduced with 1.7.8.
$ ruby -e "class Foo; def self.define; def bar; end; end; def self.remove; remove_method :bar; end; end; Foo.define; Foo.bar; Foo.remove"
NameError: method 'bar' not defined in Foo
remove_method at org/jruby/RubyModule.java:2340
remove at -e:1
(root) at -e:1
It appears remove_method is not working properly when called from a compiled body.
Actually, that doesn't work interpreted either...but it works in MRI.
The sample project there should work in interpreted mode. I assume you mean your trivial reproduction doesn't work in interpreted mode. But if you're seeing othewise with the project, let me know.
Ok, so the issue here appears to be various problems with how we determine the class into which "def" methods will go.
The interpreter does appear to be doing the right thing, and behavior matches MRI for the following script:
def bar; end
Interestingly, the method does not appear to go into Foo's metaclass as you'd expect, and calling Foo.bar fails to work.
However, in the compiler, the method does go into Foo's metaclass, causing the subsequent call to remove_method to not find it. This is likely just a problem in our pre/post-method wrapper logic for compiled methods, not pushing the appropriate thread-local context for method definition. Nearly there.
defn/defs need scope to get proper rubyClass for definition.
Fixed; test coming. The problem was that defn/defs bodies (normal and singleton method definitions) need the current class ("rubyClass" in ThreadContext) to come from the scope rather than from the class in which the surrounding method is defined. Within a class body, this is no problem since all class bodies get a scope. In a method body, however, we try to optimize the scope away, so it was not used for rubyClass.
This fix will slightly impact performance of methods that have def in them, but those cases are pretty rare and generally limited to boot-time metaprogramming.
Test for #1239.
Sorry this one sat on the shelf so long.
No problem. Thanks for taking care of it.