C setjmp-longjmp 支持

setjmp-longjmp 支持在 Emscripten 中默认启用。这由 SUPPORT_LONGJMP 设置控制,该设置可以取以下值

  • emscripten: 基于 JavaScript 的支持

  • wasm: 基于 WebAssembly 异常处理的支持

  • 0: 不支持

  • 1: 默认支持,具体取决于异常模式。如果使用 -fwasm-exception,则为 wasm,否则为 emscripten

如果使用 原生 Wasm 异常SUPPORT_LONGJMP 默认值为 wasm,如果使用 基于 JavaScript 的异常 或不使用异常支持,则默认值为 emscripten

setjmp 将有关调用环境的信息保存到一个缓冲区中,longjmp 通过使用该缓冲区将控制权转移回 setjmp 被调用的位置。 longjmp 的调用堆栈应包含调用 setjmp 的函数。

Emscripten 的支持有一个限制,即不支持对 setjmp 的间接调用。例如,以下代码不起作用

jmp_buf env;
int (*fp)(jmp_buf) = setjmp;
fp(env); // Doesn't work

基于 JavaScript 的 setjmp-longjmp 支持

在此模式下,Emscripten 使用 JavaScript 模拟 setjmp-longjmp。此选项可以通过在命令行中添加 -sSUPPORT_LONGJMP=emscripten 来设置,但目前默认情况下已启用。

请注意,此选项在代码大小方面可能会有相对较高的开销,但它将在所有支持 WebAssembly 的 JavaScript 引擎上运行,即使它们尚未支持 新的 WebAssembly 异常处理提案

基于 WebAssembly 异常处理的 setjmp-longjmp 支持

或者,你可以选择使用新的支持 WebAssembly 异常处理 提议。要启用它,请在编译时和链接时都传递 -sSUPPORT_LONGJMP=wasm

此选项利用了一项新功能,该功能为 WebAssembly 带来了用于抛出和捕获异常的内置指令。因此,与基于 JavaScript 的实现相比,它可以减少代码大小和性能开销。此选项目前在几个主要的 Web 浏览器中受支持,但 可能尚未在所有 WebAssembly 引擎中受支持

将异常和 setjmp-longjmp 结合使用

我们还有两种 异常处理支持:基于 JavaScript 的支持和新的基于 WebAssembly EH 的支持。我们的 setjmp-longjmp 支持使用相同的机制。因此,在将异常和 setjmp-longjmp 结合使用时,应使用相同类型的 EH 和 setjmp-longjmp 支持。

例如,要将基于 JavaScript 的 EH 和 setjmp-longjmp 支持结合使用

em++ -fexceptions test.cpp -o test.js

-sSUPPORT_LONGJMP 默认值为 emscriptenwasm(具体取决于异常模式),默认情况下已启用,因此你无需显式传递它。

要将 WebAssembly EH 和 setjmp-longjmp 支持结合使用

em++ -fwasm-exceptions -sSUPPORT_LONGJMP=wasm test.cpp -o test.js

在同时使用基于 WebAssembly EH 的异常支持和 setjmp-longjmp 时,存在一项特定限制。你不能在 C++ catch 子句中调用 setjmp。例如,以下代码将在编译时出错

try {
  ...
catch (int n) {
  setjmp(buf); // Doesn't work
}

try 子句中调用 setjmp 是可以的。在 catch 子句中调用另一个调用 setjmp 的用户函数也是可以的。

try {
  setjmp(buf); // Works
catch (int n) {
  ...
}

try {
  ...
} catch (int n) {
  function_that_calls_setjmp(); // Works
}