打造基于 JS 的跨平台脚本引擎
前文 介绍过基于 Lua 的脚本引擎实现,其实如果不是游戏或渲染开发,JS/TS 才是更合适的选择:强类型、更新迭代更快、第三方库更丰富、对前端开发者更友好。
JS 的引擎不少,考虑到性能、跨平台移植、移动端友好等因素,我们跳过 V8 和 Hermes,选择了 QuickJS,具体评测对比过程不展开,有兴趣可移步这里。
QuickJS 的作者 Fabrice Bellard,同时还是 ffmpeg 等知名项目的作者,因此代码质量是有保障的。
不过 GitHub 官方镜像项目不太活跃,这里我们选择了另一个版本 QuickJS-NG,它在原有项目基础上新增了不少 Feature,如直接编译生成二进制文件(而不是 .c)。
话不多说,下面我们直奔主题:打造基于 QuickJS 的 JS 脚本引擎。
基本的 JS 引擎
跟之前 Lua 脚本引擎一样,我们还是先实现基本的功能。
VM 实例管理
相比 Lua 核心的就一个 lua_state
走天下,JS 要略显复杂,涉及到两个概念:JSRuntime
和 JSContext
:
JSContext
类似 OpenGL 的GLContext
,可以理解为一个状态机,且和线程一一对应(Worker 需要单独的 context);JSRuntime
就是一个 JS VM 实例,内部可以包含多个 context。
下面我们看代码:
JsBridge()
{
this->runtime = JS_NewRuntime();
ctx = JS_NewContext(rt);
js_std_add_helpers(ctx, 0, NULL);
js_init_module_std(ctx, "qjs:std");
js_init_module_os(ctx, "qjs:os");
}
~JSBridge()
{
JS_FreeContext(this->context);
js_std_free_handlers(this->runtime);
JS_FreeRuntime(this->runtime);
}
- 首先创建
JSRuntime
,然后通过它创建JSContext
; - 加载帮助类和内置库;
- 析构时释放
JSContext
和JSRuntime
。