打造基于 JS 的跨平台脚本引擎

前文 介绍过基于 Lua 的脚本引擎实现,其实如果不是游戏或渲染开发,JS/TS 才是更合适的选择:强类型、更新迭代更快、第三方库更丰富、对前端开发者更友好。

JS 的引擎不少,考虑到性能、跨平台移植、移动端友好等因素,我们跳过 V8Hermes,选择了 QuickJS,具体评测对比过程不展开,有兴趣可移步这里

QuickJS 的作者 Fabrice Bellard,同时还是 ffmpeg 等知名项目的作者,因此代码质量是有保障的。

不过 GitHub 官方镜像项目不太活跃,这里我们选择了另一个版本 QuickJS-NG,它在原有项目基础上新增了不少 Feature,如直接编译生成二进制文件(而不是 .c)。

话不多说,下面我们直奔主题:打造基于 QuickJS 的 JS 脚本引擎。

基本的 JS 引擎

跟之前 Lua 脚本引擎一样,我们还是先实现基本的功能。

VM 实例管理

相比 Lua 核心的就一个 lua_state 走天下,JS 要略显复杂,涉及到两个概念:JSRuntimeJSContext

  • 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
  • 加载帮助类和内置库
  • 析构时释放 JSContextJSRuntime