This commit is contained in:
SheetJS 2024-01-23 04:26:06 -05:00
parent 963168e7b4
commit 6e3b91f9e5
13 changed files with 661 additions and 153 deletions

View File

@ -38,11 +38,10 @@ Under the hood, `ArrayBuffer` objects represent raw binary data. "Typed arrays"
such as `Float64Array` and `Float32Array` are objects designed for efficient
interpretation and mutation of `ArrayBuffer` data.
:::note pass
`ArrayBuffer` are roughly analogous to heap-allocated memory. Typed arrays
behave like typed pointers.
`ArrayBuffer` objects are roughly analogous to heap-allocated memory. Typed
arrays behave like typed pointers.
**JavaScript**
@ -262,7 +261,7 @@ arrays of arrays.
A single typed array can be converted to a pure JS array with `Array.from`:
```js
const arr = Array.from(column);
const arr = Array.from(row);
```
An array of arrays can be created from the array:

View File

@ -386,7 +386,7 @@ https.get('https://sheetjs.com/pres.numbers', function(res) {
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
:::note Tested Environments
This demo was last tested on 2024 January 15 against NodeJS `20.11.0`
@ -433,7 +433,7 @@ async function parse_from_url(url) {
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
:::note Tested Environments
This demo was last tested on 2024 January 15 against NodeJS `20.11.0`
@ -512,7 +512,7 @@ request(url, {encoding: null}, function(err, res, data) {
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
:::note Tested Environments
This demo was last tested on 2024 January 15 against request `2.88.2`
@ -554,7 +554,7 @@ async function workbook_dl_axios(url) {
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
:::note Tested Environments
This demo was last tested on 2024 January 15 against Axios `1.6.5`

View File

@ -301,7 +301,7 @@ This demo was tested in the following environments:
| macOS 14.1.2 | `darwin-arm` | `v2.6.0` | 2023-12-01 |
| Windows 10 | `win10-x64` | `v2.6.0` | 2023-12-09 |
| Windows 11 | `win11-arm` | `v2.6.0` | 2023-12-01 |
| Linux (HoloOS) | `linux-x64` | `v2.6.0` | 2023-10-11 |
| Linux (HoloOS) | `linux-x64` | `v2.7.1` | 2024-01-22 |
| Linux (Debian) | `linux-arm` | `v2.6.0` | 2023-12-01 |
:::

View File

@ -261,7 +261,7 @@ This demo was tested in the following environments:
| macOS 14.0 | `darwin-arm` | `v1.5.2` | 2023-10-18 |
| Windows 10 | `win10-x64` | `v1.5.0` | 2023-10-01 |
| Windows 11 | `win11-arm` | `v1.5.7` | 2023-12-01 |
| Linux (HoloOS) | `linux-x64` | `v1.5.2` | 2023-10-11 |
| Linux (HoloOS) | `linux-x64` | `v1.5.9` | 2024-01-23 |
| Linux (Debian) | `linux-arm` | `v1.5.7` | 2023-12-01 |
:::

View File

@ -421,7 +421,7 @@ This demo was last tested in the following deployments:
| `win10-x64` | `1.37.1` | 2023-10-09 |
| `win11-x64` | `1.37.2` | 2023-10-14 |
| `win11-arm` | `1.38.4` | 2023-12-01 |
| `linux-x64` | `1.37.1` | 2023-10-11 |
| `linux-x64` | `1.39.4` | 2024-01-22 |
| `linux-arm` | `1.38.4` | 2023-12-01 |
:::

View File

@ -41,7 +41,7 @@ setting the environment variable on supported platforms.
:::note pass
Most of the integration functions are not documented. This explanation is based
on version `3.0.0-beta-2056`.
on version `3.0.0`.
:::
@ -108,7 +108,7 @@ byte[] buf = File.ReadAllBytes(filename);
Jint natively supports `Uint8Array` construction from the byte array:
```csharp
Jint.Native.JsValue u8 = engine.Realm.Intrinsics.Uint8Array.Construct(buf);
Jint.Native.JsValue u8 = engine.Intrinsics.Uint8Array.Construct(buf);
```
`Jint.Engine#SetValue` will assign the `Uint8Array` to a scope variable in JS:
@ -163,11 +163,11 @@ This demo was tested in the following deployments:
| Architecture | Jint Version | Date |
|:-------------|:------------------|:-----------|
| `darwin-x64` | `3.0.0-beta-2055` | 2023-11-14 |
| `darwin-x64` | `3.0.0` | 2024-01-22 |
| `darwin-arm` | `3.0.0-beta-2056` | 2023-12-01 |
| `win10-x64` | `3.0.0-beta-2053` | 2023-10-28 |
| `win11-arm` | `3.0.0-beta-2056` | 2023-12-01 |
| `linux-x64` | `3.0.0-beta-2052` | 2023-10-11 |
| `linux-x64` | `3.0.0` | 2024-01-22 |
| `linux-arm` | `3.0.0-beta-2056` | 2023-12-01 |
:::
@ -211,10 +211,18 @@ Click "OK" in each window (3 windows) and restart your computer.
<details><summary><b>Installation Notes</b> (click to show)</summary>
For macOS x64 and ARM64, install with Homebrew: `brew install dotnet@6`
For macOS x64 and ARM64, install the `dotnet-sdk` Cask with Homebrew:
```bash
brew install --cask dotnet-sdk
```
For Steam Deck Holo and other Arch Linux x64 distributions, the `dotnet-sdk` and
`dotnet-runtime` packages should be installed using `pacman`.
`dotnet-runtime` packages should be installed using `pacman`:
```bash
sudo pacman -Syu dotnet-sdk dotnet-runtime
```
<https://dotnet.microsoft.com/en-us/download/dotnet/6.0> is the official source
for Windows and ARM64 Linux versions.
@ -230,7 +238,7 @@ for Windows and ARM64 Linux versions.
```bash
mkdir SheetJSJint
cd SheetJSJint
dotnet new console --framework net6.0
dotnet new console
dotnet run
```
@ -238,7 +246,7 @@ dotnet run
```bash
dotnet nuget add source https://www.myget.org/F/jint/api/v3/index.json
dotnet add package Jint --version 3.0.0-beta-2056
dotnet add package Jint --version 3.0.0
```
To verify Jint is installed, replace `Program.cs` with the following:
@ -309,7 +317,7 @@ Console.WriteLine("SheetJS version {0}", engine.Evaluate("XLSX.version"));
/* Read and Parse File */
byte[] filedata = File.ReadAllBytes(args[0]);
Jint.Native.JsValue u8 = engine.Realm.Intrinsics.Uint8Array.Construct(filedata);
Jint.Native.JsValue u8 = engine.Intrinsics.Uint8Array.Construct(filedata);
engine.SetValue("buf", u8);
engine.Evaluate("var wb = XLSX.read(buf);");

View File

@ -27,7 +27,7 @@ command-line tool for reading data from files.
:::note pass
Many QuickJS functions are not documented. The explanation was verified against
the latest release (commit `daa35bc`).
the latest release (commit `9e561d5`).
:::
@ -266,10 +266,10 @@ This demo was tested in the following deployments:
| `darwin-arm` | `2788d71` | 2023-10-18 |
| `win10-x64` | `daa35bc` | 2023-12-09 |
| `win11-arm` | `03cc5ec` | 2023-12-01 |
| `linux-x64` | `03cc5ec` | 2023-12-07 |
| `linux-x64` | `9e561d5` | 2024-01-22 |
| `linux-arm` | `03cc5ec` | 2023-12-01 |
When the demo was tested, commit `daa35bc` corresponded to the latest release.
When the demo was tested, commit `9e561d5` corresponded to the latest release.
:::
@ -285,7 +285,7 @@ tests were run entirely within Windows Subsystem for Linux.
```bash
git clone https://github.com/bellard/quickjs
cd quickjs
git checkout daa35bc
git checkout 9e561d5
make
cd ..
```
@ -342,10 +342,9 @@ This demo was tested in the following environments:
| Git Commit | Date |
|:-----------|:-----------|
| `daa35bc` | 2023-12-09 |
| `2788d71` | 2023-12-09 |
| `9e561d5` | 2024-01-22 |
When the demo was tested, commit `daa35bc` corresponded to the latest release.
When the demo was tested, commit `9e561d5` corresponded to the latest release.
:::
@ -354,7 +353,7 @@ When the demo was tested, commit `daa35bc` corresponded to the latest release.
```bash
git clone https://github.com/bellard/quickjs
cd quickjs
git checkout daa35bc
git checkout 9e561d5
make
cd ..
cp quickjs/qjs .

View File

@ -16,8 +16,8 @@ ChakraCore is an embeddable JS engine written in C++.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Hermes and SheetJS to pull data from a spreadsheet and print CSV
rows. We'll explore how to load SheetJS in a ChakraCore context and process
This demo uses ChakraCore and SheetJS to pull data from a spreadsheet and print
CSV rows. We'll explore how to load SheetJS in a ChakraCore context and process
spreadsheets from a C++ program.
The ["Integration Example"](#integration-example) section includes a complete

View File

@ -0,0 +1,473 @@
---
title: C + JerryScript
pagination_prev: demos/bigdata/index
pagination_next: solutions/input
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[JerryScript](https://jerryscript.net/) is a lightweight JavaScript engine. It
is designed for microcontrollers and similar environments.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses JerryScript and SheetJS to pull data from a spreadsheet and print
CSV rows. We'll explore how to load SheetJS in a JerryScript realm and process
spreadsheets from C programs.
The ["Integration Example"](#integration-example) section includes a complete
command-line tool for reading data from files.
:::caution pass
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>
:::
:::note Tested Environments
This demo was tested in the following environments:
| Architecture | Commit | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `514fa67` | 2024-01-22 |
| `darwin-arm` | `ef4cb2b` | 2023-12-08 |
| `win11-x64` | `ef4cb2b` | 2023-12-08 |
| `win11-arm` | `ef4cb2b` | 2023-12-08 |
| `linux-x64` | `ef4cb2b` | 2023-12-08 |
| `linux-arm` | `ef4cb2b` | 2023-12-08 |
The Windows tests were run in WSL.
Debian and WSL require the `cmake`, `python3` and `python-is-python3` packages.
:::
## Integration Details
:::info pass
The official JerryScript documentation and examples are out of date. This
explanation was verified against the latest release (commit `514fa67`).
:::
### Initialize JerryScript
The global engine instance can be initialized with `jerry_init` and cleaned up
with `jerry_cleanup`:
```c
#include "jerryscript.h"
int main (int argc, char **argv) {
/* Initialize engine */
/* highlight-next-line */
jerry_init(JERRY_INIT_EMPTY);
// ... use engine methods ...
/* cleanup before exiting */
/* highlight-next-line */
jerry_cleanup();
return 0;
}
```
API methods use `jerry_value_t` values to represent JS values and miscellany.
Values representing errors can be distinguished using `jerry_value_is_error`.
`jerry_value_t` values can be freed with `jerry_value_free`.
### Evaluate Code
Evaluating code involves two steps:
- `jerry_parse` will parse the script
- `jerry_run` will run the parsed script object
:::note pass
The return value of `jerry_parse` is a `jerry_value_t` value that can be safely
freed after `jerry_run`.
:::
The following `eval_str` function parses and executes scripts. If parsing fails,
the function will return the parsing error. If parsing succeeds, the function
will return the result of executing the code.
```c
jerry_value_t eval_str(const char *code, size_t sz) {
/* try to parse code */
jerry_value_t parsed = jerry_parse(code, sz, NULL);
/* return the parse error if parsing failed */
if(jerry_value_is_error(parsed)) return parsed;
/* run the code */
jerry_value_t out = jerry_run(parsed);
/* free the parsed representation */
jerry_value_free(parsed);
/* return the result */
return out;
}
```
### Load SheetJS Scripts
[SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can
be parsed and run in JerryScript.
Scripts can be read from the filesystem using standard C functions:
```c
static char *read_file(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f) - 1;
fclose(f);
return buf;
}
```
The shim script must be evaluated before the main library. In both cases, after
reading the script file, the previous `eval_str` function can run the code:
```c
/* evaluate shim.min.js */
{
size_t sz; const jerry_char_t *script = (jerry_char_t *)read_file("shim.min.js", &sz);
jerry_value_t result = eval_str(script, sz);
if(jerry_value_is_error(result)) { // failed to parse / execute
fprintf(stderr, "Failed to evaluate shim.min.js"); return 1;
}
jerry_value_free(result);
}
/* evaluate xlsx.full.min.js */
{
size_t sz; const jerry_char_t *script = (jerry_char_t *)read_file("xlsx.full.min.js", &sz);
jerry_value_t result = eval_str(script, sz);
if(jerry_value_is_error(result)) { // failed to parse / execute
fprintf(stderr, "Failed to evaluate xlsx.full.min.js"); return 1;
}
jerry_value_free(result);
}
```
### Reading Files
Binary file data can be passed from C to JerryScript with `ArrayBuffer` objects.
#### Creating ArrayBuffers
`jerry_arraybuffer` will generate an `ArrayBuffer` object of specified length.
After creating the array, `jerry_arraybuffer_write` will copy data.
The following `load_file` function reads a file from the filesystem and loads
the data into an `ArrayBuffer`:
```c
static jerry_value_t load_file(const char *filename) {
/* read file */
size_t len; char *buf = read_file(filename, &len);
if(!buf) return 0;
/* create ArrayBuffer */
jerry_value_t out = jerry_arraybuffer(len);
/* copy file data into ArrayBuffer */
jerry_arraybuffer_write(out, 0, (const uint8_t*)buf, len);
return out;
}
```
The process may fail. The result should be tested with `jerry_value_is_error`:
```c
jerry_value_t ab = load_file("pres.xlsx");
if(!ab || jerry_value_is_error(ab)) { // failed to create ArrayBuffer
fprintf(stderr, "Failed to read pres.xlsx"); return 1;
}
```
#### Creating Global Variable
The `ArrayBuffer` object must be bound to a variable before it can be used.
:::note pass
The goal is to bind the `ArrayBuffer` to the `buf` property in global scope.
:::
1) Get the global `this` variable (using `jerry_current_realm`):
```c
/* get the global variable */
jerry_value_t this = jerry_current_realm();
if(jerry_value_is_error(this)) { // failed to get global object
fprintf(stderr, "Failed to get global object"); return 1;
}
```
2) Create a JerryScript string (`"buf"`) for the property:
```c
/* create a string "buf" for the property access */
jerry_value_t prop = jerry_string_sz("buf");
if(jerry_value_is_error(this)) { // failed to create "buf"
fprintf(stderr, "Failed to create string"); return 1;
}
```
3) Assign the property using `jerry_object_set`:
```c
/* set global["buf"] to the ArrayBuffer */
jerry_value_t set = jerry_object_set(this, prop, ab);
if(jerry_value_is_error(set)) { // failed to set property
fprintf(stderr, "Failed to assign ArrayBuffer"); return 1;
}
```
#### Parsing Data
:::note pass
The goal is to run the equivalent of the following JavaScript code:
```js
/* `buf` is the `ArrayBuffer` from the previous step */
var wb = XLSX.read(buf);
```
:::
The `ArrayBuffer` from the previous step is available in the `buf` variable.
That `ArrayBuffer` can be passed to the SheetJS `read` method[^1], which will
parse the raw data and return a SheetJS workbook object[^2].
`var wb = XLSX.read(buf)` can be stored in a byte array and evaluated directly:
```c
/* run `var wb = XLSX.read(buf)` */
{
const jerry_char_t code[] = "var wb = XLSX.read(buf);";
jerry_value_t result = eval_str(code, sizeof(code) - 1);
if(jerry_value_is_error(result)) {
fprintf(stderr, "Failed to parse file"); return 1;
}
jerry_value_free(result);
}
```
#### Generating CSV
:::note pass
The goal is to run the equivalent of the following JavaScript code:
```js
/* `wb` is the workbook from the previous step */
XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])
```
:::
A SheetJS workbook object can contain multiple sheet objects[^3]. The `Sheets`
property is an object whose keys are sheet names and whose values are sheet
objects. The `SheetNames` property is an array of worksheet names.
The first sheet name can be found at `wb.SheetNames[0]`. The first sheet object
can be found at `wb.Sheets[wb.SheetNames[0]]`.
The SheetJS `sheet_to_csv` utility function[^4] accepts a sheet object and
generates a JS string.
Combining everything, `XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])`
generates a CSV string based on the first worksheet in the workbook `wb`:
```c
const jerry_char_t code[] = "XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])";
jerry_value_t csv = eval_str(code, sizeof(code) - 1);
if(jerry_value_is_error(result)) { // CSV generation failed
fprintf(stderr, "Failed to generate csv"); return 1;
}
```
#### Pulling Strings
JerryScript exposes encoding-aware methods to pull JS strings into C. The
`JERRY_ENCODING_UTF8` encoding forces UTF8 interpretations.
The `jerry_string_size` function returns the number of bytes required to store
the string. After allocating memory, `jerry_string_to_buffer` will copy data.
The following `pull_str` function uses `malloc`:
```js
char *pull_str(jerry_value_t str, size_t *sz) {
/* determine string size in bytes */
jerry_size_t str_sz = jerry_string_size(str, JERRY_ENCODING_UTF8);
/* allocate memory */
jerry_char_t *buf = (jerry_char_t *)malloc(str_sz + 1);
/* copy from JS string to C byte array */
jerry_string_to_buffer(str, JERRY_ENCODING_UTF8, buf, str_sz + 1);
/* pass back size and return the pointer */
*sz = str_sz;
return (char *)buf;
}
```
This function can be used to pull the `csv` value from the previous section:
```c
size_t sz; char *buf = pull_str(result, &sz);
printf("%s\n", buf);
```
## Complete Example
The "Integration Example" covers a traditional integration in a C application,
while the "CLI Test" demonstrates other concepts using the `jerry` CLI tool.
### Integration Example
1) Create a project folder:
```bash
mkdir SheetJSJerry
cd SheetJSJerry
```
2) Clone the repository and build the library 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
cd ..
```
3) Download the SheetJS Standalone script, shim script and test file. Move all
three files to the `SheetJSJerry` directory:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.xlsx">pres.xlsx</a></li>
</ul>
<CodeBlock language="bash">{`\
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.xlsx`}
</CodeBlock>
4) Download [`sheetjs.jerry.c`](pathname:///jerryscript/sheetjs.jerry.c) into
the same folder:
```bash
curl -LO https://docs.sheetjs.com/jerryscript/sheetjs.jerry.c
```
5) Build the sample application:
```bash
gcc -o sheetjs.jerry -Ijerryscript/jerry-ext/include -Ijerryscript/jerry-math/include -Ijerryscript/jerry-core/include -ljerry-ext -ljerry-port -ljerry-core -Ljerryscript/build/lib sheetjs.jerry.c -Wno-pointer-sign
```
6) Run the test program:
```bash
./sheetjs.jerry pres.xlsx
```
If successful, the program will print contents of the first sheet as CSV rows.
### CLI Test
:::note pass
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 SheetJS Standalone script, shim script and test file. Move all
three files to the `jerryscript` cloned repo directory:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.xlsx">pres.xlsx</a></li>
</ul>
<CodeBlock language="bash">{`\
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.xlsx`}
</CodeBlock>
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"
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 $?
```
If successful, the contents of the test file will be displayed in CSV rows. The
status code `0` will be printed after the rows.
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book)
[^3]: See ["Sheet Objects"](/docs/csf/sheet)
[^4]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)

View File

@ -26,7 +26,7 @@ This demo showcases a number of JS engines and language bindings.
Common browser and NodeJS APIs are often missing from light-weight JS engines.
**Global**
#### 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:
@ -35,16 +35,17 @@ variable can be exposed in one line that should be run in the JS engine:
var global = (function(){ return this; }).call(null);
```
**Console**
#### Console
Some engines do not provide a `console` object. `console.log` can be shimmed
using the engine functionality. For example, `hermes`[^1] provides `print()`:
Some engines do not provide a `console` object but offer other ways to print to
standard output. For example, Hermes[^1] provides `print()`. A `console` object
should be created using the engine print function:
```js
var console = { log: function(x) { print(x); } };
```
**Binary Data**
#### 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
@ -53,7 +54,7 @@ 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`.
**Byte Conventions**
#### Byte Conventions
Java has no native concept of unsigned bytes. Values in a `byte[]` are limited
to the range `-128 .. 127`. They need to be fixed within the JS engine.
@ -116,123 +117,12 @@ 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.
environments including microcontrollers.
The simplest way to interact with the engine is to pass Base64 strings.
:::note Tested Environments
This demo was tested in the following environments:
| Architecture | Commit | Date |
|:-------------|:----------|:-----------|
| `darwin-x64` | `ef4cb2b` | 2023-12-08 |
| `darwin-arm` | `ef4cb2b` | 2023-12-08 |
| `win11-x64` | `ef4cb2b` | 2023-12-08 |
| `win11-arm` | `ef4cb2b` | 2023-12-08 |
| `linux-x64` | `ef4cb2b` | 2023-12-08 |
| `linux-arm` | `ef4cb2b` | 2023-12-08 |
The Windows tests were run in WSL.
Debian and WSL require the `cmake`, `python3` and `python-is-python3` packages.
:::
:::note pass
While applications should link against the official libraries, the standalone tool
is useful for verifying functionality.
:::
:::caution pass
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 SheetJS Standalone script, shim script and test file. Move all
three files to the `jerryscript` cloned repo directory:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.xlsx">pres.xlsx</a></li>
</ul>
<CodeBlock language="bash">{`\
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.xlsx`}
</CodeBlock>
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 $?
```
If successful, the contents of the test file will be displayed in CSV rows. The
status code `0` will be printed after the rows.
</details>
This demo has been moved [to a dedicated page](/docs/demos/engines/jerryscript).
### Jint
@ -240,14 +130,12 @@ Jint is an embeddable JS engine for .NET written in C#.
This demo has been moved [to a dedicated page](/docs/demos/engines/jint).
### Nashorn
Nashorn shipped with some versions of Java. It is now a standalone library.
This demo has been moved [to a dedicated page](/docs/demos/engines/nashorn).
### QuickJS
QuickJS is an embeddable JS engine written in C. It provides a separate set of
@ -256,14 +144,12 @@ the standalone browser scripts.
This demo has been moved [to a dedicated page](/docs/demos/engines/quickjs).
### Rhino
Rhino is an ES3+ engine in Java.
This demo has been moved [to a dedicated page](/docs/demos/engines/rhino).
### V8
V8 is an embeddable JS engine written in C++. It powers Chromium and Chrome,

View File

@ -0,0 +1,143 @@
#include <stdio.h>
#include <stdlib.h>
#include "jerryscript.h"
static char *read_file(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f) - 1;
fclose(f);
return buf;
}
jerry_value_t eval_str(const char *code, size_t sz) {
jerry_value_t parsed = jerry_parse(code, sz, NULL);
if(jerry_value_is_error(parsed)) return parsed;
jerry_value_t out = jerry_run(parsed);
jerry_value_free(parsed);
return out;
}
static jerry_value_t load_file(const char *filename) {
size_t len; char *buf = read_file(filename, &len);
if(!buf) return 0;
jerry_value_t out = jerry_arraybuffer(len);
jerry_arraybuffer_write(out, 0, (const uint8_t*)buf, len);
return out;
}
char *pull_str(jerry_value_t str, size_t *sz) {
jerry_size_t str_sz = jerry_string_size(str, JERRY_ENCODING_UTF8);
jerry_char_t *buf = (jerry_char_t *)malloc(str_sz + 1);
jerry_string_to_buffer(str, JERRY_ENCODING_UTF8, buf, str_sz + 1);
*sz = str_sz;
return (char *)buf;
}
int main (int argc, char **argv) {
int res = 0;
/* Initialize engine */
jerry_init(JERRY_INIT_EMPTY);
/* evaluate shim.min.js */
{
size_t sz; const jerry_char_t *script = (jerry_char_t *)read_file("shim.min.js", &sz);
jerry_value_t result = eval_str(script, sz);
if(jerry_value_is_error(result)) {
fprintf(stderr, "Failed to evaluate shim.min.js");
res = 1;
goto cleanup;
}
jerry_value_free(result);
}
/* evaluate xlsx.full.min.js */
{
size_t sz; const jerry_char_t *script = read_file("xlsx.full.min.js", &sz);
jerry_value_t result = eval_str(script, sz);
if(jerry_value_is_error(result)) {
fprintf(stderr, "Failed to evaluate xlsx.full.min.js");
res = 2;
goto cleanup;
}
jerry_value_free(result);
}
/* load spreadsheet */
jerry_value_t ab = load_file(argv[1]);
if(!ab || jerry_value_is_error(ab)) {
fprintf(stderr, "Failed to read %s", argv[1]);
res = 3;
goto exeunt;
}
/* assign to `buf` in the global scope */
{
/* get `this` */
jerry_value_t this = jerry_current_realm();
if(jerry_value_is_error(this)) {
fprintf(stderr, "Failed to get global object");
res = 4;
goto exeunt;
}
/* create "buf" str */
jerry_value_t prop = jerry_string_sz("buf");
if(jerry_value_is_error(this)) {
fprintf(stderr, "Failed to create string");
res = 5;
goto exeunt;
}
/* equivalent of `this["buf"] = buf` */
jerry_value_t set = jerry_object_set(this, prop, ab);
if(jerry_value_is_error(set)) {
fprintf(stderr, "Failed to assign ArrayBuffer");
res = 6;
goto exeunt;
}
}
/* run `var wb = XLSX.read(buf)` */
{
const jerry_char_t code[] = "var wb = XLSX.read(buf);";
jerry_value_t result = eval_str(code, sizeof(code) - 1);
if(jerry_value_is_error(result)) {
fprintf(stderr, "Failed to parse file");
res = 7;
goto exeunt;
}
jerry_value_free(result);
}
/* run `XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])` and print result */
{
const jerry_char_t code[] = "XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])";
jerry_value_t result = eval_str(code, sizeof(code) - 1);
if(jerry_value_is_error(result)) {
fprintf(stderr, "Failed to generate csv");
res = 8;
} else {
size_t sz; char *buf = pull_str(result, &sz);
printf("%s\n", buf);
}
jerry_value_free(result);
}
exeunt:
jerry_value_free(ab);
cleanup:
/* Cleanup engine */
jerry_cleanup();
return res;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 39 KiB