Skip to content

Commit

Permalink
Update the exception-handling validator implementation (#1333)
Browse files Browse the repository at this point in the history
* Update the exception-handling validator implementation

This commit updates the implementation of the exception-handling
proposal to the latest version of the proposal. This was already
implemented in the text format but all other crates needed updating. The
changes were:

* A new `exn` heap type is added for `exnref`. Currently this isn't a
  subtype with anything else, and this probably isn't correct but it's
  at least conservative for now.

* The `try`, `delegate`, `catch`, `catch_all`, and `rethrow`
  instructions were all removed as they're no longer present.

* New `try_table` and `throw_ref` instructions were added.

* Support for the name section subsection for tags has been added along
  with printing tag names in the text format.

* All exception-handling spec tests are now re-enabled.

* Update error message

* Restore old text format

Continue to parse the old exception opcodes both in the text and binary
format. Additionally print them too. Don't validate them, however, and
additionally don't restore the sugared paren-style parsing of the old
text format. This should enable the ability to print out old binaries
and reassemble them but will likely render handwritten examples
un-workable due to the removal of the parentheses-based form.

* No tests need skipping at this time

* Fix a bit of mixing old and new syntax
  • Loading branch information
alexcrichton authored Jan 9, 2024
1 parent 0616ef1 commit 7dc8054
Show file tree
Hide file tree
Showing 50 changed files with 1,165 additions and 686 deletions.
55 changes: 51 additions & 4 deletions crates/wasm-encoder/src/core/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,6 @@ pub enum Instruction<'a> {
Loop(BlockType),
If(BlockType),
Else,
Try(BlockType),
Delegate(u32),
Catch(u32),
CatchAll,
End,
Br(u32),
BrIf(u32),
Expand All @@ -333,7 +329,15 @@ pub enum Instruction<'a> {
ty: u32,
table: u32,
},
TryTable(BlockType, Cow<'a, [Catch]>),
Throw(u32),
ThrowRef,

// Deprecated exception-handling instructions
Try(BlockType),
Delegate(u32),
Catch(u32),
CatchAll,
Rethrow(u32),

// Parametric instructions.
Expand Down Expand Up @@ -1013,6 +1017,9 @@ impl Encode for Instruction<'_> {
sink.push(0x09);
l.encode(sink);
}
Instruction::ThrowRef => {
sink.push(0x0A);
}
Instruction::End => sink.push(0x0B),
Instruction::Br(l) => {
sink.push(0x0C);
Expand Down Expand Up @@ -1079,6 +1086,12 @@ impl Encode for Instruction<'_> {
[ty].encode(sink);
}

Instruction::TryTable(ty, ref catches) => {
sink.push(0x1f);
ty.encode(sink);
catches.encode(sink);
}

// Variable instructions.
Instruction::LocalGet(l) => {
sink.push(0x20);
Expand Down Expand Up @@ -3114,6 +3127,40 @@ impl Encode for Instruction<'_> {
}
}

#[derive(Clone, Debug)]
#[allow(missing_docs)]
pub enum Catch {
One { tag: u32, label: u32 },
OneRef { tag: u32, label: u32 },
All { label: u32 },
AllRef { label: u32 },
}

impl Encode for Catch {
fn encode(&self, sink: &mut Vec<u8>) {
match self {
Catch::One { tag, label } => {
sink.push(0x00);
tag.encode(sink);
label.encode(sink);
}
Catch::OneRef { tag, label } => {
sink.push(0x01);
tag.encode(sink);
label.encode(sink);
}
Catch::All { label } => {
sink.push(0x02);
label.encode(sink);
}
Catch::AllRef { label } => {
sink.push(0x03);
label.encode(sink);
}
}
}
}

/// A constant expression.
///
/// Usable in contexts such as offsets or initializers.
Expand Down
8 changes: 8 additions & 0 deletions crates/wasm-encoder/src/core/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ impl NameSection {
names.encode(&mut self.bytes);
}

/// Appends a subsection for the names of all tags in this wasm module.
///
/// This section should come after the data name subsection (if present).
pub fn tag(&mut self, names: &NameMap) {
self.subsection_header(Subsection::Tag, names.size());
names.encode(&mut self.bytes);
}

/// Appends a subsection for the names of fields within types in this
/// wasm module.
///
Expand Down
13 changes: 13 additions & 0 deletions crates/wasm-encoder/src/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ impl ValType {
pub const FUNCREF: ValType = ValType::Ref(RefType::FUNCREF);
/// Alias for the `externref` type in WebAssembly
pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF);
/// Alias for the `exnref` type in WebAssembly
pub const EXNREF: ValType = ValType::Ref(RefType::EXNREF);
}

impl Encode for StorageType {
Expand Down Expand Up @@ -304,6 +306,12 @@ impl RefType {
nullable: true,
heap_type: HeapType::Extern,
};

/// Alias for the `exnref` type in WebAssembly
pub const EXNREF: RefType = RefType {
nullable: true,
heap_type: HeapType::Exn,
};
}

impl Encode for RefType {
Expand Down Expand Up @@ -393,6 +401,9 @@ pub enum HeapType {
/// The unboxed `i31` heap type.
I31,

/// The abstract` exception` heap type.
Exn,

/// A concrete Wasm-defined type at the given index.
Concrete(u32),
}
Expand All @@ -410,6 +421,7 @@ impl Encode for HeapType {
HeapType::Struct => sink.push(0x6B),
HeapType::Array => sink.push(0x6A),
HeapType::I31 => sink.push(0x6C),
HeapType::Exn => sink.push(0x69),
// Note that this is encoded as a signed type rather than unsigned
// as it's decoded as an s33
HeapType::Concrete(i) => i64::from(*i).encode(sink),
Expand All @@ -434,6 +446,7 @@ impl TryFrom<wasmparser::HeapType> for HeapType {
wasmparser::HeapType::Struct => HeapType::Struct,
wasmparser::HeapType::Array => HeapType::Array,
wasmparser::HeapType::I31 => HeapType::I31,
wasmparser::HeapType::Exn => HeapType::Exn,
})
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/wasm-metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,8 @@ impl<'a> ModuleNames<'a> {
wasmparser::Name::Memory(m) => section.memories(&name_map(&m)?),
wasmparser::Name::Global(m) => section.globals(&name_map(&m)?),
wasmparser::Name::Element(m) => section.elements(&name_map(&m)?),
wasmparser::Name::Data(m) => section.types(&name_map(&m)?),
wasmparser::Name::Data(m) => section.data(&name_map(&m)?),
wasmparser::Name::Tag(m) => section.tags(&name_map(&m)?),
wasmparser::Name::Unknown { .. } => {} // wasm-encoder doesn't support it
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/wasm-mutate/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result<RefType> {
wasmparser::HeapType::Struct => HeapType::Struct,
wasmparser::HeapType::Array => HeapType::Array,
wasmparser::HeapType::I31 => HeapType::I31,
wasmparser::HeapType::Exn => HeapType::Exn,
wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().unwrap()),
},
})
Expand Down
7 changes: 7 additions & 0 deletions crates/wasm-mutate/src/mutators/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub fn heapty(t: &mut dyn Translator, ty: &wasmparser::HeapType) -> Result<HeapT
wasmparser::HeapType::Struct => Ok(HeapType::Struct),
wasmparser::HeapType::Array => Ok(HeapType::Array),
wasmparser::HeapType::I31 => Ok(HeapType::I31),
wasmparser::HeapType::Exn => Ok(HeapType::Exn),
wasmparser::HeapType::Concrete(i) => Ok(HeapType::Concrete(
t.remap(Item::Type, i.as_module_index().unwrap())?,
)),
Expand Down Expand Up @@ -374,6 +375,7 @@ pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'stat
(map $arg:ident field_index) => (*$arg);
(map $arg:ident from_type_nullable) => (*$arg);
(map $arg:ident to_type_nullable) => (*$arg);
(map $arg:ident try_table) => ($arg);

// This case takes the arguments of a wasmparser instruction and creates
// a wasm-encoder instruction. There are a few special cases for where
Expand All @@ -386,6 +388,7 @@ pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'stat
(build F32Const $arg:ident) => (I::F32Const(f32::from_bits($arg.bits())));
(build F64Const $arg:ident) => (I::F64Const(f64::from_bits($arg.bits())));
(build V128Const $arg:ident) => (I::V128Const($arg.i128()));
(build TryTable $table:ident) => (unimplemented_try_table());
(build $op:ident $arg:ident) => (I::$op($arg));
(build CallIndirect $ty:ident $table:ident $_:ident) => (I::CallIndirect {
ty: $ty,
Expand All @@ -409,6 +412,10 @@ pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'stat
wasmparser::for_each_operator!(translate)
}

fn unimplemented_try_table() -> wasm_encoder::Instruction<'static> {
unimplemented!()
}

pub fn block_type(t: &mut dyn Translator, ty: &wasmparser::BlockType) -> Result<BlockType> {
match ty {
wasmparser::BlockType::Empty => Ok(BlockType::Empty),
Expand Down
Loading

0 comments on commit 7dc8054

Please sign in to comment.