diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit-test/tests/wasm/exceptions/calls.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/wasm/exceptions/calls.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/exceptions/calls.js | 407 |
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); + } +} |