Home | Send Feedback

Migration from Nashorn to GraalVM JavaScript

Published: 1. April 2020  •  Updated: 21. May 2021  •  java

A change in Java 15 is the removal of the Nashorn JavaScript Engine. This blog post shows you how to migrate to another engine.


The Java runtime had a built-in JavaScript engine for quite some time. It started with Java SE 6 when Sun bundled Java with Rhino. Then, in Java 8, they replaced Rhino with a more modern engine: Nashorn. Oracle announced a couple of years ago that this engine would be removed from the JDK. Since Java 11, the engine is deprecated, and in Java 15, they finally removed the engine.

Note that this does not affect the javax.script API. This API stays a part of Java.

The removal of the engine does not mean that you have to remove your Nashorn based code from your project. Fortunately, there is a successor, and it's called: GraalVM JavaScript. GraalVM is a universal virtual machine for running applications written in JavaScript, Python, Ruby, R, JVM-based, and LLVM-based languages.

If you want to learn more about GraalVM, visit the project site: https://www.graalvm.org

To learn more about the JavaScript engine, visit the reference documentation.


You don't have to run your application on the Graal virtual machine to use the new Javascript engine. The GraalVM JavaScript engine is a Java library hosted on the Maven central repository.

The advantage of switching to the new engine is getting a more modern JavaScript engine. Nashorn only supports ECMAScript 5.1. GraalVM JavaScript, on the other hand, is in version 22.0.0 is compatible with ECMAScript 2022.

Migration

Here is a simple example of Java code that executes JavaScript code. The javax.script abstraction handles all the interactions with the underlying engine. The only thing you have to specify is the name of the engine (nashorn). Note you can also get a reference to the engine with getEngineByName("javascript")

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Simple {

  public static void main(String[] args) throws ScriptException {
    // Nashorn
    ScriptEngine nashornEngine = new ScriptEngineManager().getEngineByName("nashorn");
    nashornEngine.eval("print('Hello World!');");

Simple.java


This example will no longer run on Java 15+, so we switch to the GraalVM JavaScript engine. First, add the required dependencies to your project.

      <groupId>org.graalvm.js</groupId>
      <artifactId>js</artifactId>
      <version>23.0.2</version>
    </dependency>  
    <dependency>
      <groupId>org.graalvm.js</groupId>
      <artifactId>js-scriptengine</artifactId>
      <version>23.1.1</version>
    </dependency>
  </dependencies>

pom.xml

Then change the engine name to graal.js.

    // Graal
    ScriptEngine graalEngine = new ScriptEngineManager().getEngineByName("graal.js");
    graalEngine.eval("print('Hello World!');");

Simple.java

With this simple change, your code runs on the GraalVM JavaScript engine, and it works on Java 15+ virtual machines.

If your code calls getEngineByName("javascript") you don't have to change any code. Add the GraalVM JavaScript engine to the class path and your code should run fine without any modifications.

Example

Another example where the Java code calls a JavaScript function

function fibonacci(num) {
  var a = 1, b = 0, temp;

  while (num >= 0){
    temp = a;
    a = a + b;
    b = temp;
    num--;
  }

  return b;
}

fibonacci.js

import java.io.BufferedReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Fibonacci {
  public static void main(String[] args)
      throws ScriptException, IOException, URISyntaxException, NoSuchMethodException {

    Path jsPath = Paths.get(Fibonacci.class.getResource("/fibonacci.js").toURI());

    // Nashorn
    ScriptEngine nashornEngine = new ScriptEngineManager().getEngineByName("nashorn");
    try (BufferedReader reader = Files.newBufferedReader(jsPath)) {
      nashornEngine.eval(reader);

      Invocable invocable = (Invocable) nashornEngine;
      Object result = invocable.invokeFunction("fibonacci", 1_000);

      System.out.println(result);
    }

    // Graal
    ScriptEngine graalEngine = new ScriptEngineManager().getEngineByName("graal.js");
    try (BufferedReader reader = Files.newBufferedReader(jsPath)) {
      graalEngine.eval(reader);

      Invocable invocable = (Invocable) graalEngine;
      Object result = invocable.invokeFunction("fibonacci", 1_000);

      System.out.println(result);
    }

  }
}

Fibonacci.java

Except for the engine name, the code is the same.

You can also get the engine with getEngineByName("javascript"). It will automatically get a reference to the installed JavaScript engine.

Extension

The Nashorn engine adds a few extensions to the JavaScript engine. For example, it is possible to use Java types in JavaScript. The following function uses the java.math.BigDecimal class for the calculation.

function factorialize(num) {
  
  if (num === 0 || num === 1) {
    return 1;
  }
  
  var result = new java.math.BigDecimal(String(num));
  for (var i = num - 1; i >= 1; i--) {
    result *= i;
  }
  
  return result.toString();

}

factorialize.js

This code does not run on the graal.js engine by default. If you want to use these features, you must enable the Nashorn compatibility mode. In this example, we do that with a call to System.setProperty.

    // GraalVM
    System.setProperty("polyglot.js.nashorn-compat", "true");

    ScriptEngine graalEngine = new ScriptEngineManager().getEngineByName("graal.js");
    try (BufferedReader reader = Files.newBufferedReader(jsPath)) {
      graalEngine.eval(reader);

      Invocable invocable = (Invocable) graalEngine;
      Object result = invocable.invokeFunction("factorialize", 5);

      System.out.println(result);
      System.out.println(result.getClass());
    }

Extension.java

To learn more about the compatibility mode, read the reference documentation:
https://www.graalvm.org/reference-manual/js/NashornMigrationGuide/#nashorn-compatibility-mode

I also recommend checking out the migration guide:
https://github.com/oracle/graaljs/blob/master/docs/user/NashornMigrationGuide.md#extensions-only-available-in-nashorn-compatibility-mode