try_table: Wasm-Ausnahmebehandlungsanweisung
Die try_table Ausnahmebehandlungsanweisung ermöglicht es Ihnen, einen Codeblock zu testen, um festzustellen, ob eine Ausnahme geworfen wird, und diese Ausnahme in einer catch-Klausel zu behandeln.
Probieren Sie es aus
(module
;; Import error tag and console.log
(tag $my_error (import "env" "my_error") (param i32))
(import "env" "log" (func $log (param i32)))
(func $try_and_catch (param $value i32)
(block $handler (result i32)
;; In try_table block, catch thrown exception
(try_table (catch $my_error $handler)
(call $might_throw (local.get $value))
)
(return)
)
;; Log value returned by handler block
call $log
)
;; Function that throws an error of type $my_error
;; when its parameter is less than 0
(func $might_throw (param $value i32)
(local.get $value)
(i32.const 0)
(i32.lt_s)
(if
(then
;; Throw exception with payload of 42
(i32.const 42)
(throw $my_error)
)
)
)
(export "try_and_catch" (func $try_and_catch))
)
// Define error tag in JS
const myErrorTag = new WebAssembly.Tag({ parameters: ["i32"] });
// Import error tag and console.log into the module
const env = {
my_error: myErrorTag, // import the tag into the module
log: console.log,
};
WebAssembly.instantiateStreaming(fetch("{%wasm-url%}"), { env }).then(
// Negative value causes function to throw
(result) => result.instance.exports.try_and_catch(-1),
);
Syntax
try_table blocktype catch* instruction*
try_table-
Die
try_table-Anweisung. blocktypeOptional-
Gibt einen oder mehrere Parameter an, die in den
try_table-Block übergeben werden und nach Ausführung des Blocks als Ergebniswert bereitgestellt werden. catch*-
Eine oder mehrere
catch-Klauseln, die jeweils Kriterien für das Abfangen von Ausnahmen darstellen und angeben, zu welchemblockals Ergebnis verzweigt wird. Jede Klausel kann eine der folgenden sein: instruction*-
Null oder mehr Anweisungen, die innerhalb des try-Blocks ausgeführt werden sollen.
Typ
[param*] -> [result*]
Binärcodierung
| Anweisung | Binärformat |
|---|---|
try_table |
0x1f bt:blocktype n:u32 (ct:catch)^n instruction* 0x0b |
Ein grundlegendes try_table mit einer einzelnen catch-Klausel:
(try_table (catch $my_error $handler)
;; instructions ...
)
Würde wie folgt codiert:
0x1f 0x40 0x01 0x00 0x00 0x00 ...instructions binary... 0x0b
Beschreibung
Eine try_table-Anweisung, kombiniert mit catch-Klauseln, bildet das Wasm-Äquivalent einer JavaScript-try...catch-Anweisung. Die Anweisungen innerhalb des try_table-Blocks werden ausgeführt, und wenn eine Ausnahme geworfen wird, die von den verfügbaren catch-Klauseln abgefangen wird, verzweigt der Code zum angegebenen äußeren block, und die von der catch-Klausel produzierten Werte werden auf den Stapel gelegt.
Die unterschiedlichen catch-Klauseln verhalten sich wie folgt:
catch-
Wenn eine Ausnahme mit einem passenden Tag geworfen wird, verzweigen Sie zum angegebenen
blockund legen die Nutzlastwerte auf den Stapel. catch_all-
Wenn irgendeine Ausnahme geworfen wird, verzweigen Sie zum angegebenen
block, ohne etwas auf den Stapel zu legen. catch_ref-
Wenn eine Ausnahme mit einem passenden Tag geworfen wird, verzweigen Sie zum angegebenen
blockund legen die Nutzlastwerte sowie einenexnref-Wert, der die Ausnahme darstellt, auf den Stapel. catch_all_ref-
Wenn irgendeine Ausnahme geworfen wird, verzweigen Sie zum angegebenen
blockund legen einenexnref-Wert, der die Ausnahme darstellt, auf den Stapel.
Jede catch-Klausel, die zu einem äußeren block verzweigt, muss Werte produzieren, die dem Ergebnistyp dieses blocks entsprechen, wenn eine geworfene Ausnahme abgefangen wird.
Blocktype-Parameter
Die optionalen blocktypen Parameter werden in den try_table-Block übergeben und als Ergebniswert bereitgestellt, nachdem der Block ausgeführt wurde. Der Wert kann vor dem try_table-Block oder innerhalb davon angegeben werden. Zum Beispiel:
;; Push an i32
i32.const 42
;; pops an i32 as the param
try_table (param i32)
;; The single i32 const 42 is still on the stack
end
Oder:
try_table (result i32)
;; Push an i32
i32.const 42
;; The end of the block pops the results
end
;; The result i32 is now available to be used here
Oder Sie können jede Kombination dieser Strukturen verwenden.
Beispiele
Umgang mit mehreren Ausnahmen
Dieses Beispiel zeigt, wie mehrere Ausnahmen in einer einzigen try_table-Struktur behandelt werden können.
JavaScript
In unserem Skript beginnen wir damit, eine Referenz zu einem <p>-Element zu erhalten, auf dem wir Ergebnisse ausgeben werden. Wir definieren dann zwei verschiedene Fehler-Tags, um einen Typfehler und einen Bereichsfehler zu repräsentieren, indem wir den WebAssembly.Tag()-Konstruktor verwenden.
const output = document.querySelector("p");
const typeErrorTag = new WebAssembly.Tag({ parameters: ["i32"] });
const rangeErrorTag = new WebAssembly.Tag({ parameters: ["i32", "i32"] });
Als nächstes kompilieren und instanziieren wir unser Wasm-Modul mit der Methode WebAssembly.instantiateStreaming(), indem wir die beiden Fehler-Tags importieren und eine Funktion, um Ergebnisse in das <p>-Element zu protokollieren.
Wir rufen die exportierte Wasm-try_multiple()-Funktion auf, die im WebAssembly-Instance-exports-Objekt verfügbar ist, mehrfach auf, indem wir ihr zwei verschiedene Parameter übergeben, um unterschiedliche Ausnahmen zu werfen, und schließlich einen Wert, der keine Ausnahme wirft.
async function init() {
const { instance } = await WebAssembly.instantiateStreaming(
fetch("{%wasm-url%}"),
{
env: {
type_error: typeErrorTag,
range_error: rangeErrorTag,
log: (code) => {
output.textContent += `Error code: ${code} | `;
},
},
},
);
instance.exports.try_multiple(-1); // Throws type_error, logs 10
instance.exports.try_multiple(101); // Throws range_error, logs 99
instance.exports.try_multiple(50); // Doesn't throw
}
init();
Wasm
In unserem Wasm-Modul importieren wir zuerst unsere zwei Fehler-Tags und die Protokollfunktion. Dann erstellen wir eine Funktion namens $try_multiple, die zwei verschachtelte blocks hat, um $type_errors und $range_errors zu behandeln. In der Mitte der blocks befindet sich eine try_table-Struktur, die zwei catch-Klauseln enthält, um jeden Fehler abzufangen. Wir rufen dann die Funktion $might_throw auf, um zu sehen, ob sie Ausnahmen wirft:
- Wenn ein
$type_errorgeworfen wird, verzweigen wir zum$on_type_error-blockund protokollieren den Nutzlastwert. - Wenn ein
$range_errorgeworfen wird, verzweigen wir zum$on_range_error-block, lassen den ersten der beiden Nutzlastwerte fallen und protokollieren den zweiten, dann verlassen wir denblock. - Wenn keine Ausnahme geworfen wird, verlassen wir einfach den
block.
Die Funktion $might_throw selbst nimmt einen einzigen Parameter und überprüft dessen Wert. Wenn der Wert kleiner als 0 ist, wirft sie einen $type_error mit dem Fehlercode 10. Wenn der Wert größer als 100 ist, wirft sie einen $range_error mit dem Code 99.
(module
(tag $type_error (import "env" "type_error") (param i32))
(tag $range_error (import "env" "range_error") (param i32 i32)) ;; carries two values
(import "env" "log" (func $log (param i32)))
(func $try_multiple (param $value i32)
(block $on_type_error (result i32)
(block $on_range_error (result i32 i32)
(try_table
(catch $type_error $on_type_error)
(catch $range_error $on_range_error)
(call $might_throw (local.get $value))
)
(return) ;; no exception thrown
)
;; $range_error was caught — stack has i32 i32
(drop) ;; drop second payload value
(call $log) ;; log first payload value
(return)
)
;; $type_error was caught — stack has i32
(call $log) ;; log the payload
)
(func $might_throw (param $value i32)
(local.get $value)
(i32.const 0)
(i32.lt_s)
(if
(then
(i32.const 10)
(throw $type_error)
)
)
(local.get $value)
(i32.const 100)
(i32.gt_s)
(if
(then
(i32.const 99)
(i32.const 100)
(throw $range_error)
)
)
)
(export "try_multiple" (func $try_multiple))
)
Ergebnis
Wir rufen die try_multiple()-Funktion dreimal auf. Das erste Mal wird ein $type_error geworfen, daher wird der Fehlercode 10 protokolliert. Das zweite Mal wird ein $range_error geworfen, daher wird der Fehlercode 99 protokolliert. Das dritte Mal wird keine Ausnahme geworfen.