Skip to content

Commit d3cbb38

Browse files
authored
Exception handling support (bytecodealliance#141)
* Implements exception handling * fix bench * rm ExceptionSection -> EventSection * fix linking checks * bump wasmparser and wasmprinter versions * address review
1 parent 05a2bb8 commit d3cbb38

18 files changed

Lines changed: 421 additions & 24 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/dump/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct Indices {
2222
globals: u32,
2323
tables: u32,
2424
memories: u32,
25+
events: u32,
2526
modules: u32,
2627
instances: u32,
2728
types: u32,
@@ -73,6 +74,10 @@ impl<'a> Dump<'a> {
7374
write!(me.state, "[memory {}]", i.memories)?;
7475
i.memories += 1;
7576
}
77+
ImportSectionEntryType::Event(_) => {
78+
write!(me.state, "[event {}]", i.events)?;
79+
i.events += 1;
80+
}
7681
ImportSectionEntryType::Table(_) => {
7782
write!(me.state, "[table {}]", i.tables)?;
7883
i.tables += 1;
@@ -111,6 +116,11 @@ impl<'a> Dump<'a> {
111116
i.memories += 1;
112117
me.print(end)
113118
})?,
119+
Payload::EventSection(s) => self.section(s, "event", |me, end, m| {
120+
write!(me.state, "[event {}] {:?}", i.events, m)?;
121+
i.events += 1;
122+
me.print(end)
123+
})?,
114124
Payload::ExportSection(s) => self.section(s, "export", |me, end, e| {
115125
write!(me.state, "export {:?}", e)?;
116126
me.print(end)
@@ -130,6 +140,7 @@ impl<'a> Dump<'a> {
130140
ExternalKind::Table => i.tables += 1,
131141
ExternalKind::Instance => i.instances += 1,
132142
ExternalKind::Memory => i.memories += 1,
143+
ExternalKind::Event => i.events += 1,
133144
ExternalKind::Type => i.types += 1,
134145
}
135146
me.print(end)

crates/wasmparser/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "wasmparser"
3-
version = "0.67.0"
3+
version = "0.68.0"
44
authors = ["Yury Delendik <ydelendik@mozilla.com>"]
55
license = "Apache-2.0 WITH LLVM-exception"
66
repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser"

crates/wasmparser/benches/benchmark.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> {
124124
item?;
125125
}
126126
}
127+
EventSection(s) => {
128+
for item in s {
129+
item?;
130+
}
131+
}
127132
GlobalSection(s) => {
128133
for item in s {
129134
for op in item?.init_expr.get_operators_reader() {
@@ -210,6 +215,7 @@ fn validate_benchmark(c: &mut Criterion) {
210215
reference_types: true,
211216
multi_value: true,
212217
simd: true,
218+
exceptions: true,
213219
module_linking: true,
214220
bulk_memory: true,
215221
threads: true,

crates/wasmparser/compare-with-main.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ git checkout main && \
66
cargo bench --bench benchmark -- --noplot --save-baseline before
77

88
# record current bench results
9-
git checkout - && \
9+
git checkout -f - && \
1010
cargo bench --bench benchmark -- --noplot --baseline before

crates/wasmparser/src/binary_reader.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::primitives::{
2525
ResizableLimits, ResizableLimits64, Result, SIMDLaneIndex, SectionCode, TableType, Type,
2626
TypeOrFuncType, V128,
2727
};
28-
use crate::{ExportType, Import, ImportSectionEntryType, InstanceType, ModuleType};
28+
use crate::{EventType, ExportType, Import, ImportSectionEntryType, InstanceType, ModuleType};
2929

3030
const MAX_WASM_BR_TABLE_SIZE: usize = MAX_WASM_FUNCTION_SIZE;
3131

@@ -186,6 +186,7 @@ impl<'a> BinaryReader<'a> {
186186
-0x05 => Ok(Type::V128),
187187
-0x10 => Ok(Type::FuncRef),
188188
-0x11 => Ok(Type::ExternRef),
189+
-0x18 => Ok(Type::ExnRef),
189190
-0x20 => Ok(Type::Func),
190191
-0x40 => Ok(Type::EmptyBlockType),
191192
_ => Err(BinaryReaderError::new(
@@ -202,6 +203,7 @@ impl<'a> BinaryReader<'a> {
202203
1 => Ok(ExternalKind::Table),
203204
2 => Ok(ExternalKind::Memory),
204205
3 => Ok(ExternalKind::Global),
206+
4 => Ok(ExternalKind::Event),
205207
5 => Ok(ExternalKind::Module),
206208
6 => Ok(ExternalKind::Instance),
207209
7 => Ok(ExternalKind::Type),
@@ -300,6 +302,7 @@ impl<'a> BinaryReader<'a> {
300302
ExternalKind::Function => ImportSectionEntryType::Function(self.read_var_u32()?),
301303
ExternalKind::Table => ImportSectionEntryType::Table(self.read_table_type()?),
302304
ExternalKind::Memory => ImportSectionEntryType::Memory(self.read_memory_type()?),
305+
ExternalKind::Event => ImportSectionEntryType::Event(self.read_event_type()?),
303306
ExternalKind::Global => ImportSectionEntryType::Global(self.read_global_type()?),
304307
ExternalKind::Module => ImportSectionEntryType::Module(self.read_var_u32()?),
305308
ExternalKind::Instance => ImportSectionEntryType::Instance(self.read_var_u32()?),
@@ -368,6 +371,18 @@ impl<'a> BinaryReader<'a> {
368371
}
369372
}
370373

374+
pub(crate) fn read_event_type(&mut self) -> Result<EventType> {
375+
let attribute = self.read_var_u32()?;
376+
if attribute != 0 {
377+
return Err(BinaryReaderError::new(
378+
"invalid event attributes",
379+
self.original_position() - 1,
380+
));
381+
}
382+
let type_index = self.read_var_u32()?;
383+
Ok(EventType { type_index })
384+
}
385+
371386
pub(crate) fn read_global_type(&mut self) -> Result<GlobalType> {
372387
Ok(GlobalType {
373388
content_type: self.read_type()?,
@@ -434,6 +449,7 @@ impl<'a> BinaryReader<'a> {
434449
10 => Ok(SectionCode::Code),
435450
11 => Ok(SectionCode::Data),
436451
12 => Ok(SectionCode::DataCount),
452+
13 => Ok(SectionCode::Event),
437453
100 => Ok(SectionCode::Module),
438454
101 => Ok(SectionCode::Instance),
439455
102 => Ok(SectionCode::Alias),
@@ -1082,6 +1098,18 @@ impl<'a> BinaryReader<'a> {
10821098
ty: self.read_blocktype()?,
10831099
},
10841100
0x05 => Operator::Else,
1101+
0x06 => Operator::Try {
1102+
ty: self.read_blocktype()?,
1103+
},
1104+
0x07 => Operator::Catch,
1105+
0x08 => Operator::Throw {
1106+
index: self.read_var_u32()?,
1107+
},
1108+
0x09 => Operator::Rethrow,
1109+
0x0a => Operator::BrOnExn {
1110+
relative_depth: self.read_var_u32()?,
1111+
index: self.read_var_u32()?,
1112+
},
10851113
0x0b => Operator::End,
10861114
0x0c => Operator::Br {
10871115
relative_depth: self.read_var_u32()?,

crates/wasmparser/src/limits.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ pub const MAX_WASM_TABLES: usize = 100;
3535
pub const MAX_WASM_MEMORIES: usize = 100;
3636
pub const MAX_WASM_MODULES: usize = 1_000;
3737
pub const MAX_WASM_INSTANCES: usize = 1_000;
38+
pub const MAX_WASM_EVENTS: usize = 1_000_000;

crates/wasmparser/src/operators_validator.rs

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ enum FrameKind {
110110
If,
111111
Else,
112112
Loop,
113+
Try,
114+
Catch,
113115
}
114116

115117
impl OperatorValidator {
@@ -394,6 +396,15 @@ impl OperatorValidator {
394396
Ok(())
395397
}
396398

399+
fn check_exceptions_enabled(&self) -> OperatorValidatorResult<()> {
400+
if !self.features.exceptions {
401+
return Err(OperatorValidatorError::new(
402+
"Exceptions support is not enabled",
403+
));
404+
}
405+
Ok(())
406+
}
407+
397408
fn check_bulk_memory_enabled(&self) -> OperatorValidatorResult<()> {
398409
if !self.features.bulk_memory {
399410
return Err(OperatorValidatorError::new(
@@ -561,17 +572,78 @@ impl OperatorValidator {
561572
}
562573
self.push_ctrl(FrameKind::Else, frame.block_type, resources)?;
563574
}
575+
Operator::Try { ty } => {
576+
self.check_exceptions_enabled()?;
577+
self.check_block_type(ty, resources)?;
578+
for ty in params(ty, resources)?.rev() {
579+
self.pop_operand(Some(ty))?;
580+
}
581+
self.push_ctrl(FrameKind::Try, ty, resources)?;
582+
}
583+
Operator::Catch => {
584+
self.check_exceptions_enabled()?;
585+
let frame = self.pop_ctrl(resources)?;
586+
if frame.kind != FrameKind::Try {
587+
bail_op_err!("catch found outside of an `try` block");
588+
}
589+
// Start a new frame and push `exnref` value.
590+
self.control.push(Frame {
591+
kind: FrameKind::Catch,
592+
block_type: frame.block_type,
593+
height: self.operands.len(),
594+
unreachable: false,
595+
});
596+
self.push_operand(Type::ExnRef)?;
597+
}
598+
Operator::Throw { index } => {
599+
self.check_exceptions_enabled()?;
600+
// Check values associated with the exception.
601+
let ty = func_type_at(&resources, index)?;
602+
for ty in ty.inputs().rev() {
603+
self.pop_operand(Some(ty))?;
604+
}
605+
if ty.outputs().len() > 0 {
606+
bail_op_err!("result type expected to be empty for exception");
607+
}
608+
self.unreachable();
609+
}
610+
Operator::Rethrow => {
611+
self.check_exceptions_enabled()?;
612+
self.pop_operand(Some(Type::ExnRef))?;
613+
self.unreachable();
614+
}
615+
Operator::BrOnExn {
616+
relative_depth,
617+
index,
618+
} => {
619+
self.check_exceptions_enabled()?;
620+
let (ty, kind) = self.jump(relative_depth)?;
621+
self.pop_operand(Some(Type::ExnRef))?;
622+
// Check the exception's argument values with target block's.
623+
let exn_args = func_type_at(&resources, index)?;
624+
if Iterator::ne(exn_args.inputs(), label_types(ty, resources, kind)?) {
625+
bail_op_err!("target block types do not match");
626+
}
627+
self.push_operand(Type::ExnRef)?;
628+
}
564629
Operator::End => {
565630
let mut frame = self.pop_ctrl(resources)?;
566631

567632
// Note that this `if` isn't included in the appendix right
568633
// now, but it's used to allow for `if` statements that are
569634
// missing an `else` block which have the same parameter/return
570635
// types on the block (since that's valid).
571-
if frame.kind == FrameKind::If {
572-
self.push_ctrl(FrameKind::Else, frame.block_type, resources)?;
573-
frame = self.pop_ctrl(resources)?;
636+
match frame.kind {
637+
FrameKind::If => {
638+
self.push_ctrl(FrameKind::Else, frame.block_type, resources)?;
639+
frame = self.pop_ctrl(resources)?;
640+
}
641+
FrameKind::Try => {
642+
bail_op_err!("expected catch block");
643+
}
644+
_ => (),
574645
}
646+
575647
for ty in results(frame.block_type, resources)? {
576648
self.push_operand(ty)?;
577649
}
@@ -1842,6 +1914,7 @@ fn ty_to_str(ty: Type) -> &'static str {
18421914
Type::V128 => "v128",
18431915
Type::FuncRef => "funcref",
18441916
Type::ExternRef => "externref",
1917+
Type::ExnRef => "exnref",
18451918
Type::Func => "func",
18461919
Type::EmptyBlockType => "nil",
18471920
}

crates/wasmparser/src/parser.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::EventSectionReader;
12
use crate::{AliasSectionReader, InstanceSectionReader, ModuleSectionReader};
23
use crate::{BinaryReader, BinaryReaderError, FunctionBody, Range, Result};
34
use crate::{DataSectionReader, ElementSectionReader, ExportSectionReader};
@@ -111,6 +112,9 @@ pub enum Payload<'a> {
111112
/// A memory section was received, and the provided reader can be used to
112113
/// parse the contents of the memory section.
113114
MemorySection(crate::MemorySectionReader<'a>),
115+
/// An event section was received, and the provided reader can be used to
116+
/// parse the contents of the event section.
117+
EventSection(crate::EventSectionReader<'a>),
114118
/// A global section was received, and the provided reader can be used to
115119
/// parse the contents of the global section.
116120
GlobalSection(crate::GlobalSectionReader<'a>),
@@ -342,6 +346,7 @@ impl Parser {
342346
/// FunctionSection(_) => { /* ... */ }
343347
/// TableSection(_) => { /* ... */ }
344348
/// MemorySection(_) => { /* ... */ }
349+
/// EventSection(_) => { /* ... */ }
345350
/// GlobalSection(_) => { /* ... */ }
346351
/// ExportSection(_) => { /* ... */ }
347352
/// StartSection { .. } => { /* ... */ }
@@ -526,6 +531,7 @@ impl Parser {
526531
let (count, range) = single_u32(reader, len, "data count")?;
527532
Ok(DataCountSection { count, range })
528533
}
534+
13 => section(reader, len, EventSectionReader::new, EventSection),
529535
100 => section(reader, len, ModuleSectionReader::new, ModuleSection),
530536
101 => section(reader, len, InstanceSectionReader::new, InstanceSection),
531537
102 => section(reader, len, AliasSectionReader::new, AliasSection),
@@ -906,6 +912,7 @@ impl fmt::Debug for Payload<'_> {
906912
FunctionSection(_) => f.debug_tuple("FunctionSection").field(&"...").finish(),
907913
TableSection(_) => f.debug_tuple("TableSection").field(&"...").finish(),
908914
MemorySection(_) => f.debug_tuple("MemorySection").field(&"...").finish(),
915+
EventSection(_) => f.debug_tuple("EventSection").field(&"...").finish(),
909916
GlobalSection(_) => f.debug_tuple("GlobalSection").field(&"...").finish(),
910917
ExportSection(_) => f.debug_tuple("ExportSection").field(&"...").finish(),
911918
ElementSection(_) => f.debug_tuple("ElementSection").field(&"...").finish(),

crates/wasmparser/src/primitives.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub enum SectionCode<'a> {
114114
Code, // Function bodies (code)
115115
Data, // Data segments
116116
DataCount, // Count of passive data segments
117+
Event, // Event declarations
117118
}
118119

119120
/// Types as defined [here].
@@ -128,6 +129,7 @@ pub enum Type {
128129
V128,
129130
FuncRef,
130131
ExternRef,
132+
ExnRef,
131133
Func,
132134
EmptyBlockType,
133135
}
@@ -153,6 +155,7 @@ pub enum ExternalKind {
153155
Function,
154156
Table,
155157
Memory,
158+
Event,
156159
Global,
157160
Type,
158161
Module,
@@ -219,6 +222,11 @@ pub enum MemoryType {
219222
},
220223
}
221224

225+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
226+
pub struct EventType {
227+
pub type_index: u32,
228+
}
229+
222230
impl MemoryType {
223231
pub fn index_type(&self) -> Type {
224232
match self {
@@ -239,6 +247,7 @@ pub enum ImportSectionEntryType {
239247
Function(u32),
240248
Table(TableType),
241249
Memory(MemoryType),
250+
Event(EventType),
242251
Global(GlobalType),
243252
Module(u32),
244253
Instance(u32),
@@ -337,6 +346,11 @@ pub enum Operator<'a> {
337346
Loop { ty: TypeOrFuncType },
338347
If { ty: TypeOrFuncType },
339348
Else,
349+
Try { ty: TypeOrFuncType },
350+
Catch,
351+
Throw { index: u32 },
352+
Rethrow,
353+
BrOnExn { relative_depth: u32, index: u32 },
340354
End,
341355
Br { relative_depth: u32 },
342356
BrIf { relative_depth: u32 },

0 commit comments

Comments
 (0)