1 | /* Copyright 2018 Mozilla Foundation |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | * you may not use this file except in compliance with the License. |
5 | * You may obtain a copy of the License at |
6 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | * |
9 | * Unless required by applicable law or agreed to in writing, software |
10 | * distributed under the License is distributed on an "AS IS" BASIS, |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | * See the License for the specific language governing permissions and |
13 | * limitations under the License. |
14 | */ |
15 | |
16 | use crate::limits::{MAX_WASM_CATCHES, MAX_WASM_HANDLERS}; |
17 | use crate::prelude::*; |
18 | use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType}; |
19 | |
20 | /// Represents a block type. |
21 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
22 | pub enum BlockType { |
23 | /// The block produces consumes nor produces any values. |
24 | Empty, |
25 | /// The block produces a singular value of the given type ([] -> \[t]). |
26 | Type(ValType), |
27 | /// The block is described by a function type. |
28 | /// |
29 | /// The index is to a function type in the types section. |
30 | FuncType(u32), |
31 | } |
32 | |
33 | /// The kind of a control flow `Frame`. |
34 | #[derive (Copy, Clone, Debug, PartialEq, Eq)] |
35 | pub enum FrameKind { |
36 | /// A Wasm `block` control block. |
37 | Block, |
38 | /// A Wasm `if` control block. |
39 | If, |
40 | /// A Wasm `else` control block. |
41 | Else, |
42 | /// A Wasm `loop` control block. |
43 | Loop, |
44 | /// A Wasm `try` control block. |
45 | /// |
46 | /// # Note |
47 | /// |
48 | /// This belongs to the Wasm exception handling proposal. |
49 | TryTable, |
50 | /// A Wasm legacy `try` control block. |
51 | /// |
52 | /// # Note |
53 | /// |
54 | /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs` |
55 | LegacyTry, |
56 | /// A Wasm legacy `catch` control block. |
57 | /// |
58 | /// # Note |
59 | /// |
60 | /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs` |
61 | LegacyCatch, |
62 | /// A Wasm legacy `catch_all` control block. |
63 | /// |
64 | /// # Note |
65 | /// |
66 | /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs` |
67 | LegacyCatchAll, |
68 | } |
69 | |
70 | /// Represents a memory immediate in a WebAssembly memory instruction. |
71 | #[derive (Debug, Copy, Clone, Eq, PartialEq)] |
72 | pub struct MemArg { |
73 | /// Alignment, stored as `n` where the actual alignment is `2^n` |
74 | pub align: u8, |
75 | /// Maximum alignment, stored as `n` where the actual alignment is `2^n`. |
76 | /// |
77 | /// Note that this field is not actually read from the binary format, it |
78 | /// will be a constant depending on which instruction this `MemArg` is a |
79 | /// payload for. |
80 | pub max_align: u8, |
81 | /// A fixed byte-offset that this memory immediate specifies. |
82 | /// |
83 | /// Note that the memory64 proposal can specify a full 64-bit byte offset |
84 | /// while otherwise only 32-bit offsets are allowed. Once validated |
85 | /// memory immediates for 32-bit memories are guaranteed to be at most |
86 | /// `u32::MAX` whereas 64-bit memories can use the full 64-bits. |
87 | pub offset: u64, |
88 | /// The index of the memory this immediate points to. |
89 | /// |
90 | /// Note that this points within the module's own memory index space, and |
91 | /// is always zero unless the multi-memory proposal of WebAssembly is |
92 | /// enabled. |
93 | pub memory: u32, |
94 | } |
95 | |
96 | /// A br_table entries representation. |
97 | #[derive (Clone)] |
98 | pub struct BrTable<'a> { |
99 | pub(crate) reader: crate::BinaryReader<'a>, |
100 | pub(crate) cnt: u32, |
101 | pub(crate) default: u32, |
102 | } |
103 | |
104 | impl PartialEq<Self> for BrTable<'_> { |
105 | fn eq(&self, other: &Self) -> bool { |
106 | self.cnt == other.cnt |
107 | && self.default == other.default |
108 | && self.reader.remaining_buffer() == other.reader.remaining_buffer() |
109 | } |
110 | } |
111 | |
112 | impl Eq for BrTable<'_> {} |
113 | |
114 | /// An IEEE binary32 immediate floating point value, represented as a u32 |
115 | /// containing the bit pattern. |
116 | /// |
117 | /// All bit patterns are allowed. |
118 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Hash)] |
119 | pub struct Ieee32(pub(crate) u32); |
120 | |
121 | impl Ieee32 { |
122 | /// Gets the underlying bits of the 32-bit float. |
123 | pub fn bits(self) -> u32 { |
124 | self.0 |
125 | } |
126 | } |
127 | |
128 | impl From<f32> for Ieee32 { |
129 | fn from(value: f32) -> Self { |
130 | Ieee32 { |
131 | 0: u32::from_le_bytes(value.to_le_bytes()), |
132 | } |
133 | } |
134 | } |
135 | |
136 | impl From<Ieee32> for f32 { |
137 | fn from(bits: Ieee32) -> f32 { |
138 | f32::from_bits(bits.bits()) |
139 | } |
140 | } |
141 | |
142 | /// An IEEE binary64 immediate floating point value, represented as a u64 |
143 | /// containing the bit pattern. |
144 | /// |
145 | /// All bit patterns are allowed. |
146 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Hash)] |
147 | pub struct Ieee64(pub(crate) u64); |
148 | |
149 | impl Ieee64 { |
150 | /// Gets the underlying bits of the 64-bit float. |
151 | pub fn bits(self) -> u64 { |
152 | self.0 |
153 | } |
154 | } |
155 | |
156 | impl From<f64> for Ieee64 { |
157 | fn from(value: f64) -> Self { |
158 | Ieee64 { |
159 | 0: u64::from_le_bytes(value.to_le_bytes()), |
160 | } |
161 | } |
162 | } |
163 | |
164 | impl From<Ieee64> for f64 { |
165 | fn from(bits: Ieee64) -> f64 { |
166 | f64::from_bits(bits.bits()) |
167 | } |
168 | } |
169 | |
170 | /// Represents a 128-bit vector value. |
171 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Hash)] |
172 | pub struct V128(pub(crate) [u8; 16]); |
173 | |
174 | impl V128 { |
175 | /// Gets the bytes of the vector value. |
176 | pub fn bytes(&self) -> &[u8; 16] { |
177 | &self.0 |
178 | } |
179 | |
180 | /// Gets a signed 128-bit integer value from the vector's bytes. |
181 | pub fn i128(&self) -> i128 { |
182 | i128::from_le_bytes(self.0) |
183 | } |
184 | } |
185 | |
186 | impl From<V128> for i128 { |
187 | fn from(bits: V128) -> i128 { |
188 | bits.i128() |
189 | } |
190 | } |
191 | |
192 | impl From<V128> for u128 { |
193 | fn from(bits: V128) -> u128 { |
194 | u128::from_le_bytes(bits.0) |
195 | } |
196 | } |
197 | |
198 | /// Represents the memory ordering for atomic instructions. |
199 | /// |
200 | /// For an in-depth explanation of memory orderings, see the C++ documentation |
201 | /// for [`memory_order`] or the Rust documentation for [`atomic::Ordering`]. |
202 | /// |
203 | /// [`memory_order`]: https://en.cppreference.com/w/cpp/atomic/memory_order |
204 | /// [`atomic::Ordering`]: https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html |
205 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Hash)] |
206 | pub enum Ordering { |
207 | /// For a load, it acquires; this orders all operations before the last |
208 | /// "releasing" store. For a store, it releases; this orders all operations |
209 | /// before it at the next "acquiring" load. |
210 | AcqRel, |
211 | /// Like `AcqRel` but all threads see all sequentially consistent operations |
212 | /// in the same order. |
213 | SeqCst, |
214 | } |
215 | |
216 | macro_rules! define_operator { |
217 | ($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident ($($ann:tt)*))*) => { |
218 | /// Instructions as defined [here]. |
219 | /// |
220 | /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html |
221 | #[derive(Debug, Clone, Eq, PartialEq)] |
222 | #[allow(missing_docs)] |
223 | #[non_exhaustive] |
224 | pub enum Operator<'a> { |
225 | $( |
226 | $op $({ $($payload)* })?, |
227 | )* |
228 | } |
229 | } |
230 | } |
231 | crate::for_each_operator!(define_operator); |
232 | |
233 | /// A reader for a core WebAssembly function's operators. |
234 | #[derive (Clone)] |
235 | pub struct OperatorsReader<'a> { |
236 | reader: BinaryReader<'a>, |
237 | } |
238 | |
239 | impl<'a> OperatorsReader<'a> { |
240 | pub(crate) fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> { |
241 | OperatorsReader { reader } |
242 | } |
243 | |
244 | /// Determines if the reader is at the end of the operators. |
245 | pub fn eof(&self) -> bool { |
246 | self.reader.eof() |
247 | } |
248 | |
249 | /// Gets the original position of the reader. |
250 | pub fn original_position(&self) -> usize { |
251 | self.reader.original_position() |
252 | } |
253 | |
254 | /// Ensures the reader is at the end. |
255 | /// |
256 | /// This function returns an error if there is extra data after the operators. |
257 | pub fn ensure_end(&self) -> Result<()> { |
258 | if self.eof() { |
259 | return Ok(()); |
260 | } |
261 | Err(BinaryReaderError::new( |
262 | "unexpected data at the end of operators" , |
263 | self.reader.original_position(), |
264 | )) |
265 | } |
266 | |
267 | /// Reads an operator from the reader. |
268 | pub fn read(&mut self) -> Result<Operator<'a>> { |
269 | self.reader.read_operator() |
270 | } |
271 | |
272 | /// Converts to an iterator of operators paired with offsets. |
273 | pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> { |
274 | OperatorsIteratorWithOffsets { |
275 | reader: self, |
276 | err: false, |
277 | } |
278 | } |
279 | |
280 | /// Reads an operator with its offset. |
281 | pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> { |
282 | let pos = self.reader.original_position(); |
283 | Ok((self.read()?, pos)) |
284 | } |
285 | |
286 | /// Visit a single operator with the specified [`VisitOperator`] instance. |
287 | /// |
288 | /// See [`BinaryReader::visit_operator`] for more information. |
289 | pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::Output> |
290 | where |
291 | T: VisitOperator<'a>, |
292 | { |
293 | self.reader.visit_operator(visitor) |
294 | } |
295 | |
296 | /// Gets a binary reader from this operators reader. |
297 | pub fn get_binary_reader(&self) -> BinaryReader<'a> { |
298 | self.reader.clone() |
299 | } |
300 | |
301 | /// Returns whether there is an `end` opcode followed by eof remaining in |
302 | /// this reader. |
303 | pub fn is_end_then_eof(&self) -> bool { |
304 | self.reader.is_end_then_eof() |
305 | } |
306 | } |
307 | |
308 | impl<'a> IntoIterator for OperatorsReader<'a> { |
309 | type Item = Result<Operator<'a>>; |
310 | type IntoIter = OperatorsIterator<'a>; |
311 | |
312 | /// Reads content of the code section. |
313 | /// |
314 | /// # Examples |
315 | /// ``` |
316 | /// # use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader}; |
317 | /// # let data: &[u8] = &[ |
318 | /// # 0x01, 0x03, 0x00, 0x01, 0x0b]; |
319 | /// let reader = BinaryReader::new(data, 0); |
320 | /// let code_reader = CodeSectionReader::new(reader).unwrap(); |
321 | /// for body in code_reader { |
322 | /// let body = body.expect("function body" ); |
323 | /// let mut op_reader = body.get_operators_reader().expect("op reader" ); |
324 | /// let ops = op_reader.into_iter().collect::<Result<Vec<Operator>>>().expect("ops" ); |
325 | /// assert!( |
326 | /// if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false }, |
327 | /// "found {:?}" , |
328 | /// ops |
329 | /// ); |
330 | /// } |
331 | /// ``` |
332 | fn into_iter(self) -> Self::IntoIter { |
333 | OperatorsIterator { |
334 | reader: self, |
335 | err: false, |
336 | } |
337 | } |
338 | } |
339 | |
340 | /// An iterator over a function's operators. |
341 | pub struct OperatorsIterator<'a> { |
342 | reader: OperatorsReader<'a>, |
343 | err: bool, |
344 | } |
345 | |
346 | impl<'a> Iterator for OperatorsIterator<'a> { |
347 | type Item = Result<Operator<'a>>; |
348 | |
349 | fn next(&mut self) -> Option<Self::Item> { |
350 | if self.err || self.reader.eof() { |
351 | return None; |
352 | } |
353 | let result: Result, BinaryReaderError> = self.reader.read(); |
354 | self.err = result.is_err(); |
355 | Some(result) |
356 | } |
357 | } |
358 | |
359 | /// An iterator over a function's operators with offsets. |
360 | pub struct OperatorsIteratorWithOffsets<'a> { |
361 | reader: OperatorsReader<'a>, |
362 | err: bool, |
363 | } |
364 | |
365 | impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { |
366 | type Item = Result<(Operator<'a>, usize)>; |
367 | |
368 | /// Reads content of the code section with offsets. |
369 | /// |
370 | /// # Examples |
371 | /// ``` |
372 | /// use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader}; |
373 | /// # let data: &[u8] = &[ |
374 | /// # 0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b]; |
375 | /// let reader = BinaryReader::new(data, 20); |
376 | /// let code_reader = CodeSectionReader::new(reader).unwrap(); |
377 | /// for body in code_reader { |
378 | /// let body = body.expect("function body" ); |
379 | /// let mut op_reader = body.get_operators_reader().expect("op reader" ); |
380 | /// let ops = op_reader.into_iter_with_offsets().collect::<Result<Vec<(Operator, usize)>>>().expect("ops" ); |
381 | /// assert!( |
382 | /// if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false }, |
383 | /// "found {:?}" , |
384 | /// ops |
385 | /// ); |
386 | /// } |
387 | /// ``` |
388 | fn next(&mut self) -> Option<Self::Item> { |
389 | if self.err || self.reader.eof() { |
390 | return None; |
391 | } |
392 | let result = self.reader.read_with_offset(); |
393 | self.err = result.is_err(); |
394 | Some(result) |
395 | } |
396 | } |
397 | |
398 | macro_rules! define_visit_operator { |
399 | ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { |
400 | $( |
401 | fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output; |
402 | )* |
403 | } |
404 | } |
405 | |
406 | /// Trait implemented by types that can visit all [`Operator`] variants. |
407 | #[allow (missing_docs)] |
408 | pub trait VisitOperator<'a> { |
409 | /// The result type of the visitor. |
410 | type Output: 'a; |
411 | |
412 | /// Visits the [`Operator`] `op` using the given `offset`. |
413 | /// |
414 | /// # Note |
415 | /// |
416 | /// This is a convenience method that is intended for non-performance |
417 | /// critical use cases. For performance critical implementations users |
418 | /// are recommended to directly use the respective `visit` methods or |
419 | /// implement [`VisitOperator`] on their own. |
420 | fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { |
421 | macro_rules! visit_operator { |
422 | ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {{ |
423 | match op { |
424 | $( Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?), )* |
425 | #[cfg(feature = "simd" )] |
426 | other => visit_simd_operator(self, other), |
427 | } |
428 | }}; |
429 | } |
430 | crate::for_each_visit_operator!(visit_operator) |
431 | } |
432 | |
433 | /// Returns a mutable reference to a [`VisitSimdOperator`] visitor. |
434 | /// |
435 | /// - If an implementer does _not_ want to support Wasm `simd` proposal |
436 | /// nothing has to be done since the default implementation already suffices. |
437 | /// - If an implementer _does_ want to support Wasm `simd` proposal this |
438 | /// method usually is implemented as `Some(self)` where the implementing |
439 | /// type (`Self`) typically also implements `VisitSimdOperator`. |
440 | /// |
441 | /// # Example |
442 | /// |
443 | /// ``` |
444 | /// # macro_rules! define_visit_operator { |
445 | /// # ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { |
446 | /// # $( fn $visit(&mut self $($(,$arg: $argty)*)?) {} )* |
447 | /// # } |
448 | /// # } |
449 | /// # use wasmparser::{VisitOperator, VisitSimdOperator}; |
450 | /// pub struct MyVisitor; |
451 | /// |
452 | /// impl<'a> VisitOperator<'a> for MyVisitor { |
453 | /// type Output = (); |
454 | /// |
455 | /// fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> { |
456 | /// Some(self) |
457 | /// } |
458 | /// |
459 | /// // implement remaining visitation methods here ... |
460 | /// # wasmparser::for_each_visit_operator!(define_visit_operator); |
461 | /// } |
462 | /// |
463 | /// impl VisitSimdOperator<'_> for MyVisitor { |
464 | /// // implement SIMD visitation methods here ... |
465 | /// # wasmparser::for_each_visit_simd_operator!(define_visit_operator); |
466 | /// } |
467 | /// ``` |
468 | #[cfg (feature = "simd" )] |
469 | fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> { |
470 | None |
471 | } |
472 | |
473 | crate::for_each_visit_operator!(define_visit_operator); |
474 | } |
475 | |
476 | /// Special handler for visiting `simd` and `relaxed-simd` [`Operator`] variants. |
477 | #[cfg (feature = "simd" )] |
478 | fn visit_simd_operator<'a, V>(visitor: &mut V, op: &Operator<'a>) -> V::Output |
479 | where |
480 | V: VisitOperator<'a> + ?Sized, |
481 | { |
482 | let Some(simd_visitor: &mut (dyn VisitSimdOperator<'a, Output = …> + 'static)) = visitor.simd_visitor() else { |
483 | panic!("missing SIMD visitor to visit operator: {op:?}" ) |
484 | }; |
485 | macro_rules! visit_simd_operator { |
486 | ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {{ |
487 | match op { |
488 | $( Operator::$op $({ $($arg),* })? => simd_visitor.$visit($($($arg.clone()),*)?), )* |
489 | unexpected => unreachable!("unexpected non-SIMD operator: {unexpected:?}" ), |
490 | } |
491 | }}; |
492 | } |
493 | crate::for_each_visit_simd_operator!(visit_simd_operator) |
494 | } |
495 | |
496 | /// Trait implemented by types that can visit all Wasm `simd` and `relaxed-simd` [`Operator`]s. |
497 | #[cfg (feature = "simd" )] |
498 | #[allow (missing_docs)] |
499 | pub trait VisitSimdOperator<'a>: VisitOperator<'a> { |
500 | crate::for_each_visit_simd_operator!(define_visit_operator); |
501 | } |
502 | |
503 | macro_rules! define_visit_operator_delegate { |
504 | ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { |
505 | $( |
506 | fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { |
507 | V::$visit(&mut *self, $($($arg),*)?) |
508 | } |
509 | )* |
510 | } |
511 | } |
512 | |
513 | impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V { |
514 | type Output = V::Output; |
515 | fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { |
516 | V::visit_operator(*self, op) |
517 | } |
518 | #[cfg (feature = "simd" )] |
519 | fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = V::Output>> { |
520 | V::simd_visitor(*self) |
521 | } |
522 | crate::for_each_visit_operator!(define_visit_operator_delegate); |
523 | } |
524 | |
525 | #[cfg (feature = "simd" )] |
526 | impl<'a, 'b, V: VisitSimdOperator<'a> + ?Sized> VisitSimdOperator<'a> for &'b mut V { |
527 | crate::for_each_visit_simd_operator!(define_visit_operator_delegate); |
528 | } |
529 | |
530 | impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box<V> { |
531 | type Output = V::Output; |
532 | fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { |
533 | V::visit_operator(&mut *self, op) |
534 | } |
535 | #[cfg (feature = "simd" )] |
536 | fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = V::Output>> { |
537 | V::simd_visitor(&mut *self) |
538 | } |
539 | crate::for_each_visit_operator!(define_visit_operator_delegate); |
540 | } |
541 | |
542 | #[cfg (feature = "simd" )] |
543 | impl<'a, V: VisitSimdOperator<'a> + ?Sized> VisitSimdOperator<'a> for Box<V> { |
544 | crate::for_each_visit_simd_operator!(define_visit_operator_delegate); |
545 | } |
546 | |
547 | /// A `try_table` entries representation. |
548 | #[derive (Clone, Debug, Eq, PartialEq)] |
549 | pub struct TryTable { |
550 | /// The block type describing the try block itself. |
551 | pub ty: BlockType, |
552 | /// Outer blocks which will receive exceptions. |
553 | pub catches: Vec<Catch>, |
554 | } |
555 | |
556 | /// Catch clauses that can be specified in [`TryTable`]. |
557 | #[derive (Copy, Clone, Debug, Eq, PartialEq)] |
558 | #[allow (missing_docs)] |
559 | pub enum Catch { |
560 | /// Equivalent of `catch` |
561 | One { tag: u32, label: u32 }, |
562 | /// Equivalent of `catch_ref` |
563 | OneRef { tag: u32, label: u32 }, |
564 | /// Equivalent of `catch_all` |
565 | All { label: u32 }, |
566 | /// Equivalent of `catch_all_ref` |
567 | AllRef { label: u32 }, |
568 | } |
569 | |
570 | impl<'a> FromReader<'a> for TryTable { |
571 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
572 | let ty: BlockType = reader.read_block_type()?; |
573 | let catches: Vec = readerBinaryReaderIter<'a, '_, …> |
574 | .read_iter(MAX_WASM_CATCHES, desc:"catches" )? |
575 | .collect::<Result<_>>()?; |
576 | Ok(TryTable { ty, catches }) |
577 | } |
578 | } |
579 | |
580 | impl<'a> FromReader<'a> for Catch { |
581 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
582 | Ok(match reader.read_u8()? { |
583 | 0x00 => Catch::One { |
584 | tag: reader.read_var_u32()?, |
585 | label: reader.read_var_u32()?, |
586 | }, |
587 | 0x01 => Catch::OneRef { |
588 | tag: reader.read_var_u32()?, |
589 | label: reader.read_var_u32()?, |
590 | }, |
591 | 0x02 => Catch::All { |
592 | label: reader.read_var_u32()?, |
593 | }, |
594 | 0x03 => Catch::AllRef { |
595 | label: reader.read_var_u32()?, |
596 | }, |
597 | |
598 | x: u8 => return reader.invalid_leading_byte(byte:x, desc:"catch" ), |
599 | }) |
600 | } |
601 | } |
602 | |
603 | /// A representation of dispatch tables on `resume` and `resume_throw` |
604 | /// instructions. |
605 | #[derive (Clone, Debug, Eq, PartialEq)] |
606 | pub struct ResumeTable { |
607 | /// Either the outer blocks which will handle suspensions or |
608 | /// "switch-to" handlers. |
609 | pub handlers: Vec<Handle>, |
610 | } |
611 | |
612 | /// Handle clauses that can be specified in [`ResumeTable`]. |
613 | #[derive (Copy, Clone, Debug, Eq, PartialEq)] |
614 | #[allow (missing_docs)] |
615 | pub enum Handle { |
616 | /// Equivalent of `(on $tag $lbl)`. |
617 | OnLabel { tag: u32, label: u32 }, |
618 | /// Equivalent of `(on $tag switch)`. |
619 | OnSwitch { tag: u32 }, |
620 | } |
621 | |
622 | impl ResumeTable { |
623 | /// Returns the number of entries in the table. |
624 | pub fn len(&self) -> usize { |
625 | self.handlers.len() |
626 | } |
627 | } |
628 | |
629 | impl<'a> FromReader<'a> for ResumeTable { |
630 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
631 | let handlers: Vec = readerBinaryReaderIter<'a, '_, …> |
632 | .read_iter(MAX_WASM_HANDLERS, desc:"resume table" )? |
633 | .collect::<Result<_>>()?; |
634 | let table: ResumeTable = ResumeTable { handlers }; |
635 | Ok(table) |
636 | } |
637 | } |
638 | |
639 | impl<'a> FromReader<'a> for Handle { |
640 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
641 | Ok(match reader.read_u8()? { |
642 | 0x00 => Handle::OnLabel { |
643 | tag: reader.read_var_u32()?, |
644 | label: reader.read_var_u32()?, |
645 | }, |
646 | 0x01 => Handle::OnSwitch { |
647 | tag: reader.read_var_u32()?, |
648 | }, |
649 | x: u8 => return reader.invalid_leading_byte(byte:x, desc:"on clause" ), |
650 | }) |
651 | } |
652 | } |
653 | |