From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- js/src/jit-test/tests/wasm/exnref/casting.js | 110 +++++++ js/src/jit-test/tests/wasm/exnref/directives.txt | 1 + js/src/jit-test/tests/wasm/exnref/throw-ref.js | 70 +++++ js/src/jit-test/tests/wasm/exnref/try-table.js | 384 +++++++++++++++++++++++ 4 files changed, 565 insertions(+) create mode 100644 js/src/jit-test/tests/wasm/exnref/casting.js create mode 100644 js/src/jit-test/tests/wasm/exnref/directives.txt create mode 100644 js/src/jit-test/tests/wasm/exnref/throw-ref.js create mode 100644 js/src/jit-test/tests/wasm/exnref/try-table.js (limited to 'js/src/jit-test/tests/wasm/exnref') diff --git a/js/src/jit-test/tests/wasm/exnref/casting.js b/js/src/jit-test/tests/wasm/exnref/casting.js new file mode 100644 index 0000000000..fa433fc152 --- /dev/null +++ b/js/src/jit-test/tests/wasm/exnref/casting.js @@ -0,0 +1,110 @@ +// |jit-test| skip-if: !wasmGcEnabled() + +const { + refCast, + refTest, + branch, + branchFail, + refCastNullable, + refTestNullable, + branchNullable, + branchFailNullable, +} = wasmEvalText(`(module + (tag $a) + (func $make (param $null i32) (result exnref) + (if (local.get $null) + (then + (return (ref.null exn)) + ) + ) + + try_table (catch_all_ref 0) + throw $a + end + unreachable + ) + + (func (export "refCast") (param $null i32) + (call $make (local.get $null)) + ref.cast (ref exn) + drop + ) + (func (export "refTest") (param $null i32) (result i32) + (call $make (local.get $null)) + ref.test (ref exn) + ) + (func (export "branch") (param $null i32) (result i32) + (block (result (ref exn)) + (call $make (local.get $null)) + br_on_cast 0 exnref (ref exn) + drop + (return (i32.const 0)) + ) + drop + (return (i32.const 1)) + ) + (func (export "branchFail") (param $null i32) (result i32) + (block (result exnref) + (call $make (local.get $null)) + br_on_cast_fail 0 exnref (ref exn) + drop + (return (i32.const 1)) + ) + drop + (return (i32.const 0)) + ) + + (func (export "refCastNullable") (param $null i32) + (call $make (local.get $null)) + ref.cast exnref + drop + ) + (func (export "refTestNullable") (param $null i32) (result i32) + (call $make (local.get $null)) + ref.test exnref + ) + (func (export "branchNullable") (param $null i32) (result i32) + (block (result exnref) + (call $make (local.get $null)) + br_on_cast 0 exnref exnref + drop + (return (i32.const 0)) + ) + drop + (return (i32.const 1)) + ) + (func (export "branchFailNullable") (param $null i32) (result i32) + (block (result exnref) + (call $make (local.get $null)) + br_on_cast_fail 0 exnref exnref + drop + (return (i32.const 1)) + ) + drop + (return (i32.const 0)) + ) +)`).exports; + +// cast non-null exnref -> (ref exn) +refCast(0); +assertEq(refTest(0), 1); +assertEq(branch(0), 1); +assertEq(branchFail(0), 1); + +// cast non-null exnref -> exnref +refCastNullable(0); +assertEq(refTestNullable(0), 1); +assertEq(branchNullable(0), 1); +assertEq(branchFailNullable(0), 1); + +// cast null exnref -> (ref exn) +assertErrorMessage(() => refCast(1), WebAssembly.RuntimeError, /bad cast/); +assertEq(refTest(1), 0); +assertEq(branch(1), 0); +assertEq(branchFail(1), 0); + +// cast null exnref -> exnref +refCastNullable(1); +assertEq(refTestNullable(1), 1); +assertEq(branchNullable(1), 1); +assertEq(branchFailNullable(1), 1); diff --git a/js/src/jit-test/tests/wasm/exnref/directives.txt b/js/src/jit-test/tests/wasm/exnref/directives.txt new file mode 100644 index 0000000000..bc17009ea8 --- /dev/null +++ b/js/src/jit-test/tests/wasm/exnref/directives.txt @@ -0,0 +1 @@ +|jit-test| --wasm-exnref; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmExnRefEnabled() diff --git a/js/src/jit-test/tests/wasm/exnref/throw-ref.js b/js/src/jit-test/tests/wasm/exnref/throw-ref.js new file mode 100644 index 0000000000..bed8c4eca7 --- /dev/null +++ b/js/src/jit-test/tests/wasm/exnref/throw-ref.js @@ -0,0 +1,70 @@ +wasmFailValidateText(`(module + (func + throw_ref + ) +)`, /popping value from empty stack/); + +wasmValidateText(`(module + (func (param exnref) + local.get 0 + throw_ref + ) +)`); + +// Can rethrow a value +{ + let {test} = wasmEvalText(`(module + (tag $a) + (func (export "test") + (block (result exnref) + try_table (catch_all_ref 0) + throw $a + end + unreachable + ) + throw_ref + ) + )`).exports; + + assertErrorMessage(test, WebAssembly.Exception, /.*/); +} + +// Rethrowing a value inside a try works +{ + let {test} = wasmEvalText(`(module + (tag $E) + (func (export "test") (param $shouldRethrow i32) (result i32) + (local $e exnref) + (block $catch (result exnref) + (try_table (catch_ref $E $catch) (throw $E)) + unreachable + ) + (local.set $e) + (block $catch (result exnref) + (try_table (result i32) (catch_ref $E $catch) + (if (i32.eqz (local.get $shouldRethrow)) + (then (throw_ref (local.get $e))) + ) + (i32.const 2) + ) + (return) + ) + (drop) (i32.const 1) + ) + )`).exports; + assertEq(test(0), 1); + assertEq(test(1), 2); +} + +// Traps on null +{ + let {test} = wasmEvalText(`(module + (tag $a) + (func (export "test") + ref.null exn + throw_ref + ) + )`).exports; + + assertErrorMessage(test, WebAssembly.RuntimeError, /null/); +} diff --git a/js/src/jit-test/tests/wasm/exnref/try-table.js b/js/src/jit-test/tests/wasm/exnref/try-table.js new file mode 100644 index 0000000000..c89330917c --- /dev/null +++ b/js/src/jit-test/tests/wasm/exnref/try-table.js @@ -0,0 +1,384 @@ +// A try_table acts like a block label, with results +{ + let maxResults1 = Array.from(Array(1000).keys()); + let maxResults2 = maxResults1.map((x) => x - 1); + const TESTS = [ + ['i32', 'i32.const', [0], [1]], + ['i32 '.repeat(1000), 'i32.const', maxResults1, maxResults2], + ['i64', 'i64.const', [0], [1]], + ['i64 '.repeat(1000), 'i64.const', maxResults1, maxResults2], + ['f32', 'f32.const', [0], [1]], + ['f32 '.repeat(1000), 'f32.const', maxResults1, maxResults2], + ['f64', 'f64.const', [0], [1]], + ['f64 '.repeat(1000), 'f64.const', maxResults1, maxResults2], + ]; + for (let [types, constructor, values1, values2] of TESTS) { + let {test} = wasmEvalText(`(module + (func (export "test") (param $shouldBranch i32) (result ${types}) + try_table (result ${types}) + ${values2.map((x) => `${constructor} ${x}`).join(" ")} + (br_if 1 local.get $shouldBranch) + ${values1.map((x) => `${constructor} ${x}`).join(" ")} + br 0 + end + ) + )`).exports; + assertEqResults(test(0), values1); + assertEqResults(test(1), values2); + } +} + +// A try_table can have params +{ + let maxParams1 = Array.from(Array(1000).keys()); + const TESTS = [ + ['i32', 'i32.const', [0]], + ['i32 '.repeat(1000), 'i32.const', maxParams1], + ['i64', 'i64.const', [0]], + ['i64 '.repeat(1000), 'i64.const', maxParams1], + ['f32', 'f32.const', [0]], + ['f32 '.repeat(1000), 'f32.const', maxParams1], + ['f64', 'f64.const', [0]], + ['f64 '.repeat(1000), 'f64.const', maxParams1], + ]; + for (let [types, constructor, params] of TESTS) { + let {test} = wasmEvalText(`(module + (func (export "test") (result ${types}) + ${params.map((x) => `${constructor} ${x}`).join(" ")} + try_table (param ${types}) + return + end + unreachable + ) + )`).exports; + assertEqResults(test(), params); + } +} + +// Test try_table catching exceptions +{ + let {test} = wasmEvalText(`(module + (tag $A (param i32)) + (tag $B (param i32)) + (tag $C) + + (table funcref (elem $throwA $throwB $throwC $doNothing)) + + (type $empty (func)) + (func $throwA + i32.const 1 + throw $A + ) + (func $throwB + i32.const 2 + throw $B + ) + (func $throwC + throw $C + ) + (func $doNothing) + + (func (export "test") (param i32) (result i32) + block $handleA (result i32 exnref) + block $handleB (result i32 exnref) + block $handleUnknown (result exnref) + try_table + (catch_ref $A $handleA) + (catch_ref $B $handleB) + (catch_all_ref $handleUnknown) + + (call_indirect (type $empty) + local.get 0) + end + (; nothing threw ;) + i32.const -1 + return + end + (; $handleUnknown ;) + drop + i32.const 3 + return + end + (; $handleB ;) + drop + return + end + (; $handleA ;) + drop + return + ) + )`).exports; + // Throwing A results in 1 from the payload from the catch + assertEq(test(0), 1); + // Throwing B results in 2 from the payload from the catch + assertEq(test(1), 2); + // Throwing C results in 3 from the constant in the catch_all + assertEq(test(2), 3); + // Not throwing anything gets -1 from the fallthrough + assertEq(test(3), -1); +} + +// Test try_table catching exceptions without capturing the exnref +{ + let {test} = wasmEvalText(`(module + (tag $A (param i32)) + (tag $B (param i32)) + (tag $C) + + (table funcref (elem $throwA $throwB $throwC $doNothing)) + + (type $empty (func)) + (func $throwA + i32.const 1 + throw $A + ) + (func $throwB + i32.const 2 + throw $B + ) + (func $throwC + throw $C + ) + (func $doNothing) + + (func (export "test") (param i32) (result i32) + block $handleA (result i32) + block $handleB (result i32) + block $handleUnknown + try_table + (catch $A $handleA) + (catch $B $handleB) + (catch_all $handleUnknown) + + (call_indirect (type $empty) + local.get 0) + end + (; nothing threw ;) + i32.const -1 + return + end + (; $handleUnknown ;) + i32.const 3 + return + end + (; $handleB ;) + return + end + (; $handleA ;) + return + ) + )`).exports; + // Throwing A results in 1 from the payload from the catch + assertEq(test(0), 1); + // Throwing B results in 2 from the payload from the catch + assertEq(test(1), 2); + // Throwing C results in 3 from the constant in the catch_all + assertEq(test(2), 3); + // Not throwing anything gets -1 from the fallthrough + assertEq(test(3), -1); +} + +// Test try_table catching exceptions with various payloads +{ + let maxResults1 = Array.from(Array(999).keys()); + const TESTS = [ + ['i32', 'i32.const', [0]], + ['i32 '.repeat(999), 'i32.const', maxResults1], + ['i64', 'i64.const', [0]], + ['i64 '.repeat(999), 'i64.const', maxResults1], + ['f32', 'f32.const', [0]], + ['f32 '.repeat(999), 'f32.const', maxResults1], + ['f64', 'f64.const', [0]], + ['f64 '.repeat(999), 'f64.const', maxResults1], + ]; + for (let [types, constructor, params] of TESTS) { + let {testCatch, testCatchRef} = wasmEvalText(`(module + (tag $E (param ${types})) + + (func (export "testCatch") (result ${types}) + try_table (catch $E 0) + ${params.map((x) => `${constructor} ${x}`).join(" ")} + throw $E + end + unreachable + ) + (func (export "testCatchRef") (result ${types}) + (block (result ${types} exnref) + try_table (catch_ref $E 0) + ${params.map((x) => `${constructor} ${x}`).join(" ")} + throw $E + end + unreachable + ) + drop + return + ) + + )`).exports; + assertEqResults(testCatch(), params); + assertEqResults(testCatchRef(), params); + } +} + +// Test setting locals in conditional control flow +{ + let {test} = wasmEvalText(`(module + (tag $E) + + (func (export "test") (param $shouldThrow i32) (result i32) + (local $result i32) + + (block $join + (block $catch + try_table (catch $E $catch) + local.get $shouldThrow + if + throw $E + end + (local.set $result i32.const 0) + br $join + end + ) + (local.set $result i32.const 1) + br $join + ) + + local.get $result + ) + + )`).exports; + assertEq(test(0), 0); + assertEq(test(1), 1); +} + +// Matching catch clauses is done in order +{ + let {testCatch, testCatchRef, testCatchAll, testCatchAllRef} = wasmEvalText(`(module + (tag $E) + + (func (export "testCatch") + (block $good + (block $bad + try_table (catch $E $good) (catch $E $bad) (catch_all $bad) + throw $E + end + ) + unreachable + ) + ) + (func (export "testCatchAll") + (block $good + (block $bad + try_table (catch_all $good) (catch $E $bad) (catch $E $bad) + throw $E + end + ) + unreachable + ) + ) + (func (export "testCatchRef") + (block $good (result exnref) + (block $bad (result exnref) + try_table (catch_ref $E $good) (catch_ref $E $bad) (catch_all_ref $bad) + throw $E + end + unreachable + ) + unreachable + ) + drop + ) + (func (export "testCatchAllRef") + (block $good (result exnref) + (block $bad (result exnref) + try_table (catch_all_ref $good) (catch_ref $E $bad) (catch_ref $E $bad) + throw $E + end + unreachable + ) + unreachable + ) + drop + ) + )`).exports; + testCatch(); + testCatchAll(); + testCatchRef(); + testCatchAllRef(); +} + +// Test try_table as target of a delegate +{ + let {test} = wasmEvalText(`(module + (tag $E) + (func (export "test") + block $good + block $bad + try_table $a (catch_all $good) + try + try + throw $E + delegate $a + catch $E + br $bad + end + end + end + unreachable + end + ) + )`).exports; + test(); +} + +// Try table cannot be target of rethrow +{ + wasmFailValidateText(`(module + (func + try_table (catch_all 0) rethrow 0 end + ) + )`, /rethrow target/); +} + +// Test try_table catching and rethrowing JS exceptions +{ + let tag = new WebAssembly.Tag({parameters: []}); + let exn = new WebAssembly.Exception(tag, []); + let values = [...WasmExternrefValues, exn]; + function throwJS(value) { + throw value; + } + let {test} = wasmEvalText(`(module + (import "" "tag" (tag $tag)) + (import "" "throwJS" (func $throwJS (param externref))) + (func $innerRethrow (param externref) + (block (result exnref) + try_table (catch_ref $tag 0) (catch_all_ref 0) + local.get 0 + call $throwJS + end + return + ) + throw_ref + ) + (func (export "test") (param externref) + (block (result exnref) + try_table (catch_ref $tag 0) (catch_all_ref 0) + local.get 0 + call $innerRethrow + end + return + ) + throw_ref + ) + )`, {"": {tag, throwJS}}).exports; + + for (let value of values) { + try { + test(value); + assertEq(true, false); + } catch (thrownValue) { + assertEq(thrownValue, value); + } + } +} -- cgit v1.2.3