diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs
index 1c9b2b0b31..96dc786756 100644
--- a/crates/wasmparser/src/binary_reader.rs
+++ b/crates/wasmparser/src/binary_reader.rs
@@ -1101,15 +1101,16 @@ impl<'a> BinaryReader<'a> {
0x06 => Operator::Try {
ty: self.read_blocktype()?,
},
- 0x07 => Operator::Catch,
+ 0x07 => Operator::Catch {
+ index: self.read_var_u32()?,
+ },
0x08 => Operator::Throw {
index: self.read_var_u32()?,
},
- 0x09 => Operator::Rethrow,
- 0x0a => Operator::BrOnExn {
+ 0x09 => Operator::Rethrow {
relative_depth: self.read_var_u32()?,
- index: self.read_var_u32()?,
},
+ 0x0a => Operator::Unwind,
0x0b => Operator::End,
0x0c => Operator::Br {
relative_depth: self.read_var_u32()?,
diff --git a/crates/wasmparser/src/operators_validator.rs b/crates/wasmparser/src/operators_validator.rs
index e49c10fca7..e764db784c 100644
--- a/crates/wasmparser/src/operators_validator.rs
+++ b/crates/wasmparser/src/operators_validator.rs
@@ -114,6 +114,8 @@ enum FrameKind {
Loop,
Try,
Catch,
+ CatchAll,
+ Unwind,
}
impl OperatorValidator {
@@ -578,10 +580,24 @@ impl OperatorValidator {
}
Operator::Else => {
let frame = self.pop_ctrl(resources)?;
- if frame.kind != FrameKind::If {
- bail_op_err!("else found outside of an `if` block");
+ // The `catch_all` instruction shares an opcode with `else`,
+ // so we check the frame to see how it's interpreted.
+ match frame.kind {
+ FrameKind::If => {
+ self.push_ctrl(FrameKind::Else, frame.block_type, resources)?
+ }
+ FrameKind::Try | FrameKind::Catch => {
+ // We assume `self.features.exceptions` is true when
+ // these frame kinds are present.
+ self.control.push(Frame {
+ kind: FrameKind::CatchAll,
+ block_type: frame.block_type,
+ height: self.operands.len(),
+ unreachable: false,
+ });
+ }
+ _ => bail_op_err!("else found outside of an `if` block"),
}
- self.push_ctrl(FrameKind::Else, frame.block_type, resources)?;
}
Operator::Try { ty } => {
self.check_exceptions_enabled()?;
@@ -591,10 +607,10 @@ impl OperatorValidator {
}
self.push_ctrl(FrameKind::Try, ty, resources)?;
}
- Operator::Catch => {
+ Operator::Catch { index } => {
self.check_exceptions_enabled()?;
let frame = self.pop_ctrl(resources)?;
- if frame.kind != FrameKind::Try {
+ if frame.kind != FrameKind::Try && frame.kind != FrameKind::Catch {
bail_op_err!("catch found outside of an `try` block");
}
// Start a new frame and push `exnref` value.
@@ -604,7 +620,12 @@ impl OperatorValidator {
height: self.operands.len(),
unreachable: false,
});
- self.push_operand(Type::ExnRef)?;
+ // Push exception argument types.
+ let event_ty = event_at(&resources, index)?;
+ let ty = func_type_at(&resources, event_ty.type_index)?;
+ for ty in ty.inputs() {
+ self.push_operand(ty)?;
+ }
}
Operator::Throw { index } => {
self.check_exceptions_enabled()?;
@@ -619,25 +640,30 @@ impl OperatorValidator {
}
self.unreachable();
}
- Operator::Rethrow => {
+ Operator::Rethrow { relative_depth } => {
self.check_exceptions_enabled()?;
- self.pop_operand(Some(Type::ExnRef))?;
+ // This is not a jump, but we need to check that the `rethrow`
+ // targets an actual `catch` to get the exception.
+ let (_, kind) = self.jump(relative_depth)?;
+ if kind != FrameKind::Catch && kind != FrameKind::CatchAll {
+ bail_op_err!("rethrow target was not a `catch` block");
+ }
self.unreachable();
}
- Operator::BrOnExn {
- relative_depth,
- index,
- } => {
+ Operator::Unwind => {
self.check_exceptions_enabled()?;
- let (ty, kind) = self.jump(relative_depth)?;
- self.pop_operand(Some(Type::ExnRef))?;
- // Check the exception's argument values with target block's.
- let event_ty = event_at(&resources, index)?;
- let exn_args = func_type_at(&resources, event_ty.type_index)?;
- if Iterator::ne(exn_args.inputs(), label_types(ty, resources, kind)?) {
- bail_op_err!("target block types do not match");
+ // Switch from `try` to an `unwind` frame, so we can check that
+ // the result type is empty.
+ let frame = self.pop_ctrl(resources)?;
+ if frame.kind != FrameKind::Try {
+ bail_op_err!("unwind found outside of an `try` block");
}
- self.push_operand(Type::ExnRef)?;
+ self.control.push(Frame {
+ kind: FrameKind::Unwind,
+ block_type: TypeOrFuncType::Type(Type::EmptyBlockType),
+ height: self.operands.len(),
+ unreachable: false,
+ });
}
Operator::End => {
let mut frame = self.pop_ctrl(resources)?;
diff --git a/crates/wasmparser/src/primitives.rs b/crates/wasmparser/src/primitives.rs
index a8fedb7961..3f92a09ed4 100644
--- a/crates/wasmparser/src/primitives.rs
+++ b/crates/wasmparser/src/primitives.rs
@@ -347,10 +347,10 @@ pub enum Operator<'a> {
If { ty: TypeOrFuncType },
Else,
Try { ty: TypeOrFuncType },
- Catch,
+ Catch { index: u32 },
Throw { index: u32 },
- Rethrow,
- BrOnExn { relative_depth: u32, index: u32 },
+ Rethrow { relative_depth: u32 },
+ Unwind,
End,
Br { relative_depth: u32 },
BrIf { relative_depth: u32 },
diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs
index 066b5fd5ca..9ed318548e 100644
--- a/crates/wasmprinter/src/lib.rs
+++ b/crates/wasmprinter/src/lib.rs
@@ -665,7 +665,7 @@ impl Printer {
// `else`/`catch` are special in that it's printed at
// the previous indentation, but it doesn't actually change
// our nesting level.
- Operator::Else | Operator::Catch => {
+ Operator::Else | Operator::Catch { .. } | Operator::Unwind => {
self.nesting -= 1;
self.newline();
self.nesting += 1;
@@ -729,24 +729,21 @@ impl Printer {
self.print_blockty(ty)?;
write!(self.result, " ;; label = @{}", cur_label)?;
}
- Catch => self.result.push_str("catch"),
+ Catch { index } => {
+ write!(self.result, "catch {}", index)?;
+ }
Throw { index } => {
write!(self.result, "throw {}", index)?;
}
- Rethrow => self.result.push_str("rethrow"),
- BrOnExn {
- relative_depth,
- index,
- } => {
+ Rethrow { relative_depth } => {
write!(
self.result,
- "br_on_exn {} (;{};)",
+ "rethrow {} (;{};)",
relative_depth,
- label(*relative_depth),
+ label(*relative_depth)
)?;
- write!(self.result, " {}", index)?;
}
-
+ Unwind => self.result.push_str("unwind"),
End => self.result.push_str("end"),
Br { relative_depth } => {
write!(
diff --git a/crates/wast/src/ast/expr.rs b/crates/wast/src/ast/expr.rs
index b771c96097..1dd3bd0ebd 100644
--- a/crates/wast/src/ast/expr.rs
+++ b/crates/wast/src/ast/expr.rs
@@ -84,7 +84,9 @@ enum If<'a> {
enum Try<'a> {
/// Next thing to parse is the `do` block.
Do(Instruction<'a>),
- /// Next thing to parse is the `catch` block.
+ /// Next thing to parse is `catch`/`catch_all`, or `unwind`.
+ CatchOrUnwind,
+ /// Next thing to parse is a `catch` block or `catch_all`.
Catch,
/// This `try` statement has finished parsing and if anything remains it's a
/// syntax error.
@@ -195,8 +197,8 @@ impl<'a> ExpressionParser<'a> {
Level::Try(Try::Do(_)) => {
return Err(parser.error("previous `try` had no `do`"));
}
- Level::Try(Try::Catch) => {
- return Err(parser.error("previous `try` had no `catch`"));
+ Level::Try(Try::CatchOrUnwind) => {
+ return Err(parser.error("previous `try` had no `catch`, `catch_all`, or `unwind`"));
}
Level::Try(_) => {
self.instrs.push(Instruction::End(None));
@@ -305,7 +307,7 @@ impl<'a> ExpressionParser<'a> {
/// than an `if` as the syntactic form is:
///
/// ```wat
- /// (try (do $do) (catch $catch))
+ /// (try (do $do) (catch $event $catch))
/// ```
///
/// where the `do` and `catch` keywords are mandatory, even for an empty
@@ -328,7 +330,7 @@ impl<'a> ExpressionParser<'a> {
if parser.parse::