默认情况下,Emscripten 中禁用了异常捕获。例如,如果编译以下程序
#include <stdio.h>
int main() {
try {
puts("throw...");
throw 1;
puts("(never reached)");
} catch(...) {
puts("catch!");
}
return 0;
}
第一个 throw
将中止程序,你将在输出中看到类似以下内容
throw...
Aborted(Assertion failed: Exception thrown, but exception catching is not enabled. Compile with -sNO_DISABLE_EXCEPTION_CATCHING or -sEXCEPTION_CATCHING_ALLOWED=[..] to catch.)
如果你想选择加入,你还有以下两种选择。
首先,你可以通过 Emscripten 的基于 JavaScript 的支持启用异常。要启用它,在编译时和链接时都传递 -fexceptions
。
使用此标志重新构建上面的示例后,输出将变为
throw...
catch!
请注意,此选项的开销相对较高,但它将在所有支持 WebAssembly 的 JavaScript 引擎上运行。你可以通过指定允许异常启用的函数列表来减少开销,请参阅 EXCEPTION_CATCHING_ALLOWED
设置。
或者,你可以选择加入 原生 WebAssembly 异常处理 提案。要启用它,在编译时和链接时都传递 -fwasm-exceptions
。
使用此标志重新构建示例将产生与上面使用 -fexceptions
相同的输出
throw...
catch!
此选项利用了一项新功能,该功能为 WebAssembly 带来了用于抛出和捕获异常的内置指令。因此,与基于 JavaScript 的实现相比,它可以减少代码大小和性能开销。此选项目前在几个主要的 Web 浏览器中受支持,但 可能尚未在所有 WebAssembly 引擎中受支持.
对于 原生 Wasm 异常,当 断言 启用时,未捕获的异常将打印堆栈跟踪以进行调试。 断言 默认情况下在 -O0 中启用,并在优化构建中禁用(-O1 及更高版本)。你也可以在优化构建中通过将 -sASSERTIONS
传递给 emcc
命令行来启用它。要在堆栈跟踪中显示 Wasm 函数名称,你还需要 –profiling-funcs(或 -g 或 -gsource-map)。
在 JavaScript 中,你还可以使用 WebAssembly.Exception.prototype.stack 属性检查堆栈跟踪。例如
try {
... // some code that calls WebAssembly
} catch (e) {
// Do something with e.stack
console.log(e.stack);
}
在 基于 JavaScript 的异常 中,不支持 Wasm 代码内的堆栈跟踪。
你也可以从 JavaScript 捕获和检查 C++ 异常的类型和消息,以防它们继承自 std::exception
并且因此具有 what
方法。
getExceptionMessage
返回一个包含两个字符串的列表:[type, message]
。 message
是在异常是 std::exception
的子类时调用 what
方法的结果。否则,它将只是一个空字符串。
var sp = stackSave();
try {
... // some code that calls WebAssembly
} catch (e) {
stackRestore(sp);
console.log(getExceptionMessage(e).toString());
} finally {
...
}
如果抛出的值为整数 3,则这将打印 int,
,因为消息部分为空。如果抛出的值为 MyException
的实例,它是 std::exception
的子类,并且它的 what
消息是 My exception thrown
,则此代码将打印 MyException,My exception thrown
。
要使用此函数,你需要将 -sEXPORT_EXCEPTION_HANDLING_HELPERS
传递给选项。你需要启用 Emscripten EH 或 Wasm EH 中的任何一个才能使用此选项。
如果堆栈指针在抛出异常之前由于 Wasm 函数内的堆栈分配而移动,则可以使用 stackSave()
和 stackRestore()
来恢复堆栈指针,以便不会泄漏堆栈内存。
注意
如果你捕获了一个 Wasm 异常,并且没有重新抛出它,你需要使用 decrementExceptionRefcount
方法在 JS 中释放与异常关联的存储,因为 Wasm 中的异常捕获代码没有机会释放它。但目前由于 Wasm EH 和 Emscripten(基于 JS)EH 的实现问题,你需要在 Emscripten EH 的情况下额外调用 incrementExceptionRefcount。有关详细信息和代码示例,请参阅 https://github.com/emscripten-core/emscripten/issues/17115。