docs.sheetjs.com/docz/docs/03-demos/12-engines/index.md

482 lines
14 KiB
Markdown
Raw Normal View History

2022-08-08 06:59:57 +00:00
---
title: JavaScript Engines
2023-02-28 11:40:44 +00:00
pagination_prev: demos/bigdata/index
pagination_next: solutions/input
2022-08-08 06:59:57 +00:00
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
2023-02-13 04:07:25 +00:00
Browser vendors and other organizations have built "JavaScript engines". They
are independent software libraries that are capable of running JS scripts.
2022-08-08 06:59:57 +00:00
The most popular JavaScript engine is V8. Designed for embedding in software,
2022-08-25 08:22:28 +00:00
it powers Chrome, NodeJS, UXP, Deno and many other platforms.
2022-08-08 06:59:57 +00:00
2022-08-25 08:22:28 +00:00
There are many other JS engines with different design goals. Some are designed
2022-08-08 06:59:57 +00:00
for low-power or low-memory environments. Others aim for interoperability with
2022-08-25 08:22:28 +00:00
specific programming languages or environments. Typically they support ES3 and
are capable of running SheetJS code.
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
This demo showcases a number of JS engines and language bindings.
2022-08-08 06:59:57 +00:00
## 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**
2022-08-25 08:22:28 +00:00
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.
2022-08-08 06:59:57 +00:00
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`.
2023-02-13 04:07:25 +00:00
## Engines
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
This list is sorted in alphabetical order.
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
### Duktape
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
Duktape is an embeddable JS engine written in C. It has been ported to a number
of exotic architectures and operating systems.
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
This demo has been moved [to a dedicated page](/docs/demos/engines/duktape).
2023-02-13 09:20:49 +00:00
The demo includes examples in C and Perl.
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
### Goja
2022-08-08 06:59:57 +00:00
2023-02-14 06:54:02 +00:00
Goja is a pure Go implementation of ECMAScript 5.
2022-08-08 06:59:57 +00:00
2023-02-14 06:54:02 +00:00
This demo has been moved [to a dedicated page](/docs/demos/engines/goja).
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
### Hermes
2022-08-08 06:59:57 +00:00
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.
:::
<details><summary><b>Complete Example</b> (click to show)</summary>
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:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
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"
2022-10-20 18:47:20 +00:00
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
2022-08-08 06:59:57 +00:00
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
2022-08-25 08:22:28 +00:00
ready, it will read the bundled test data and print the contents as CSV.
2022-08-08 06:59:57 +00:00
5) Run the script using the Hermes standalone binary:
2022-08-18 08:41:34 +00:00
```bash
2022-08-08 06:59:57 +00:00
hermes xlsx.hermes.js
```
</details>
2023-02-13 04:07:25 +00:00
### JavaScriptCore
2022-08-08 06:59:57 +00:00
2022-08-23 03:20:02 +00:00
iOS and MacOS ship with the JavaScriptCore framework for running JS code from
2023-02-13 04:07:25 +00:00
Swift and Objective-C.
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
This demo has been moved [to a dedicated page](/docs/demos/engines/jsc).
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
### JerryScript
2022-08-18 08:41:34 +00:00
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 <https://sheetjs.com/pres.xlsx>
- 65536 (64M) for <https://sheetjs.com/pres.numbers>
This works on a Raspberry Pi.
:::
<details><summary><b>Complete Example</b> (click to show)</summary>
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:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.xlsx">pres.xlsx</a></li>
</ul>
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"
2022-10-20 18:47:20 +00:00
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
2022-08-18 08:41:34 +00:00
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
2022-08-25 08:22:28 +00:00
ready, it will read the bundled test data and print the contents as CSV.
2022-08-18 08:41:34 +00:00
5) Run the script using the `jerry` standalone binary:
```bash
build/bin/jerry xlsx.jerry.js; echo $?
```
</details>
2022-08-08 06:59:57 +00:00
2023-02-13 04:07:25 +00:00
### QuickJS
2022-08-08 06:59:57 +00:00
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.
<details><summary><b>Complete Example</b> (click to show)</summary>
0) Ensure `quickjs` command line utility is installed
1) Download the standalone script, the shim and the test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
2) Save the following script to `SheetJSQuick.js`:
2022-11-13 20:45:13 +00:00
```js title="SheetJSQuick.js"
2022-10-20 18:47:20 +00:00
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
2022-08-08 06:59:57 +00:00
/* 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 */
2022-11-13 20:45:13 +00:00
var wb = XLSX.read(ab);
2022-08-08 06:59:57 +00:00
2022-11-13 20:45:13 +00:00
/* write XLSX */
var out = XLSX.write(wb, {bookType: "xlsx", type: "array"});
2022-08-08 06:59:57 +00:00
/* 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`.
</details>
2023-02-13 04:07:25 +00:00
### Rhino
2022-08-08 06:59:57 +00:00
2023-02-15 01:00:49 +00:00
Rhino is an ES3+ engine in Java.
2022-08-08 06:59:57 +00:00
2023-02-15 01:00:49 +00:00
This demo has been moved [to a dedicated page](/docs/demos/engines/rhino).
2022-08-08 06:59:57 +00:00
## 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.
:::
<details><summary><b>Complete Example</b> (click to show)</summary>
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:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
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"
2022-10-20 18:47:20 +00:00
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
2022-08-08 06:59:57 +00:00
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
2022-08-25 08:22:28 +00:00
ready, it will read the bundled test data and print the contents as CSV.
2022-08-08 06:59:57 +00:00
5) Run the script using the ChakraCore standalone binary:
```
./ch xlsx.chakra.js
```
</details>
### 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);
2022-11-13 20:45:13 +00:00
load('shim.min.js');
2022-08-08 06:59:57 +00:00
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"`.
<details><summary><b>Complete Example</b> (click to show)</summary>
0) Ensure `jjs` is available on system path
1) Download the standalone script, the shim and the test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
2) Save the following script to `SheetJSNashorn.js`:
```js title="SheetJSNashorn.js"
2022-10-20 18:47:20 +00:00
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
2022-08-08 06:59:57 +00:00
/* load module */
var global = (function(){ return this; }).call(null);
2022-11-13 20:45:13 +00:00
load('shim.min.js');
2022-08-08 06:59:57 +00:00
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 */
2022-11-13 20:45:13 +00:00
var wb = XLSX.read(u8a);
2022-08-08 06:59:57 +00:00
/* 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.
</details>