I came across an issue with Nashorn, when evaluating a large expression, it works fine in Java 8 , but throws a java.lang.StackOverflowError
in Java 11.
Exception in thread "main" java.lang.StackOverflowError
at org.openjdk.nashorn.internal.codegen.LocalVariableTypesCalculator.enterBinaryNode(LocalVariableTypesCalculator.java:444)
at org.openjdk.nashorn.internal.ir.BinaryNode.accept(BinaryNode.java:329)
at org.openjdk.nashorn.internal.codegen.LocalVariableTypesCalculator.enterJoinPredecessorExpression(LocalVariableTypesCalculator.java:777)
at org.openjdk.nashorn.internal.ir.JoinPredecessorExpression.accept(JoinPredecessorExpression.java:114)
at org.openjdk.nashorn.internal.codegen.LocalVariableTypesCalculator.visitExpression(LocalVariableTypesCalculator.java:603)
at org.openjdk.nashorn.internal.codegen.LocalVariableTypesCalculator.enterBinaryNode(LocalVariableTypesCalculator.java:447)
at org.openjdk.nashorn.internal.ir.BinaryNode.accept(BinaryNode.java:329)
I came across this question, and in an attempt to fix this issue, as suggested in this comment, I'm trying to use the Standalone Nashorn with Java 11, by using org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory
in my code (See this page for info on "why do we need to do that").
I'm having a simple Maven project, where I have added the following to my pom. For simplicity, I've included only the following important parts:
- Nashorn dependency
- Maven Compiler Plugin
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.3</version>
</dependency>
</dependencies>
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
(Apart from these, I'm using maven-dependency-plugin
and maven-jar-plugin
to bundle this into an executable Jar).
The following is my Java code: Main.java
package main;
import org.openjdk.nashorn.api.scripting.NashornScriptEngine;
import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
import javax.script.ScriptEngine;
public class Main {
public static void main(String[] args) {
// write your code here
String expression = "(\"BUY\" == \"BUY\") && (\"BUY\" == \"BUY\") && (1.00 >= 1000.000)"; // It's a long expression which contains conditions like this.
try {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
System.out.println("================================================================================");
System.out.println("factory.engineName: " + factory.getEngineName());
System.out.println("factory.engineVersion: " + factory.getEngineVersion());
System.out.println("factory.languageName: " + factory.getLanguageName());
System.out.println("factory.languageVersion: " + factory.getLanguageVersion());
System.out.println("================================================================================");
ScriptEngine engine = factory.getScriptEngine();
Object results = engine.eval(expression);
System.out.println("Successful");
System.out.println(results);
} catch (Exception e) {
e.printStackTrace();
}
}
}
With Java version java version "11.0.9" 2020-10-20 LTS, I'm compiling a jar called maven-project-1.0-SNAPSHOT.jar
via doing mvn clean install
.
When running the main class of the Jar as follows:
java -jar target/maven-project-1.0-SNAPSHOT.jar
Based on this information, the standalone version of Nashorn will be loaded with Java 11 since I have used org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory
, and therefore, I'm not expecting a java.lang.StackOverflowError
with Java 11.
But my output produces a StackOverflowError:
================================================================================
factory.engineName: OpenJDK Nashorn
factory.engineVersion: 15.3
factory.languageName: ECMAScript
factory.languageVersion: ECMA - 262 Edition 5.1
================================================================================
Exception in thread "main" java.lang.StackOverflowError
at org.openjdk.nashorn.internal.codegen.LocalVariableTypesCalculator.enterJoinPredecessorExpression(LocalVariableTypesCalculator.java:775)
at org.openjdk.nashorn.internal.ir.JoinPredecessorExpression.accept(JoinPredecessorExpression.java:114)
at org.openjdk.nashorn.internal.codegen.LocalVariableTypesCalculator.visitExpression(LocalVariableTypesCalculator.java:603)
at org.openjdk.nashorn.internal.codegen.LocalVariableTypesCalculator.enterBinaryNode(LocalVariableTypesCalculator.java:447)
at org.openjdk.nashorn.internal.ir.BinaryNode.accept(BinaryNode.java:329)
Update: When compiling this with Java 17 and testing, I did not get a StackOverflowError. As pointed out by @Thorbjørn Ravn Andersen in the comments, the reason to check this was, to rule out the possibility of the in-built Nashorn being picked in Java 11, instead of the standalone one. (Since Java 17 doesn't have the inbuilt Nashorn, it would pick only the standalone one).
Based on this, I can also think of an addition to this question:
how can we make sure that the standalone Nashorn is picked up in Java 11?
I tried executing the jar as follows (referred to this answer of this related question), but the StackOverflowError is still being thrown. (The /path/to/my/maven-project/target/libs/
directory contains jars such as nashorn-core-15.3.jar
, asm-7.3.1.jar
, asm-commons-7.3.1.jar
...)
java --module-path "/path/to/my/maven-project/target/libs/" --add-modules org.openjdk.nashorn -jar target/maven-project-1.0-SNAPSHOT.jar
(Instead of passing arguments/flags like this, a preferrable way would be to add these as a configuration or so in the pom - and handle it "neater").
FYI, The following code works fine with Java 8, but throws the same StackOverflowError in Java 11, which is why I had to use org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory
instead of javax.script.ScriptEngineManager
.
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class Main {
public ScriptEngine engine = new ScriptEngineManager().getEngineByName(ENGINE_NAME);;
public static final String ENGINE_NAME = "nashorn";
public static void main(String[] args) {
// write your code here
Main main = new Main();
Object result = null;
String exp = "(1640 >= 10) && (1640 <= 100) && (1640 >= 123)";
try {
result = main.engine.eval(exp);
System.out.println(result);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
Am I missing anything here? Thanks in advance for any solutions.