docs.sheetjs.com/docz/docs/03-demos/31-engines/index.md
2023-02-14 20:03:19 -05:00

14 KiB

title pagination_prev pagination_next
JavaScript Engines demos/cli 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:

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():

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. The demo includes examples in C and Perl.

Goja

Goja is a pure Go implementation of ECMAScript 5.

This demo has been moved to a dedicated page.

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.

  1. Install the hermes command line tool

  2. Download the standalone script, shim, and test file:

  1. Bundle the test file and create payload.js:
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
  1. Create support scripts:
  • global.js creates a global variable and defines a fake console:
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:
/* 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]]));
  1. Create the amalgamation xlsx.hermes.js:
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.

  1. Run the script using the Hermes standalone binary:
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.

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:

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.

  1. Build the library and command line tool with required options:
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:
  1. Bundle the test file and create payload.js:
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.xlsx').toString('base64') + '\";')"
  1. Create support scripts:
  • global.js creates a global variable and defines a fake console:
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:
/* 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]]));
  1. Create the amalgamation xlsx.jerry.js:
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.

  1. Run the script using the jerry standalone binary:
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)
  1. Ensure quickjs command line utility is installed

  2. Download the standalone script, the shim and the test file:

  1. Save the following script to 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();
  1. Test the program:
quickjs SheetJSQuick.js

If successful, the script will generate SheetJSQuick.xlsx.

Rhino

Rhino is an ES3+ engine in Java.

This demo has been moved to a dedicated page.

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.

  1. Download and extract the ChakraCore release ZIP. Copy the binary (bin/ch) to your project folder.

  2. Download the standalone script, shim, and test file:

  1. Bundle the test file and create payload.js:
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
  1. Create support scripts:
  • global.js creates a global variable:
var global = (function(){ return this; }).call(null);
  • chakra.js will call XLSX.read and XLSX.utils.sheet_to_csv:
/* 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]]));
  1. Create the amalgamation xlsx.chakra.js:
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.

  1. 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.

:::

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:

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)
  1. Ensure jjs is available on system path

  2. Download the standalone script, the shim and the test file:

  1. Save the following script to 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');
  1. Test the script:
jjs SheetJSNashorn.js

It will print out the first worksheet contents.