Skip to content

Commit

Permalink
wast: Add exnref, try_table, and throw_ref (#1225)
Browse files Browse the repository at this point in the history
* wast: Add exnref, try_table, and throw_ref

These types and instructions are part of the exception handling rework tracked
in (WebAssembly/exception-handling#281).

* Add catch_ref and catch_all_ref as catch variants that capture the exnref

* Fix order of flag bytes
  • Loading branch information
eqrion committed Oct 2, 2023
1 parent 4f61ab8 commit c950866
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 1 deletion.
3 changes: 3 additions & 0 deletions crates/wast/src/component/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,9 @@ impl From<core::HeapType<'_>> for wasm_encoder::HeapType {
match r {
core::HeapType::Func => Self::Func,
core::HeapType::Extern => Self::Extern,
core::HeapType::Exn => {
todo!("encoding of exceptions proposal types not yet implemented")
}
core::HeapType::Index(Index::Num(i, _)) => Self::Indexed(i),
core::HeapType::Index(_) => panic!("unresolved index"),
core::HeapType::Any
Expand Down
1 change: 1 addition & 0 deletions crates/wast/src/component/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ impl<'a> Resolver<'a> {
ValType::Ref(r) => match &mut r.heap {
core::HeapType::Func
| core::HeapType::Extern
| core::HeapType::Exn
| core::HeapType::Any
| core::HeapType::Eq
| core::HeapType::Array
Expand Down
47 changes: 47 additions & 0 deletions crates/wast/src/core/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ impl<'a> Encode for HeapType<'a> {
match self {
HeapType::Func => e.push(0x70),
HeapType::Extern => e.push(0x6f),
HeapType::Exn => e.push(0x69),
HeapType::Any => e.push(0x6e),
HeapType::Eq => e.push(0x6d),
HeapType::Struct => e.push(0x6b),
Expand Down Expand Up @@ -277,6 +278,11 @@ impl<'a> Encode for RefType<'a> {
nullable: true,
heap: HeapType::Extern,
} => e.push(0x6f),
// The 'exnref' binary abbreviation
RefType {
nullable: true,
heap: HeapType::Exn,
} => e.push(0x69),
// The 'eqref' binary abbreviation
RefType {
nullable: true,
Expand Down Expand Up @@ -1028,6 +1034,47 @@ impl Encode for Id<'_> {
}
}

impl<'a> Encode for TryTable<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.block.encode(dst);
self.catches.encode(dst);

let catch_all_flag_byte: u8 = match self.catch_all {
None => 0,
Some(TryTableCatchAll {
kind: TryTableCatchKind::Drop,
..
}) => 1,
Some(TryTableCatchAll {
kind: TryTableCatchKind::Ref,
..
}) => 2,
};
catch_all_flag_byte.encode(dst);
if let Some(catch_all) = &self.catch_all {
catch_all.encode(dst);
}
}
}

impl<'a> Encode for TryTableCatch<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
let catch_flag_byte: u8 = match self.kind {
TryTableCatchKind::Drop => 0,
TryTableCatchKind::Ref => 1,
};
catch_flag_byte.encode(dst);
self.tag.encode(dst);
self.label.encode(dst);
}
}

impl<'a> Encode for TryTableCatchAll<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.label.encode(dst);
}
}

impl Encode for V128Const {
fn encode(&self, dst: &mut Vec<u8>) {
dst.extend_from_slice(&self.to_le_bytes());
Expand Down
83 changes: 83 additions & 0 deletions crates/wast/src/core/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,10 @@ instructions! {
Delegate(Index<'a>) : [0x18] : "delegate",
CatchAll : [0x19] : "catch_all",

// Exception handling proposal extension for 'exnref'
ThrowRef : [0x0a] : "throw_ref",
TryTable(TryTable<'a>) : [0x1f] : "try_table",

// Relaxed SIMD proposal
I8x16RelaxedSwizzle : [0xfd, 0x100]: "i8x16.relaxed_swizzle",
I32x4RelaxedTruncF32x4S : [0xfd, 0x101]: "i32x4.relaxed_trunc_f32x4_s",
Expand Down Expand Up @@ -1219,6 +1223,85 @@ impl<'a> Parse<'a> for BlockType<'a> {
}
}

#[derive(Debug)]
#[allow(missing_docs)]
pub struct TryTable<'a> {
pub block: Box<BlockType<'a>>,
pub catches: Vec<TryTableCatch<'a>>,
pub catch_all: Option<TryTableCatchAll<'a>>,
}

impl<'a> Parse<'a> for TryTable<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let block = parser.parse()?;

let mut catches = Vec::new();
while parser.peek2::<kw::catch>()? || parser.peek2::<kw::catch_ref>()? {
catches.push(parser.parens(|p| {
let kind = if parser.peek::<kw::catch_ref>()? {
p.parse::<kw::catch_ref>()?;
TryTableCatchKind::Ref
} else {
p.parse::<kw::catch>()?;
TryTableCatchKind::Drop
};
Ok(TryTableCatch {
kind,
tag: p.parse()?,
label: p.parse()?,
})
})?);
}

let mut catch_all = None;
if parser.peek2::<kw::catch_all>()? || parser.peek2::<kw::catch_all_ref>()? {
catch_all = Some(parser.parens(|p| {
let kind = if parser.peek::<kw::catch_all_ref>()? {
p.parse::<kw::catch_all_ref>()?;
TryTableCatchKind::Ref
} else {
p.parse::<kw::catch_all>()?;
TryTableCatchKind::Drop
};
Ok(TryTableCatchAll {
kind,
label: p.parse()?,
})
})?);
}

Ok(TryTable {
block,
catches,
catch_all,
})
}
}

#[derive(Debug)]
#[allow(missing_docs)]
pub enum TryTableCatchKind {
// Capture the exnref for the try
Ref,
// Drop the exnref for the try
Drop,
}

#[derive(Debug)]
#[allow(missing_docs)]
pub struct TryTableCatch<'a> {
pub kind: TryTableCatchKind,
pub tag: Index<'a>,
pub label: Index<'a>,
}

#[derive(Debug)]
#[allow(missing_docs)]
pub struct TryTableCatchAll<'a> {
pub kind: TryTableCatchKind,
pub label: Index<'a>,
}

/// Extra information associated with the func.bind instruction.
#[derive(Debug)]
#[allow(missing_docs)]
Expand Down
14 changes: 14 additions & 0 deletions crates/wast/src/core/resolve/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,20 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
});
self.resolve_block_type(bt)?;
}
TryTable(try_table) => {
self.blocks.push(ExprBlock {
label: try_table.block.label,
pushed_scope: false,
});
self.resolve_block_type(&mut try_table.block)?;
for catch in &mut try_table.catches {
self.resolver.resolve(&mut catch.tag, Ns::Tag)?;
self.resolve_label(&mut catch.label)?;
}
if let Some(catch_all) = &mut try_table.catch_all {
self.resolve_label(&mut catch_all.label)?;
}
}

// On `End` instructions we pop a label from the stack, and for both
// `End` and `Else` instructions if they have labels listed we
Expand Down
3 changes: 2 additions & 1 deletion crates/wast/src/core/resolve/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ impl<'a> Expander<'a> {
| Instruction::If(bt)
| Instruction::Loop(bt)
| Instruction::Let(LetType { block: bt, .. })
| Instruction::Try(bt) => {
| Instruction::Try(bt)
| Instruction::TryTable(TryTable { block: bt, .. }) => {
// No expansion necessary, a type reference is already here.
// We'll verify that it's the same as the inline type, if any,
// later.
Expand Down
18 changes: 18 additions & 0 deletions crates/wast/src/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub enum HeapType<'a> {
/// A reference to any host value: externref. This is part of the reference
/// types proposal.
Extern,
/// A reference to a wasm exception. This is part of the exceptions proposal.
Exn,
/// A reference to any reference value: anyref. This is part of the GC
/// proposal.
Any,
Expand Down Expand Up @@ -98,6 +100,9 @@ impl<'a> Parse<'a> for HeapType<'a> {
} else if l.peek::<kw::r#extern>()? {
parser.parse::<kw::r#extern>()?;
Ok(HeapType::Extern)
} else if l.peek::<kw::exn>()? {
parser.parse::<kw::exn>()?;
Ok(HeapType::Exn)
} else if l.peek::<kw::r#any>()? {
parser.parse::<kw::r#any>()?;
Ok(HeapType::Any)
Expand Down Expand Up @@ -134,6 +139,7 @@ impl<'a> Peek for HeapType<'a> {
fn peek(cursor: Cursor<'_>) -> Result<bool> {
Ok(kw::func::peek(cursor)?
|| kw::r#extern::peek(cursor)?
|| kw::exn::peek(cursor)?
|| kw::any::peek(cursor)?
|| kw::eq::peek(cursor)?
|| kw::r#struct::peek(cursor)?
Expand Down Expand Up @@ -174,6 +180,14 @@ impl<'a> RefType<'a> {
}
}

/// An `exnrefr` as an abbreviation for `(ref null exn)`.
pub fn exn() -> Self {
RefType {
nullable: true,
heap: HeapType::Exn,
}
}

/// An `anyref` as an abbreviation for `(ref null any)`.
pub fn any() -> Self {
RefType {
Expand Down Expand Up @@ -251,6 +265,9 @@ impl<'a> Parse<'a> for RefType<'a> {
} else if l.peek::<kw::externref>()? {
parser.parse::<kw::externref>()?;
Ok(RefType::r#extern())
} else if l.peek::<kw::exnref>()? {
parser.parse::<kw::exnref>()?;
Ok(RefType::exn())
} else if l.peek::<kw::anyref>()? {
parser.parse::<kw::anyref>()?;
Ok(RefType::any())
Expand Down Expand Up @@ -306,6 +323,7 @@ impl<'a> Peek for RefType<'a> {
Ok(kw::funcref::peek(cursor)?
|| /* legacy */ kw::anyfunc::peek(cursor)?
|| kw::externref::peek(cursor)?
|| kw::exnref::peek(cursor)?
|| kw::anyref::peek(cursor)?
|| kw::eqref::peek(cursor)?
|| kw::structref::peek(cursor)?
Expand Down
4 changes: 4 additions & 0 deletions crates/wast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,9 @@ pub mod kw {
custom_keyword!(block);
custom_keyword!(borrow);
custom_keyword!(catch);
custom_keyword!(catch_ref);
custom_keyword!(catch_all);
custom_keyword!(catch_all_ref);
custom_keyword!(code);
custom_keyword!(component);
custom_keyword!(data);
Expand All @@ -404,6 +406,8 @@ pub mod kw {
custom_keyword!(elem);
custom_keyword!(end);
custom_keyword!(tag);
custom_keyword!(exn);
custom_keyword!(exnref);
custom_keyword!(export);
custom_keyword!(r#extern = "extern");
custom_keyword!(externref);
Expand Down
5 changes: 5 additions & 0 deletions tests/local/exnref/exnref.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(module
(func (param exnref))
(func (param (ref null exn)))
(func (param (ref exn)))
)
6 changes: 6 additions & 0 deletions tests/local/exnref/throw_ref.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(module
(func (param exnref)
local.get 0
throw_ref
)
)
Loading

0 comments on commit c950866

Please sign in to comment.