diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs index 888f450c15..0d38741f03 100644 --- a/crates/wasm-encoder/src/core/code.rs +++ b/crates/wasm-encoder/src/core/code.rs @@ -1308,6 +1308,10 @@ pub enum Instruction<'a> { tag_index: u32, resume_table: Cow<'a, [Handle]>, }, + ResumeThrowRef { + cont_type_index: u32, + resume_table: Cow<'a, [Handle]>, + }, Switch { cont_type_index: u32, tag_index: u32, @@ -2156,6 +2160,10 @@ impl Encode for Instruction<'_> { tag_index, ref resume_table, } => sink.resume_throw(cont_type_index, tag_index, resume_table.iter().cloned()), + Instruction::ResumeThrowRef { + cont_type_index, + ref resume_table, + } => sink.resume_throw_ref(cont_type_index, resume_table.iter().cloned()), Instruction::Switch { cont_type_index, tag_index, diff --git a/crates/wasm-encoder/src/core/instructions.rs b/crates/wasm-encoder/src/core/instructions.rs index 40fb084dca..f8f1d4d59e 100644 --- a/crates/wasm-encoder/src/core/instructions.rs +++ b/crates/wasm-encoder/src/core/instructions.rs @@ -4588,9 +4588,24 @@ impl<'a> InstructionSink<'a> { self } + /// Encode [`Instruction::ResumeThrowRef`]. + pub fn resume_throw_ref>( + &mut self, + cont_type_index: u32, + resume_table: V, + ) -> &mut Self + where + V::IntoIter: ExactSizeIterator, + { + self.sink.push(0xE5); + cont_type_index.encode(self.sink); + encode_vec(resume_table, self.sink); + self + } + /// Encode [`Instruction::Switch`]. pub fn switch(&mut self, cont_type_index: u32, tag_index: u32) -> &mut Self { - self.sink.push(0xE5); + self.sink.push(0xE6); cont_type_index.encode(self.sink); tag_index.encode(self.sink); self diff --git a/crates/wasmparser/src/arity.rs b/crates/wasmparser/src/arity.rs index ab41c7e3bd..c05b4d2360 100644 --- a/crates/wasmparser/src/arity.rs +++ b/crates/wasmparser/src/arity.rs @@ -285,6 +285,16 @@ fn visit_resume_throw( Some((params + 1, results)) } +fn visit_resume_throw_ref( + module: &dyn ModuleArity, + cont: u32, + _table: ResumeTable, +) -> Option<(u32, u32)> { + let (_, results) = module.sub_type_arity(module.sub_type_at(cont)?)?; + // in: [exnref, cont], out: [result(cont)] + Some((2, results)) +} + fn visit_switch(module: &dyn ModuleArity, cont: u32, _tag: u32) -> Option<(u32, u32)> { let (params, _) = module.sub_type_arity(module.sub_type_at(cont)?)?; let st = &module.sub_type_at(cont)?.composite_type.inner; diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index b2abb3b432..0dce75d574 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -1187,7 +1187,8 @@ impl<'a> BinaryReader<'a> { 0xe4 => { visitor.visit_resume_throw(self.read_var_u32()?, self.read_var_u32()?, self.read()?) } - 0xe5 => visitor.visit_switch(self.read_var_u32()?, self.read_var_u32()?), + 0xe5 => visitor.visit_resume_throw_ref(self.read_var_u32()?, self.read()?), + 0xe6 => visitor.visit_switch(self.read_var_u32()?, self.read_var_u32()?), 0xfb => self.visit_0xfb_operator(pos, visitor)?, 0xfc => self.visit_0xfc_operator(pos, visitor)?, diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs index fc7e37cc43..189ce3f7b8 100644 --- a/crates/wasmparser/src/lib.rs +++ b/crates/wasmparser/src/lib.rs @@ -788,6 +788,7 @@ macro_rules! _for_each_operator_group { Suspend { tag_index: u32 } => visit_suspend (arity custom) Resume { cont_type_index: u32, resume_table: $crate::ResumeTable } => visit_resume (arity custom) ResumeThrow { cont_type_index: u32, tag_index: u32, resume_table: $crate::ResumeTable } => visit_resume_throw (arity custom) + ResumeThrowRef { cont_type_index: u32, resume_table: $crate::ResumeTable } => visit_resume_throw_ref (arity custom) Switch { cont_type_index: u32, tag_index: u32 } => visit_switch (arity custom) } diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index e37a1107e7..406a8bb347 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -4368,6 +4368,16 @@ where } Ok(()) } + fn visit_resume_throw_ref(&mut self, type_index: u32, table: ResumeTable) -> Self::Output { + let ft = self.check_resume_table(table, type_index)?; + self.pop_concrete_ref(true, type_index)?; + self.pop_operand(Some(ValType::EXNREF))?; + + for &ty in ft.results() { + self.push_operand(ty)? + } + Ok(()) + } fn visit_switch(&mut self, type_index: u32, tag_index: u32) -> Self::Output { // [t1* (ref null $ct2)] -> [te1*] let cont_ty = self.cont_type_at(type_index)?; diff --git a/crates/wasmprinter/src/operator.rs b/crates/wasmprinter/src/operator.rs index f1c76500a9..6f17bc9cbf 100644 --- a/crates/wasmprinter/src/operator.rs +++ b/crates/wasmprinter/src/operator.rs @@ -1402,6 +1402,7 @@ macro_rules! define_visit { (name Suspend) => ("suspend"); (name Resume) => ("resume"); (name ResumeThrow) => ("resume_throw"); + (name ResumeThrowRef) => ("resume_throw_ref"); (name Switch) => ("switch"); (name I64Add128) => ("i64.add128"); (name I64Sub128) => ("i64.sub128"); diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs index 358bc77fc0..e84f4610a2 100644 --- a/crates/wast/src/core/binary.rs +++ b/crates/wast/src/core/binary.rs @@ -1334,6 +1334,13 @@ impl<'a> Encode for ResumeThrow<'a> { } } +impl<'a> Encode for ResumeThrowRef<'a> { + fn encode(&self, dst: &mut Vec) { + self.type_index.encode(dst); + self.table.encode(dst); + } +} + impl<'a> Encode for ResumeTable<'a> { fn encode(&self, dst: &mut Vec) { self.handlers.encode(dst); diff --git a/crates/wast/src/core/expr.rs b/crates/wast/src/core/expr.rs index d00d13c666..820edecd4b 100644 --- a/crates/wast/src/core/expr.rs +++ b/crates/wast/src/core/expr.rs @@ -1195,7 +1195,8 @@ instructions! { Suspend(Index<'a>) : [0xe2] : "suspend", Resume(Resume<'a>) : [0xe3] : "resume", ResumeThrow(ResumeThrow<'a>) : [0xe4] : "resume_throw", - Switch(Switch<'a>) : [0xe5] : "switch", + ResumeThrowRef(ResumeThrowRef<'a>) : [0xe5] : "resume_throw_ref", + Switch(Switch<'a>) : [0xe6] : "switch", // Wide arithmetic proposal I64Add128 : [0xfc, 19] : "i64.add128", @@ -1313,6 +1314,23 @@ impl<'a> Parse<'a> for ResumeThrow<'a> { } } +/// Extra information associated with the resume_throw_ref instruction +#[derive(Debug, Clone)] +#[allow(missing_docs)] +pub struct ResumeThrowRef<'a> { + pub type_index: Index<'a>, + pub table: ResumeTable<'a>, +} + +impl<'a> Parse<'a> for ResumeThrowRef<'a> { + fn parse(parser: Parser<'a>) -> Result { + Ok(ResumeThrowRef { + type_index: parser.parse()?, + table: parser.parse()?, + }) + } +} + /// Extra information associated with the switch instruction #[derive(Debug, Clone)] #[allow(missing_docs)] diff --git a/crates/wast/src/core/resolve/names.rs b/crates/wast/src/core/resolve/names.rs index 9f5fcc00d0..5c21673d78 100644 --- a/crates/wast/src/core/resolve/names.rs +++ b/crates/wast/src/core/resolve/names.rs @@ -677,6 +677,10 @@ impl<'a, 'b> ExprResolver<'a, 'b> { self.resolver.resolve(&mut rt.tag_index, Ns::Tag)?; self.resolve_resume_table(&mut rt.table)?; } + ResumeThrowRef(rt) => { + self.resolver.resolve(&mut rt.type_index, Ns::Type)?; + self.resolve_resume_table(&mut rt.table)?; + } Switch(s) => { self.resolver.resolve(&mut s.type_index, Ns::Type)?; self.resolver.resolve(&mut s.tag_index, Ns::Tag)?; diff --git a/tests/cli/stack-switching/resume_throw.wast b/tests/cli/stack-switching/resume_throw.wast new file mode 100644 index 0000000000..ece32225d4 --- /dev/null +++ b/tests/cli/stack-switching/resume_throw.wast @@ -0,0 +1,315 @@ +;; RUN: wast --assert default --snapshot tests/snapshots % -f stack-switching + +;; Tests for resume_throw and resume_throw_ref + +;; Test resume_throw on a continuation that is never resumed. +(module + (tag $exn) + + (type $f (func)) + (type $k (cont $f)) + + (func $never (unreachable)) + (elem declare func $never) + + (func (export "throw_never") + (block $h + (try_table (catch $exn $h) + (cont.new $k (ref.func $never)) + (resume_throw $k $exn) + (unreachable) + ) + ) + ) +) +(assert_return (invoke "throw_never")) + +;; Test resume_throw with a value type argument. +(module + (tag $exn_i32 (param i32)) + + (type $f (func)) + (type $k (cont $f)) + + (func $never (unreachable)) + (elem declare func $never) + + (func (export "throw_never_i32") (result i32) + (block $h (result i32) + (try_table (result i32) (catch $exn_i32 $h) + (i32.const 42) + (cont.new $k (ref.func $never)) + (resume_throw $k $exn_i32) + (unreachable) + ) + ) + ) +) +(assert_return (invoke "throw_never_i32") (i32.const 42)) + +;; Test resume_throw with a reference type argument. +(module + (tag $exn_ref (param externref)) + + (type $f (func)) + (type $k (cont $f)) + + (func $never (unreachable)) + (elem declare func $never) + + (func (export "throw_never_ref") (param $val externref) (result externref) + (block $h (result externref) + (try_table (result externref) (catch $exn_ref $h) + (local.get $val) + (cont.new $k (ref.func $never)) + (resume_throw $k $exn_ref) + (unreachable) + ) + ) + ) +) +(assert_return (invoke "throw_never_ref" (ref.extern 1)) (ref.extern 1)) + +;; Test resume_throw where the continuation handles the exception. +(module + (tag $exn) + (tag $e1) + + (type $f (func)) + (type $k (cont $f)) + + (func $handler + (block $h + (try_table (catch $exn $h) + (suspend $e1) + ) + ) + ) + (elem declare func $handler) + + (func (export "throw_handled") + (block $h (result (ref $k)) + (resume $k (on $e1 $h) (cont.new $k (ref.func $handler))) + (unreachable) + ) + (resume_throw $k $exn) + ) +) +(assert_return (invoke "throw_handled")) + +;; Test resume_throw where the continuation does not handle the exception. +(module + (tag $exn) + (tag $e1) + + (type $f (func)) + (type $k (cont $f)) + + (func $no_handler + (suspend $e1) + ) + (elem declare func $no_handler) + + (func (export "throw_unhandled") + (block $h (result (ref $k)) + (resume $k (on $e1 $h) (cont.new $k (ref.func $no_handler))) + (unreachable) + ) + (resume_throw $k $exn) + ) +) +(assert_exception (invoke "throw_unhandled")) + +;; Test resume_throw on a consumed continuation. +(module + (tag $exn) + + (type $f (func)) + (type $k (cont $f)) + + (func $f1) + (elem declare func $f1) + + (func (export "throw_consumed") + (local $k_ref (ref $k)) + (local.set $k_ref (cont.new $k (ref.func $f1))) + (resume $k (local.get $k_ref)) ;; consume it + (resume_throw $k $exn (local.get $k_ref)) ;; should trap + ) +) +(assert_trap (invoke "throw_consumed") "continuation already consumed") + +;; Test resume_throw on a null continuation reference. +(module + (tag $exn) + (type $f (func)) + (type $k (cont $f)) + (func (export "throw_null") + (resume_throw $k $exn (ref.null $k)) + ) +) +(assert_trap (invoke "throw_null") "null continuation reference") + +;; Test resume_throw_ref where the continuation handles the exception. +(module + (tag $e0 (param i32)) + (tag $yield) + + (type $f (func (result i32))) + (type $k (cont $f)) + + (func (export "throw_handled_ref") (result i32) + (local $k_ref (ref $k)) + (local.set $k_ref (cont.new $k (ref.func $yield42))) + + (block $y (result (ref $k)) + (resume $k (on $yield $y) + (local.get $k_ref)) + (unreachable)) + (local.set $k_ref) + + (block $h (result i32 exnref) + (try_table (catch_ref $e0 $h) + (i32.const 42) + (throw $e0)) + (unreachable) + ) + + (resume_throw_ref $k (local.get $k_ref)) + (return) + ) + + (func $yield42 (result i32) + (block $h (result i32) + (try_table (result i32) (catch $e0 $h) + (suspend $yield) + (unreachable) + ) + ) + ) + (elem declare func $yield42) +) +(assert_return (invoke "throw_handled_ref") (i32.const 42)) + + +;; Test resume_throw_ref where the continuation does not handle the exception. +(module + (tag $e0) + + (type $f (func)) + (type $k (cont $f)) + + (func $no_handler + (unreachable) ;; We only throw into this function + ) + (elem declare func $no_handler) + + (func (export "throw_unhandled_ref") + (block $h (result exnref) + (try_table (catch_ref $e0 $h) (throw $e0)) + (unreachable) + ) + (resume_throw_ref $k (cont.new $k (ref.func $no_handler))) + ) +) +(assert_exception (invoke "throw_unhandled_ref")) + +;; Test resume_throw_ref on a consumed continuation. +(module + (tag $e0) + + (type $f (func)) + (type $k (cont $f)) + + (func $f1) + (elem declare func $f1) + + (func (export "throw_consumed_ref") + (local $k_ref (ref $k)) + (local.set $k_ref (cont.new $k (ref.func $f1))) + (resume $k (local.get $k_ref)) ;; consume it + + (block $h (result exnref) + (try_table (result exnref) (catch_ref $e0 $h) + (throw $e0) + ) + ) + (local.get $k_ref) + + (resume_throw_ref $k) ;; should trap + ) +) +(assert_trap (invoke "throw_consumed_ref") "continuation already consumed") + +;; Test resume_throw_ref on a null continuation reference. +(module + (tag $e0) + (type $f (func)) + (type $k (cont $f)) + (func (export "throw_null_ref") + (block $h (result exnref) + (try_table (catch_ref $e0 $h) + (throw $e0)) + (unreachable) + ) + (resume_throw_ref $k (ref.null $k)) + ) +) +(assert_trap (invoke "throw_null_ref") "null continuation reference") + +;; ---- Validation ---- + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn (param i32)) + (func + (i64.const 0) + (resume_throw $ct $exn (ref.null $ct)) ;; null continuation + (unreachable))) + "type mismatch") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn) + (func + (ref.null $ct) + (i32.const 0) + (resume_throw $ct $exn) ;; exception tag does not take paramter + (unreachable))) + "type mismatch") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn (param i32)) + (func + (resume_throw $ct $exn (ref.null $ct)) ;; missing exception payload + (unreachable))) + "type mismatch") + + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn (param externref)) + (func + (i64.const 0) + (resume_throw_ref $ct (ref.null $ct)) ;; expecting an exception ref + (unreachable))) + "type mismatch") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (func + (resume_throw_ref $ct (ref.null $ct)) ;; expecting an exception ref + (unreachable))) + "type mismatch") + diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast.json b/tests/snapshots/cli/stack-switching/resume_throw.wast.json new file mode 100644 index 0000000000..9958f5cabc --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast.json @@ -0,0 +1,234 @@ +{ + "source_filename": "tests/cli/stack-switching/resume_throw.wast", + "commands": [ + { + "type": "module", + "line": 6, + "filename": "resume_throw.0.wasm", + "module_type": "binary" + }, + { + "type": "assert_return", + "line": 25, + "action": { + "type": "invoke", + "field": "throw_never", + "args": [] + }, + "expected": [] + }, + { + "type": "module", + "line": 28, + "filename": "resume_throw.1.wasm", + "module_type": "binary" + }, + { + "type": "assert_return", + "line": 48, + "action": { + "type": "invoke", + "field": "throw_never_i32", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "42" + } + ] + }, + { + "type": "module", + "line": 51, + "filename": "resume_throw.2.wasm", + "module_type": "binary" + }, + { + "type": "assert_return", + "line": 71, + "action": { + "type": "invoke", + "field": "throw_never_ref", + "args": [ + { + "type": "externref", + "value": "1" + } + ] + }, + "expected": [ + { + "type": "externref", + "value": "1" + } + ] + }, + { + "type": "module", + "line": 74, + "filename": "resume_throw.3.wasm", + "module_type": "binary" + }, + { + "type": "assert_return", + "line": 98, + "action": { + "type": "invoke", + "field": "throw_handled", + "args": [] + }, + "expected": [] + }, + { + "type": "module", + "line": 101, + "filename": "resume_throw.4.wasm", + "module_type": "binary" + }, + { + "type": "assert_exception", + "line": 121, + "action": { + "type": "invoke", + "field": "throw_unhandled", + "args": [] + } + }, + { + "type": "module", + "line": 124, + "filename": "resume_throw.5.wasm", + "module_type": "binary" + }, + { + "type": "assert_trap", + "line": 140, + "action": { + "type": "invoke", + "field": "throw_consumed", + "args": [] + }, + "text": "continuation already consumed" + }, + { + "type": "module", + "line": 143, + "filename": "resume_throw.6.wasm", + "module_type": "binary" + }, + { + "type": "assert_trap", + "line": 151, + "action": { + "type": "invoke", + "field": "throw_null", + "args": [] + }, + "text": "null continuation reference" + }, + { + "type": "module", + "line": 154, + "filename": "resume_throw.7.wasm", + "module_type": "binary" + }, + { + "type": "assert_return", + "line": 192, + "action": { + "type": "invoke", + "field": "throw_handled_ref", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "42" + } + ] + }, + { + "type": "module", + "line": 196, + "filename": "resume_throw.8.wasm", + "module_type": "binary" + }, + { + "type": "assert_exception", + "line": 215, + "action": { + "type": "invoke", + "field": "throw_unhandled_ref", + "args": [] + } + }, + { + "type": "module", + "line": 218, + "filename": "resume_throw.9.wasm", + "module_type": "binary" + }, + { + "type": "assert_trap", + "line": 242, + "action": { + "type": "invoke", + "field": "throw_consumed_ref", + "args": [] + }, + "text": "continuation already consumed" + }, + { + "type": "module", + "line": 245, + "filename": "resume_throw.10.wasm", + "module_type": "binary" + }, + { + "type": "assert_trap", + "line": 258, + "action": { + "type": "invoke", + "field": "throw_null_ref", + "args": [] + }, + "text": "null continuation reference" + }, + { + "type": "assert_invalid", + "line": 263, + "filename": "resume_throw.11.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 274, + "filename": "resume_throw.12.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 286, + "filename": "resume_throw.13.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 297, + "filename": "resume_throw.14.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 308, + "filename": "resume_throw.15.wasm", + "module_type": "binary", + "text": "type mismatch" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/0.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/0.print new file mode 100644 index 0000000000..7574fea0fe --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/0.print @@ -0,0 +1,20 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $exn (;0;) (type $f)) + (export "throw_never" (func 1)) + (elem (;0;) declare func $never) + (func $never (;0;) (type $f) + unreachable + ) + (func (;1;) (type $f) + block $h + try_table (catch $exn $h) ;; label = @2 + ref.func $never + cont.new $k + resume_throw $k $exn + unreachable + end + end + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/10.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/10.print new file mode 100644 index 0000000000..3a756566de --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/10.print @@ -0,0 +1,18 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $exn (;0;) (type $f)) + (export "throw_consumed" (func 1)) + (elem (;0;) declare func $f1) + (func $f1 (;0;) (type $f)) + (func (;1;) (type $f) + (local $k_ref (ref $k)) + ref.func $f1 + cont.new $k + local.set $k_ref + local.get $k_ref + resume $k + local.get $k_ref + resume_throw $k $exn + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/12.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/12.print new file mode 100644 index 0000000000..82733d7ed0 --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/12.print @@ -0,0 +1,10 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $exn (;0;) (type $f)) + (export "throw_null" (func 0)) + (func (;0;) (type $f) + ref.null $k + resume_throw $k $exn + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/14.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/14.print new file mode 100644 index 0000000000..e48a1606f9 --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/14.print @@ -0,0 +1,41 @@ +(module + (type $f (;0;) (func (result i32))) + (type $k (;1;) (cont $f)) + (type (;2;) (func (param i32))) + (type (;3;) (func)) + (type (;4;) (func (result i32 exnref))) + (tag $e0 (;0;) (type 2) (param i32)) + (tag $yield (;1;) (type 3)) + (export "throw_handled_ref" (func 0)) + (elem (;0;) declare func $yield42) + (func (;0;) (type $f) (result i32) + (local $k_ref (ref $k)) + ref.func $yield42 + cont.new $k + local.set $k_ref + block $y (result (ref $k)) + local.get $k_ref + resume $k (on $yield $y) + unreachable + end + local.set $k_ref + block $h (type 4) (result i32 exnref) + try_table (catch_ref $e0 $h) ;; label = @2 + i32.const 42 + throw $e0 + end + unreachable + end + local.get $k_ref + resume_throw_ref $k + return + ) + (func $yield42 (;1;) (type $f) (result i32) + block $h (result i32) + try_table (result i32) (catch $e0 $h) ;; label = @2 + suspend $yield + unreachable + end + end + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/16.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/16.print new file mode 100644 index 0000000000..d0a6f90ace --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/16.print @@ -0,0 +1,21 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $e0 (;0;) (type $f)) + (export "throw_unhandled_ref" (func 1)) + (elem (;0;) declare func $no_handler) + (func $no_handler (;0;) (type $f) + unreachable + ) + (func (;1;) (type $f) + block $h (result exnref) + try_table (catch_ref $e0 $h) ;; label = @2 + throw $e0 + end + unreachable + end + ref.func $no_handler + cont.new $k + resume_throw_ref $k + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/18.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/18.print new file mode 100644 index 0000000000..2392db1399 --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/18.print @@ -0,0 +1,23 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $e0 (;0;) (type $f)) + (export "throw_consumed_ref" (func 1)) + (elem (;0;) declare func $f1) + (func $f1 (;0;) (type $f)) + (func (;1;) (type $f) + (local $k_ref (ref $k)) + ref.func $f1 + cont.new $k + local.set $k_ref + local.get $k_ref + resume $k + block $h (result exnref) + try_table (result exnref) (catch_ref $e0 $h) ;; label = @2 + throw $e0 + end + end + local.get $k_ref + resume_throw_ref $k + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/2.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/2.print new file mode 100644 index 0000000000..ce1b40b670 --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/2.print @@ -0,0 +1,23 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (type (;2;) (func (param i32))) + (type (;3;) (func (result i32))) + (tag $exn_i32 (;0;) (type 2) (param i32)) + (export "throw_never_i32" (func 1)) + (elem (;0;) declare func $never) + (func $never (;0;) (type $f) + unreachable + ) + (func (;1;) (type 3) (result i32) + block $h (result i32) + try_table (result i32) (catch $exn_i32 $h) ;; label = @2 + i32.const 42 + ref.func $never + cont.new $k + resume_throw $k $exn_i32 + unreachable + end + end + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/20.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/20.print new file mode 100644 index 0000000000..bb462da4c1 --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/20.print @@ -0,0 +1,16 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $e0 (;0;) (type $f)) + (export "throw_null_ref" (func 0)) + (func (;0;) (type $f) + block $h (result exnref) + try_table (catch_ref $e0 $h) ;; label = @2 + throw $e0 + end + unreachable + end + ref.null $k + resume_throw_ref $k + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/4.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/4.print new file mode 100644 index 0000000000..3106e8924b --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/4.print @@ -0,0 +1,23 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (type (;2;) (func (param externref))) + (type (;3;) (func (param externref) (result externref))) + (tag $exn_ref (;0;) (type 2) (param externref)) + (export "throw_never_ref" (func 1)) + (elem (;0;) declare func $never) + (func $never (;0;) (type $f) + unreachable + ) + (func (;1;) (type 3) (param $val externref) (result externref) + block $h (result externref) + try_table (result externref) (catch $exn_ref $h) ;; label = @2 + local.get $val + ref.func $never + cont.new $k + resume_throw $k $exn_ref + unreachable + end + end + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/6.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/6.print new file mode 100644 index 0000000000..95cf126d66 --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/6.print @@ -0,0 +1,24 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $exn (;0;) (type $f)) + (tag $e1 (;1;) (type $f)) + (export "throw_handled" (func 1)) + (elem (;0;) declare func $handler) + (func $handler (;0;) (type $f) + block $h + try_table (catch $exn $h) ;; label = @2 + suspend $e1 + end + end + ) + (func (;1;) (type $f) + block $h (result (ref $k)) + ref.func $handler + cont.new $k + resume $k (on $e1 $h) + unreachable + end + resume_throw $k $exn + ) +) diff --git a/tests/snapshots/cli/stack-switching/resume_throw.wast/8.print b/tests/snapshots/cli/stack-switching/resume_throw.wast/8.print new file mode 100644 index 0000000000..7708d18c30 --- /dev/null +++ b/tests/snapshots/cli/stack-switching/resume_throw.wast/8.print @@ -0,0 +1,20 @@ +(module + (type $f (;0;) (func)) + (type $k (;1;) (cont $f)) + (tag $exn (;0;) (type $f)) + (tag $e1 (;1;) (type $f)) + (export "throw_unhandled" (func 1)) + (elem (;0;) declare func $no_handler) + (func $no_handler (;0;) (type $f) + suspend $e1 + ) + (func (;1;) (type $f) + block $h (result (ref $k)) + ref.func $no_handler + cont.new $k + resume $k (on $e1 $h) + unreachable + end + resume_throw $k $exn + ) +)