GraalVM includes an ECMAScript compliant JavaScript engine. It is designed to be fully standard compliant, execute applications with high performance, and provide all the benefits from the Graal stack, including language interoperability and common tooling. With that engine, GraalVM can execute JavaScript and Node.js applications.

Running JavaScript

GraalVM can execute plain JavaScript code:

js [options] [filename...] -- [args]

Running Node.js apps

GraalVM can run unmodified Node.js applications on GraalVM. Applications can import npm modules, including native ones.

To run Node.js-based applications, use the node utility in the GraalVM distribution:

node [options] [filename] [args]

To install a Node.js module, use the npm executable in the /bin folder of the GraalVM package. The npm command is equivalent to the default Node.js command and supports all Node.js APIs.

1. Install the colors and ansispan modules using npm install as follows:

npm install colors ansispan

After the modules are installed, you can use them from your application.

2. Add the following code snippet to a file named app.js and save it in the same directory where you installed Node.js modules:

  // RUN-CMD: rm -rf node_modules
// RUN-CMD: npm install ansispan colors
// RUN-CMD: node {file}
// RUN-CMD: rm -r node_modules

// BEGIN-SNIPPET

const http = require("http");
const span = require("ansispan");
require("colors");

http.createServer(function (request, response) {
    response.writeHead(200, {"Content-Type": "text/html"});
    response.end(span("Hello Graal.js!".green));
}).listen(8000, function() { console.log("Graal.js server running at http://127.0.0.1:8000/".red); });
// END-SNIPPET

setTimeout(function() { console.log("DONE!"); process.exit(); }, 2000);
  

3. Execute it on GraalVM using the node command as follows:

node app.js

Interoperability

GraalVM supports several other programming languages, including Ruby, R, Python 3 and LLVM. While GraalVM is designed to run Node.js and JavaScript applications, it also provides an API for programming language interoperability that lets you execute code from any other language that GraalVM supports.

From JavaScript code you can access Java applications, as in the following example:

$ node --jvm
> var BigInteger = Java.type('java.math.BigInteger');
> console.log(BigInteger.valueOf(2).pow(100).toString(16));
10000000000000000000000000

You can also call methods in other programming languages that GraalVM supports:

$ node --jvm --polyglot
> console.log(Polyglot.eval('R', 'runif(100)')[0]);
0.8198353068437427

See the Polyglot Reference and the Embedding documentation for more information about interoperability with other programming languages.

GraalVM JavaScript Compatibility

GraalVM is ECMAScript 2017 compliant and fully compatible with a diverse range of active Node.js (npm) modules. More than 45,000 npm packages are regularly tested and compatible with GraalVM, including modules like express, react, async, request, browserify, grunt, mocha, and underscore. This release of GraalVM is based on Node.js version 8.9.4.

Is GraalVM compatible with the JavaScript language?

What version of ECMAScript do we support?

GraalVM is compatible to the ECMAScript 2017 specification. Most features of ECMAScript 2018 and some proposed features and extensions are available as well, but might not be fully implemented or compliant, yet.

How do we know it?

GraalVM is tested against the official test suite of ECMAScript, test262. Even though this test set includes some draft features, GraalVM compliance is around 95% and rising.

In our internal CI system, we test against test262, tests published by Nashorn and V8, Node unit tests, as well as GraalVM’s own unit tests.

From the graaljs code repository, you can execute the whole test262 test suite:

mx test262 gate

This will execute test262 in a mode to guarantee that local changes don’t regress compatibility, i.e., results in an error if any tests expected to pass actually fail. Individual tests can be executed with

mx test262 single=built-ins/Array/length.js

Is GraalVM compatible with the original node implementation?

Node.js based on GraalVM is largely compatible with the original Node.js (based on the V8 engine). This leads to a high number of npm-based modules being compatible with GraalVM (out of the 45k modules we test, 95% of them pass all tests). Several sources of differences have to be considered.

  • Setup GraalVM mostly mimicks the original setup of Node, including the node executable, npm, and similar. However, not all command-line options are supported (or behave exactly identically), you need to (re-)compile native modules against our v8.h file, etc.

  • Internals GraalVM is implemented on top of a JVM, and thus has a different internal architecture. This implies that some internal mechanisms behave differently and cannot exactly replicate V8 behavior. This will hardly ever affect user code, but might affect modules implemented natively, depending on V8 internals.

  • Performance Due to GraalVM being implemented on top of a JVM, performance characteristics vary from the original native implementation. While GraalVM’s peak performance can match V8 on many benchmarks, it will typically take longer to reach the peak (known as warmup). Be sure to give the Graal compiler some extra time when measuring (peak) performance.

How do we determine GraalVM’s JavaScript compatibility?

GraalVM is compatible to ECMAScript 2017, guaranteeing compatibility on the language level. In addition, GraalVM uses the following approaches to check and retain compatibility to Node.js code:

  • node-compat-table: GraalVM is compared against other engines using the node-compat-table module, highlighting incompatibilities that might break Node.js code.
  • automated mass-testing of modules using mocha: In order to test a large set of modules, GraalVM is tested against 50k modules that use the mocha test framework. Using mocha allows automating the process of executing the test and comprehending the test result.
  • manual testing of popular modules: A select list of npm modules is tested in a manual test setup. These highly-relevant modules are tested in a more sophisticated manner.

If you want your module to be tested by GraalVM in the future, ensure the module provides some mocha tests (and send us an email so we can ensure it’s on the list of tested modules).

How can one verify GraalVM works on their application?

If your module ships with tests, execute them with GraalVM. Of course, this will only test your app, but not its dependencies. You can use the compatibility checker to find whether the module you’re interested in is tested on GraalVM, whether the tests pass successfully and so on. Additionally, you can upload your package-lock.json or package.json file into that utility and it’ll analyze all your dependencies at once.

GraalVM JavaScript Options

On the command line, --js.<property>=<value> sets options that tune language features and extensions. The following options are currently supported:

  • --js.annex-b: enables ECMAScript Annex B web compatibility features. Boolean value, default is true.
  • --js.array-sort-inherited: defines whether Array.protoype.sort should sort inherited keys (implementation-defined behavior). Boolean value, default is true.
  • --js.atomics: enables ES2017 Atomics. Boolean value, default is true.
  • --js.ecmascript-version: emulates a specific ECMAScript version. Integer value (5-9), default is the latest version.
  • --js.intl-402: enables ECMAScript Internationalization API. Boolean value, default is false.
  • --js.regexp-static-result: provides static RegExp properties containing results of the last successful match, e.g.: RegExp.$1 (legacy). Boolean value, default is true.
  • --js.shared-array-buffer: enables ES2017 SharedArrayBuffer. Boolean value, default is false.
  • --js.strict: enables strict mode for all scripts. Boolean value, default is false.
  • --js.timezone: sets the local time zone. String value, default is the system default.
  • --js.v8-compatibility-mode: provides better compatibility with Google’s V8 engine. Boolean value, default is false. Use --help:languages to see the full list of available options.

See the Polyglot Reference for information on how to set options programmatically when embedding.

GraalVM Options

--jvm executes the application on the JVM instead of the native image.

--jvm.<option> passes JVM options to GraalVM (e.g. --jvm.Xmx<size>). List available JVM options with --jvm.help. System properties can be set as follows: --jvm.D<name>=<value>. For example, --jvm.Dgraal.TraceTruffleCompilation=true will print finished compilations.

Likewise, --native.<option> passes VM options and system properties to the native image. List available VM options with --native.help.

--compiler.<property>=<value> passes settings to the compiler. For example, --compiler.CompilationThreshold=<Integer> sets the minimum number of invocations or loop iterations before a function is compiled.

Polyglot Options

--polyglot enables you to interoperate with other programming languages.

--<languageID>.<property>=<value> passes options to guest languages through the Graal Polyglot SDK. Use --help:languages to find out which options are available.

GraalVM JavaScript Operations Manual

What’s the difference between running GraalVM’s JavaScript from the native image vs. on the JVM?

In essence, the JavaScript engine of GraalVM is a plain Java application. Running it on any JVM (JDK 8 or higher) is possible; for best performance, it should be the GraalVM or a compatible JVMCI-enabled JDK using the Graal compiler. This mode gives the JavaScript engine full access to Java at runtime, but also requires the JVM to first (just-in-time) compile the JavaScript engine when executed, just like any other Java application.

Running from a native image means that the JavaScript engine, including all its dependencies from, e.g., the JDK, are pre-compiled into a native binary. This will tremendously speed up the startup of any JavaScript application, as GraalVM can immediately start to compile JavaScript code, without itself requiring to be compiled first. This mode, however, will only give GraalVM access to Java classes known at the time of image creation. Most significantly, this means that the JavaScript-to-Java interoperability features are not available in this mode, as they would require dynamic class loading and execution of arbitrary Java code at runtime.

How to achieve the best peak performance?

Optimizing JVM-based applications is a science in itself. Here are a few tips and tricks you can follow to analyse and improve peak performance:

  • When measuring, ensure you have given Graal enough time to compile all hot methods before starting to measure peak performance. A useful command line option for that is --jvm.Dgraal.TraceTruffleCompilation=true - this outputs a message whenever a (JavaScript) method is compiled. As long as this still prints frequently, measurement should not yet start.
  • Compare the performance between the native image and the JVM mode if possible. Depending on the characteristics of your application, one or the other might show better peak performance.
  • The Polyglot API comes with several tools and options to inspect the performance of your application:
    • --cpusampler and --cputracer will print a list of the hottest methods when the application is terminated. Use that list to figure out where most time is spent in your application. More details about the command line options for the polyglot commands can be found from the polyglot documentation.
    • --memtracer can help you understand the memory allocations of your application.