前言
quickjs是一个轻量化的JavaScript解释器,能够支持ES6语法。不同于v8和nodejs几十M的体积,quickjs编译后只有几M大小,而且作为第三方库也只需要引用几个头文件和一个库文件,非常方便。可以将c语言代码块编译成动态链接库像一般的ES6 module一样在js代码中引用,有良好的封装性和性能表现。
相比lua而言,JavaScript使用范围更广,资料和组件较多,对用户较为友好。
准备工作
- 安装MSYS2
- https://www.msys2.org/
- 在MSYS2中安装mingw64
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-make mingw-w64-x86_64-dlfcn
- 拉取quickjs源代码
git clone https://github.com/bellard/quickjs.git
修改源码
- quickjs-libc.c
#if defined(_WIN32)
static JSModuleDef *js_module_loader_so(JSContext *ctx, const char *module_name)
函数修改为
static JSModuleDef *js_module_loader_so(JSContext *ctx,
const char *module_name)
{
//JS_ThrowReferenceError(ctx, "shared library modules are not supported yet");
JSModuleDef *m;
HINSTANCE hd;
JSInitModuleFunc *init;
char *filename;
if (!strchr(module_name, '/')) {
/* must add a '/' so that the DLL is not searched in the
system library paths */
filename = js_malloc(ctx, strlen(module_name) + 2 + 1);
if (!filename)
return NULL;
strcpy(filename, "./");
strcpy(filename + 2, module_name);
} else {
filename = (char *)module_name;
}
/* C module */
hd = LoadLibrary(filename);
if (filename != module_name)
js_free(ctx, filename);
if (!hd) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library",
module_name);
goto fail;
}
init = (JSInitModuleFunc*)GetProcAddress(hd, "js_init_module");
if (!init) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found",
module_name);
goto fail;
}
m = init(ctx, module_name);
if (!m) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error",
module_name);
fail:
if (hd)
FreeLibrary(hd);
return NULL;
}
return m;
}
JSModuleDef *js_module_loader(JSContext *ctx, const char *module_name, void *opaque)
函数中
if (has_suffix(module_name, ".so") {
修改为
if (has_suffix(module_name, ".so") || has_suffix(module_name, ".dll")) {
编译
所有命令在MSYS2中完成
/mingw64/bin/mingw64-make LDEXPORT="-static -s"
strip -g
gcc -shared -o libquickjs.dll -static -s -Wl,--whole-archive libquickjs.a -lm -Wl,--no-whole-archive
第一步可能会有报错,只要能编译出exe文件可以暂时忽略报错
测试
- hello world
./qjsc -e -o hello.c examples/hello.js
gcc -D_GNU_SOURCE -I./ -o hello hello.c -static -s -L./ -lquickjs -lm -ldl -lpthread
./hello
打包
在MSYS2中
zip -9 -r quickjs-$(cat version)-win$(echo ${MSYSTEM:0-2}).zip qjs.exe run-test262.exe
mkdir ./bin
cp /mingw64/bin/libgcc_s_seh-1.dll ./bin/
cp /mingw64/bin/libstdc++-6.dll ./bin/
cp /mingw64/bin/libwinpthread-1.dll ./bin/
mv qjs.exe qjsc.exe run-test262.exe ./bin
mkdir -p ./lib/quickjs
strip -g libquickjs.a
mv libquickjs.a libquickjs.lto.a ./lib/quickjs
mkdir -p ./include/quickjs
cp -p quickjs.h quickjs-libc.h ./include/quickjs
zip -9 -r quickjs-$(cat version)-win$(echo ${MSYSTEM:0-2})-all.zip ./bin ./doc ./examples ./include ./lib Changelog readme.txt TODO VERSION
如果要用动态链接库链接hello.c,则第二行去掉-static
、-ldl
和-lpthread参数
。
2.js中引用第三方模块
在MSYS2中
/mingw64/bin/x86_64-w64-mingw32-gcc fib.c libquickjs.a -fPIC -shared -DJS_SHARED_LIBRARY -o fib.dll
把test_fib.js修改为
import { fib } from "fib.dll";
console.log("Hello World");
console.log("fib(10)=", fib(10));
如果成功输出斐波那契数列则通过
reference
- https://github.com/bellard/quickjs
- https://github.com/mengmo/QuickJS-Windows-Build
- https://zhuanlan.zhihu.com/p/380018939
- https://zhuanlan.zhihu.com/p/379697068
- https://zhuanlan.zhihu.com/p/623863082#:~:text=static%20JSModuleDef%20%2Ajs_module_loader_so%28JSContext%20%2Actx%2C%20const%20char%20%2Amodule_name%29%20%7B,%28hd%29%20FreeLibrary%28hd%29%3B%20return%20NULL%3B%20%7D%20return%20m%3B%20%7D