summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/exceptions/calls.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/exceptions/calls.js')
-rw-r--r--js/src/jit-test/tests/wasm/exceptions/calls.js407
1 files changed, 407 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/exceptions/calls.js b/js/src/jit-test/tests/wasm/exceptions/calls.js
new file mode 100644
index 0000000000..f12e5d1dbe
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/exceptions/calls.js
@@ -0,0 +1,407 @@
+// -----------------------------------------------------------------------------
+// This file contains tests checking Wasm functions with throwing functions and
+// try-catch code involving more complex control flow, testing that multiple
+// values returned from calls in try code are not affected by multiple branching
+// towards the landing pad, as well as making sure exceptions carrying multiple
+// values of any Wasm numtype transport the exception values correctly across
+// calls.
+//
+// There are tests for local direct calls, for imported direct calls, for
+// indirect calls in a local table with local functions, for indirect calls in a
+// local table of imported functions, and for indirect calls in an imported
+// table of imported functions.
+//
+// - TODO: Add reftype values, when support for reftypes in exceptions is
+// implemented.
+// -----------------------------------------------------------------------------
+
+load(libdir + "eqArrayHelper.js");
+
+// All individual tests take a string 'localThrow' as argument, and will be run
+// with each element of the result of the following function.
+
+function generateLocalThrows(types, baseThrow) {
+ // Arguments:
+ // - 'types': A string of space separated Wasm types.
+ // - 'baseThrow': A string with a Wasm instruction sequence of Wasm functype
+ // `[${types}]-> [t*], which takes `types` arguments and ends
+ // up throwing the tag '$exn'.
+ // Result:
+ // - A JS array of strings, each representing a Wasm instruction sequence
+ // which is like `baseThrow', i.e., a Wasm instruction sequence of Wasm
+ // functype `[${types}]-> [t*], which takes `types` arguments and ends up
+ // throwing the tag '$exn'. The result does not include 'baseThrow'.
+ //
+ // All strings in Wasm text format.
+
+ // Basic throws;
+ let catchlessTryThrow =
+ `(try (param ${types})
+ (do ${baseThrow}))`;
+
+ let catchAndThrow =
+ `(try (param ${types})
+ (do ${baseThrow})
+ (catch $exn
+ ${baseThrow})
+ (catch_all))`;
+
+ let blockThrow =
+ `(block (param ${types})
+ ${baseThrow})`;
+
+ // This Wasm code requires that the function it appears in has an i32 local
+ // with name "$ifPredicate".
+ let conditionalThrow =
+ `(if (param ${types})
+ (local.get $ifPredicate)
+ (then ${baseThrow})
+ (else ${baseThrow}))`;
+
+ // Including try-delegate.
+ let baseDelegate =
+ `(try (param ${types})
+ (do ${baseThrow})
+ (delegate 0))`;
+
+ // Delegate just outside the block.
+ let nestedDelegate1InBlock =
+ `(block $label1 (param ${types})
+ (try (param ${types})
+ (do ${baseThrow})
+ (delegate $label1)))`;
+
+ let basicThrows = [catchlessTryThrow, blockThrow, conditionalThrow,
+ baseDelegate, nestedDelegate1InBlock];
+
+ // Secondary throws (will end up inside a catch block).
+
+ let baseRethrow =
+ `(rethrow 0)`;
+
+ let nestedRethrow =
+ `(try (param ${types})
+ (do
+ ${baseThrow})
+ (catch $exn
+ (rethrow 1))
+ (catch_all
+ (rethrow 0)))`;
+
+ let catchAllRethrowOriginal =
+ `(try (param ${types})
+ (do
+ ${baseThrow})
+ (catch_all
+ (rethrow 1)))`;
+
+ let secondaryThrows =
+ [].concat(basicThrows,
+ [baseRethrow, nestedRethrow, catchAllRethrowOriginal]);
+
+ // Nestings.
+
+ function basicNesting (basicThrow, secondaryThrow) {
+ return `(try (param ${types})
+ (do ${basicThrow})
+ (catch $exn
+ ${secondaryThrow})
+ (catch_all))`;
+ };
+
+ let result = [];
+
+ for (let basicThrow of basicThrows) {
+ result.push(basicThrow);
+ for (let secondaryThrow of secondaryThrows) {
+ result.push(basicNesting(basicThrow, secondaryThrow));
+ }
+ }
+
+ return result;
+};
+
+{
+ // Some variables to be used in all tests.
+ let typesJS = ["i32", "i64", "f32", "f64", "externref"];
+ let types = typesJS.join(" ");
+
+ // The following depend on whether simd is enabled or not. We write it like
+ // this so we can run this test also when SIMD is not enabled.
+ let exntype = "";
+ let wrongV128 = "";
+ let throwV128 = "";
+ let checkV128Value = "";
+
+ if (wasmSimdEnabled()) {
+ exntype = types + " v128";
+ wrongV128 = `(v128.const i32x4 11 22 33 44)`;
+ throwV128 = `(v128.const i32x4 55 66 77 88)`;
+ checkV128Value = `;; Check the V128 value
+ ${throwV128}
+ (i32x4.eq)
+ (i32x4.all_true)`;
+ } else {
+ exntype = types + " i32";
+ wrongV128 = "(i32.const 0)";
+ throwV128 = "(i32.const 1)";
+ checkV128Value = "";
+ }
+
+ let exnTypeDef = `(type $exnType (func (param ${exntype})))`;
+
+ let throwValues =
+ `;; Values to throw.
+ (i32.const 2)
+ (i64.const 3)
+ (f32.const 4)
+ (f64.const 13.37)
+ (local.get $correctRef)
+ ${throwV128}`;
+
+ // The last 1 is the result of the test that the v128 value is correct, done
+ // in wasm code (if simd is enabled).
+ let correctResultsJS = [2, 3n, 4, 13.37, "foo", 1];
+
+ let wrongValues =
+ `;; Wrong values
+ (i32.const 5)
+ (i64.const 6)
+ (f32.const 0.1)
+ (f64.const 0.6437)
+ (local.get $wrongRef)
+ ${wrongV128}`;
+
+ // The individual tests. -----------------------------------------------------
+
+ function testDirectCallsThrowing(localThrow) {
+ // Test direct function calls throwing any numeric value.
+
+ let throwifTypeInline =
+ // The result of the "throwif" function will be used as an argument the
+ // second time "throwif" is called.
+ `(param $ifPredicate i32) (param $correctRef externref) (result i32)`;
+
+ let moduleHeaderThrowif =
+ `(module
+ ${exnTypeDef}
+ (tag $exn (export "exn") (type $exnType))
+ (func $throwif (export "throwif") ${throwifTypeInline}
+ (if
+ (local.get $ifPredicate)
+ (then
+ ${throwValues}
+ ${localThrow}))
+ (i32.const 1))`;
+
+ let testModuleRest =
+ `(tag $notThrownExn)
+ (func $doNothing)
+ (func (export "testFunc") (param $correctRef externref)
+ (param $wrongRef externref)
+ (result ${types} i32)
+ (local $ifPredicate i32)
+ (local.get $ifPredicate)
+ (try (param i32) (result ${exntype})
+ (do
+ (local.get $wrongRef)
+ (call $throwif) ;; Returns 1.
+ (call $doNothing) ;; Does nothing.
+ (local.get $correctRef)
+ (call $throwif) ;; Throws $exn.
+ (drop)
+ ${wrongValues} ;; Won't reach this point.
+ ${localThrow}
+ unreachable)
+ (catch $notThrownExn
+ ${wrongValues})
+ (catch $exn))
+ ${checkV128Value}))`;
+
+ function testDirectLocalCallsThrowing() {
+ let mod = moduleHeaderThrowif + testModuleRest;
+ // console.log("DIRECT LOCAL MOD = " + mod); // Uncomment for debugging.
+
+ assertEqArray(wasmEvalText(mod).exports.testFunc("foo", "bar"),
+ correctResultsJS);
+ };
+
+ function testDirectImportedCallsThrowing() {
+ let exports = wasmEvalText(moduleHeaderThrowif + `)`).exports;
+ // Uncomment for debugging.
+ //console.log("DIRECT EXPORTS = " + moduleHeaderThrowif + ")");
+
+ let mod =
+ `(module
+ ${exnTypeDef}
+ (import "m" "exn" (tag $exn (type $exnType)))
+ (import "m" "throwif" (func $throwif ${throwifTypeInline}))` +
+ testModuleRest;
+ // console.log("DIRECT IMPORT MOD = " + mod); // Uncomment for debugging.
+
+ assertEqArray(
+ wasmEvalText(mod, { m : exports}).exports.testFunc("foo", "bar"),
+ correctResultsJS);
+ };
+
+ testDirectLocalCallsThrowing();
+ testDirectImportedCallsThrowing();
+ };
+
+ function testIndirectCallsThrowing(localThrow) {
+ // Test indirect calls throwing exceptions.
+
+ let indirectFunctypeInline = `(param ${exntype})
+ (result ${exntype})`;
+ let getIndirectArgs = `(local.get 0) ;; i32
+ (local.get 1) ;; i64
+ (local.get 2) ;; f32
+ (local.get 3) ;; f64
+ (local.get 4) ;; ref
+ ;; v128
+ (local.get 5)`;
+
+ let testFunctypeInline = `(param $correctRef externref)
+ (param $wrongRef externref)
+ ;; The last i32 result is the v128 check.
+ (result ${types} i32)`;
+
+ let moduleHeader =
+ `(module
+ ${exnTypeDef}
+ (type $indirectFunctype (func ${indirectFunctypeInline}))
+ (tag $exn (export "exn") (type $exnType))
+ (tag $emptyExn (export "emptyExn"))
+ (func $throwExn (export "throwExn") ${indirectFunctypeInline}
+ (local $ifPredicate i32)
+ ${getIndirectArgs}
+ ${localThrow}
+ unreachable)
+ (func $throwEmptyExn (export "throwEmptyExn")
+ ${indirectFunctypeInline}
+ (throw $emptyExn)
+ unreachable)
+ (func $returnArgs (export "returnArgs") ${indirectFunctypeInline}
+ ${getIndirectArgs})
+ (table (export "tab") funcref (elem $throwExn ;; 0
+ $throwEmptyExn ;; 1
+ $returnArgs)) ;; 2
+ `;
+
+ // The main test function happens to have the same Wasm functype as the
+ // indirect calls.
+ let testFuncHeader = `(func (export "testFunc") ${testFunctypeInline}
+ (local $ifPredicate i32)
+ `;
+
+ // To test indirect calls to a local table of local functions
+ function moduleIndirectLocalLocal(functionBody) {
+ return moduleHeader + testFuncHeader + functionBody + `))`;
+ };
+
+ let exports = wasmEvalText(moduleHeader + ")").exports;
+ // Uncomment for debugging.
+ //console.log("INDIRECT EXPORTS = " + moduleHeader + ")");
+
+ let moduleHeaderImporting =
+ `(module
+ ${exnTypeDef}
+ (type $indirectFunctype (func ${indirectFunctypeInline}))
+ (import "m" "exn" (tag $exn (type $exnType)))
+ (import "m" "emptyExn" (tag $emptyExn))
+ (import "m" "throwExn" (func $throwExn (type $indirectFunctype)))
+ (import "m" "throwEmptyExn"
+ (func $throwEmptyExn (type $indirectFunctype)))
+ (import "m" "returnArgs"
+ (func $returnArgs (type $indirectFunctype)))`;
+
+ // To test indirect calls to a local table of imported functions.
+ function moduleIndirectLocalImport(functionBody) {
+ return moduleHeaderImporting +
+ `(table funcref (elem $throwExn $throwEmptyExn $returnArgs))` +
+ testFuncHeader + functionBody + `))`;
+ };
+
+ // To test indirect calls to an imported table of imported functions.
+ function moduleIndirectImportImport(functionBody) {
+ return moduleHeaderImporting +
+ `(import "m" "tab" (table 3 funcref))` +
+ testFuncHeader + functionBody + `))`;
+ };
+
+ function getModuleTextsForFunctionBody(functionBody) {
+ return [moduleIndirectLocalLocal(functionBody),
+ moduleIndirectLocalImport(functionBody),
+ moduleIndirectImportImport(functionBody)];
+ };
+
+ // The function bodies for the tests.
+
+ // Three indirect calls, the middle of which throws and will be caught. The
+ // results of the first and second indirect calls are used by the next
+ // indirect call. This should be called from try code, to check that the
+ // pad-branches don't interfere with the results of each call.
+ let indirectThrow = `${throwValues}
+ (call_indirect (type $indirectFunctype) (i32.const 2)) ;; returnArgs
+ (call_indirect (type $indirectFunctype) (i32.const 0)) ;; throwExn
+ drop drop ;; Drop v128 and externref to do trivial and irrelevant ops.
+ (f64.const 5)
+ (f64.add)
+ (local.get $wrongRef)
+ ${wrongV128}
+ ;; throwEmptyExn
+ (call_indirect (type $indirectFunctype) (i32.const 1))
+ unreachable`;
+
+ // Simple try indirect throw and catch.
+ let simpleTryIndirect = `(try (result ${exntype})
+ (do ${indirectThrow})
+ (catch $emptyExn
+ ${wrongValues})
+ (catch $exn)
+ (catch_all
+ ${wrongValues}))
+ ${checkV128Value}`;
+
+ // Indirect throw/catch_all, with a simple try indirect throw nested in the
+ // catch_all.
+ let nestedTryIndirect =
+ `(try (result ${types} i32)
+ (do
+ ${wrongValues}
+ ;; throwEmptyExn
+ (call_indirect (type $indirectFunctype) (i32.const 1))
+ drop ;; Drop the last v128 value.
+ (i32.const 0))
+ (catch_all
+ ${simpleTryIndirect}))`;
+
+ let functionBodies = [simpleTryIndirect, nestedTryIndirect];
+
+ // Test throwing from indirect calls.
+ for (let functionBody of functionBodies) {
+ console.log("functionBody = : " + functionBody); // Uncomment for debugging.
+
+ for (let mod of getModuleTextsForFunctionBody(functionBody)) {
+ //console.log("mod = : " + mod); // Uncomment for debugging.
+
+ let testFunction = wasmEvalText(mod, { m : exports}).exports.testFunc;
+ assertEqArray(testFunction("foo", "bar"),
+ correctResultsJS);
+ }
+ }
+ };
+
+ // Run all tests. ------------------------------------------------------------
+
+ let localThrows =
+ ["(throw $exn)"].concat(generateLocalThrows(exntype, "(throw $exn)"));
+
+ for (let localThrow of localThrows) {
+ // Uncomment for debugging.
+ //console.log("Testing with localThrow = " + localThrow);
+
+ testDirectCallsThrowing(localThrow);
+ testIndirectCallsThrowing(localThrow);
+ }
+}