Skip to content

JIT can’t compile a range with one string literal endpoint and one non-literal endpoint (IllegalArgumentException: value null) #9822

Closed

Description

@sferik

A method containing a range where one endpoint is a string literal and the other isn't, such as "a"..x or x.."b", is JIT-compiled like any other method. That is how JRuby 9.4.15.0 behaves.

def string_to_var(x) = "a"..x
def var_to_string(x) = x.."b"
def strings = "a".."b"
def endless = "a"..

string_to_var("z"); var_to_string("a"); strings; endless

On 9.4.15.0, the script compiles and nothing fails:

$ jruby -Xjit.threshold=0 -Xjit.logging=true repro.rb
INFO Ruby : done compiling target script: repro.rb

On 10.0.7.0, 10.1.1.0, 10.1.2.0 and master, string_to_var, var_to_string and endless can't be compiled, so they run in the interpreter for good. strings compiles:

$ jruby -Xjit.threshold=0 -Xjit.background=false -Xjit.logging=true -Xjit.logging.verbose=true repro.rb
ERROR Ruby : failed to compile target script: repro.rb
INFO JITCompiler : could not compile method; passes run: [...]: Object var_to_string at repro.rb:1 because of: "java.lang.IllegalArgumentException: value null"
INFO JITCompiler : method done jitting: Object strings at repro.rb:2
INFO JITCompiler : could not compile method; passes run: [...]: Object endless at repro.rb:3 because of: "java.lang.IllegalArgumentException: value null"
INFO JITCompiler : could not compile method; passes run: [...]: Object string_to_var at repro.rb:0 because of: "java.lang.IllegalArgumentException: value null"
java.lang.IllegalArgumentException: value null
	at org.jruby.org.objectweb.asm.SymbolTable.addConstant(SymbolTable.java:527)
	at org.jruby.org.objectweb.asm.SymbolTable.addBootstrapMethod(SymbolTable.java:1098)
	at org.jruby.org.objectweb.asm.SymbolTable.addConstantInvokeDynamic(SymbolTable.java:951)
	at org.jruby.org.objectweb.asm.MethodWriter.visitInvokeDynamicInsn(MethodWriter.java:1074)
	at org.jruby.compiler.impl.SkinnyMethodAdapter.invokedynamic(SkinnyMethodAdapter.java:243)
	at org.jruby.ir.targets.indy.IndyValueCompiler.pushFrozenString(IndyValueCompiler.java:97)
	at org.jruby.ir.targets.JVMVisitor.FrozenString(JVMVisitor.java:2931)
	at org.jruby.ir.operands.FrozenString.visit(FrozenString.java:114)
	at org.jruby.ir.targets.JVMVisitor.visit(JVMVisitor.java:646)
	at org.jruby.ir.targets.JVMVisitor.BuildRangeInstr(JVMVisitor.java:1251)
	...

These forms fail to compile:

  • "a"..x and x.."b", with x a method argument
  • "a".. and .."b"
  • "a"..1, "a"..:b
  • "a"..nil and nil.."b"

These compile:

  • "a".."b" and "a"..."b"
  • x..1, x..:b, x..nil, x..y
  • x.."b#{1}" and x..'b'.freeze

Run over JRuby's stdlib, ruby/spec and the MRI tests, it keeps 87 methods and blocks interpreted, all in ruby/spec and the MRI tests (mostly the Range and Array#[] specs and test/mri/ruby/test_range.rb). I didn't find it in the stdlib or in Rails 8.1 and its dependencies, so it is probably rare in application code, but forms like str.."z" or .."b" are valid Ruby that should compile.

IRBuilder#buildRange turns a StrNode endpoint into a FrozenString with beginString.getFile() or endString.getFile(), which seems to always be null, since Node#getFile returns null and StrNode doesn't override it. When both endpoints are literals, the range becomes a cached Range operand and the file isn't used. When only one is, the BuildRangeInstr path visits the FrozenString, and IndyValueCompiler#pushFrozenString passes the null file as an invokedynamic bootstrap argument, which ASM rejects. This appears to date from 01e3eed ("Simplify fixnum and string ranges"), first released in 10.0.0.0. One possible fix is to pass scope.getFile(), as IRBuilderAST does when it builds other FrozenString operands.

Found with the check in #9821, which compiles every method and block in JRuby's stdlib, ruby/spec and the MRI tests without running them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions