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-x64c3ead3f2024-03-15
darwin-arm3a7b1202024-05-23
win10-x64c3ead3f2024-03-04
win11-arm13358c62024-07-14
linux-x641f6e17c2024-04-25
  1. 安装依赖:

    ¥Install dependencies:

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

    ¥Download ChakraCore:

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

    ¥Build ChakraCore:

cd ChakraCore
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8
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 ..
  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

测试部署

此演示最后一次测试是在 2024-07-14,针对 ch 提交 13358c6

¥This demo was last tested on 2024-07-14 against ch commit 13358c6.

由于 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