JRuby inlining causes bugged behavior (Regexp globals) #1041

Closed
mrbrdo opened this Issue Sep 26, 2013 · 4 comments

Projects

None yet

2 participants

@mrbrdo
Contributor
mrbrdo commented Sep 26, 2013

It seems JRuby is inlining blank? in the following case which is causing problems, due to how weirdly Regexps are supposed to behave in Ruby (at least according to normal JRuby behavior and Ruby MRI in all cases). Note that I am not loading Rails, just copied the blank? method from ActiveSupport - the issue is the same, but in Rails it usually inlines already after loading.

jruby-1.7.4 :001 > class String
jruby-1.7.4 :002?>     def blank?
jruby-1.7.4 :003?>         self !~ /[^[:space:]]/
jruby-1.7.4 :004?>       end
jruby-1.7.4 :005?>   end
 => nil
jruby-1.7.4 :006 > "a" =~ /(a)/ && "".blank? && $1.nil?
 => false
jruby-1.7.4 :007 > 10000.times { "a" =~ /(a)/ && "".blank? && $1.nil? }
 => 10000
jruby-1.7.4 :008 > "a" =~ /(a)/ && "".blank? && $1.nil?
 => true

Ruby MRI does not exhibit this kind of behavior. So basically it would seem that JRuby inlines blank at which point the regexp overrides the $1 global, while until it's in a separate method it does not (and yes this is really weird behavior but I guess you are aware of it since JRuby works like this too until it inlines the method).

@headius
Member
headius commented Sep 27, 2013

You're pretty close on your guess as to the cause of this. The issue is that !~ and friends are not marked as needing a place to put backref ($~, $1 and friends) so the backref ends up going into the nearest scope/frame that has such storage.

I could not reproduce on master (1.7.5) so other changes to framing/scoping may have fixed this, but I did see that this could still be an issue in other cases. Fix coming.

@mrbrdo
Contributor
mrbrdo commented Sep 27, 2013

But it's still related to inlining, right? Since it only happens after optimizations start kicking in.

Thanks, looking forward to fix.

@headius headius added a commit that referenced this issue Sep 27, 2013
@headius headius Mark =~ and !~ methods as writing backref, for compiler use.
This is an additional fix related to #1041. That issue appears to
be fixed already without this change, but I still believe this
change is necessary.
fed0f53
@headius
Member
headius commented Sep 27, 2013

@mrbrdo It's not really related to inlining, since JRuby itself doesn't inline these methods and the JVM may or may not. It's just related to how methods are optimized by the current compiled. In order to keep methods as inexpensive as possible, we look for calls that are known to read or write "special" frame-local variables like $~ and only allocate space for those variables when they're likely to be needed. This allocation occurs on a thread-local stack. If we fail to allocate when there's a call that writes those variables, it will end up in the wrong place...usually the next method in the stack that did allocate the space. That's what was happening here.

JRuby 1.7.5 changed how backref and lastline are stored, putting it on the preallocated frame stack rather than the dynamically allocated scope stack. I believe this fixed the issue because we appear to be allocating the frame more often than is necessary. I'm looking into that now.

@headius headius closed this Sep 27, 2013
@mrbrdo
Contributor
mrbrdo commented Sep 27, 2013

Thanks and good job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment