---
title: JavaScript Engines
pagination_prev: demos/cli
pagination_next: demos/clipboard
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Browser vendors and other organizations have built "JavaScript engines". They
are independent software libraries that are capable of running JS scripts.
The most popular JavaScript engine is V8. Designed for embedding in software,
it powers Chrome, NodeJS, UXP, Deno and many other platforms.
There are many other JS engines with different design goals. Some are designed
for low-power or low-memory environments. Others aim for interoperability with
specific programming languages or environments. Typically they support ES3 and
are capable of running SheetJS code.
This demo showcases a number of JS engines and language bindings.
## General Caveats
Common browser and NodeJS APIs are often missing from light-weight JS engines.
**Global**
Some engines do not provide `globalThis` or `global` or `window`. A `global`
variable can be exposed in one line that should be run in the JS engine:
```js
var global = (function(){ return this; }).call(null);
```
**Console**
Some engines do not provide a `console` object. `console.log` can be shimmed
using the engine functionality. For example, `hermes` provides `print()`:
```js
var console = { log: function(x) { print(x); } };
```
**Binary Data**
Some engines do not provide easy ways to exchange binary data. For example, it
is common to pass null-terminated arrays, which would truncate XLSX, XLS, and
other exports. APIs that accept pointers without length should be avoided.
Base64 strings are safe for passing between JS and native code, but they should
only be used when there is no safe way to pass `ArrayBuffer` or `Uint8Array`.
## Engines
This list is sorted in alphabetical order.
### Duktape
Duktape is an embeddable JS engine written in C. It has been ported to a number
of exotic architectures and operating systems.
This demo has been moved [to a dedicated page](/docs/demos/engines/duktape).
The demo includes examples in C and Perl.
### Goja
Goja is a pure Go implementation of ECMAScript 5. It supports the standalone
scripts out of the box.
**Reading data**
Files can be read into `[]byte`:
```go
/* read file */
data, _ := ioutil.ReadFile("sheetjs.xlsx")
```
`[]byte` should be converted to an `ArrayBuffer` from Go:
```go
/* load into engine */
vm.Set("buf", vm.ToValue(vm.NewArrayBuffer(data)))
/* parse */
wb, _ = vm.RunString("wb = XLSX.read(buf, {type:'buffer'});")
```
**Writing data**
`"base64"` strings can be decoded in Go:
```go
/* write to Base64 string */
b64str, _ := vm.RunString("XLSX.write(wb, {type:'base64', bookType:'xlsx'})")
/* pull data back into Go and write to file */
buf, _ := base64.StdEncoding.DecodeString(b64str.String())
_ = ioutil.WriteFile("sheetjs.xlsx", buf, 0644)
```
Complete Example (click to show)
0) Install Go
1) Create a `go.mod` file and install dependencies:
```bash
go mod init SheetGoja
go get github.com/dop251/goja
```
2) Download the standalone script and the shim:
3) Save the following code to `SheetGoja.go`:
```go title="SheetGoja.go"
package main
import (
b64 "encoding/base64"
"fmt"
"os"
"io/ioutil"
"github.com/dop251/goja"
)
func safe_run_file(vm *goja.Runtime, file string) {
data, err := ioutil.ReadFile(file)
if err != nil { panic(err) }
src := string(data)
_, err = vm.RunString(src)
if err != nil { panic(err) }
}
func eval_string(vm *goja.Runtime, cmd string) goja.Value {
v, err := vm.RunString(cmd)
if err != nil { panic(err) }
return v
}
func write_type(vm *goja.Runtime, t string) {
b64str := eval_string(vm, "XLSX.write(wb, {type:'base64', bookType:'" + t + "'})")
buf, err := b64.StdEncoding.DecodeString(b64str.String());
if err != nil { panic(err) }
err = ioutil.WriteFile("sheetjsg." + t, buf, 0644)
if err != nil { panic(err) }
}
func main() {
vm := goja.New()
/* initialize */
eval_string(vm, "if(typeof global == 'undefined') global = (function(){ return this; }).call(null);")
/* load library */
safe_run_file(vm, "shim.min.js")
safe_run_file(vm, "xlsx.full.min.js")
/* get version string */
v := eval_string(vm, "XLSX.version")
fmt.Printf("SheetJS library version %s\n", v)
/* read file */
data, err := ioutil.ReadFile(os.Args[1])
if err != nil { panic(err) }
vm.Set("buf", vm.ToValue(vm.NewArrayBuffer(data)))
fmt.Printf("Loaded file %s\n", os.Args[1])
/* parse workbook */
eval_string(vm, "wb = XLSX.read(buf, {type:'buffer'});")
fmt.Printf("Parsed %s\n", os.Args[1])
eval_string(vm, "ws = wb.Sheets[wb.SheetNames[0]]")
fmt.Printf("Grabbed %s\n", os.Args[1])
/* print CSV */
csv := eval_string(vm, "XLSX.utils.sheet_to_csv(ws)")
fmt.Printf("%s\n", csv)
/* write file */
write_type(vm, "csv")
}
```
4) Build `SheetGoja`:
```bash
go build SheetGoja.go
```
For testing, download and run
```bash
./SheetGoja pres.numbers
```
This will print the contents as a CSV to screen AND write to `sheetjsg.csv`
### Hermes
Hermes is an embeddable JS engine for React Native. The library and binary
distributions include a command-line tool `hermes` for running JS scripts.
The simplest way to interact with the engine is to pass Base64 strings. The make
target builds a very simple payload with the data.
:::note
The official release includes the `hermes` standalone tool. While applications
should link against the official libraries, the standalone tool is useful for
verifying functionality.
:::
Complete Example (click to show)
Due to limitations of the standalone binary, this demo will encode a test file
as a Base64 string and directly add it to an amalgamated script.
0) Install the `hermes` command line tool
1) Download the standalone script, shim, and test file:
2) Bundle the test file and create `payload.js`:
```bash
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
```
3) Create support scripts:
- `global.js` creates a `global` variable and defines a fake `console`:
```js title="global.js"
var global = (function(){ return this; }).call(null);
var console = { log: function(x) { print(x); } };
```
- `hermes.js` will call `XLSX.read` and `XLSX.utils.sheet_to_csv`:
```js title="hermes.js"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
var wb = XLSX.read(payload, {type:'base64'});
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
```
4) Create the amalgamation `xlsx.hermes.js`:
```bash
cat global.js xlsx.full.min.js payload.js hermes.js > xlsx.hermes.js
```
The final script defines `global` before loading the standalone library. Once
ready, it will read the bundled test data and print the contents as CSV.
5) Run the script using the Hermes standalone binary:
```bash
hermes xlsx.hermes.js
```
### JavaScriptCore
iOS and MacOS ship with the JavaScriptCore framework for running JS code from
Swift and Objective-C.
This demo has been moved [to a dedicated page](/docs/demos/engines/jsc).
### JerryScript
JerryScript is a lightweight JavaScript engine designed for use in low-memory
environments like microcontrollers. As part of the build suite, the project
generates a C library and a standalone CLI tool.
The simplest way to interact with the engine is to pass Base64 strings.
:::note
While applications should link against the official libraries, the standalone tool
is useful for verifying functionality.
:::
:::caution
This demo requires a much larger heap size than is normally used in JerryScript
deployments! In local testing, the following sizes were needed:
- 8192 (8M) for
- 65536 (64M) for
This works on a Raspberry Pi.
:::
Complete Example (click to show)
Due to limitations of the standalone binary, this demo will encode a test file
as a Base64 string and directly add it to an amalgamated script.
0) Build the library and command line tool with required options:
```bash
git clone --depth=1 https://github.com/jerryscript-project/jerryscript.git
cd jerryscript
python tools/build.py --error-messages=ON --logging=ON --mem-heap=8192 --cpointer-32bit=ON
```
1) Download the standalone script, shim, and test file:
2) Bundle the test file and create `payload.js`:
```bash
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.xlsx').toString('base64') + '\";')"
```
3) Create support scripts:
- `global.js` creates a `global` variable and defines a fake `console`:
```js title="global.js"
var global = (function(){ return this; }).call(null);
var console = { log: function(x) { print(x); } };
```
- `jerry.js` will call `XLSX.read` and `XLSX.utils.sheet_to_csv`:
```js title="jerry.js"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
var wb = XLSX.read(payload, {type:'base64'});
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
```
4) Create the amalgamation `xlsx.jerry.js`:
```bash
cat global.js xlsx.full.min.js payload.js jerry.js > xlsx.jerry.js
```
The final script defines `global` before loading the standalone library. Once
ready, it will read the bundled test data and print the contents as CSV.
5) Run the script using the `jerry` standalone binary:
```bash
build/bin/jerry xlsx.jerry.js; echo $?
```
### QuickJS
QuickJS is an embeddable JS engine written in C. It provides a separate set of
functions for interacting with the filesystem and the global object. It can run
the standalone browser scripts.
Complete Example (click to show)
0) Ensure `quickjs` command line utility is installed
1) Download the standalone script, the shim and the test file:
2) Save the following script to `SheetJSQuick.js`:
```js title="SheetJSQuick.js"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
/* load XLSX */
import * as std from "std";
globalThis.global = globalThis;
std.loadScript("xlsx.full.min.js");
/* read contents of file */
var rh = std.open("pres.numbers", "rb");
rh.seek(0, std.SEEK_END);
var sz = rh.tell();
var ab = new ArrayBuffer(sz);
rh.seek();
rh.read(ab, 0, sz);
rh.close();
/* parse file */
var wb = XLSX.read(ab);
/* write XLSX */
var out = XLSX.write(wb, {bookType: "xlsx", type: "array"});
/* write contents to file */
var wh = std.open("SheetJSQuick.xlsx", "wb");
wh.write(out, 0, out.byteLength);
wh.close();
```
3) Test the program:
```bash
quickjs SheetJSQuick.js
```
If successful, the script will generate `SheetJSQuick.xlsx`.
### Rhino
Rhino is an ES3+ engine in Java. The `SheetJSRhino` class and `com.sheetjs`
package show a complete JAR deployment, including the full XLSX source.
Due to code generation errors, optimization must be turned off:
```java
Context context = Context.enter();
context.setOptimizationLevel(-1);
```
Complete Example (click to show)
0) Download the appropriate Rhino build and rename to `rhino.jar`
1) Download [`SheetJSRhino.zip`](pathname:///rhino/SheetJSRhino.zip) and unzip
2) Save the following code to `SheetJSRhino.java`:
```java title="SheetJSRhino.java"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
/* vim: set ts=2: */
import com.sheetjs.SheetJS;
import com.sheetjs.SheetJSFile;
import com.sheetjs.SheetJSSheet;
public class SheetJSRhino {
public static void main(String args[]) throws Exception {
try {
SheetJS sjs = new SheetJS();
/* open file */
SheetJSFile xl = sjs.read_file(args[0]);
/* get sheetnames */
String[] sheetnames = xl.get_sheet_names();
System.err.println(sheetnames[0]);
/* convert to CSV */
SheetJSSheet sheet = xl.get_sheet(0);
String csv = sheet.get_csv();
System.out.println(csv);
} catch(Exception e) {
throw e;
} finally {
SheetJS.close();
}
}
}
```
3) Assemble `SheetJS.jar` from the demo code:
```bash
javac -cp .:rhino.jar SheetJSRhino.java
jar -cf SheetJS.jar SheetJSRhino.class com/sheetjs/*.class
```
4) Download and test:
```bash
java -cp .:SheetJS.jar:rhino.jar SheetJSRhino pres.xlsx
```
## Legacy Engines
:::warning
These examples were written when the engines were maintained. New projects
should not use these engines. The demos are included for legacy deployments.
:::
### ChakraCore
:::caution
ChakraCore was an open source JavaScript engine released by Microsoft. It was a
fork of the Chakra engine that powered Internet Explorer. When Microsoft Edge
switched to become a fork of Chromium, Microsoft stopped providing support.
:::
ChakraCore is an embeddable JS engine written in C++. The library and binary
distributions include a command-line tool `chakra` for running JS scripts.
The simplest way to interact with the engine is to pass Base64 strings. The make
target builds a very simple payload with the data.
:::note
The official release includes the `ch` standalone binary. While applications
should link against the official libraries, the standalone tool is useful for
verifying functionality.
:::
Complete Example (click to show)
Due to limitations of the standalone binary, this demo will encode a test file
as a Base64 string and directly add it to an amalgamated script.
0) Download and extract the ChakraCore release ZIP. Copy the binary (`bin/ch`)
to your project folder.
1) Download the standalone script, shim, and test file:
2) Bundle the test file and create `payload.js`:
```bash
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
```
3) Create support scripts:
- `global.js` creates a `global` variable:
```js title="global.js"
var global = (function(){ return this; }).call(null);
```
- `chakra.js` will call `XLSX.read` and `XLSX.utils.sheet_to_csv`:
```js title="chakra.js"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
var wb = XLSX.read(payload, {type:'base64'});
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
```
4) Create the amalgamation `xlsx.chakra.js`:
```bash
cat global.js xlsx.full.min.js payload.js chakra.js > xlsx.chakra.js
```
The final script defines `global` before loading the standalone library. Once
ready, it will read the bundled test data and print the contents as CSV.
5) Run the script using the ChakraCore standalone binary:
```
./ch xlsx.chakra.js
```
### Nashorn
:::caution
Nashorn shipped with Java 8. It was deprecated in Java 11 and was officially
removed in JDK 15. New Java applications should use [Rhino](#rhino).
:::
Nashorn ships with Java. It includes a command-line tool `jjs` for running JS
scripts. It is somewhat limited but does offer access to the full Java runtime.
The `load` function in `jjs` can load the minified source directly:
```js
var global = (function(){ return this; }).call(null);
load('shim.min.js');
load('xlsx.full.min.js');
```
The Java `nio` API provides the `Files.readAllBytes` method to read a file into
a byte array. To use in `XLSX.read`, the demo copies the bytes into a plain JS
array and calls `XLSX.read` with type `"array"`.
Complete Example (click to show)
0) Ensure `jjs` is available on system path
1) Download the standalone script, the shim and the test file:
2) Save the following script to `SheetJSNashorn.js`:
```js title="SheetJSNashorn.js"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
/* load module */
var global = (function(){ return this; }).call(null);
load('shim.min.js');
load('xlsx.full.min.js');
/* helper to convert byte array to plain JS array */
function b2a(b) {
var out = new Array(b.length);
for(var i = 0; i < out.length; i++) out[i] = (b[i] < 0 ? b[i] + 256 : b[i]);
return out;
}
function process_file(path) {
java.lang.System.out.println(path);
/* read file */
var path = java.nio.file.Paths.get(path);
var bytes = java.nio.file.Files.readAllBytes(path);
var u8a = b2a(bytes);
/* read data */
var wb = XLSX.read(u8a);
/* get first worksheet as an array of arrays */
var ws = wb.Sheets[wb.SheetNames[0]];
var js = XLSX.utils.sheet_to_json(ws, {header:1});
/* print out every line */
js.forEach(function(l) { java.lang.System.out.println(JSON.stringify(l)); });
}
process_file('pres.numbers');
```
3) Test the script:
```bash
jjs SheetJSNashorn.js
```
It will print out the first worksheet contents.