C++ 例外支援

預設情況下,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) 例外支援

首先,您可以透過 Emscripten 基於 JavaScript 的支援啟用例外。若要啟用它,請在編譯時間和連結時間都傳遞 -fexceptions

當您使用此旗標重新建置上述範例時,輸出將變更為

throw...
catch!

請注意,此選項的額外負荷相對較高,但它將在所有支援 WebAssembly 的 JavaScript 引擎上運作。您可以透過指定啟用例外的允許函式清單來減少額外負荷,請參閱 EXCEPTION_CATCHING_ALLOWED 設定。

基於 WebAssembly 例外處理的支援

或者,您可以選擇加入 原生 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++ 例外

您也可以從 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 以取得詳細資料和程式碼範例。

一起使用例外和 setjmp-longjmp

請參閱 一起使用例外和 setjmp-longjmp