Skip to main content

ChakraCore 中的工作表

ChakraCore 是一个用 C++ 编写的嵌入式 JS 引擎。

¥ChakraCore is an embeddable JS engine written in C++.

SheetJS 是一个用于从电子表格读取和写入数据的 JavaScript 库。

¥SheetJS is a JavaScript library for reading and writing data from spreadsheets.

该演示使用 ChakraCore 和 SheetJS 从电子表格中提取数据并打印 CSV 行。我们将探索如何在 ChakraCore 上下文中加载 SheetJS 并从 C++ 程序处理电子表格。

¥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" section includes a complete command-line tool for reading data from files.

集成详情

¥Integration Details

初始化 ChakraCore

¥Initialize ChakraCore

ChakraCore 通过 JsGetGlobalObject 提供 global 对象:

¥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.

加载 SheetJS 脚本

¥Load SheetJS Scripts

SheetJS 独立脚本 可以在 ChakraCore 上下文中进行解析和评估。

¥SheetJS Standalone scripts can be parsed and evaluated in a ChakraCore context.

可以通过从文件系统读取脚本并在 ChakraCore 上下文中进行评估来加载主库:

¥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 可以从 C 字节数组生成 ArrayBuffer

¥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);

推送数据后,最简单的方法是在 globalThis 上存储属性:

¥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

"集成示例" 涵盖了 C 应用中的传统集成,而 "CLI 测试" 使用 ch CLI 工具演示了其他概念。

¥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 tested in the following deployments:

架构Git 提交日期
darwin-x64e26c81f2024-12-17
darwin-arme26c81f2025-01-13
win11-x64e26c81f2024-12-19
win11-arme26c81f2025-02-23
linux-x64e26c81f2025-01-09
  1. 安装依赖:

    ¥Install dependencies:

brew install icu4c cmake
  1. 下载脉轮核心:

    ¥Download ChakraCore:

git clone https://github.com/chakra-core/ChakraCore.git
cd ChakraCore
git checkout e26c81f
cd ..
  1. 构建 ChakraCore:

    ¥Build ChakraCore:

cd ChakraCore
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8 --system-icu --no-jit
cd ..

在某些测试运行中,构建失败并显示以下消息:

¥In some test runs, the build failed with the message:

!!! couldn't find ICU ...

在构建步骤之前,已通过指向 icu4c 文件夹的本地符号链接修复了此问题:

¥This was fixed with a local symlink to the icu4c folder before the build step:

cd ChakraCore
mkdir -p usr/local/opt
ln -s /opt/homebrew/opt/icu4c usr/local/opt/icu4c
cd ..

MacOS 15.1 SDK 和 ChakraCore JIT 存在已知问题。这些问题不会影响针对 MacOS 14.5 的早期测试。当前建议禁用 JIT 并使用系统 ICU 实现。

¥There are known issues with MacOS 15.1 SDK and ChakraCore JIT. These issues did not affect earlier tests against MacOS 14.5. The current recommendation is to disable JIT and use the system ICU implementation.

  1. 下载源文件和 Makefile

    ¥Download the source file and Makefile:

curl -L -O https://xlsx.nodejs.cn/chakra/sheetjs.ch.cpp
curl -L -O https://xlsx.nodejs.cn/chakra/Makefile
  1. 构建示例应用:

    ¥Build the sample application:

make

在某些 macOS 测试运行中,构建失败并显示以下消息:

¥In some macOS test runs, the build failed with the message:

clang: error: no such file or directory: '/usr/local/opt/icu4c/lib/libicudata.a'

这是通过创建符号链接修复的:

¥This was fixed by creating a symbolic link:

sudo mkdir -p /usr/local/opt
sudo ln -s /opt/homebrew/opt/icu4c /usr/local/opt
make
  1. 下载 SheetJS Standalone 脚本、shim 脚本和测试文件。将所有三个文件移动到项目目录:

    ¥Download the SheetJS Standalone script, shim script and test file. Move all three files to the project directory:

curl -L -O https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js
curl -L -O https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/shim.min.js
curl -L -O https://xlsx.nodejs.cn/pres.numbers
  1. 运行测试程序:

    ¥Run the test program:

./sheetjs.ch pres.numbers

如果成功,程序将以 CSV 格式打印第一张纸的内容。

¥If successful, the program will print the contents of the first sheet as CSV.

CLI 测试

¥CLI Test

测试部署

此演示最后一次测试是在 2025-01-09,针对 ch 提交 e26c81f

¥This demo was last tested on 2025-01-09 against ch commit e26c81f.

由于 ch 独立二进制文件的限制,此演示将测试文件编码为 Base64 字符串,并将其直接添加到合并脚本中。

¥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.

  1. 下载并解压 ChakraCore 版本 ZIP。将二进制文件 (bin/ch) 复制到你的项目文件夹。

    ¥Download and extract the ChakraCore release ZIP. Copy the binary (bin/ch) to your project folder.

"集成示例" 还构建了 ch 二进制文件!它通常放置在 Linux/macOS 上的 ChakraCore/out/Test/ 文件夹中或 x64 Windows 上的 ChakraCore\Build\VcBuild\bin\x64_debug\ 文件夹中。

¥The "Integration Example" also builds the ch binary! It will typically be placed in the ChakraCore/out/Test/ folder on Linux/macOS or ChakraCore\Build\VcBuild\bin\x64_debug\ on x64 Windows.

  1. 下载 SheetJS Standalone 脚本、shim 脚本和测试文件。将所有三个文件移动到项目目录:

    ¥Download the SheetJS Standalone script, shim script and test file. Move all three files to the project directory:

curl -L -O https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js
curl -L -O https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/shim.min.js
curl -L -O https://xlsx.nodejs.cn/pres.numbers
  1. 打包测试文件并创建 payload.js

    ¥Bundle the test file and create payload.js:

node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
  1. 创建支持脚本:

    ¥Create support scripts:

  • global.js 创建 global 变量:

    ¥global.js creates a global variable:

global.js
var global = (function(){ return this; }).call(null);
  • chakra.js 将调用 XLSX.readXLSX.utils.sheet_to_csv

    ¥chakra.js will call XLSX.read and XLSX.utils.sheet_to_csv:

chakra.js
var wb = XLSX.read(payload, {type:'base64'});
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
  1. 创建合并 xlsx.chakra.js

    ¥Create the amalgamation xlsx.chakra.js:

cat global.js xlsx.full.min.js payload.js chakra.js > xlsx.chakra.js

最终脚本在加载独立库之前定义了 global。准备就绪后,它将读取打包的测试数据并将内容打印为 CSV。

¥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.

在 Windows 上,命令应在 WSL 中运行。

¥On Windows, the command should be run in WSL.

  1. 使用 ChakraCore 独立二进制文件运行脚本:

    ¥Run the script using the ChakraCore standalone binary:

./ch xlsx.chakra.js

[^1]: 请参阅 ChakraCore 项目 wiki 中的 "构建脉轮核心"

¥See "Building ChakraCore" in the ChakraCore project wiki