Nashorn removal: GraalVM to the rescue!

Christian Wirth
graalvm
Published in
8 min readMar 11, 2020

Nashorn, the JavaScript engine in the OpenJDK, has been deprecated in JDK 11 by JEP 335 and has recently been scheduled to be removed in a future JDK version by JEP 372. So, if you are running on Nashorn today, the clock is ticking faster now until the day when you will be unsupported. However, there is a solution — GraalVM’s modern JavaScript engine.

GraalVM JavaScript includes a modern, compatible, and highly performant JavaScript engine. Unlike Nashorn, this engine is compatible to the latest ECMAScript specification (ECMAScript 2019 currently).

In addition, it allows interoperability with Java code just like Nashorn (and before, Rhino) and provides most of the extensions that Nashorn introduced. In some corner cases, your code might require a few modifications, but generally it should run on GraalVM JavaScript — with additional advantages like higher performance, better tooling, and interoperability with additional languages. Finally, GraalVM can execute Node.js applications, giving you access to a whole new universe of possibilities for your application.

GraalVM JavaScript can be executed from GraalVM, or on any stock JDK 11+ after downloading it from Maven central and enabling the GraalVM Compiler there, see further below.

Why you should use GraalVM JavaScript to replace Nashorn

Before deciding to switch from Nashorn to GraalVM you might have a few doubts: is GraalVM compatible? is it fast? will it be maintained and supported? The answer to all those questions is YES, and here is why:

Yes, GraalVM JavaScript is compatible with ECMAScript

GraalVM JavaScript is a fully compatible JavaScript/ECMAScript engine — this is checked with test suites like ECMAScript’s official test262 suite, and verified externally e.g., by projects like the Kangax compatibility table. We closely follow the evolution of the specification and implement new features as they mature in the spec. As of March 2020, GraalVM JavaScript is compatible with the latest ECMAScript 2019 specification, and already support all the features expected for the upcoming ECMAScript 2020 specification (currently behind the --js.ecmascript-version=2020 flag).

Relevant engines from the ES6 section of the Kangax compatibility table.

Yes, GraalVM JavaScript is compatible with Nashorn’s extensions

Nashorn ships a few extensions that go beyond the ECMAScript specification. This includes interoperability with Java, and some non-standard built-in objects and functions mostly around Java interoperability. GraalVM JavaScript supports most of those extensions, some behind a flag. While most Nashorn-compatible code will run out-of-the-box on GraalVM, some might require minor modifications. This is especially the case for some security-relevant features where GraalVM uses a security-by-default approach and might require additional flags to enable less secure legacy features. A detailed description can be found below and in our Nashorn migration guide. An example migration story can be found in a previous article where we moved a Server Side Rendered React app from Nashorn to GraaVM.

Yes, GraalVM JavaScript is as fast as Nashorn–up to 6x faster!

GraalVM JavaScript was developed to bring the highest possible peak performance to your application. It competes on performance with other JavaScript engines including those used to execute JavaScript in browsers. We previously published benchmark results showing GraalVM JavaScript is roughly 4x (Community Edition) or 6x (Enterprise Edition) faster than Nashorn on peak performance as measured by the Octane JavaScript benchmark suite. The GraalVM team cares a lot about performance of all supported languages and we see performance improvements of our JavaScript engine in every release.

Yes, GraalVM is a supported product

GraalVM is shipped in two variants: there is a fully open-sourced Community Edition and there is an Oracle product, Oracle GraalVM Enterprise Edition.

The Community Edition is available as open-source. It is backed by the community that maintains and frequently updates it.

The Enterprise Edition is a fully supported Oracle product. Users will get the expected maintenance service of Oracle including 24/7 support and security updates.

Yes, GraalVM is compatible with Node.js!

Unlike Nashorn, GraalVM JavaScript is able to execute Node.js applications. Out of the box, GraalVM can execute almost any application that was written for the original Node.js platform. We verify this by testing against more than 100000 npm modules. GraalVM can execute both JavaScript-based modules as well as native (C based) modules. Only few modules that depend on proprietary V8 internals GraalVM cannot easily support at the moment. Any normal web application will execute perfectly on GraalVM, and allow you to harness all other advantages of GraalVM in the Node.js ecosystem, from language interoperability to tooling support!

Here’s a small Node.js application that uses npm packages and can use Java code too.

const http = require("http");
const span = require("ansispan");
require("colors");
var sys = Java.type('java.lang.System');
var javaVersion = sys.getProperties().getProperty("java.version");
http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.end(span(("GraalVM with Java "+javaVersion).green));
response.end(span("Hello GraalVM!".green));
}).listen(8000, function() {
console.log("Graal.js server at http://127.0.0.1:8000/".red);
});

To run it use, put the above into a file, for example index.js and run:

> npm install http ansispan colors
> node index.js
Graal.js server at http://127.0.0.1:8000/

How to use GraalVM to replace Nashorn

The preferred way to run GraalVM JavaScript is directly from a GraalVM installation. GraalVM can replace your existing JDK/JRE: all you have to do is to install it and put it on your $PATH before any other JDK. Using this setup guarantees the highest performance and the best integration of our tools as everything is readily packaged in the GraalVM download. If for any reason you cannot replace your existing JDK with GraalVM — and we believe there really shouldn’t be such a reason — we describe below how to run GraalVM JavaScript on a stock JDK from a Maven download.

GraalVM ships with a js binary in the graalvm/bin folder. Use that to execute any JavaScript code directly.

js myPlainJavaScriptApp.js

If you want to allow interoperability with Java code the jvm flag will ensure you run on a full JVM. Without that flag, your code executes in what is called “native” mode where the Java code is ahead-of-time compiled using GraalVM’s native-image tool. In that mode, some access to Java is still possible, but some features like dynamic classloading might be limited.

js --jvm myAppWithJavaInterop.js

Likewise, if you want to enable interoperability with other programming languages supported on GraalVM the polyglot flag will enable them. You will have to use the graalvm/bin/gu tool first to install additional languages like Python, Ruby, or R.

js --polyglot myAppWithPolyglotInterop.js

Use GraalVM’s polyglot Contexts for interoperability with other languages

GraalVM ships with advanced polyglot features that allow you to interact between different programming languages (e.g. Java, JavaScript, Ruby, Python, R, LLVM, WebAssembly, etc.). To fully support this case, we ship a Context API that replaces the existing JSR 223 Scripting API. The Context API not only allows full exchange of code and data from a diverse set of programming languages, it also gives you more control over what the contexts in different languages may access.

Writing code for the Context API is similar to the use-cases of the Scripting API and in many cases moving from a Scripting API implementation to a Context is straightforward:

Instead of using ScriptEngine

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
Object result = engine.eval("40+2");
assert result.equals(42);

you now code on GraalVM using a Context:

Context context = Context.create();
Value result = context.eval("js", "40+2");
assert result.asInt() == 42;

With that minor modification, your application might already be able to harness the full might of Polyglot language interoperability on GraalVM!

Use Scripting API to be backwards compatible with existing code

Of course, GraalVM JavaScript is still compatible with code written against the Scripting API. There are some good advantages to migrating to the Context API eventually (i.e. interoperability with other languages, finer-grained security, better tooling), but for a first step using our compatibility layer might be good enough for your application.

Due to the extended security features of the Context API you might have to set additional options to a ScriptEngine in order to allow it to access Java classes from JavaScript code. See the documentation in our repository for more information on that.

Use the Nashorn compatibility mode for legacy code

GraalVM JavaScript by default is an ECMAScript (2019, currently) compatible JavaScript engine. This is a huge leap compared to Nashorn, which was mostly ECMAScript 5.1 compatible, with some limited ECMAScript 6 features.

Nashorn, however, shipped its own extensions to the ECMAScript specification, some already known from Rhino. GraalVM JavaScript supports most of those extensions, for instance Java.type, Java.from, and Java.extend. Some extensions are only available behind a nashorn-compat flag, especially if they conflict with ECMAScript features or are otherwise disregarded when used. This includes, e.g., Java.isJavaFunction, JSAdapter, and JavaImporter. Note: you need to enable the experimental-options flag before the nashorn-compat flag:

js --experimental-options --nashorn-compat myNashornCompatApp.js

We strongly advise you to migrate your application to work without the Nashorn compatibility mode in production environments (thus, the experimental-options flag is not required). Only this guarantees long-time compatibility with GraalVM and the evolving JavaScript/ECMAScript ecosystem. However, using the compatibility mode can help you with your first steps of migrating towards GraalVM.

Migration guide from Nashorn to GraalVM JavaScript

A more detailed migration guide is available that describes the features available by default and in Nashorn compatibility mode. In the same folder you find additional documentation for GraalVM JavaScript, e.g., for Java interoperability and more about JavaScript/ECMAScript/engine compatibility.

How to run GraalVM JavaScript on a stock JDK

Above we described how to run GraalVM JavaScript from a GraalVM installation. In some rare use-cases you might not be able to replace your existing JDK with GraalVM. While this is not our suggested setup, it is still possible to run GraalVM’s JavaScript engine on them.

GraalVM JavaScript is a JavaScript engine fully implemented in Java. In principle, this allows you to execute it on any Java-compatible JVM (JDK 8 or newer). However, that will only execute your JavaScript application in a JavaScript interpreter — resulting in suboptimal performance. GraalVM JavaScript uses the GraalVM Compiler to optimize the JavaScript application it executes — we call that technique Partial Evaluation. That way, your JavaScript application is effectively compiled to a native application at run time. JavaScript applications can still be executed when no GraalVM Compiler is available, but the code will only be interpreted (not compiled) and thus performance will be lower than on a GraalVM.

Both GraalVM JavaScript and the GraalVM Compiler are available for download from Maven. This allows you to setup your existing JDK to use those modules and thus execute GraalVM JavaScript with good performance on any Java-compatible JVM. We have described this process in a previous blog post (GraalVM’s JavaScript engine on JDK11 with high performance), and we share an example application on GitHub that shows the setup (graal-js-jdk11-maven-demo). The crucial point to note is that you don’t just need to download the JARs that GraalVM JavaScript requires, but you also need to configure the JVM to use the GraalVM optimizing compiler for the JavaScript parts.

Moving beyond Nashorn

Congratulations — your application is now running on GraalVM! What comes next? GraalVM has so much more to offer that your application can benefit from:

  • GraalVM’s native-image tool can ahead-of-time compile Java applications — significantly speeding up startup time and reducing resource utilization of the generated binary.
  • GraalVM offers polyglot language integration — allowing your application to interop with applications written in LLVM, Python, R, Ruby, and others.
  • GraalVM offers a common tooling platform — allowing language-agnostic analysis, debugging, performance tuning, and more.
  • GraalVM JavaScript is Node.js compatible — execute any Node.js application and add interoperability (and many other GraalVM features) to it!

There is a blogpost describing the Top 10 Things to do with GraalVM.

Conclusion

GraalVM JavaScript is available to help running your existing Nashorn integrations. With just a few steps your application will be able to run on GraalVM with better compatibility to modern JavaScript development and increased performance.

We are looking forward to hearing your success stories of migrating to GraalVM. In case something does not work for you as expected, please let us know!

--

--

Christian Wirth
graalvm

Manager with Oracle Labs, working on the Graal/JavaScript and TruffleRuby implementations. Opinions are my own.