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-x64 | c3ead3f | 2024-03-15 |
darwin-arm | 3a7b120 | 2024-05-23 |
win10-x64 | c3ead3f | 2024-03-04 |
win11-arm | 13358c6 | 2024-07-14 |
linux-x64 | 1f6e17c | 2024-04-25 |
-
安装依赖:
¥Install dependencies:
- Intel Mac
- ARM64 Mac
- Linux
- Intel Windows
- ARM64 Windows
brew install icu4c cmake
brew install icu4c cmake
在 Arch Linux / HoloOS 上:
¥On Arch Linux / HoloOS:
sudo pacman -S cmake clang
安装带有 "使用 C++ 进行桌面开发" 工作流和 "适用于 Windows 的 Git" 单独组件的 Visual Studio 2022。
¥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 "Native Tools Command Prompt".
安装带有 "使用 C++ 进行桌面开发" 工作流和 "适用于 Windows 的 Git" 单独组件的 Visual Studio 2022。
¥Install Visual Studio 2022 with the "Desktop Development with C++" workflow and the "Git for Windows" individual component.
此演示中的命令应在 "ARM64 原生工具命令提示符" 中运行。
¥The commands in this demo should be run in "ARM64 Native Tools Command Prompt".
-
下载脉轮核心:
¥Download ChakraCore:
git clone https://github.com/chakra-core/ChakraCore.git
cd ChakraCore
git checkout 13358c6
cd ..
-
构建 ChakraCore:
¥Build ChakraCore:
- Intel Mac
- ARM64 Mac
- Linux
- Intel Windows
- ARM64 Windows
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 ..
上次测试演示时,不支持 ChakraCore JIT。
¥When the demo was last tested, ChakraCore JIT was not supported.
cd ChakraCore
./build.sh --static --icu=/usr/local/opt/icu4c/include --test-build -j=8 --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 ..
上次测试演示时,不支持 ChakraCore JIT。
¥When the demo was last tested, ChakraCore JIT was not supported.
cd ChakraCore
./build.sh --static --embed-icu --test-build -j=8 --no-jit
cd ..
正如 ChakraCore 项目 wiki[^1] 中所解释的,该构建接受一些标志:
¥As explained in the ChakraCore project wiki[^1], the build accepts a few flags:
-
/p:Platform=x64
控制架构¥
/p:Platform=x64
controls the architecture -
/p:Configuration=Debug
启用运行时检查¥
/p:Configuration=Debug
enables runtime checks -
/p:RuntimeLib=static_library
确保 MSVC 库是静态链接的¥
/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 ..
在某些测试运行期间,构建失败并显示一条引用 cfguard.h
的消息:
¥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
必须对源文件 lib\Runtime\Base\ThreadContextInfo.cpp
进行修补。高亮的行必须注释:
¥The source file lib\Runtime\Base\ThreadContextInfo.cpp
must be patched. The
highlighted lines must be commented:
#if defined(_UCRT) && _CONTROL_FLOW_GUARD
//# if _MSC_VER >= 1913
//# include <cfguard.h>
//# else
extern "C" void __fastcall _guard_check_icall(_In_ uintptr_t _Target);
//# endif
#endif
构建完成后,应将生成的 DLL 复制到项目文件夹中:
¥After building, the generated DLL should be copied into the project folder:
copy .\ChakraCore\Build\VcBuild\bin\x64_debug\ChakraCore.dll .
正如 ChakraCore 项目 wiki[^1] 中所解释的,该构建接受一些标志:
¥As explained in the ChakraCore project wiki[^1], the build accepts a few flags:
-
/p:Platform=arm64
控制架构¥
/p:Platform=arm64
controls the architecture -
/p:Configuration=Debug
启用运行时检查¥
/p:Configuration=Debug
enables runtime checks -
/p:RuntimeLib=static_library
确保 MSVC 库是静态链接的¥
/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 ..
在某些测试运行期间,构建失败并显示一条引用 LegalizeMD.cpp
的消息:
¥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 [...]
必须对源文件 lib\Backend\arm64\LegalizeMD.cpp
进行修补。必须注释掉高亮的行:
¥The source file lib\Backend\arm64\LegalizeMD.cpp
must be patched. The
highlighted line must be commented:
void LegalizeMD::LegalizeIndirOffset(IR::Instr * instr, IR::IndirOpnd * indirOpnd, LegalForms forms)
{
//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.
构建完成后,应将生成的 DLL 复制到项目文件夹中:
¥After building, the generated DLL should be copied into the project folder:
copy .\ChakraCore\Build\VcBuild\bin\arm64_debug\ChakraCore.dll .
-
下载源文件和
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
-
构建示例应用:
¥Build the sample application:
- Linux/MacOS
- Windows
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
- Intel Windows
- ARM64 Windows
cl sheetjs.ch.cpp ChakraCore.lib /I ChakraCore\lib\Jsrt /link /LIBPATH:ChakraCore\Build\VcBuild\bin\x64_debug
cl sheetjs.ch.cpp ChakraCore.lib /I ChakraCore\lib\Jsrt /link /LIBPATH:ChakraCore\Build\VcBuild\bin\arm64_debug
-
下载 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
-
运行测试程序:
¥Run the test program:
- Linux/MacOS
- Windows
./sheetjs.ch pres.numbers
.\sheetjs.ch.exe 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.
-
下载并解压 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.
-
下载 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
-
打包测试文件并创建
payload.js
:¥Bundle the test file and create
payload.js
:
node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')"
-
创建支持脚本:
¥Create support scripts:
-
global.js
创建global
变量:¥
global.js
creates aglobal
variable:
var global = (function(){ return this; }).call(null);
-
chakra.js
将调用XLSX.read
和XLSX.utils.sheet_to_csv
:¥
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]]));
-
创建合并
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.
-
使用 ChakraCore 独立二进制文件运行脚本:
¥Run the script using the ChakraCore standalone binary:
- Linux/MacOS
- Windows
./ch xlsx.chakra.js
.\ch.exe xlsx.chakra.js
[^1]: 请参阅 ChakraCore 项目 wiki 中的 "构建脉轮核心"
¥See "Building ChakraCore" in the ChakraCore project wiki