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