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}