ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

跨语言异常不再难排查:仓颉与 JavaScript 双向异常传播机制完整解析

2026/9/24 14:12:44 拓冰建站 浏览量
跨语言异常不再难排查:仓颉与 JavaScript 双向异常传播机制完整解析 跨语言异常不再难排查仓颉与 JavaScript 双向异常传播机制完整解析【免费下载链接】cangjie_js_interop项目地址: https://gitcode.com/Cangjie/cangjie_js_interopCangjie/cangjie_js_interop仓颉与 JavaScript 互操作库提供了跨语言异常传播机制JavaScript 侧未捕获的错误会自动转换为仓颉异常抛回仓颉侧仓颉侧未捕获的异常也会转换为 JavaScript 异常传入 JS 运行时。这篇文章带你完整看懂这套双向异常传播原理、源码实现与排查技巧让跨语言报错从此有迹可循。为什么跨语言异常排查这么难单语言应用中异常从抛出到捕获都在同一个运行时里完成栈信息完整、消息格式统一。而仓颉调用 JavaScript 的场景下调用链横跨了两个运行时仓颉 → JavaScriptevaluate执行 JS 代码时如果 JS 报错如调用了不存在的函数错误发生在 JS 虚拟机内部仓颉代码却需要一个自己认识的异常对象来catch。JavaScript → 仓颉JS 代码回调仓颉函数时仓颉函数内部抛出的Exception/Error如何被 JS 侧感知互操作库对这两个方向都做了自动处理你不需要手写任何翻译代码。异常传播机制全景两条路径、自动转换互操作库的核心接口是JSRuntimeJS 运行时与JSContext执行上下文异常传播就围绕它们展开。官方用户手册中对两个方向的定义非常明确可参见 js_interop_user_manual.md仓颉调用 JavaScript 时JS 异常未捕获会转换为仓颉异常抛出JavaScript 回调仓颉时仓颉异常未捕获会转换为 JS 异常抛出。整体架构如图README.md 中的 System Architecture 章节路径一JS 异常 → 仓颉异常正向传播当你在仓颉侧调用evaluate执行 JS 代码或调用已注册的 JS 函数时若 JS 侧发生报错互操作库会在每次 C 接口调用后检查 JS 运行时的异常状态。核心逻辑在 runtime.cj 的checkAndHandleError函数中先通过OH_JSVM_IsExceptionPending判断 JS 虚拟机是否有挂起的异常若有用OH_JSVM_GetAndClearLastException取出异常对象并读取它的stack属性最终throw Exception(...)把 JS 异常消息含 JS 堆栈文本包装成仓颉Exception抛出。也就是说你熟悉的 JS 报错信息例如console.log1 is not defined会原样出现在仓颉异常的 message 里直接可用try/catch捕获context.newScope { try { context.evaluate(console.log1(hello, world!)) // log1 不存在JS 报错 } catch (e: Exception) { Hilog.error(0, js_error, e.toString()) // 捕获到的是仓颉异常 } }这两个底层 C 接口的声明位于 napi_cffi.cj如果你需要深入调试可以从这里入手。路径二仓颉异常 → JS 异常反向传播反方向更精妙JS 代码回调仓颉函数时仓颉函数内部抛出的异常如何越过语言边界答案在 function.cj 的回调桩函数jsFunctionCallbackStub中——它捕获仓颉函数抛出的Exception和Error然后调用 throwExceptionToJS把仓颉异常的堆栈逐帧转换成类 JS 的at 类名.方法名(文件:行号)格式文本拼接成Cangjie ${异常信息}\n${堆栈}的消息通过OH_JSVM_ThrowError抛入 JS 运行时。注意一个容易困惑的细节仓颉异常会折返。JS 回调桩抛出 JS 异常后控制权回到仓颉侧的evaluate此时 JS 侧存在挂起异常于是路径一的检查逻辑再次生效——该异常最终又以一个仓颉Exception的形式抛给仓颉调用者。用户手册中的示例正是如此// 仓颉异常传播到 JS 再传播到仓颉抛出参数类型错误 Hilog.info(0, add, 1 2 context.evaluate(add(1, 2)).toInt64().toString())add(1, 2)中第二个参数是字符串仓颉侧toInt64转换失败抛出异常 → 转换为 JS 异常 → 传播回仓颉侧调用者拿到的仍是一个可catch的仓颉异常。异常排查清单3 步定位跨语言报错结合上述机制跨语言异常排查可以固化为三步看异常消息前缀message 以Cangjie开头说明源头是仓颉函数内部没有前缀、带有at xxx (eval:1:xx)风格的堆栈则源头是 JS 代码。看堆栈行号仓颉侧堆栈携带真实的.cj文件与行号可直接跳转源码JS 侧堆栈则指向evaluate传入的代码位置。检查调用方向正向传播仓颉调 JS的异常在evaluate/globalCall处抛出用try/catch包住即可反向传播JS 回调仓颉的异常同样在evaluate返回前抛出但要先确认是参数类型转换失败还是仓颉业务逻辑出错。下面是完整的调用示例工程结构异常捕获应放在newScope作用域内的调用点常见报错速查表报错特征异常源头对应机制xxx is not defined/ 函数未定义JS 代码JS 异常 → 仓颉异常Cangjie Invalid index: ...仓颉互操作库自身参数校验如 array.cjContext has been destroyed!仓颉侧context.cj 上下文生命周期错误Should be run in the libuv thread.仓颉侧未切换到 JS 线程执行见 context.cj其中应在 JS 线程执行的报错值得单独提醒仓颉默认不绑定系统线程而互操作逻辑必须运行在 JS 运行时所在线程上互操作库通过 libuv 任务队列切换线程uv.cj。所有互操作 API 都应放在newScope内调用即可自动满足这一约束。相关资源用户手册含类型映射、执行 JS 代码、JS 回调仓颉章节js_interop_user_manual.md完整 API 参考js_interop_api_docs.md互操作库源码目录src/构建配置cjpm.toml掌握JS 异常转仓颉异常、仓颉异常绕道 JS 再折返这两条传播路径后跨语言报错的排查路径就清晰了异常消息保留原始信息堆栈保留原始行号try/catch在仓颉侧即可统一拦截。✅【免费下载链接】cangjie_js_interop项目地址: https://gitcode.com/Cangjie/cangjie_js_interop创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考