docs.sheetjs.com/docz/docs/03-demos/42-engines/20-chakra.md

575 lines
14 KiB
Markdown
Raw Permalink Normal View History

2023-04-09 06:58:43 +00:00
---
2023-11-04 05:05:26 +00:00
title: Sheets in ChakraCore
sidebar_label: C++ + ChakraCore
description: Process structured data in C++ programs. Seamlessly integrate spreadsheets into your program by pairing ChakraCore and SheetJS. Handle the most complex files without breaking a sweat.
2023-04-09 06:58:43 +00:00
pagination_prev: demos/bigdata/index
pagination_next: solutions/input
---
2023-04-27 09:12:19 +00:00
import current from '/version.js';
2023-06-05 20:12:53 +00:00
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
2023-05-07 13:58:36 +00:00
import CodeBlock from '@theme/CodeBlock';
2023-04-27 09:12:19 +00:00
2023-04-09 06:58:43 +00:00
ChakraCore is an embeddable JS engine written in C++.
2023-11-04 05:05:26 +00:00
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
2024-01-23 09:26:06 +00:00
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
2023-11-04 05:05:26 +00:00
spreadsheets from a C++ program.
The ["Integration Example"](#integration-example) section includes a complete
command-line tool for reading data from files.
2023-04-09 06:58:43 +00:00
## Integration Details
2023-11-04 05:05:26 +00:00
### Initialize ChakraCore
2023-04-09 06:58:43 +00:00
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);
```
2023-09-24 03:59:48 +00:00
:::note pass
2023-04-09 06:58:43 +00:00
Cleanup and validation code is omitted from the discussion. The integration
example shows structured validation and controlled memory usage.
:::
2023-11-04 05:05:26 +00:00
### Load SheetJS Scripts
[SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can
be parsed and evaluated in a ChakraCore context.
2023-04-09 06:58:43 +00:00
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
2023-11-04 05:05:26 +00:00
:::note Tested Deployments
2023-04-09 06:58:43 +00:00
2023-06-05 20:12:53 +00:00
This demo was tested in the following deployments:
2023-04-09 06:58:43 +00:00
2023-06-05 20:12:53 +00:00
| Architecture | Git Commit | Date |
|:-------------|:-----------|:-----------|
2024-03-16 16:04:18 +00:00
| `darwin-x64` | `c3ead3f` | 2024-03-15 |
2024-05-24 08:24:50 +00:00
| `darwin-arm` | `3a7b120` | 2024-05-23 |
2024-03-12 06:47:52 +00:00
| `win10-x64` | `c3ead3f` | 2024-03-04 |
2024-07-15 01:55:09 +00:00
| `win11-arm` | `13358c6` | 2024-07-14 |
2024-04-26 04:16:13 +00:00
| `linux-x64` | `1f6e17c` | 2024-04-25 |
2023-04-09 06:58:43 +00:00
:::
2023-08-26 23:05:59 +00:00
0) Install dependencies:
2023-07-06 07:21:41 +00:00
<Tabs groupId="triple">
<TabItem value="darwin-x64" label="Intel Mac">
```bash
brew install icu4c cmake
```
</TabItem>
2023-09-27 04:43:00 +00:00
<TabItem value="darwin-arm" label="ARM64 Mac">
2023-06-05 20:12:53 +00:00
```bash
brew install icu4c cmake
```
2023-07-06 07:21:41 +00:00
</TabItem>
<TabItem value="linux-x64" label="Linux">
On Arch Linux / HoloOS:
```bash
sudo pacman -S cmake clang
```
2023-08-26 23:05:59 +00:00
</TabItem>
2024-07-15 01:55:09 +00:00
<TabItem value="win10-x64" label="Intel Windows">
2023-08-26 23:05:59 +00:00
2024-03-12 06:47:52 +00:00
Install Visual Studio 2022 with the "Desktop Development with C++" workflow and
the "Git for Windows" individual component.
2024-07-15 01:55:09 +00:00
The commands in this demo should be run in "Native Tools Command Prompt".
</TabItem>
<TabItem value="win11-arm" label="ARM64 Windows">
Install Visual Studio 2022 with the "Desktop Development with C++" workflow and
the "Git for Windows" individual component.
The commands in this demo should be run in "ARM64 Native Tools Command Prompt".
2023-08-26 23:05:59 +00:00
2023-07-06 07:21:41 +00:00
</TabItem>
</Tabs>
2023-06-05 20:12:53 +00:00
1) Download ChakraCore:
2023-04-09 06:58:43 +00:00
```bash
2023-11-04 05:05:26 +00:00
git clone https://github.com/chakra-core/ChakraCore.git
2023-04-09 06:58:43 +00:00
cd ChakraCore
2024-07-15 01:55:09 +00:00
git checkout 13358c6
2023-06-05 20:12:53 +00:00
cd ..
```
2) Build ChakraCore:
<Tabs groupId="triple">
<TabItem value="darwin-x64" label="Intel Mac">
```bash
cd ChakraCore
2023-04-09 06:58:43 +00:00
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8
cd ..
```
2024-03-16 16:04:18 +00:00
:::note pass
2023-07-06 07:21:41 +00:00
2024-03-16 16:04:18 +00:00
In some test runs, the build failed with the message:
2023-07-06 07:21:41 +00:00
```
!!! couldn't find ICU ...
```
This was fixed with a local symlink to the `icu4c` folder before the build step:
```bash
cd ChakraCore
mkdir -p usr/local/opt
ln -s /opt/homebrew/opt/icu4c usr/local/opt/icu4c
cd ..
```
:::
2023-06-05 20:12:53 +00:00
</TabItem>
2023-09-27 04:43:00 +00:00
<TabItem value="darwin-arm" label="ARM64 Mac">
2023-06-05 20:12:53 +00:00
2023-09-19 19:08:29 +00:00
:::info pass
2023-06-05 20:12:53 +00:00
When the demo was last tested, ChakraCore JIT was not supported.
:::
```bash
cd ChakraCore
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8 --no-jit
cd ..
```
2024-03-16 16:04:18 +00:00
:::note pass
2023-06-05 20:12:53 +00:00
2024-03-16 16:04:18 +00:00
In some test runs, the build failed with the message:
2023-06-05 20:12:53 +00:00
```
!!! couldn't find ICU ...
```
This was fixed with a local symlink to the `icu4c` folder before the build step:
```bash
cd ChakraCore
mkdir -p usr/local/opt
ln -s /opt/homebrew/opt/icu4c usr/local/opt/icu4c
cd ..
```
:::
2023-07-06 07:21:41 +00:00
</TabItem>
<TabItem value="linux-x64" label="Linux">
2023-09-24 03:59:48 +00:00
:::caution pass
2023-07-06 07:21:41 +00:00
When the demo was last tested, ChakraCore JIT was not supported.
:::
```bash
cd ChakraCore
./build.sh --static --embed-icu --test-build -j=8 --no-jit
cd ..
2023-08-26 23:05:59 +00:00
```
</TabItem>
2024-07-15 01:55:09 +00:00
<TabItem value="win10-x64" label="Intel Windows">
2023-08-26 23:05:59 +00:00
:::info pass
As explained in the ChakraCore project wiki[^1], the build accepts a few flags:
- `/p:Platform=x64` controls the architecture
- `/p:Configuration=Debug` enables runtime checks
- `/p:RuntimeLib=static_library` ensures MSVC libraries are statically linked
:::
```
cd ChakraCore
msbuild /m /p:Platform=x64 /p:Configuration=Debug /p:RuntimeLib=static_library Build\Chakra.Core.sln
cd ..
```
2024-03-12 06:47:52 +00:00
:::caution pass
During some test runs, the build failed with a message referencing `cfguard.h`:
```
44>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\cfguard.h(44,1): error C2220: the following warning is treated as an error
(compiling source file 'ThreadContextInfo.cpp')
44>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\cfguard.h(44,1): warning C4005: '_GUARD_CHECK_ICALL': macro redefinition
```
The source file `lib\Runtime\Base\ThreadContextInfo.cpp` must be patched. The
highlighted lines must be commented:
```cpp title="lib\Runtime\Base\ThreadContextInfo.cpp (comment highlighted lines)"
#if defined(_UCRT) && _CONTROL_FLOW_GUARD
// highlight-start
//# if _MSC_VER >= 1913
//# include <cfguard.h>
//# else
// highlight-end
extern "C" void __fastcall _guard_check_icall(_In_ uintptr_t _Target);
// highlight-next-line
//# endif
#endif
```
:::
2023-08-26 23:05:59 +00:00
After building, the generated DLL should be copied into the project folder:
```
copy .\ChakraCore\Build\VcBuild\bin\x64_debug\ChakraCore.dll .
2024-07-15 01:55:09 +00:00
```
</TabItem>
<TabItem value="win11-arm" label="ARM64 Windows">
:::info pass
As explained in the ChakraCore project wiki[^1], the build accepts a few flags:
- `/p:Platform=arm64` controls the architecture
- `/p:Configuration=Debug` enables runtime checks
- `/p:RuntimeLib=static_library` ensures MSVC libraries are statically linked
:::
```
cd ChakraCore
msbuild /m /p:Platform=arm64 /p:Configuration=Debug /p:RuntimeLib=static_library Build\Chakra.Core.sln
cd ..
```
:::caution pass
During some test runs, the build failed with a message referencing `LegalizeMD.cpp`:
```
...\ChakraCore\lib\Backend\arm64\LegalizeMD.cpp(323,16): warning C1489: 'fPostRegAlloc': local variable is initialized but not referenced [...]
```
The source file `lib\Backend\arm64\LegalizeMD.cpp` must be patched. The
highlighted line must be commented:
```cpp title="lib\Backend\arm64\LegalizeMD.cpp (comment highlighted line)"
void LegalizeMD::LegalizeIndirOffset(IR::Instr * instr, IR::IndirOpnd * indirOpnd, LegalForms forms)
{
// highlight-next-line
//const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
// For LEA, we have special handling of indiropnds
auto correctSize = [](IR::Instr* instr, IR::IndirOpnd* indirOpnd)#if defined(_UCRT) && _CONTROL_FLOW_GUARD
```
After commenting the line, run the command again.
:::
After building, the generated DLL should be copied into the project folder:
```
copy .\ChakraCore\Build\VcBuild\bin\arm64_debug\ChakraCore.dll .
2023-07-06 07:21:41 +00:00
```
</TabItem>
</Tabs>
2023-06-05 20:12:53 +00:00
3) Download the source file and `Makefile`:
2023-04-09 06:58:43 +00:00
- [`sheetjs.ch.cpp`](pathname:///chakra/sheetjs.ch.cpp)
- [`Makefile`](pathname:///chakra/Makefile)
```bash
2023-08-26 23:05:59 +00:00
curl -L -O https://docs.sheetjs.com/chakra/sheetjs.ch.cpp
curl -L -O https://docs.sheetjs.com/chakra/Makefile
2023-04-09 06:58:43 +00:00
```
2023-06-05 20:12:53 +00:00
4) Build the sample application:
2023-04-09 06:58:43 +00:00
2023-08-26 23:05:59 +00:00
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
2023-04-09 06:58:43 +00:00
```bash
make
```
2023-09-24 03:59:48 +00:00
:::caution pass
2023-06-05 20:12:53 +00:00
2024-03-16 16:04:18 +00:00
In some macOS test runs, the build failed with the message:
2023-06-05 20:12:53 +00:00
```
clang: error: no such file or directory: '/usr/local/opt/icu4c/lib/libicudata.a'
```
This was fixed by creating a symbolic link:
```bash
2023-10-20 09:35:44 +00:00
sudo mkdir -p /usr/local/opt
sudo ln -s /opt/homebrew/opt/icu4c /usr/local/opt
make
2023-06-05 20:12:53 +00:00
```
:::
2023-08-26 23:05:59 +00:00
</TabItem>
<TabItem value="win" label="Windows">
2024-07-15 01:55:09 +00:00
<Tabs groupId="triple">
<TabItem value="win10-x64" label="Intel Windows">
2023-08-26 23:05:59 +00:00
```
cl sheetjs.ch.cpp ChakraCore.lib /I ChakraCore\lib\Jsrt /link /LIBPATH:ChakraCore\Build\VcBuild\bin\x64_debug
```
2024-07-15 01:55:09 +00:00
</TabItem>
<TabItem value="win11-arm" label="ARM64 Windows">
```
cl sheetjs.ch.cpp ChakraCore.lib /I ChakraCore\lib\Jsrt /link /LIBPATH:ChakraCore\Build\VcBuild\bin\arm64_debug
```
</TabItem>
</Tabs>
2023-08-26 23:05:59 +00:00
</TabItem>
</Tabs>
2023-09-22 06:32:55 +00:00
5) Download the SheetJS Standalone script, shim script and test file. Move all
three files to the project directory:
2023-04-09 06:58:43 +00:00
<ul>
2023-04-27 09:12:19 +00:00
<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>
2024-04-26 04:16:13 +00:00
<li><a href="https://docs.sheetjs.com/pres.numbers">pres.numbers</a></li>
2023-04-09 06:58:43 +00:00
</ul>
2023-05-07 13:58:36 +00:00
<CodeBlock language="bash">{`\
2023-08-26 23:05:59 +00:00
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
2024-04-26 04:16:13 +00:00
curl -L -O https://docs.sheetjs.com/pres.numbers`}
2023-05-07 13:58:36 +00:00
</CodeBlock>
2023-04-09 06:58:43 +00:00
2023-06-05 20:12:53 +00:00
6) Run the test program:
2023-04-09 06:58:43 +00:00
2023-08-26 23:05:59 +00:00
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
2023-08-20 20:39:35 +00:00
```bash
2023-04-09 06:58:43 +00:00
./sheetjs.ch pres.numbers
```
2023-08-26 23:05:59 +00:00
</TabItem>
<TabItem value="win" label="Windows">
```
.\sheetjs.ch.exe pres.numbers
```
</TabItem>
</Tabs>
2023-04-09 06:58:43 +00:00
If successful, the program will print the contents of the first sheet as CSV.
### CLI Test
2023-11-04 05:05:26 +00:00
:::note Tested Deployments
2023-04-09 06:58:43 +00:00
2024-07-15 01:55:09 +00:00
This demo was last tested on 2024-07-14 against `ch` commit `13358c6`.
2023-04-09 06:58:43 +00:00
:::
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.
2023-10-29 03:22:50 +00:00
:::tip pass
2023-08-26 23:05:59 +00:00
The ["Integration Example"](#integration-example) also builds the `ch` binary!
2023-10-29 03:22:50 +00:00
It will typically be placed in the `ChakraCore/out/Test/` folder on Linux/macOS
or `ChakraCore\Build\VcBuild\bin\x64_debug\` on x64 Windows.
2023-08-26 23:05:59 +00:00
:::
2023-09-22 06:32:55 +00:00
1) Download the SheetJS Standalone script, shim script and test file. Move all
three files to the project directory:
2023-04-09 06:58:43 +00:00
<ul>
2023-04-27 09:12:19 +00:00
<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>
2024-04-26 04:16:13 +00:00
<li><a href="https://docs.sheetjs.com/pres.numbers">pres.numbers</a></li>
2023-04-09 06:58:43 +00:00
</ul>
2023-08-26 23:05:59 +00:00
<CodeBlock language="bash">{`\
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -L -O https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
2024-04-26 04:16:13 +00:00
curl -L -O https://docs.sheetjs.com/pres.numbers`}
2023-08-26 23:05:59 +00:00
</CodeBlock>
2023-04-09 06:58:43 +00:00
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.
2024-07-15 01:55:09 +00:00
:::note pass
On Windows, the command should be run in WSL.
:::
2023-04-09 06:58:43 +00:00
5) Run the script using the ChakraCore standalone binary:
2024-07-15 01:55:09 +00:00
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
2023-08-20 20:39:35 +00:00
```bash
2023-04-09 06:58:43 +00:00
./ch xlsx.chakra.js
```
2023-08-26 23:05:59 +00:00
2024-07-15 01:55:09 +00:00
</TabItem>
<TabItem value="win" label="Windows">
```bash
.\ch.exe xlsx.chakra.js
```
</TabItem>
</Tabs>
2023-08-26 23:05:59 +00:00
[^1]: See ["Building ChakraCore"](https://github.com/chakra-core/ChakraCore/wiki/Building-ChakraCore#deployment) in the ChakraCore project wiki