You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QuickJS调用JS_Eval运行模块脚本时触发段错误如何解决

QuickJS 加载ES模块触发段错误修复方案

这个是QuickJS宿主开发的经典新手坑,核心是漏了官方qjs启动流程里几个必须的初始化和执行步骤,和你写的逻辑差异点如下,改完就能跑:

  • 第一,JS_Eval用*JS_EVAL_TYPE_MODULE*模式时,必须接收返回值并做异常判断。这个模式下返回值是编译后的模块对象,虽然漏接返回值不会直接崩,但完全跳过异常检查的话,内部一旦触发错误就会走到非法内存访问,直接段错误,连错误提示都拿不到。
  • 第二,光调用js_init_module_std、js_init_module_os不够,必须先给runtime注册模块加载器。QuickJS解析到import语句时会调用注册的加载器函数找对应模块,没注册的话这个函数指针是空,调用直接跳0地址,这就是你现在段错误的直接原因。QuickJS源码自带内置模块加载器初始化函数,创建完runtime直接调js_module_loader_init(jsr.get(), NULL);就行,不用自己写加载逻辑,官方qjs启动第一时间就会调这个接口。
  • 第三,模块模式下JS_Eval只做编译和依赖解析,不会直接执行代码。eval完成后必须循环调用JS_ExecutePendingJob清空待执行任务队列,模块顶层代码才会真正跑起来,不然连第一行print都出不来。
  • 额外细节:js_load_file返回的文件buffer是堆上分配的,用完要记得用js_free释放,所有JSValue类型的返回值用完也要调JS_FreeValue释放,不然会有内存泄漏。

修复后的可运行代码片段:

auto jsr = shared_ptr<JSRuntime>(JS_NewRuntime(), JS_FreeRuntime);
// 初始化内置模块加载器,必须在创建context之前调用
js_module_loader_init(jsr.get(), NULL);

for (auto &f : files){
    auto ctx = shared_ptr<JSContext>(JS_NewContext(jsr.get()), JS_FreeContext);
    // 注册内置std、os模块
    js_init_module_os(ctx.get(), "os");
    js_init_module_std(ctx.get(), "std");
    
    size_t bufLen = 0;
    auto buf = js_load_file(ctx.get(), &bufLen, f.c_str());
    if (!buf) {
        cout << "Load script failed: " << f << endl;
        continue;
    }
    cout << "Starting Evaluation\n";
    JSValue eval_ret = JS_Eval(ctx.get(), (char*)buf, bufLen, f.c_str(), JS_EVAL_TYPE_MODULE);
    js_free(ctx.get(), buf); // 释放文件内容内存
    
    if (JS_IsException(eval_ret)) {
        JSValue err = JS_GetException(ctx.get());
        const char* err_msg = JS_ToCString(ctx.get(), err);
        cout << "Compile error: " << err_msg << endl;
        JS_FreeCString(ctx.get(), err_msg);
        JS_FreeValue(ctx.get(), err);
        JS_FreeValue(ctx.get(), eval_ret);
        continue;
    }
    JS_FreeValue(ctx.get(), eval_ret);
    
    // 跑任务队列,执行模块代码,等待异步任务完成
    int job_ret;
    JSContext* job_ctx;
    while (JS_IsPendingJob(jsr.get())) {
        job_ret = JS_ExecutePendingJob(jsr.get(), &job_ctx);
        if (job_ret <= 0) {
            JSValue runtime_err = JS_GetException(job_ctx);
            const char* err_msg = JS_ToCString(job_ctx, runtime_err);
            cout << "Runtime error: " << err_msg << endl;
            JS_FreeCString(job_ctx, err_msg);
            JS_FreeValue(job_ctx, runtime_err);
            break;
        }
    }
    cout << "Ending Evaluation\n";
}

改完之后你那个测试脚本就能和官方qjs跑的效果一样,每秒打印一次计数,直到循环跑完。注意你用的os模块的sleep是异步函数,任务队列循环会等所有异步任务执行完才退出,不用额外加事件循环逻辑。

内容的提问来源于stack exchange,提问作者JvJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 07:33:17