JEP 174: Nashorn JavaScript Engine

OwnerJim Laskey
TypeFeature
ScopeJDK
StatusClosed / Delivered
Release8
Componentcore-libs / jdk.nashorn
Discussionnashorn dash dev at openjdk dot java dot net
EffortXL
DurationXL
Reviewed bySundararajan Athijegannathan
Endorsed byBrian Goetz
Created2012/11/21 20:00
Updated2015/02/13 19:38
Issue8046164

Summary

Design and implement a new lightweight, high-performance implementation of JavaScript, and integrate it into the JDK. The new engine will be made available to Java applications via the existing javax.script API, and also more generally via a new command-line tool.

Goals

Non-Goals

Motivation

The performance of Rhino has fallen far behind that of other JavaScript engines. In order to improve performance, at this point Rhino would have to be rewritten to replace its interpreter with a code generator designed to fully utilize the JVM. Rather than undertake a major rewrite of the very old Rhino code, we have chosen instead to start from scratch.

It is also in the interest of the Java community to promote the JVM as a viable platform for languages other than Java. This advances the technology and attracts new developers.

Description

The Nashorn engine evaluates JavaScript source code in five stages: Lexer -> parser -> code generation -> loading -> runtime.

The lexer takes an array of Unicode characters (source) and translates it into a stream of lexical units or tokens; e.g., numbers, strings, identifiers, or special characters.

Nashorn uses a recursive descent parser based on the ECMAScript 262 language specification. The parser takes the token stream produced by the lexer and collects tokens that match the language syntax. As syntactic units are recognized, the parser constructs an intermediate representation (AST/IR) of the JavaScript code.

The code generator takes the AST/IR and generates JVM byte code. When executed, this byte code implements the semantics of the original JavaScript source code.

Code generation is done in two steps. The first step lowers the AST/IR to something closer to the JVM instruction set. This conversion includes transforming controls, reducing expressions to primitive operations, and simplifying call expressions. This step is also responsible for defining symbols used to manage data usage, space, and types.

Step two translates the AST/IR into byte code via the ASM library. ASM emits the actual byte codes to form a script class. This class is cached in memory for later use by the script loader.

Once the script class has been generated, it needs to be installed into the JVM. This is done using defineClass via a secure custom class loader. This loader is also used to synthesize special Nashorn object classes when encountered in generated code. Once the script class is loaded, its runMethod is invoked.

Several libraries are needed to support the executing code.

The main runtime library includes methods that directly support executing code, e.g., type-conversion routines needed for toString and toNumber, and allocation routines like allocateArray.

The linker library includes methods to assist the binding of invokedynamic calls using the java.lang.invoke API (JSR 292). The binding process is quite complex, searching for the correct method, defining guards to ensure that call sites continues to be the correct, and relinking if something changes to require a different correct method. The linker also manages a history of calls, so that lookup is faster on successive relinks.

The JavaScript objects library includes support for all the standard JavaScript objects, e.g., Object, Function, Number, String, Date, Array, RegExp, and so forth. It also includes functions for string manipulation, complex math, applying functions, et cetera.

Nashorn uses invokedynamic to implement all of its invocations. If an invocation has a Java object receiver, Nashorn attempts to bind the call to an appropriate Java method instead of a JavaScript function. Nashorn has full discretion about how it resolves methods. As an example, if it can't find a field in the receiver, it looks for an equivalent Java Bean method. The result is completely transparent for calls from JavaScript to Java.

Java developers can use the javax.script (JSR 223) API to evaluate and call back into JavaScript code.

Alternatives

Rhino has a long history, and source code to match. Most of the Rhino code supports an interpreted execution model. To gain significant performance, the execution model needs to take advantage of the Hotspot interpreter/compiler.

Other JavaScript engines, such as V8 and Nitro, provide good performance but would require a second VM (additional code and memory) and a bridging API to communicate with Java.

Testing

JCK

Existing javax.script JCK test should be sufficient. Nashorn is just another scripting engine accessible via the javax.script API. Nashorn itself does not expose its own new Java standard API, so no additional JCK tests are needed.

Functional/Unit tests

Existing tests include:

These cover most of the functional areas, but these tests have to be adapted into Nashorn's TestNG-based test suite. Not all of these tests may be applicable since Nashorn is ECMAScript-262 Edition 5.1 compliant with few extensions. In particular, the mozilla_js_tests cover many Mozilla-specific features which Nashorn will not support. So, some sub-setting and adaptation work is required to use these tests within Nashorn's test framework.

Existing unit tests for the javax.script API and the jrunscript command-line tool can be used against Nashorn as well.

Additional unit/regression tests in the Nashorn repo

These are tests developed by Nashorn developers in the course of implementing features and fixing bugs.

Performance tests

There has been some adaptation work to run these tests under the Nashorn test framework.

Note: These performance tests do not include browser API access (DOM/CSS, et al.) Performance test suites such as Dromaeo do provide such tests, but they are beyond the current scope of Nashorn.

Security tests

Nashorn compiles JavaScript source code and generates Java classes. These classes are loaded by a special class loader. Nashorn allows Java calls to be made from JavaScript. Care must be taken to ensure that such generated classes follow the Java security model. Tests are needed to ensure that, under a security manager, Nashorn is secure as well. No additional permissions that are not available to compiled Java class files should be available to script classes generated by Nashorn.

Impact