From 5b755a13708e7bd88fa07dd0fd1116b4da0ce0f1 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Sun, 9 Apr 2023 02:58:43 -0400 Subject: [PATCH] chakra --- docz/docs/03-demos/12-engines/07_chakra.md | 232 ++++++++++++++++++++ docz/docs/03-demos/12-engines/08_quickjs.md | 2 +- docz/docs/03-demos/12-engines/index.md | 85 +------ docz/docusaurus.config.js | 2 +- docz/static/chakra/Makefile | 13 ++ docz/static/chakra/sheetjs.ch.cpp | 89 ++++++++ 6 files changed, 342 insertions(+), 81 deletions(-) create mode 100644 docz/docs/03-demos/12-engines/07_chakra.md create mode 100644 docz/static/chakra/Makefile create mode 100644 docz/static/chakra/sheetjs.ch.cpp diff --git a/docz/docs/03-demos/12-engines/07_chakra.md b/docz/docs/03-demos/12-engines/07_chakra.md new file mode 100644 index 0000000..45bb1f6 --- /dev/null +++ b/docz/docs/03-demos/12-engines/07_chakra.md @@ -0,0 +1,232 @@ +--- +title: C++ + ChakraCore +pagination_prev: demos/bigdata/index +pagination_next: solutions/input +--- + +ChakraCore is an embeddable JS engine written in C++. + +The [Standalone scripts](/docs/getting-started/installation/standalone) can be +parsed and evaluated in a ChakraCore context. + +## Integration Details + +_Initialize ChakraCore_ + +ChakraCore provides a `global` object through `JsGetGlobalObject`: + +```cpp +/* initialize */ +JsRuntimeHandle runtime; +JsContextRef context; +size_t cookie = 0; +JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime); +JsCreateContext(runtime, &context); +JsSetCurrentContext(context); + +/* obtain reference to global object */ +JsValueRef global; +JsGetGlobalObject(&global); + +/* DO WORK HERE */ + +/* cleanup */ +JsSetCurrentContext(JS_INVALID_REFERENCE); +JsDisposeRuntime(runtime); +``` + +:::note + +Cleanup and validation code is omitted from the discussion. The integration +example shows structured validation and controlled memory usage. + +::: + +_Load SheetJS Scripts_ + +The main library can be loaded by reading the script from the file system and +evaluating in the ChakraCore context: + +```cpp +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); + fclose(f); + return buf; +} + +#define EVAL_FILE(path) {\ + JsValueRef filename; \ + JsValueRef result; \ + JsCreateString(path, strlen(path), &filename); \ + size_t len; const char* script = read_file(path, &len);\ + JsValueRef src;\ + JsCreateExternalArrayBuffer((void*)script, len, nullptr, nullptr, &src);\ + JsRun(src, cookie++, filename, JsParseScriptAttributeNone, &result); \ +} + +// ... + /* load library */ + EVAL_FILE("shim.min.js") + EVAL_FILE("xlsx.full.min.js") +``` + +### Reading Files + +`JsCreateExternalArrayBuffer` can generate an `ArrayBuffer` from a C byte array: + +```cpp +/* read file */ +size_t len; char *buf = read_file(argv[1], &len); + +/* load data into array buffer */ +JsValueRef ab; +JsCreateExternalArrayBuffer((void*)buf, len, nullptr, nullptr, &ab); +``` + +After pushing the data, it is easiest to store properties on `globalThis`: + +```cpp +/* assign to the `buf` global variable */ +JsValueRef buf_str; JsCreateString("buf", strlen("buf"), &buf_str); +JsObjectSetProperty(global, buf_str, ab, true); + +/* call globalThis.wb = XLSX.read(ab) */ +const char* script_str ="globalThis.wb = XLSX.read(buf);" + +JsValueRef script; +JsCreateExternalArrayBuffer((void*)script_str, (size_t)strlen(script_str), nullptr, nullptr, &script); +JsRun(script, cookie++, fname, JsParseScriptAttributeNone, &result); +``` + +## Complete Example + +The "Integration Example" covers a traditional integration in a C application, +while the "CLI Test" demonstrates other concepts using the `ch` CLI tool. + +### Integration Example + +:::note + +This demo was last tested on 2023 April 09 against ChakraCore commit `c3ead3f` +on a Intel Mac. `gcc -v` printed: + +``` +Apple clang version 14.0.0 (clang-1400.0.29.202) +Target: x86_64-apple-darwin21.6.0 +``` + +::: + +0) Build ChakraCore: + +```bash +git clone https://github.com/Microsoft/ChakraCore +cd ChakraCore +git checkout c3ead3f +./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8 +cd .. +``` + +1) Download the source file and Makefile: + +- [`sheetjs.ch.cpp`](pathname:///chakra/sheetjs.ch.cpp) +- [`Makefile`](pathname:///chakra/Makefile) + +```bash +curl -LO https://docs.sheetjs.com/chakra/sheetjs.ch.c +curl -LO https://docs.sheetjs.com/chakra/Makefile +``` + +2) Build the sample application: + +```bash +make +``` + +This program tries to parse the file specified by the first argument + +4) Download the standalone script, shim script, and test file: + + + +```bash +curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js +curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js +curl -LO https://sheetjs.com/pres.numbers +``` + +5) Run the test program: + +``` +./sheetjs.ch pres.numbers +``` + +If successful, the program will print the contents of the first sheet as CSV. + + +### CLI Test + +:::note + +This demo was last tested on 2023 April 09 against `ch` `1.13.0.0-beta`. +The command line tool was built against commit `c3ead3f`. + +::: + +Due to limitations of the `ch` 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" +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 +``` diff --git a/docz/docs/03-demos/12-engines/08_quickjs.md b/docz/docs/03-demos/12-engines/08_quickjs.md index 80faf86..06771e2 100644 --- a/docz/docs/03-demos/12-engines/08_quickjs.md +++ b/docz/docs/03-demos/12-engines/08_quickjs.md @@ -126,7 +126,7 @@ Target: x86_64-apple-darwin21.6.0 0) Build `libquickjs.a`: ```bash -git clone --depth=1 https://github.com/bellard/quickjs +git clone https://github.com/bellard/quickjs cd quickjs git checkout 2788d71 make diff --git a/docz/docs/03-demos/12-engines/index.md b/docz/docs/03-demos/12-engines/index.md index b0b2e7f..e9dd687 100644 --- a/docz/docs/03-demos/12-engines/index.md +++ b/docz/docs/03-demos/12-engines/index.md @@ -57,6 +57,12 @@ only be used when there is no safe way to pass `ArrayBuffer` or `Uint8Array`. This list is sorted in alphabetical order. +### ChakraCore + +ChakraCore is an embeddable JS engine written in C++. + +This demo has been moved [to a dedicated page](/docs/demos/engines/chakra). + ### Duktape Duktape is an embeddable JS engine written in C. It has been ported to a number @@ -261,82 +267,3 @@ This demo has been moved [to a dedicated page](/docs/demos/engines/quickjs). Rhino is an ES3+ engine in Java. This demo has been moved [to a dedicated page](/docs/demos/engines/rhino). - -### 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 -``` - -
diff --git a/docz/docusaurus.config.js b/docz/docusaurus.config.js index 4e8ae42..d3a3c94 100644 --- a/docz/docusaurus.config.js +++ b/docz/docusaurus.config.js @@ -142,7 +142,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, - additionalLanguages: [ "swift", "java", "csharp", "perl", "ruby" ], + additionalLanguages: [ "swift", "java", "csharp", "perl", "ruby", "cpp" ], }, liveCodeBlock: { playgroundPosition: 'top' diff --git a/docz/static/chakra/Makefile b/docz/static/chakra/Makefile new file mode 100644 index 0000000..f974536 --- /dev/null +++ b/docz/static/chakra/Makefile @@ -0,0 +1,13 @@ +CC=g++ +CHAKRALIB=ChakraCore/out/Test/lib/libChakraCoreStatic.a +ICU4C=/usr/local/opt/icu4c/lib +ICULIB=$(ICU4C)/libicudata.a $(ICU4C)/libicuuc.a $(ICU4C)/libicui18n.a + +CFLAGS=-lstdc++ -std=c++11 -IChakraCore/lib/Jsrt + +sheetjs.ch: sheetjs.ch.cpp + g++ $< $(CFLAGS) -Wl,-force_load $(CHAKRALIB) $(ICULIB) -framework CoreFoundation -framework Security -lm -ldl -Wno-c++11-compat-deprecated-writable-strings -Wno-deprecated-declarations -Wno-unknown-warning-option -o $@ + +.PHONY: clean +clean: + rm sheetjs.ch diff --git a/docz/static/chakra/sheetjs.ch.cpp b/docz/static/chakra/sheetjs.ch.cpp new file mode 100644 index 0000000..a5f0347 --- /dev/null +++ b/docz/static/chakra/sheetjs.ch.cpp @@ -0,0 +1,89 @@ +/* this sample is based off of the official ChakraCore examples */ +#include "ChakraCore.h" +#include +#include +#include +#include + +#define FAIL_CHECK(cmd) \ + do { \ + JsErrorCode errCode = cmd; \ + if (errCode != JsNoError) { \ + printf("Error %d at '%s'\n", errCode, #cmd); \ + return 1; \ + } \ + } while(0) + +using namespace std; + +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); + fclose(f); + return buf; +} + +#define EVAL_FILE(path) {\ + JsValueRef filename; \ + JsValueRef result; \ + FAIL_CHECK(JsCreateString(path, strlen(path), &filename)); \ + size_t len; const char* script = read_file(path, &len);\ + JsValueRef src;\ + FAIL_CHECK(JsCreateExternalArrayBuffer((void*)script, len, nullptr, nullptr, &src));\ + FAIL_CHECK(JsRun(src, cookie++, filename, JsParseScriptAttributeNone, &result)); \ +} + + +int main(int argc, char *argv[]) { + JsRuntimeHandle runtime; + JsContextRef context; + JsValueRef result; + size_t cookie = 0; + + FAIL_CHECK(JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime)); + FAIL_CHECK(JsCreateContext(runtime, &context)); + FAIL_CHECK(JsSetCurrentContext(context)); + + JsValueRef global; + FAIL_CHECK(JsGetGlobalObject(&global)); + + EVAL_FILE("shim.min.js") + + EVAL_FILE("xlsx.full.min.js") + + JsValueRef buf_str; + FAIL_CHECK(JsCreateString("buf", strlen("buf"), &buf_str)); + + size_t len; char *buf = read_file(argv[1], &len); + JsValueRef ab; + FAIL_CHECK(JsCreateExternalArrayBuffer((void*)buf, len, nullptr, nullptr, &ab)); + FAIL_CHECK(JsObjectSetProperty(global, buf_str, ab, true)); + + JsValueRef fname; + FAIL_CHECK(JsCreateString("