C++ + ChakraCore
ChakraCore is an embeddable JS engine written in C++.
The Standalone scripts can be parsed and evaluated in a ChakraCore context.
Integration Details
Initialize ChakraCore
ChakraCore provides a global
object through JsGetGlobalObject
:
/* 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);
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:
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:
/* 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
:
/* 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
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:
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
:
curl -LO https://docs.sheetjs.com/chakra/sheetjs.ch.c
curl -LO https://docs.sheetjs.com/chakra/Makefile
2) Build the sample application:
make
This program tries to parse the file specified by the first argument
4) Download the standalone script, shim script, and test file:
curl -LO https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js
curl -LO https://cdn.sheetjs.com/xlsx-0.19.3/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
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
:
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
3) Create support scripts:
global.js
creates aglobal
variable:
var global = (function(){ return this; }).call(null);
chakra.js
will callXLSX.read
andXLSX.utils.sheet_to_csv
:
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
:
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