Standardmäßig ist das Abfangen von Ausnahmen in Emscripten deaktiviert. Wenn Sie beispielsweise das folgende Programm kompilieren,
#include <stdio.h>
int main() {
try {
puts("throw...");
throw 1;
puts("(never reached)");
} catch(...) {
puts("catch!");
}
return 0;
}
Der erste throw wird das Programm abbrechen und Sie werden etwas Ähnliches wie dies in der Ausgabe sehen
throw...
Aborted(Assertion failed: Exception thrown, but exception catching is not enabled. Compile with -sNO_DISABLE_EXCEPTION_CATCHING or -sEXCEPTION_CATCHING_ALLOWED=[..] to catch.)
Wenn Sie sich dafür entscheiden möchten, haben Sie die folgenden zwei Optionen.
Zunächst können Sie Ausnahmen über die JavaScript-basierte Unterstützung von Emscripten aktivieren. Um dies zu aktivieren, übergeben Sie -fexceptions sowohl zur Kompilierungszeit als auch zur Linkzeit.
Wenn Sie das obige Beispiel mit diesem Flag neu erstellen, ändert sich die Ausgabe zu
throw...
catch!
Beachten Sie, dass diese Option einen relativ hohen Overhead hat, aber auf allen JavaScript-Engines mit WebAssembly-Unterstützung funktioniert. Sie können den Overhead reduzieren, indem Sie eine Liste zulässiger Funktionen angeben, in denen Ausnahmen aktiviert sind. Siehe die Einstellung EXCEPTION_CATCHING_ALLOWED.
Alternativ können Sie sich für den Vorschlag zur nativen WebAssembly-Ausnahmebehandlung entscheiden. Um dies zu aktivieren, übergeben Sie -fwasm-exceptions sowohl zur Kompilierungszeit als auch zur Linkzeit.
Das erneute Kompilieren des Beispiels mit diesem Flag führt zur gleichen Ausgabe wie mit -fexceptions oben
throw...
catch!
Diese Option nutzt eine neue Funktion, die integrierte Anweisungen zum Werfen und Abfangen von Ausnahmen in WebAssembly bietet. Dadurch kann sie die Code-Größe und den Performance-Overhead im Vergleich zur JavaScript-basierten Implementierung reduzieren. Diese Option wird derzeit in mehreren wichtigen Webbrowsern unterstützt, aber möglicherweise noch nicht in allen WebAssembly-Engines.
Bei nativen Wasm-Ausnahmen werden, wenn ASSERTIONS aktiviert ist, nicht abgefangene Ausnahmen Stack-Traces zum Debuggen ausgeben. ASSERTIONS ist standardmäßig in -O0 aktiviert und in optimierten Builds (-O1 und höher) deaktiviert. Sie können es auch in optimierten Builds aktivieren, indem Sie -sASSERTIONS an die Befehlszeile von emcc übergeben. Um Wasm-Funktionsnamen in Stack-Traces anzuzeigen, benötigen Sie auch –profiling-funcs (oder -g oder -gsource-map).
In JavaScript können Sie die Stack-Traces auch mit der Eigenschaft WebAssembly.Exception.prototype.stack untersuchen. Zum Beispiel
try {
... // some code that calls WebAssembly
} catch (e) {
// Do something with e.stack
console.log(e.stack);
}
Stack-Traces innerhalb von Wasm-Code werden bei JavaScript-basierten Ausnahmen nicht unterstützt.
Sie können auch den Typ und die Nachricht von C++-Ausnahmen von JavaScript aus abfangen und untersuchen, falls diese von std::exception erben und somit eine what-Methode besitzen.
getExceptionMessage gibt eine Liste von zwei Strings zurück: [type, message]. Die message ist das Ergebnis des Aufrufs der what-Methode, falls die Ausnahme eine Unterklasse von std::exception ist. Andernfalls ist es ein leerer String.
var sp = stackSave();
try {
... // some code that calls WebAssembly
} catch (e) {
stackRestore(sp);
console.log(getExceptionMessage(e).toString());
} finally {
...
}
Falls der geworfene Wert eine Ganzzahl 3 ist, wird dies int, ausgeben, da der Nachrichten-Teil leer ist. Wenn der geworfene Wert eine Instanz von MyException ist, die eine Unterklasse von std::exception ist und ihre what-Nachricht My exception thrown ist, wird dieser Code MyException,My exception thrown ausgeben.
Um diese Funktion zu verwenden, müssen Sie -sEXPORT_EXCEPTION_HANDLING_HELPERS an die Optionen übergeben. Sie müssen entweder Emscripten EH oder Wasm EH aktivieren, um diese Option zu nutzen.
Wenn der Stack-Pointer aufgrund von Stack-Allokationen innerhalb der Wasm-Funktion vor dem Werfen einer Ausnahme verschoben wurde, können Sie stackSave() und stackRestore() verwenden, um den Stack-Pointer wiederherzustellen, damit kein Stack-Speicher verloren geht.
Hinweis
Wenn Sie eine Wasm-Ausnahme abfangen und nicht erneut werfen, müssen Sie den mit der Ausnahme in JS verbundenen Speicher mithilfe der Methode decrementExceptionRefcount freigeben, da der Ausnahmeabfangcode in Wasm keine Möglichkeit hat, ihn freizugeben. Derzeit müssen Sie jedoch aufgrund eines Implementierungsproblems bei Wasm EH und Emscripten (JS-basiert) EH zusätzlich incrementExceptionRefcount aufrufen, wenn es sich um Emscripten EH handelt. Siehe https://github.com/emscripten-core/emscripten/issues/17115 für Details und ein Codebeispiel.