wasmparser/readers/core/
operators.rs

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
16use crate::limits::{MAX_WASM_CATCHES, MAX_WASM_HANDLERS};
17use crate::prelude::*;
18use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType};
19use core::{fmt, mem};
20
21/// Represents a block type.
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23pub enum BlockType {
24    /// The block produces consumes nor produces any values.
25    Empty,
26    /// The block produces a singular value of the given type ([] -> \[t]).
27    Type(ValType),
28    /// The block is described by a function type.
29    ///
30    /// The index is to a function type in the types section.
31    FuncType(u32),
32}
33
34/// The kind of a control flow `Frame`.
35#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36pub enum FrameKind {
37    /// A Wasm `block` control block.
38    Block,
39    /// A Wasm `if` control block.
40    If,
41    /// A Wasm `else` control block.
42    Else,
43    /// A Wasm `loop` control block.
44    Loop,
45    /// A Wasm `try` control block.
46    ///
47    /// # Note
48    ///
49    /// This belongs to the Wasm exception handling proposal.
50    TryTable,
51    /// A Wasm legacy `try` control block.
52    ///
53    /// # Note
54    ///
55    /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs`
56    LegacyTry,
57    /// A Wasm legacy `catch` control block.
58    ///
59    /// # Note
60    ///
61    /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs`
62    LegacyCatch,
63    /// A Wasm legacy `catch_all` control block.
64    ///
65    /// # Note
66    ///
67    /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs`
68    LegacyCatchAll,
69}
70
71/// Represents a memory immediate in a WebAssembly memory instruction.
72#[derive(Debug, Copy, Clone, Eq, PartialEq)]
73pub struct MemArg {
74    /// Alignment, stored as `n` where the actual alignment is `2^n`
75    pub align: u8,
76    /// Maximum alignment, stored as `n` where the actual alignment is `2^n`.
77    ///
78    /// Note that this field is not actually read from the binary format, it
79    /// will be a constant depending on which instruction this `MemArg` is a
80    /// payload for.
81    pub max_align: u8,
82    /// A fixed byte-offset that this memory immediate specifies.
83    ///
84    /// Note that the memory64 proposal can specify a full 64-bit byte offset
85    /// while otherwise only 32-bit offsets are allowed. Once validated
86    /// memory immediates for 32-bit memories are guaranteed to be at most
87    /// `u32::MAX` whereas 64-bit memories can use the full 64-bits.
88    pub offset: u64,
89    /// The index of the memory this immediate points to.
90    ///
91    /// Note that this points within the module's own memory index space, and
92    /// is always zero unless the multi-memory proposal of WebAssembly is
93    /// enabled.
94    pub memory: u32,
95}
96
97/// A br_table entries representation.
98#[derive(Clone)]
99pub struct BrTable<'a> {
100    pub(crate) reader: crate::BinaryReader<'a>,
101    pub(crate) cnt: u32,
102    pub(crate) default: u32,
103}
104
105impl PartialEq<Self> for BrTable<'_> {
106    fn eq(&self, other: &Self) -> bool {
107        self.cnt == other.cnt
108            && self.default == other.default
109            && self.reader.remaining_buffer() == other.reader.remaining_buffer()
110    }
111}
112
113impl Eq for BrTable<'_> {}
114
115impl<'a> BrTable<'a> {
116    /// Returns the number of `br_table` entries, not including the default
117    /// label
118    pub fn len(&self) -> u32 {
119        self.cnt
120    }
121
122    /// Returns whether `BrTable` doesn't have any labels apart from the default one.
123    pub fn is_empty(&self) -> bool {
124        self.len() == 0
125    }
126
127    /// Returns the default target of this `br_table` instruction.
128    pub fn default(&self) -> u32 {
129        self.default
130    }
131
132    /// Returns the list of targets that this `br_table` instruction will be
133    /// jumping to.
134    ///
135    /// This method will return an iterator which parses each target of this
136    /// `br_table` except the default target. The returned iterator will
137    /// yield `self.len()` elements.
138    ///
139    /// # Examples
140    ///
141    /// ```rust
142    /// use wasmparser::{BinaryReader, OperatorsReader, Operator};
143    ///
144    /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00];
145    /// let mut reader = OperatorsReader::new(BinaryReader::new(&buf, 0));
146    /// let op = reader.read().unwrap();
147    /// if let Operator::BrTable { targets } = op {
148    ///     let targets = targets.targets().collect::<Result<Vec<_>, _>>().unwrap();
149    ///     assert_eq!(targets, [1, 2]);
150    /// }
151    /// ```
152    pub fn targets(&self) -> BrTableTargets<'_> {
153        BrTableTargets {
154            reader: self.reader.clone(),
155            remaining: self.cnt,
156        }
157    }
158}
159
160/// An iterator over the targets of a [`BrTable`].
161///
162/// # Note
163///
164/// This iterator parses each target of the underlying `br_table`
165/// except for the default target.
166/// The iterator will yield exactly as many targets as the `br_table` has.
167pub struct BrTableTargets<'a> {
168    reader: crate::BinaryReader<'a>,
169    remaining: u32,
170}
171
172impl<'a> Iterator for BrTableTargets<'a> {
173    type Item = Result<u32>;
174
175    fn size_hint(&self) -> (usize, Option<usize>) {
176        let remaining = usize::try_from(self.remaining).unwrap_or_else(|error| {
177            panic!("could not convert remaining `u32` into `usize`: {error}")
178        });
179        (remaining, Some(remaining))
180    }
181
182    fn next(&mut self) -> Option<Self::Item> {
183        if self.remaining == 0 {
184            if !self.reader.eof() {
185                return Some(Err(BinaryReaderError::new(
186                    "trailing data in br_table",
187                    self.reader.original_position(),
188                )));
189            }
190            return None;
191        }
192        self.remaining -= 1;
193        Some(self.reader.read_var_u32())
194    }
195}
196
197impl fmt::Debug for BrTable<'_> {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        let mut f = f.debug_struct("BrTable");
200        f.field("count", &self.cnt);
201        f.field("default", &self.default);
202        match self.targets().collect::<Result<Vec<_>>>() {
203            Ok(targets) => {
204                f.field("targets", &targets);
205            }
206            Err(_) => {
207                f.field("reader", &self.reader);
208            }
209        }
210        f.finish()
211    }
212}
213
214/// An IEEE binary32 immediate floating point value, represented as a u32
215/// containing the bit pattern.
216///
217/// All bit patterns are allowed.
218#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
219pub struct Ieee32(pub(crate) u32);
220
221impl Ieee32 {
222    /// Gets the underlying bits of the 32-bit float.
223    pub fn bits(self) -> u32 {
224        self.0
225    }
226}
227
228impl From<f32> for Ieee32 {
229    fn from(value: f32) -> Self {
230        Ieee32 {
231            0: u32::from_le_bytes(value.to_le_bytes()),
232        }
233    }
234}
235
236impl From<Ieee32> for f32 {
237    fn from(bits: Ieee32) -> f32 {
238        f32::from_bits(bits.bits())
239    }
240}
241
242/// An IEEE binary64 immediate floating point value, represented as a u64
243/// containing the bit pattern.
244///
245/// All bit patterns are allowed.
246#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
247pub struct Ieee64(pub(crate) u64);
248
249impl Ieee64 {
250    /// Gets the underlying bits of the 64-bit float.
251    pub fn bits(self) -> u64 {
252        self.0
253    }
254}
255
256impl From<f64> for Ieee64 {
257    fn from(value: f64) -> Self {
258        Ieee64 {
259            0: u64::from_le_bytes(value.to_le_bytes()),
260        }
261    }
262}
263
264impl From<Ieee64> for f64 {
265    fn from(bits: Ieee64) -> f64 {
266        f64::from_bits(bits.bits())
267    }
268}
269
270/// Represents a 128-bit vector value.
271#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
272pub struct V128(pub(crate) [u8; 16]);
273
274impl V128 {
275    /// Gets the bytes of the vector value.
276    pub fn bytes(&self) -> &[u8; 16] {
277        &self.0
278    }
279
280    /// Gets a signed 128-bit integer value from the vector's bytes.
281    pub fn i128(&self) -> i128 {
282        i128::from_le_bytes(self.0)
283    }
284}
285
286impl From<V128> for i128 {
287    fn from(bits: V128) -> i128 {
288        bits.i128()
289    }
290}
291
292impl From<V128> for u128 {
293    fn from(bits: V128) -> u128 {
294        u128::from_le_bytes(bits.0)
295    }
296}
297
298/// Represents the memory ordering for atomic instructions.
299///
300/// For an in-depth explanation of memory orderings, see the C++ documentation
301/// for [`memory_order`] or the Rust documentation for [`atomic::Ordering`].
302///
303/// [`memory_order`]: https://en.cppreference.com/w/cpp/atomic/memory_order
304/// [`atomic::Ordering`]: https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html
305#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
306pub enum Ordering {
307    /// For a load, it acquires; this orders all operations before the last
308    /// "releasing" store. For a store, it releases; this orders all operations
309    /// before it at the next "acquiring" load.
310    AcqRel,
311    /// Like `AcqRel` but all threads see all sequentially consistent operations
312    /// in the same order.
313    SeqCst,
314}
315
316macro_rules! define_operator {
317    ($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident ($($ann:tt)*))*) => {
318        /// Instructions as defined [here].
319        ///
320        /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html
321        #[derive(Debug, Clone, Eq, PartialEq)]
322        #[allow(missing_docs)]
323        #[non_exhaustive]
324        pub enum Operator<'a> {
325            $(
326                $op $({ $($payload)* })?,
327            )*
328        }
329    }
330}
331crate::for_each_operator!(define_operator);
332
333/// A trait representing the stack of frames within a function.
334///
335/// The [`BinaryReader::visit_operator`] and
336/// [`OperatorsReaders`](crate::OperatorsReader) type use
337/// information about the current frame kind to enforce the syntactic
338/// requirements of the binary format.
339pub trait FrameStack {
340    /// The current frame kind.
341    fn current_frame(&self) -> Option<FrameKind>;
342}
343
344/// The Wasm control stack for the [`OperatorsReader`].
345#[derive(Debug, Default, Clone)]
346pub struct ControlStack {
347    /// All frames on the control stack exclusing the top-most frame.
348    frames: Vec<FrameKind>,
349    /// The top-most frame on the control stack if any.
350    top: Option<FrameKind>,
351}
352
353impl ControlStack {
354    /// Resets `self` but keeps heap allocations.
355    pub fn clear(&mut self) {
356        self.frames.clear();
357        self.top = None;
358    }
359
360    /// Returns `true` if `self` is empty.
361    #[inline]
362    pub fn is_empty(&self) -> bool {
363        self.top.is_none()
364    }
365
366    /// Pushes the `frame` to `self`.
367    #[inline]
368    pub fn push(&mut self, frame: FrameKind) {
369        if let Some(old_top) = self.top.replace(frame) {
370            self.frames.push(old_top);
371        }
372    }
373
374    /// Pops the top-most [`FrameKind`] from `self`.
375    pub fn pop(&mut self) -> Option<FrameKind> {
376        mem::replace(&mut self.top, self.frames.pop())
377    }
378
379    /// Returns the top-mot [`FrameKind`].
380    #[inline]
381    pub fn last(&self) -> Option<FrameKind> {
382        self.top
383    }
384}
385
386/// Adapters from VisitOperators to FrameStacks
387struct FrameStackAdapter<'a, T> {
388    stack: &'a mut ControlStack,
389    visitor: &'a mut T,
390}
391
392impl<T> FrameStack for FrameStackAdapter<'_, T> {
393    fn current_frame(&self) -> Option<FrameKind> {
394        self.stack.last()
395    }
396}
397
398struct SingleFrameAdapter<'a, T> {
399    current_frame: FrameKind,
400    visitor: &'a mut T,
401}
402
403impl<T> FrameStack for SingleFrameAdapter<'_, T> {
404    fn current_frame(&self) -> Option<FrameKind> {
405        Some(self.current_frame)
406    }
407}
408
409/// A reader for a core WebAssembly function's operators. The [`OperatorsReader`] internally
410/// maintains a stack of the kinds of frames within an expression or function body.
411/// This is necessary to enforce the syntactic requirements of the binary format.
412/// The BinaryReader can also be used to read the operators by providing an external [`FrameStack`] instance.
413#[derive(Clone)]
414pub struct OperatorsReader<'a> {
415    reader: BinaryReader<'a>,
416    stack: ControlStack,
417}
418
419/// External handle to the internal allocations used by the OperatorsReader
420///
421/// This is created with either the `Default` implementation or with
422/// [`OperatorsReader::into_allocations`]. It is then passed as an argument to
423/// [`OperatorsReader::new`] to provide a means of reusing allocations
424/// between each expression or function body.
425#[derive(Default)]
426pub struct OperatorsReaderAllocations(ControlStack);
427
428impl<'a> OperatorsReader<'a> {
429    /// Creates a new reader for an expression (instruction sequence).
430    ///
431    /// This method, in conjunction with [`OperatorsReader::into_allocations`],
432    /// provides a means to reuse allocations across reading each
433    /// individual expression or function body. Note that it is also sufficient
434    /// to call this method with `Default::default()` if no prior allocations are
435    /// available.
436    pub fn new(reader: BinaryReader<'a>) -> Self {
437        Self::new_with_allocs(reader, Default::default())
438    }
439
440    /// Same as [`OperatorsReader::new`] except that the
441    /// [`OperatorsReaderAllocations`] can be specified here to amortize the
442    /// cost of them over multiple readers.
443    pub fn new_with_allocs(
444        reader: BinaryReader<'a>,
445        mut allocs: OperatorsReaderAllocations,
446    ) -> Self {
447        allocs.0.clear();
448        allocs.0.push(FrameKind::Block);
449        Self {
450            reader,
451            stack: allocs.0,
452        }
453    }
454
455    /// Get binary reader
456    pub fn get_binary_reader(&self) -> BinaryReader<'a> {
457        self.reader.clone()
458    }
459
460    /// Determines if the reader is at the end of the operators.
461    pub fn eof(&self) -> bool {
462        self.reader.eof()
463    }
464
465    /// Gets the original position of the reader.
466    pub fn original_position(&self) -> usize {
467        self.reader.original_position()
468    }
469
470    /// Returns whether there is an `end` opcode followed by eof remaining in
471    /// this reader.
472    pub fn is_end_then_eof(&self) -> bool {
473        self.reader.is_end_then_eof()
474    }
475
476    /// Consumes this reader and returns the underlying allocations that
477    /// were used to store the frame stack.
478    ///
479    /// The returned value here can be paired with
480    /// [`OperatorsReader::new`] to reuse the allocations already
481    /// created by this reader.
482    pub fn into_allocations(self) -> OperatorsReaderAllocations {
483        OperatorsReaderAllocations(self.stack)
484    }
485
486    /// Reads the next available `Operator`.
487    ///
488    /// # Errors
489    ///
490    /// If `OperatorsReader` has less bytes remaining than required to parse
491    /// the `Operator`, or if the input is malformed.
492    pub fn read(&mut self) -> Result<Operator<'a>> {
493        self.visit_operator(&mut OperatorFactory)
494    }
495
496    /// Visit the next available operator with the specified [`VisitOperator`] instance.
497    ///
498    /// Note that this does not implicitly propagate any additional information such as instruction
499    /// offsets. In order to do so, consider storing such data within the visitor before visiting.
500    ///
501    /// # Errors
502    ///
503    /// If `OperatorsReader` has less bytes remaining than required to parse the `Operator`,
504    /// or if the input is malformed.
505    ///
506    /// # Examples
507    ///
508    /// Store an offset for use in diagnostics or any other purposes:
509    ///
510    /// ```
511    /// # use wasmparser::{OperatorsReader, VisitOperator, Result, for_each_visit_operator};
512    ///
513    /// pub fn dump(mut reader: OperatorsReader) -> Result<()> {
514    ///     let mut visitor = Dumper { offset: 0 };
515    ///     while !reader.eof() {
516    ///         visitor.offset = reader.original_position();
517    ///         reader.visit_operator(&mut visitor)?;
518    ///     }
519    ///     Ok(())
520    /// }
521    ///
522    /// struct Dumper {
523    ///     offset: usize
524    /// }
525    ///
526    /// macro_rules! define_visit_operator {
527    ///     ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
528    ///         $(
529    ///             fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
530    ///                 println!("{}: {}", self.offset, stringify!($visit));
531    ///             }
532    ///         )*
533    ///     }
534    /// }
535    ///
536    /// impl<'a> VisitOperator<'a> for Dumper {
537    ///     type Output = ();
538    ///     for_each_visit_operator!(define_visit_operator);
539    /// }
540    ///
541    /// ```
542    pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::Output>
543    where
544        T: VisitOperator<'a>,
545    {
546        self.reader.visit_operator(&mut FrameStackAdapter {
547            stack: &mut self.stack,
548            visitor,
549        })
550    }
551
552    /// Reads an operator with its offset.
553    pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> {
554        let pos = self.reader.original_position();
555        Ok((self.read()?, pos))
556    }
557
558    /// Converts to an iterator of operators paired with offsets.
559    pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> {
560        OperatorsIteratorWithOffsets {
561            reader: self,
562            err: false,
563        }
564    }
565
566    pub(crate) fn skip_const_expr(&mut self) -> Result<()> {
567        // TODO add skip_operator() method and/or validate ConstExpr operators.
568        loop {
569            if let Operator::End = self.read()? {
570                if self.current_frame().is_some() {
571                    bail!(
572                        self.original_position(),
573                        "control frames remain at end of expression"
574                    );
575                }
576                return Ok(());
577            }
578        }
579    }
580
581    /// Function that must be called after the last opcode has been processed.
582    ///
583    /// This function returns an error if there is extra data after the operators.
584    /// It does *not* check the binary format requirement that if the data count
585    /// section is absent, a data index may not occur in the code section.
586    pub fn finish(&self) -> Result<()> {
587        self.reader.finish_expression(self)
588    }
589}
590
591impl<'a> FrameStack for OperatorsReader<'a> {
592    fn current_frame(&self) -> Option<FrameKind> {
593        self.stack.last()
594    }
595}
596
597impl<'a> IntoIterator for OperatorsReader<'a> {
598    type Item = Result<Operator<'a>>;
599    type IntoIter = OperatorsIterator<'a>;
600
601    /// Reads content of the code section.
602    ///
603    /// # Examples
604    /// ```
605    /// # use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
606    /// # let data: &[u8] = &[
607    /// #     0x01, 0x03, 0x00, 0x01, 0x0b];
608    /// let reader = BinaryReader::new(data, 0);
609    /// let code_reader = CodeSectionReader::new(reader).unwrap();
610    /// for body in code_reader {
611    ///     let body = body.expect("function body");
612    ///     let mut op_reader = body.get_operators_reader().expect("op reader");
613    ///     let ops = op_reader.into_iter().collect::<Result<Vec<Operator>>>().expect("ops");
614    ///     assert!(
615    ///         if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false },
616    ///         "found {:?}",
617    ///         ops
618    ///     );
619    /// }
620    /// ```
621    fn into_iter(self) -> Self::IntoIter {
622        OperatorsIterator {
623            reader: self,
624            err: false,
625        }
626    }
627}
628
629/// An iterator over a function's operators.
630pub struct OperatorsIterator<'a> {
631    reader: OperatorsReader<'a>,
632    err: bool,
633}
634
635impl<'a> OperatorsIterator<'a> {
636    /// Consumes this iterator and returns the underlying allocations.
637    /// See [`OperatorsReader::into_allocations`].
638    pub fn into_allocations(self) -> OperatorsReaderAllocations {
639        self.reader.into_allocations()
640    }
641}
642
643impl<'a> Iterator for OperatorsIterator<'a> {
644    type Item = Result<Operator<'a>>;
645
646    fn next(&mut self) -> Option<Self::Item> {
647        if self.err || self.reader.eof() {
648            return None;
649        }
650        let result = self.reader.read();
651        self.err = result.is_err();
652        Some(result)
653    }
654}
655
656/// An iterator over a function's operators with offsets.
657pub struct OperatorsIteratorWithOffsets<'a> {
658    reader: OperatorsReader<'a>,
659    err: bool,
660}
661
662impl<'a> OperatorsIteratorWithOffsets<'a> {
663    /// Consumes this iterator and returns the underlying allocations.
664    /// See [`OperatorsReader::into_allocations`].
665    pub fn into_allocations(self) -> OperatorsReaderAllocations {
666        self.reader.into_allocations()
667    }
668}
669
670impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> {
671    type Item = Result<(Operator<'a>, usize)>;
672
673    /// Reads content of the code section with offsets.
674    ///
675    /// # Examples
676    /// ```
677    /// use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
678    /// # let data: &[u8] = &[
679    /// #     0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b];
680    /// let reader = BinaryReader::new(data, 20);
681    /// let code_reader = CodeSectionReader::new(reader).unwrap();
682    /// for body in code_reader {
683    ///     let body = body.expect("function body");
684    ///     let mut op_reader = body.get_operators_reader().expect("op reader");
685    ///     let ops = op_reader.into_iter_with_offsets().collect::<Result<Vec<(Operator, usize)>>>().expect("ops");
686    ///     assert!(
687    ///         if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false },
688    ///         "found {:?}",
689    ///         ops
690    ///     );
691    /// }
692    /// ```
693    fn next(&mut self) -> Option<Self::Item> {
694        if self.err || self.reader.eof() {
695            return None;
696        }
697        let result = self.reader.read_with_offset();
698        self.err = result.is_err();
699        Some(result)
700    }
701}
702
703macro_rules! define_visit_operator {
704    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
705        $(
706            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output;
707        )*
708    }
709}
710
711/// Trait implemented by types that can visit all [`Operator`] variants.
712#[allow(missing_docs)]
713pub trait VisitOperator<'a> {
714    /// The result type of the visitor.
715    type Output: 'a;
716
717    /// Visits the [`Operator`] `op` using the given `offset`.
718    ///
719    /// # Note
720    ///
721    /// This is a convenience method that is intended for non-performance
722    /// critical use cases. For performance critical implementations users
723    /// are recommended to directly use the respective `visit` methods or
724    /// implement [`VisitOperator`] on their own.
725    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
726        macro_rules! visit_operator {
727            ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {{
728                match op {
729                    $( Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?), )*
730                    #[cfg(feature = "simd")]
731                    other => visit_simd_operator(self, other),
732                }
733            }};
734        }
735        crate::for_each_visit_operator!(visit_operator)
736    }
737
738    /// Returns a mutable reference to a [`VisitSimdOperator`] visitor.
739    ///
740    /// - If an implementer does _not_ want to support Wasm `simd` proposal
741    ///   nothing has to be done since the default implementation already suffices.
742    /// - If an implementer _does_ want to support Wasm `simd` proposal this
743    ///   method usually is implemented as `Some(self)` where the implementing
744    ///   type (`Self`) typically also implements `VisitSimdOperator`.
745    ///
746    /// # Example
747    ///
748    /// ```
749    /// # macro_rules! define_visit_operator {
750    /// #     ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
751    /// #         $( fn $visit(&mut self $($(,$arg: $argty)*)?) {} )*
752    /// #     }
753    /// # }
754    /// # use wasmparser::{VisitOperator, VisitSimdOperator};
755    /// pub struct MyVisitor;
756    ///
757    /// impl<'a> VisitOperator<'a> for MyVisitor {
758    ///     type Output = ();
759    ///
760    ///     fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
761    ///         Some(self)
762    ///     }
763    ///
764    ///     // implement remaining visitation methods here ...
765    ///     # wasmparser::for_each_visit_operator!(define_visit_operator);
766    /// }
767    ///
768    /// impl VisitSimdOperator<'_> for MyVisitor {
769    ///     // implement SIMD visitation methods here ...
770    ///     # wasmparser::for_each_visit_simd_operator!(define_visit_operator);
771    /// }
772    /// ```
773    #[cfg(feature = "simd")]
774    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
775        None
776    }
777
778    crate::for_each_visit_operator!(define_visit_operator);
779}
780
781/// Special handler for visiting `simd` and `relaxed-simd` [`Operator`] variants.
782#[cfg(feature = "simd")]
783fn visit_simd_operator<'a, V>(visitor: &mut V, op: &Operator<'a>) -> V::Output
784where
785    V: VisitOperator<'a> + ?Sized,
786{
787    let Some(simd_visitor) = visitor.simd_visitor() else {
788        panic!("missing SIMD visitor to visit operator: {op:?}")
789    };
790    macro_rules! visit_simd_operator {
791        ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {{
792            match op {
793                $( Operator::$op $({ $($arg),* })? => simd_visitor.$visit($($($arg.clone()),*)?), )*
794                unexpected => unreachable!("unexpected non-SIMD operator: {unexpected:?}"),
795            }
796        }};
797    }
798    crate::for_each_visit_simd_operator!(visit_simd_operator)
799}
800
801/// Trait implemented by types that can visit all Wasm `simd` and `relaxed-simd` [`Operator`]s.
802#[cfg(feature = "simd")]
803#[allow(missing_docs)]
804pub trait VisitSimdOperator<'a>: VisitOperator<'a> {
805    crate::for_each_visit_simd_operator!(define_visit_operator);
806}
807
808macro_rules! define_visit_operator_delegate {
809    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
810        $(
811            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
812                V::$visit(&mut *self, $($($arg),*)?)
813            }
814        )*
815    }
816}
817
818impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V {
819    type Output = V::Output;
820    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
821        V::visit_operator(*self, op)
822    }
823    #[cfg(feature = "simd")]
824    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = V::Output>> {
825        V::simd_visitor(*self)
826    }
827    crate::for_each_visit_operator!(define_visit_operator_delegate);
828}
829
830#[cfg(feature = "simd")]
831impl<'a, 'b, V: VisitSimdOperator<'a> + ?Sized> VisitSimdOperator<'a> for &'b mut V {
832    crate::for_each_visit_simd_operator!(define_visit_operator_delegate);
833}
834
835impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box<V> {
836    type Output = V::Output;
837    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
838        V::visit_operator(&mut *self, op)
839    }
840    #[cfg(feature = "simd")]
841    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = V::Output>> {
842        V::simd_visitor(&mut *self)
843    }
844    crate::for_each_visit_operator!(define_visit_operator_delegate);
845}
846
847#[cfg(feature = "simd")]
848impl<'a, V: VisitSimdOperator<'a> + ?Sized> VisitSimdOperator<'a> for Box<V> {
849    crate::for_each_visit_simd_operator!(define_visit_operator_delegate);
850}
851
852/// A `try_table` entries representation.
853#[derive(Clone, Debug, Eq, PartialEq)]
854pub struct TryTable {
855    /// The block type describing the try block itself.
856    pub ty: BlockType,
857    /// Outer blocks which will receive exceptions.
858    pub catches: Vec<Catch>,
859}
860
861/// Catch clauses that can be specified in [`TryTable`].
862#[derive(Copy, Clone, Debug, Eq, PartialEq)]
863#[allow(missing_docs)]
864pub enum Catch {
865    /// Equivalent of `catch`
866    One { tag: u32, label: u32 },
867    /// Equivalent of `catch_ref`
868    OneRef { tag: u32, label: u32 },
869    /// Equivalent of `catch_all`
870    All { label: u32 },
871    /// Equivalent of `catch_all_ref`
872    AllRef { label: u32 },
873}
874
875impl<'a> FromReader<'a> for TryTable {
876    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
877        let ty = reader.read_block_type()?;
878        let catches = reader
879            .read_iter(MAX_WASM_CATCHES, "catches")?
880            .collect::<Result<_>>()?;
881        Ok(TryTable { ty, catches })
882    }
883}
884
885impl<'a> FromReader<'a> for Catch {
886    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
887        Ok(match reader.read_u8()? {
888            0x00 => Catch::One {
889                tag: reader.read_var_u32()?,
890                label: reader.read_var_u32()?,
891            },
892            0x01 => Catch::OneRef {
893                tag: reader.read_var_u32()?,
894                label: reader.read_var_u32()?,
895            },
896            0x02 => Catch::All {
897                label: reader.read_var_u32()?,
898            },
899            0x03 => Catch::AllRef {
900                label: reader.read_var_u32()?,
901            },
902
903            x => return reader.invalid_leading_byte(x, "catch"),
904        })
905    }
906}
907
908/// A representation of dispatch tables on `resume` and `resume_throw`
909/// instructions.
910#[derive(Clone, Debug, Eq, PartialEq)]
911pub struct ResumeTable {
912    /// Either the outer blocks which will handle suspensions or
913    /// "switch-to" handlers.
914    pub handlers: Vec<Handle>,
915}
916
917/// Handle clauses that can be specified in [`ResumeTable`].
918#[derive(Copy, Clone, Debug, Eq, PartialEq)]
919#[allow(missing_docs)]
920pub enum Handle {
921    /// Equivalent of `(on $tag $lbl)`.
922    OnLabel { tag: u32, label: u32 },
923    /// Equivalent of `(on $tag switch)`.
924    OnSwitch { tag: u32 },
925}
926
927impl ResumeTable {
928    /// Returns the number of entries in the table.
929    pub fn len(&self) -> usize {
930        self.handlers.len()
931    }
932}
933
934impl<'a> FromReader<'a> for ResumeTable {
935    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
936        let handlers = reader
937            .read_iter(MAX_WASM_HANDLERS, "resume table")?
938            .collect::<Result<_>>()?;
939        let table = ResumeTable { handlers };
940        Ok(table)
941    }
942}
943
944impl<'a> FromReader<'a> for Handle {
945    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
946        Ok(match reader.read_u8()? {
947            0x00 => Handle::OnLabel {
948                tag: reader.read_var_u32()?,
949                label: reader.read_var_u32()?,
950            },
951            0x01 => Handle::OnSwitch {
952                tag: reader.read_var_u32()?,
953            },
954            x => return reader.invalid_leading_byte(x, "on clause"),
955        })
956    }
957}
958
959/// A factory to construct [`Operator`] instances via the [`VisitOperator`] trait.
960struct OperatorFactory;
961
962macro_rules! define_visit_operator {
963    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
964        $(
965            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Operator<'a> {
966                Operator::$op $({ $($arg),* })?
967            }
968        )*
969    }
970}
971
972impl<'a> VisitOperator<'a> for OperatorFactory {
973    type Output = Operator<'a>;
974
975    #[cfg(feature = "simd")]
976    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
977        Some(self)
978    }
979
980    crate::for_each_visit_operator!(define_visit_operator);
981}
982
983#[cfg(feature = "simd")]
984impl<'a> VisitSimdOperator<'a> for OperatorFactory {
985    crate::for_each_visit_simd_operator!(define_visit_operator);
986}
987
988macro_rules! define_visit_operator_stack_adapter {
989    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
990        $(
991            fn $visit(&mut self $($(,$arg: $argty)*)?) -> T::Output {
992                define_visit_operator_stack_adapter!(@visit self $visit $($($arg,)*)?)
993            }
994        )*
995    };
996
997    (@visit $self:ident visit_block $($rest:tt)*) => {{
998	    $self.stack.push(FrameKind::Block);
999	    $self.visitor.visit_block( $($rest)* )
1000    }};
1001
1002    (@visit $self:ident visit_loop $($rest:tt)*) => {{
1003	    $self.stack.push(FrameKind::Loop);
1004	    $self.visitor.visit_loop( $($rest)* )
1005    }};
1006
1007    (@visit $self:ident visit_if $($rest:tt)*) => {{
1008	    $self.stack.push(FrameKind::If);
1009	    $self.visitor.visit_if( $($rest)* )
1010    }};
1011
1012    (@visit $self:ident visit_else $($rest:tt)*) => {{
1013	    $self.stack.pop();
1014	    $self.stack.push(FrameKind::Else);
1015	    $self.visitor.visit_else( $($rest)* )
1016    }};
1017
1018    (@visit $self:ident visit_try $($rest:tt)*) => {{
1019	    $self.stack.push(FrameKind::LegacyTry);
1020	    $self.visitor.visit_try( $($rest)* )
1021    }};
1022
1023    (@visit $self:ident visit_catch $($rest:tt)*) => {{
1024	    $self.stack.pop();
1025	    $self.stack.push(FrameKind::LegacyCatch);
1026	    $self.visitor.visit_catch( $($rest)* )
1027    }};
1028
1029    (@visit $self:ident visit_catch_all $($rest:tt)*) => {{
1030	    $self.stack.pop();
1031	    $self.stack.push(FrameKind::LegacyCatchAll);
1032	    $self.visitor.visit_catch_all( $($rest)* )
1033    }};
1034
1035    (@visit $self:ident visit_try_table $($rest:tt)*) => {{
1036	    $self.stack.push(FrameKind::TryTable);
1037	    $self.visitor.visit_try_table( $($rest)* )
1038    }};
1039
1040    (@visit $self:ident visit_delegate $($rest:tt)*) => {{
1041	    $self.stack.pop();
1042	    $self.visitor.visit_delegate( $($rest)* )
1043    }};
1044
1045    (@visit $self:ident visit_end $($rest:tt)*) => {{
1046	    $self.stack.pop();
1047	    $self.visitor.visit_end( $($rest)* )
1048    }};
1049
1050    (@visit $self:ident $visit:ident $($rest:tt)*) => {
1051	$self.visitor.$visit( $($rest)* )
1052    };
1053}
1054
1055impl<'a, T: VisitOperator<'a>> VisitOperator<'a> for FrameStackAdapter<'_, T> {
1056    type Output = T::Output;
1057
1058    #[cfg(feature = "simd")]
1059    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
1060        self.visitor.simd_visitor()
1061    }
1062
1063    crate::for_each_visit_operator!(define_visit_operator_stack_adapter);
1064}
1065
1066macro_rules! define_passthrough_visit_operator {
1067    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
1068        $(
1069            fn $visit(&mut self $($(,$arg: $argty)*)?) -> T::Output {
1070		self.visitor.$visit( $($($arg,)*)? )
1071            }
1072        )*
1073    };
1074}
1075
1076impl<'a, T: VisitOperator<'a>> VisitOperator<'a> for SingleFrameAdapter<'_, T> {
1077    type Output = T::Output;
1078
1079    #[cfg(feature = "simd")]
1080    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
1081        self.visitor.simd_visitor()
1082    }
1083
1084    crate::for_each_visit_operator!(define_passthrough_visit_operator);
1085}
1086
1087impl<'a> BinaryReader<'a> {
1088    /// Peeks at the next available `Operator`, given a borrowed `FrameStack`.
1089    ///
1090    /// # Errors
1091    ///
1092    /// If `BinaryReader` has less bytes remaining than required to parse
1093    /// the `Operator`, or if the input is malformed.
1094    pub fn peek_operator<T: FrameStack>(&self, stack: &T) -> Result<Operator<'a>> {
1095        self.clone().visit_operator(&mut SingleFrameAdapter {
1096            current_frame: stack.current_frame().ok_or_else(|| {
1097                format_err!(
1098                    self.original_position(),
1099                    "operators remaining after end of function body or expression"
1100                )
1101            })?,
1102            visitor: &mut OperatorFactory,
1103        })
1104    }
1105}