Skip to main content

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
298impl From<i128> for V128 {
299    fn from(value: i128) -> Self {
300        V128(i128::to_le_bytes(value))
301    }
302}
303
304impl From<u128> for V128 {
305    fn from(value: u128) -> Self {
306        V128(u128::to_le_bytes(value))
307    }
308}
309
310/// Represents the memory ordering for atomic instructions.
311///
312/// For an in-depth explanation of memory orderings, see the C++ documentation
313/// for [`memory_order`] or the Rust documentation for [`atomic::Ordering`].
314///
315/// [`memory_order`]: https://en.cppreference.com/w/cpp/atomic/memory_order
316/// [`atomic::Ordering`]: https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html
317#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
318pub enum Ordering {
319    /// For a load, it acquires; this orders all operations before the last
320    /// "releasing" store. For a store, it releases; this orders all operations
321    /// before it at the next "acquiring" load.
322    AcqRel,
323    /// Like `AcqRel` but all threads see all sequentially consistent operations
324    /// in the same order.
325    SeqCst,
326}
327
328macro_rules! define_operator {
329    ($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident ($($ann:tt)*))*) => {
330        /// Instructions as defined [here].
331        ///
332        /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html
333        #[derive(Debug, Clone, Eq, PartialEq)]
334        #[allow(missing_docs)]
335        #[non_exhaustive]
336        pub enum Operator<'a> {
337            $(
338                $op $({ $($payload)* })?,
339            )*
340        }
341    }
342}
343crate::for_each_operator!(define_operator);
344
345/// A trait representing the stack of frames within a function.
346///
347/// The [`BinaryReader::visit_operator`] and
348/// [`OperatorsReaders`](crate::OperatorsReader) type use
349/// information about the current frame kind to enforce the syntactic
350/// requirements of the binary format.
351pub trait FrameStack {
352    /// The current frame kind.
353    fn current_frame(&self) -> Option<FrameKind>;
354}
355
356/// The Wasm control stack for the [`OperatorsReader`].
357#[derive(Debug, Default, Clone)]
358pub struct ControlStack {
359    /// All frames on the control stack excluding the top-most frame.
360    frames: Vec<FrameKind>,
361    /// The top-most frame on the control stack if any.
362    top: Option<FrameKind>,
363}
364
365impl ControlStack {
366    /// Resets `self` but keeps heap allocations.
367    pub fn clear(&mut self) {
368        self.frames.clear();
369        self.top = None;
370    }
371
372    /// Returns `true` if `self` is empty.
373    #[inline]
374    pub fn is_empty(&self) -> bool {
375        self.top.is_none()
376    }
377
378    /// Pushes the `frame` to `self`.
379    #[inline]
380    pub fn push(&mut self, frame: FrameKind) {
381        if let Some(old_top) = self.top.replace(frame) {
382            self.frames.push(old_top);
383        }
384    }
385
386    /// Pops the top-most [`FrameKind`] from `self`.
387    pub fn pop(&mut self) -> Option<FrameKind> {
388        mem::replace(&mut self.top, self.frames.pop())
389    }
390
391    /// Returns the top-mot [`FrameKind`].
392    #[inline]
393    pub fn last(&self) -> Option<FrameKind> {
394        self.top
395    }
396}
397
398/// Adapters from VisitOperators to FrameStacks
399struct FrameStackAdapter<'a, T> {
400    stack: &'a mut ControlStack,
401    visitor: &'a mut T,
402}
403
404impl<T> FrameStack for FrameStackAdapter<'_, T> {
405    fn current_frame(&self) -> Option<FrameKind> {
406        self.stack.last()
407    }
408}
409
410struct SingleFrameAdapter<'a, T> {
411    current_frame: FrameKind,
412    visitor: &'a mut T,
413}
414
415impl<T> FrameStack for SingleFrameAdapter<'_, T> {
416    fn current_frame(&self) -> Option<FrameKind> {
417        Some(self.current_frame)
418    }
419}
420
421/// A reader for a core WebAssembly function's operators. The [`OperatorsReader`] internally
422/// maintains a stack of the kinds of frames within an expression or function body.
423/// This is necessary to enforce the syntactic requirements of the binary format.
424/// The BinaryReader can also be used to read the operators by providing an external [`FrameStack`] instance.
425#[derive(Clone)]
426pub struct OperatorsReader<'a> {
427    reader: BinaryReader<'a>,
428    stack: ControlStack,
429}
430
431/// External handle to the internal allocations used by the OperatorsReader
432///
433/// This is created with either the `Default` implementation or with
434/// [`OperatorsReader::into_allocations`]. It is then passed as an argument to
435/// [`OperatorsReader::new`] to provide a means of reusing allocations
436/// between each expression or function body.
437#[derive(Default)]
438pub struct OperatorsReaderAllocations(ControlStack);
439
440impl<'a> OperatorsReader<'a> {
441    /// Creates a new reader for an expression (instruction sequence).
442    ///
443    /// This method, in conjunction with [`OperatorsReader::into_allocations`],
444    /// provides a means to reuse allocations across reading each
445    /// individual expression or function body. Note that it is also sufficient
446    /// to call this method with `Default::default()` if no prior allocations are
447    /// available.
448    pub fn new(reader: BinaryReader<'a>) -> Self {
449        Self::new_with_allocs(reader, Default::default())
450    }
451
452    /// Same as [`OperatorsReader::new`] except that the
453    /// [`OperatorsReaderAllocations`] can be specified here to amortize the
454    /// cost of them over multiple readers.
455    pub fn new_with_allocs(
456        reader: BinaryReader<'a>,
457        mut allocs: OperatorsReaderAllocations,
458    ) -> Self {
459        allocs.0.clear();
460        allocs.0.push(FrameKind::Block);
461        Self {
462            reader,
463            stack: allocs.0,
464        }
465    }
466
467    /// Get binary reader
468    pub fn get_binary_reader(&self) -> BinaryReader<'a> {
469        self.reader.clone()
470    }
471
472    /// Determines if the reader is at the end of the operators.
473    pub fn eof(&self) -> bool {
474        self.reader.eof()
475    }
476
477    /// Gets the original position of the reader.
478    pub fn original_position(&self) -> usize {
479        self.reader.original_position()
480    }
481
482    /// Returns whether there is an `end` opcode followed by eof remaining in
483    /// this reader.
484    pub fn is_end_then_eof(&self) -> bool {
485        self.reader.is_end_then_eof()
486    }
487
488    /// Consumes this reader and returns the underlying allocations that
489    /// were used to store the frame stack.
490    ///
491    /// The returned value here can be paired with
492    /// [`OperatorsReader::new`] to reuse the allocations already
493    /// created by this reader.
494    pub fn into_allocations(self) -> OperatorsReaderAllocations {
495        OperatorsReaderAllocations(self.stack)
496    }
497
498    /// Reads the next available `Operator`.
499    ///
500    /// # Errors
501    ///
502    /// If `OperatorsReader` has less bytes remaining than required to parse
503    /// the `Operator`, or if the input is malformed.
504    pub fn read(&mut self) -> Result<Operator<'a>> {
505        self.visit_operator(&mut OperatorFactory)
506    }
507
508    /// Visit the next available operator with the specified [`VisitOperator`] instance.
509    ///
510    /// Note that this does not implicitly propagate any additional information such as instruction
511    /// offsets. In order to do so, consider storing such data within the visitor before visiting.
512    ///
513    /// # Errors
514    ///
515    /// If `OperatorsReader` has less bytes remaining than required to parse the `Operator`,
516    /// or if the input is malformed.
517    ///
518    /// # Examples
519    ///
520    /// Store an offset for use in diagnostics or any other purposes:
521    ///
522    /// ```
523    /// # use wasmparser::{OperatorsReader, VisitOperator, Result, for_each_visit_operator};
524    ///
525    /// pub fn dump(mut reader: OperatorsReader) -> Result<()> {
526    ///     let mut visitor = Dumper { offset: 0 };
527    ///     while !reader.eof() {
528    ///         visitor.offset = reader.original_position();
529    ///         reader.visit_operator(&mut visitor)?;
530    ///     }
531    ///     Ok(())
532    /// }
533    ///
534    /// struct Dumper {
535    ///     offset: usize
536    /// }
537    ///
538    /// macro_rules! define_visit_operator {
539    ///     ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
540    ///         $(
541    ///             fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
542    ///                 println!("{}: {}", self.offset, stringify!($visit));
543    ///             }
544    ///         )*
545    ///     }
546    /// }
547    ///
548    /// impl<'a> VisitOperator<'a> for Dumper {
549    ///     type Output = ();
550    ///     for_each_visit_operator!(define_visit_operator);
551    /// }
552    ///
553    /// ```
554    pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::Output>
555    where
556        T: VisitOperator<'a>,
557    {
558        self.reader.visit_operator(&mut FrameStackAdapter {
559            stack: &mut self.stack,
560            visitor,
561        })
562    }
563
564    /// Reads an operator with its offset.
565    pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> {
566        let pos = self.reader.original_position();
567        Ok((self.read()?, pos))
568    }
569
570    /// Converts to an iterator of operators paired with offsets.
571    pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> {
572        OperatorsIteratorWithOffsets {
573            reader: self,
574            err: false,
575        }
576    }
577
578    pub(crate) fn skip_const_expr(&mut self) -> Result<()> {
579        // TODO add skip_operator() method and/or validate ConstExpr operators.
580        loop {
581            if let Operator::End = self.read()? {
582                if self.current_frame().is_some() {
583                    bail!(
584                        self.original_position(),
585                        "control frames remain at end of expression"
586                    );
587                }
588                return Ok(());
589            }
590        }
591    }
592
593    /// Function that must be called after the last opcode has been processed.
594    ///
595    /// This function returns an error if there is extra data after the operators.
596    /// It does *not* check the binary format requirement that if the data count
597    /// section is absent, a data index may not occur in the code section.
598    pub fn finish(&self) -> Result<()> {
599        self.reader.finish_expression(self)
600    }
601}
602
603impl<'a> FrameStack for OperatorsReader<'a> {
604    fn current_frame(&self) -> Option<FrameKind> {
605        self.stack.last()
606    }
607}
608
609impl<'a> IntoIterator for OperatorsReader<'a> {
610    type Item = Result<Operator<'a>>;
611    type IntoIter = OperatorsIterator<'a>;
612
613    /// Reads content of the code section.
614    ///
615    /// # Examples
616    /// ```
617    /// # use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
618    /// # let data: &[u8] = &[
619    /// #     0x01, 0x03, 0x00, 0x01, 0x0b];
620    /// let reader = BinaryReader::new(data, 0);
621    /// let code_reader = CodeSectionReader::new(reader).unwrap();
622    /// for body in code_reader {
623    ///     let body = body.expect("function body");
624    ///     let mut op_reader = body.get_operators_reader().expect("op reader");
625    ///     let ops = op_reader.into_iter().collect::<Result<Vec<Operator>>>().expect("ops");
626    ///     assert!(
627    ///         if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false },
628    ///         "found {:?}",
629    ///         ops
630    ///     );
631    /// }
632    /// ```
633    fn into_iter(self) -> Self::IntoIter {
634        OperatorsIterator {
635            reader: self,
636            err: false,
637        }
638    }
639}
640
641/// An iterator over a function's operators.
642pub struct OperatorsIterator<'a> {
643    reader: OperatorsReader<'a>,
644    err: bool,
645}
646
647impl<'a> OperatorsIterator<'a> {
648    /// Consumes this iterator and returns the underlying allocations.
649    /// See [`OperatorsReader::into_allocations`].
650    pub fn into_allocations(self) -> OperatorsReaderAllocations {
651        self.reader.into_allocations()
652    }
653}
654
655impl<'a> Iterator for OperatorsIterator<'a> {
656    type Item = Result<Operator<'a>>;
657
658    fn next(&mut self) -> Option<Self::Item> {
659        if self.err || self.reader.eof() {
660            return None;
661        }
662        let result = self.reader.read();
663        self.err = result.is_err();
664        Some(result)
665    }
666}
667
668/// An iterator over a function's operators with offsets.
669pub struct OperatorsIteratorWithOffsets<'a> {
670    reader: OperatorsReader<'a>,
671    err: bool,
672}
673
674impl<'a> OperatorsIteratorWithOffsets<'a> {
675    /// Consumes this iterator and returns the underlying allocations.
676    /// See [`OperatorsReader::into_allocations`].
677    pub fn into_allocations(self) -> OperatorsReaderAllocations {
678        self.reader.into_allocations()
679    }
680}
681
682impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> {
683    type Item = Result<(Operator<'a>, usize)>;
684
685    /// Reads content of the code section with offsets.
686    ///
687    /// # Examples
688    /// ```
689    /// use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
690    /// # let data: &[u8] = &[
691    /// #     0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b];
692    /// let reader = BinaryReader::new(data, 20);
693    /// let code_reader = CodeSectionReader::new(reader).unwrap();
694    /// for body in code_reader {
695    ///     let body = body.expect("function body");
696    ///     let mut op_reader = body.get_operators_reader().expect("op reader");
697    ///     let ops = op_reader.into_iter_with_offsets().collect::<Result<Vec<(Operator, usize)>>>().expect("ops");
698    ///     assert!(
699    ///         if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false },
700    ///         "found {:?}",
701    ///         ops
702    ///     );
703    /// }
704    /// ```
705    fn next(&mut self) -> Option<Self::Item> {
706        if self.err || self.reader.eof() {
707            return None;
708        }
709        let result = self.reader.read_with_offset();
710        self.err = result.is_err();
711        Some(result)
712    }
713}
714
715macro_rules! define_visit_operator {
716    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
717        $(
718            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output;
719        )*
720    }
721}
722
723/// Trait implemented by types that can visit all [`Operator`] variants.
724#[allow(missing_docs)]
725pub trait VisitOperator<'a> {
726    /// The result type of the visitor.
727    type Output: 'a;
728
729    /// Visits the [`Operator`] `op` using the given `offset`.
730    ///
731    /// # Note
732    ///
733    /// This is a convenience method that is intended for non-performance
734    /// critical use cases. For performance critical implementations users
735    /// are recommended to directly use the respective `visit` methods or
736    /// implement [`VisitOperator`] on their own.
737    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
738        macro_rules! visit_operator {
739            ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {{
740                match op {
741                    $( Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?), )*
742                    #[cfg(feature = "simd")]
743                    other => visit_simd_operator(self, other),
744                }
745            }};
746        }
747        crate::for_each_visit_operator!(visit_operator)
748    }
749
750    /// Returns a mutable reference to a [`VisitSimdOperator`] visitor.
751    ///
752    /// - If an implementer does _not_ want to support Wasm `simd` proposal
753    ///   nothing has to be done since the default implementation already suffices.
754    /// - If an implementer _does_ want to support Wasm `simd` proposal this
755    ///   method usually is implemented as `Some(self)` where the implementing
756    ///   type (`Self`) typically also implements `VisitSimdOperator`.
757    ///
758    /// # Example
759    ///
760    /// ```
761    /// # macro_rules! define_visit_operator {
762    /// #     ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
763    /// #         $( fn $visit(&mut self $($(,$arg: $argty)*)?) {} )*
764    /// #     }
765    /// # }
766    /// # use wasmparser::{VisitOperator, VisitSimdOperator};
767    /// pub struct MyVisitor;
768    ///
769    /// impl<'a> VisitOperator<'a> for MyVisitor {
770    ///     type Output = ();
771    ///
772    ///     fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
773    ///         Some(self)
774    ///     }
775    ///
776    ///     // implement remaining visitation methods here ...
777    ///     # wasmparser::for_each_visit_operator!(define_visit_operator);
778    /// }
779    ///
780    /// impl VisitSimdOperator<'_> for MyVisitor {
781    ///     // implement SIMD visitation methods here ...
782    ///     # wasmparser::for_each_visit_simd_operator!(define_visit_operator);
783    /// }
784    /// ```
785    #[cfg(feature = "simd")]
786    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
787        None
788    }
789
790    crate::for_each_visit_operator!(define_visit_operator);
791}
792
793/// Special handler for visiting `simd` and `relaxed-simd` [`Operator`] variants.
794#[cfg(feature = "simd")]
795fn visit_simd_operator<'a, V>(visitor: &mut V, op: &Operator<'a>) -> V::Output
796where
797    V: VisitOperator<'a> + ?Sized,
798{
799    let Some(simd_visitor) = visitor.simd_visitor() else {
800        panic!("missing SIMD visitor to visit operator: {op:?}")
801    };
802    macro_rules! visit_simd_operator {
803        ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {{
804            match op {
805                $( Operator::$op $({ $($arg),* })? => simd_visitor.$visit($($($arg.clone()),*)?), )*
806                unexpected => unreachable!("unexpected non-SIMD operator: {unexpected:?}"),
807            }
808        }};
809    }
810    crate::for_each_visit_simd_operator!(visit_simd_operator)
811}
812
813/// Trait implemented by types that can visit all Wasm `simd` and `relaxed-simd` [`Operator`]s.
814#[cfg(feature = "simd")]
815#[allow(missing_docs)]
816pub trait VisitSimdOperator<'a>: VisitOperator<'a> {
817    crate::for_each_visit_simd_operator!(define_visit_operator);
818}
819
820macro_rules! define_visit_operator_delegate {
821    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
822        $(
823            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
824                V::$visit(&mut *self, $($($arg),*)?)
825            }
826        )*
827    }
828}
829
830impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V {
831    type Output = V::Output;
832    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
833        V::visit_operator(*self, op)
834    }
835    #[cfg(feature = "simd")]
836    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = V::Output>> {
837        V::simd_visitor(*self)
838    }
839    crate::for_each_visit_operator!(define_visit_operator_delegate);
840}
841
842#[cfg(feature = "simd")]
843impl<'a, 'b, V: VisitSimdOperator<'a> + ?Sized> VisitSimdOperator<'a> for &'b mut V {
844    crate::for_each_visit_simd_operator!(define_visit_operator_delegate);
845}
846
847impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box<V> {
848    type Output = V::Output;
849    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
850        V::visit_operator(&mut *self, op)
851    }
852    #[cfg(feature = "simd")]
853    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = V::Output>> {
854        V::simd_visitor(&mut *self)
855    }
856    crate::for_each_visit_operator!(define_visit_operator_delegate);
857}
858
859#[cfg(feature = "simd")]
860impl<'a, V: VisitSimdOperator<'a> + ?Sized> VisitSimdOperator<'a> for Box<V> {
861    crate::for_each_visit_simd_operator!(define_visit_operator_delegate);
862}
863
864/// A `try_table` entries representation.
865#[derive(Clone, Debug, Eq, PartialEq)]
866pub struct TryTable {
867    /// The block type describing the try block itself.
868    pub ty: BlockType,
869    /// Outer blocks which will receive exceptions.
870    pub catches: Vec<Catch>,
871}
872
873/// Catch clauses that can be specified in [`TryTable`].
874#[derive(Copy, Clone, Debug, Eq, PartialEq)]
875#[allow(missing_docs)]
876pub enum Catch {
877    /// Equivalent of `catch`
878    One { tag: u32, label: u32 },
879    /// Equivalent of `catch_ref`
880    OneRef { tag: u32, label: u32 },
881    /// Equivalent of `catch_all`
882    All { label: u32 },
883    /// Equivalent of `catch_all_ref`
884    AllRef { label: u32 },
885}
886
887impl<'a> FromReader<'a> for TryTable {
888    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
889        let ty = reader.read_block_type()?;
890        let catches = reader
891            .read_iter(MAX_WASM_CATCHES, "catches")?
892            .collect::<Result<_>>()?;
893        Ok(TryTable { ty, catches })
894    }
895}
896
897impl<'a> FromReader<'a> for Catch {
898    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
899        Ok(match reader.read_u8()? {
900            0x00 => Catch::One {
901                tag: reader.read_var_u32()?,
902                label: reader.read_var_u32()?,
903            },
904            0x01 => Catch::OneRef {
905                tag: reader.read_var_u32()?,
906                label: reader.read_var_u32()?,
907            },
908            0x02 => Catch::All {
909                label: reader.read_var_u32()?,
910            },
911            0x03 => Catch::AllRef {
912                label: reader.read_var_u32()?,
913            },
914
915            x => return reader.invalid_leading_byte(x, "catch"),
916        })
917    }
918}
919
920/// A representation of dispatch tables on `resume` and `resume_throw`
921/// instructions.
922#[derive(Clone, Debug, Eq, PartialEq)]
923pub struct ResumeTable {
924    /// Either the outer blocks which will handle suspensions or
925    /// "switch-to" handlers.
926    pub handlers: Vec<Handle>,
927}
928
929/// Handle clauses that can be specified in [`ResumeTable`].
930#[derive(Copy, Clone, Debug, Eq, PartialEq)]
931#[allow(missing_docs)]
932pub enum Handle {
933    /// Equivalent of `(on $tag $lbl)`.
934    OnLabel { tag: u32, label: u32 },
935    /// Equivalent of `(on $tag switch)`.
936    OnSwitch { tag: u32 },
937}
938
939impl ResumeTable {
940    /// Returns the number of entries in the table.
941    pub fn len(&self) -> usize {
942        self.handlers.len()
943    }
944}
945
946impl<'a> FromReader<'a> for ResumeTable {
947    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
948        let handlers = reader
949            .read_iter(MAX_WASM_HANDLERS, "resume table")?
950            .collect::<Result<_>>()?;
951        let table = ResumeTable { handlers };
952        Ok(table)
953    }
954}
955
956impl<'a> FromReader<'a> for Handle {
957    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
958        Ok(match reader.read_u8()? {
959            0x00 => Handle::OnLabel {
960                tag: reader.read_var_u32()?,
961                label: reader.read_var_u32()?,
962            },
963            0x01 => Handle::OnSwitch {
964                tag: reader.read_var_u32()?,
965            },
966            x => return reader.invalid_leading_byte(x, "on clause"),
967        })
968    }
969}
970
971/// A factory to construct [`Operator`] instances via the [`VisitOperator`] trait.
972struct OperatorFactory;
973
974macro_rules! define_visit_operator {
975    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
976        $(
977            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Operator<'a> {
978                Operator::$op $({ $($arg),* })?
979            }
980        )*
981    }
982}
983
984impl<'a> VisitOperator<'a> for OperatorFactory {
985    type Output = Operator<'a>;
986
987    #[cfg(feature = "simd")]
988    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
989        Some(self)
990    }
991
992    crate::for_each_visit_operator!(define_visit_operator);
993}
994
995#[cfg(feature = "simd")]
996impl<'a> VisitSimdOperator<'a> for OperatorFactory {
997    crate::for_each_visit_simd_operator!(define_visit_operator);
998}
999
1000macro_rules! define_visit_operator_stack_adapter {
1001    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
1002        $(
1003            fn $visit(&mut self $($(,$arg: $argty)*)?) -> T::Output {
1004                define_visit_operator_stack_adapter!(@visit self $visit $($($arg,)*)?)
1005            }
1006        )*
1007    };
1008
1009    (@visit $self:ident visit_block $($rest:tt)*) => {{
1010	    $self.stack.push(FrameKind::Block);
1011	    $self.visitor.visit_block( $($rest)* )
1012    }};
1013
1014    (@visit $self:ident visit_loop $($rest:tt)*) => {{
1015	    $self.stack.push(FrameKind::Loop);
1016	    $self.visitor.visit_loop( $($rest)* )
1017    }};
1018
1019    (@visit $self:ident visit_if $($rest:tt)*) => {{
1020	    $self.stack.push(FrameKind::If);
1021	    $self.visitor.visit_if( $($rest)* )
1022    }};
1023
1024    (@visit $self:ident visit_else $($rest:tt)*) => {{
1025	    $self.stack.pop();
1026	    $self.stack.push(FrameKind::Else);
1027	    $self.visitor.visit_else( $($rest)* )
1028    }};
1029
1030    (@visit $self:ident visit_try $($rest:tt)*) => {{
1031	    $self.stack.push(FrameKind::LegacyTry);
1032	    $self.visitor.visit_try( $($rest)* )
1033    }};
1034
1035    (@visit $self:ident visit_catch $($rest:tt)*) => {{
1036	    $self.stack.pop();
1037	    $self.stack.push(FrameKind::LegacyCatch);
1038	    $self.visitor.visit_catch( $($rest)* )
1039    }};
1040
1041    (@visit $self:ident visit_catch_all $($rest:tt)*) => {{
1042	    $self.stack.pop();
1043	    $self.stack.push(FrameKind::LegacyCatchAll);
1044	    $self.visitor.visit_catch_all( $($rest)* )
1045    }};
1046
1047    (@visit $self:ident visit_try_table $($rest:tt)*) => {{
1048	    $self.stack.push(FrameKind::TryTable);
1049	    $self.visitor.visit_try_table( $($rest)* )
1050    }};
1051
1052    (@visit $self:ident visit_delegate $($rest:tt)*) => {{
1053	    $self.stack.pop();
1054	    $self.visitor.visit_delegate( $($rest)* )
1055    }};
1056
1057    (@visit $self:ident visit_end $($rest:tt)*) => {{
1058	    $self.stack.pop();
1059	    $self.visitor.visit_end( $($rest)* )
1060    }};
1061
1062    (@visit $self:ident $visit:ident $($rest:tt)*) => {
1063	$self.visitor.$visit( $($rest)* )
1064    };
1065}
1066
1067impl<'a, T: VisitOperator<'a>> VisitOperator<'a> for FrameStackAdapter<'_, T> {
1068    type Output = T::Output;
1069
1070    #[cfg(feature = "simd")]
1071    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
1072        self.visitor.simd_visitor()
1073    }
1074
1075    crate::for_each_visit_operator!(define_visit_operator_stack_adapter);
1076}
1077
1078macro_rules! define_passthrough_visit_operator {
1079    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
1080        $(
1081            fn $visit(&mut self $($(,$arg: $argty)*)?) -> T::Output {
1082		self.visitor.$visit( $($($arg,)*)? )
1083            }
1084        )*
1085    };
1086}
1087
1088impl<'a, T: VisitOperator<'a>> VisitOperator<'a> for SingleFrameAdapter<'_, T> {
1089    type Output = T::Output;
1090
1091    #[cfg(feature = "simd")]
1092    fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
1093        self.visitor.simd_visitor()
1094    }
1095
1096    crate::for_each_visit_operator!(define_passthrough_visit_operator);
1097}
1098
1099impl<'a> BinaryReader<'a> {
1100    /// Peeks at the next available `Operator`, given a borrowed `FrameStack`.
1101    ///
1102    /// # Errors
1103    ///
1104    /// If `BinaryReader` has less bytes remaining than required to parse
1105    /// the `Operator`, or if the input is malformed.
1106    pub fn peek_operator<T: FrameStack>(&self, stack: &T) -> Result<Operator<'a>> {
1107        self.clone().visit_operator(&mut SingleFrameAdapter {
1108            current_frame: stack.current_frame().ok_or_else(|| {
1109                format_err!(
1110                    self.original_position(),
1111                    "operators remaining after end of function body or expression"
1112                )
1113            })?,
1114            visitor: &mut OperatorFactory,
1115        })
1116    }
1117}