Home > Software design >  runtime exception using local build of scala compiler: "java.lang.NoSuchMethodError: 'bool
runtime exception using local build of scala compiler: "java.lang.NoSuchMethodError: 'bool

Time:08-28

I have a local build of the scala compiler. I get the following exception when I use the three jars for the compiler (compiler/reflect/library) from my build in another Scala application (http://ikojo.in/):

[Thu Aug 25 11:24:22 EDT 2022, Utils] SEVERE: Problem
java.lang.NoSuchMethodError: 'boolean java.lang.StringBuilder.isEmpty()'
    at scala.tools.nsc.ast.parser.Scanners$Scanner.checkNoTrailingSeparator(Scanners.scala:1264)
    at scala.tools.nsc.ast.parser.Scanners$Scanner.getNumber(Scanners.scala:1302)
    at scala.tools.nsc.ast.parser.Scanners$Scanner.fetchToken(Scanners.scala:632)
    at scala.tools.nsc.ast.parser.Scanners$Scanner.nextToken(Scanners.scala:422)
...
    at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:401)
    at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1519)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1503)
    at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compile(IMain.scala:744)
    at scala.tools.nsc.interpreter.IMain$Request.$anonfun$compile$7(IMain.scala:978)
    at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.scala:17)
    at scala.tools.nsc.interpreter.IMain$.withSuppressedSettings(IMain.scala:1407)

Please note that Java Runtime8 is a caller in this stack trace. The exception is very early in the compiler code itself: during the very first phase of the compiler, the syntax analyzer, out of 23 (or more) phases.

Also note that the code of my local build of the compiler is identical to the official scala release (from: https://github.com/scala/scala/releases/tag/v2.13.6).

I checked the answer to a related question java.lang.NoSuchMethodError: java.lang.String.isEmpty()Z but that doesn't seem to have a good answer, or at least, it is not clear to me.

Does anybody have any hints or any leads on how to debug or resolve this?

CodePudding user response:

Java version which results in the error is the following:

ben@Bulents-MacBook-Pro ~ % java -version
java -version
java version "15.0.1" 2020-10-20
Java(TM) SE Runtime Environment (build 15.0.1 9-18)
Java HotSpot(TM) 64-Bit Server VM (build 15.0.1 9-18, mixed mode, sharing)

Instead, when I compile the scala compiler with the following, the error is resolved:

ben@Bulents-MacBook-Pro ~ % java -version
java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

I am not sure why or how this works, but it does. I have some guesses, but, they are not very educated. So, if anybody has a deeper insight, please share. Thank you.

CodePudding user response:

In Java versions before 15, java.lang.StringBuilder doesn't have an isEmpty method; Java versions after Java 15 have an isEmpty method.

The Scala compiler's code has a StringBuilderOps implicit class which has an isEmpty method.

Thus, when compiling the Scala compiler with a Java version before 15, the compiler sees (at line 1264) that we're calling the isEmpty method on a StringBuilder. Since there's no isEmpty method on java.lang.StringBuilder, the compiler looks for a class which StringBuilder is implicitly convertible to which has an isEmpty method. If there's exactly one such class (in this case StringBuilderOps), then the compiler says "this isn't an error" and effectively replaces the cbuf.isEmpty call with something like either:

  • (new StringBufferOps(cbuf)).isEmpty, or
  • StringBufferOps$.isEmpty(cbuf)

(these are both effectively the same thing: the second one is devirtualized into a static call and might be generated because StringBufferOps extends AnyVal). Note that this does not result in a call to an isEmpty on a StringBufferOps.

If the Java version you're building the compiler with is Java 15 or later, then the compiler sees that there's an isEmpty method on StringBuilder and eventually when it comes time to emit the Java bytecode, it emits the usual code for calling the isEmpty method on a StringBuilder in the JVM.

Because java.lang and similar packages are provided by the Java runtime, they're not included in the jar files. So if you then run the resulting jar files on a pre-Java 15 JVM, there's a reference in the bytecode to a method that doesn't exist and the JVM throws the NoSuchMethodError.

At least for now, I don't believe the Scala compiler officially supports being built with versions of Java that aren't Java 8 or Java 11. If building with a later version, be careful to not use your built compiler with Java versions earlier than what you built with.

  • Related