Running JavaScript in the JVM has been supported for a long time, first with the Rhino engine released in JDK 6 and later with the Nashorn engine introduced in JDK 8. Recently, the Java team published a proposal (JEP-335) to deprecate the Nashorn JavaScript engine with the intention of removing it from future releases of the JDK. Coincidentally, Oracle Labs is now releasing another engine that supports JavaScript on the JVM called GraalVM. Even better, GraalVM’s support for JavaScript in the JVM is a much more complete implementation of the latest JavaScript standards. It includes full support for the Node.js server framework and is much faster. GraalVM supports efficient execution of additional languages beyond JavaScript such as Ruby, R, and Python. GraalVM can run either in the context of its own installation based on JDK 8 or via the standard JDK installation starting from JDK 11.

Because GraalVM is designed to support running a variety of languages and in a variety of engines (including Node, Oracle database, or MySQL), some of the APIs for the interaction between Java and JavaScript are different than the API choices that Nashorn’s designers made. To support Nashorn users looking at alternatives, the GraalVM team in Oracle Labs has added a new Nashorn compatibility flag to ease migration:

$ js --nashorn-compat

or when starting from a Java application:

$ java -Dpolyglot.js.nashorn-compat=true MyApplication

Compatibility

Broadly speaking, the latest version of Nashorn supports ECMAScript version 5.1 with a few features of ECMAScript 6 added in JDK 9. However, the ECMA committee has had an additional three specification releases since then — ECMAScript 2016, 2017 and 2018, each with a substantial number of new features. GraalVM supports all the features of ECMAScript 2016 and 2017, and many of the ECMAScript 2018 features already as well. The remainder of the ECMAScript spec not yet implemented (mostly in the area of regular expressions) is under active development now.

All features specified in the newer versions of ECMAScript are implemented and pass the tests provided via the official testsuite Test262.

On the image above you can see compatibility table results for GraalVM from Kangax’s ECMAScript 6 compatibility table. JJS is the abbreviation for Nashorn. GraalVM provides the same 97% level of compliance as popular browsers or the default Node.js distribution. Nashorn only supports a small fraction of the standard.

Another limitation of Nashorn is that cannot run Node.js applications. GraalVM 1.0 can run embedded in the Node.js platform, and provides a “node” launcher. GraalVM is compatible with approximately 99% of Node.js modules. This was accomplished by re-implementing the native node.h APIs on top of the internals of GraalVM’s JavaScript engine (rather than the default V8 JavaScript engine). We test 45k+ JavaScript modules from NPM weekly against GraalVM to verify that they work. The results are available via a query interface at http://www.graalvm.org/docs/reference-manual/compatibility/. The GraalVM RC4 download includes a version of Node.js 8.x linked with the JVM, which you can launch with the “node” launcher just like the V8 version of Node. We plan to upgrade to the Node.js 10.x branch by the end of 2018 when Node.js 10.x goes LTS.

Performance

Besides being much more compatible with the language standards, GraalVM shows superior performance compared to the existing JVM-based engines. GraalVM’s JavaScript implementation can take full advantage of the advanced optimizations of the GraalVM compiler. For this post we ran the Octane benchmark on four engines: Rhino, Nashorn on JDK 1.8.0_161, GraalVM CE and GraalVM EE 1.0 RC4.

There are two versions of GraalVM binaries available. The community edition is completely free, built from the open-source components available on GitHub. The Enterprise Edition of GraalVM contains more performance enhancements and security features in addition to what’s provided by the community edition and can be downloaded from the Oracle Technology Network website. Find out more at graalvm.org.

Here are the results for the peak-performance we get on the Octane benchmark, normalized to the Nashorn results, where higher is better.

The diagram shows how on all benchmarks GraalVM CE provides performance comparable or superior to Nashorn with the composite score being 4 times higher. GraalVM EE is even faster. Rhino is far behind in terms of performance and didn’t even succeed in running some of the larger benchmarks. Running Nashorn on JDK 9 or JDK 10 doesn’t change the composite score significantly.

Polyglot Programming

GraalVM offers interoperability with all supported languages, through a common polyglot API. The most important class to start with is Context — an entity which allows evaluating programs in various languages, provides bindings between them, configuration, error handling and so on. In addition, Value is the polyglot approach to representing objects from all supported languages and providing language agnostic operations on them. This has the same general idea as Nashorn’s JSObject but more general.

Below is a short snippet of a Java program creating a GraalVM context and evaluating JavaScript code in it, returning a JavaScript array literal. The second element of the array defined in the JavaScript context is accessed as a Java int and printed out.

import org.graalvm.polyglot.*; ... Context context = Context.create();

Value array = context.eval("js", "[1,2,42,4]");

int result = array.getArrayElement(2).asInt();

One can also use the ScriptEngine API (JSR 223) with graal.js as the engine name (start Java with -Dpolyglot.js.nashorn-compat=true in order to enable Nashorn compatibility mode):

ScriptEngineManager m = new ScriptEngineManager();

ScriptEngine engine = m.getEngineByName("graal.js");

Object fn = engine.eval("(function() { return this; })");

Invocable inv = (Invocable) engine;

Object result = inv.invokeMethod(fn, "call", fn);

While ScriptEngine is supported for backwards compatibility, using the polyglot API should be preferred whenever possible, because the clearer typing of results reduces errors.

Backwards Compatibility With Nashorn

In general, GraalVM supports interoperability with Java in mostly compatible fashion to Nashorn, with identical syntax. There is a migration guide describing the differences between GraalVM and Nashorn (and Rhino) available in the GitHub repo docs. We know that non-trivial applications can support both Nashorn and GraalVM.

The most likely issue you may run into when upgrading from Nashorn to GraalVM is that GraalVM does not expose internal objects directly the way Nashorn does, including for example ScriptObjectMirror or JSObject — GraalVM offers viable alternatives though. JavaScript constructs (like classes, objects, fields) can be accessed like in Nashorn — but GraalVM does not allow the direct access to Java classes via fully qualified name for security reasons, but instead requires the use of an explicit call to Java.type() . Another difference with Nashorn is that GraalVM prevents lossy conversion of datatypes when calling a Java method. Instead of converting, GraalVM throws a type error. A list of the known differences and migration solutions can be found in the Nashorn migration guide.

Nashorn compatibility mode ( --nashorn-compat ) supports the following features:

Methods for navigating the Java class hierarchy, e.g. Java.extend and Java.super and methods like Java.isScriptFunction, Java.isJavaFunction or Java.isScriptObject to distinguish Java and JavaScript objects/functions in JavaScript user code

Java getters and setters mapped to JS properties (Java getter javaObject.getProperty() can be expressed in JavaScript code as javaObject.property ).

can be expressed in JavaScript code as ). A String’s length can be read both in JavaScript style (as property length ) and in Java style (as function length() ). Similarly, other functions from java.lang.String are exposed to JavaScript strings.

) and in Java style (as function ). Similarly, other functions from java.lang.String are exposed to JavaScript strings. Methods and objects for Nashorn’s scripting mode, like $ARG , $ENV , and $EXEC .

The Nashorn compatibility mode features are not recommended for new code, for style, consistency and performance reasons. As an example, the Nashorn compatibility mode makes available direct access to java.lang.String internals. This may force copies & conversions in cases where the underlying data is from a source (e.g., the Oracle database) with a different character set or with a different implementation. More details on this are in the migration guide.

Conclusion