預設情況下,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 的實作相比,它可以減少程式碼大小和效能額外負荷。此選項目前在數個主要的網路瀏覽器中受到支援,但 可能尚未在所有 WebAssembly 引擎中受到支援。
對於 原生 Wasm 例外,當啟用 ASSERTIONS 時,未捕捉的例外將會列印堆疊追蹤以進行偵錯。ASSERTIONS 在 -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
是呼叫 what
方法的結果,以防例外是 std::exception
的子類別。否則它只會是空字串。
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 的實作問題,您需要額外呼叫 incrementExceptionRefcount 來處理 Emscripten EH。請參閱 https://github.com/emscripten-core/emscripten/issues/17115 以取得詳細資料和程式碼範例。