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;
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/// Represents a memory immediate in a WebAssembly memory instruction.
34#[derive(Debug, Copy, Clone, Eq, PartialEq)]
35pub struct MemArg {
36    /// Alignment, stored as `n` where the actual alignment is `2^n`
37    pub align: u8,
38    /// Maximum alignment, stored as `n` where the actual alignment is `2^n`.
39    ///
40    /// Note that this field is not actually read from the binary format, it
41    /// will be a constant depending on which instruction this `MemArg` is a
42    /// payload for.
43    pub max_align: u8,
44    /// A fixed byte-offset that this memory immediate specifies.
45    ///
46    /// Note that the memory64 proposal can specify a full 64-bit byte offset
47    /// while otherwise only 32-bit offsets are allowed. Once validated
48    /// memory immediates for 32-bit memories are guaranteed to be at most
49    /// `u32::MAX` whereas 64-bit memories can use the full 64-bits.
50    pub offset: u64,
51    /// The index of the memory this immediate points to.
52    ///
53    /// Note that this points within the module's own memory index space, and
54    /// is always zero unless the multi-memory proposal of WebAssembly is
55    /// enabled.
56    pub memory: u32,
57}
58
59/// A br_table entries representation.
60#[derive(Clone)]
61pub struct BrTable<'a> {
62    pub(crate) reader: crate::BinaryReader<'a>,
63    pub(crate) cnt: u32,
64    pub(crate) default: u32,
65}
66
67impl PartialEq<Self> for BrTable<'_> {
68    fn eq(&self, other: &Self) -> bool {
69        self.cnt == other.cnt
70            && self.default == other.default
71            && self.reader.remaining_buffer() == other.reader.remaining_buffer()
72    }
73}
74
75impl Eq for BrTable<'_> {}
76
77/// An IEEE binary32 immediate floating point value, represented as a u32
78/// containing the bit pattern.
79///
80/// All bit patterns are allowed.
81#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
82pub struct Ieee32(pub(crate) u32);
83
84impl Ieee32 {
85    /// Gets the underlying bits of the 32-bit float.
86    pub fn bits(self) -> u32 {
87        self.0
88    }
89}
90
91impl From<f32> for Ieee32 {
92    fn from(value: f32) -> Self {
93        Ieee32 {
94            0: u32::from_le_bytes(value.to_le_bytes()),
95        }
96    }
97}
98
99impl From<Ieee32> for f32 {
100    fn from(bits: Ieee32) -> f32 {
101        f32::from_bits(bits.bits())
102    }
103}
104
105/// An IEEE binary64 immediate floating point value, represented as a u64
106/// containing the bit pattern.
107///
108/// All bit patterns are allowed.
109#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
110pub struct Ieee64(pub(crate) u64);
111
112impl Ieee64 {
113    /// Gets the underlying bits of the 64-bit float.
114    pub fn bits(self) -> u64 {
115        self.0
116    }
117}
118
119impl From<f64> for Ieee64 {
120    fn from(value: f64) -> Self {
121        Ieee64 {
122            0: u64::from_le_bytes(value.to_le_bytes()),
123        }
124    }
125}
126
127impl From<Ieee64> for f64 {
128    fn from(bits: Ieee64) -> f64 {
129        f64::from_bits(bits.bits())
130    }
131}
132
133/// Represents a 128-bit vector value.
134#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
135pub struct V128(pub(crate) [u8; 16]);
136
137impl V128 {
138    /// Gets the bytes of the vector value.
139    pub fn bytes(&self) -> &[u8; 16] {
140        &self.0
141    }
142
143    /// Gets a signed 128-bit integer value from the vector's bytes.
144    pub fn i128(&self) -> i128 {
145        i128::from_le_bytes(self.0)
146    }
147}
148
149impl From<V128> for i128 {
150    fn from(bits: V128) -> i128 {
151        bits.i128()
152    }
153}
154
155impl From<V128> for u128 {
156    fn from(bits: V128) -> u128 {
157        u128::from_le_bytes(bits.0)
158    }
159}
160
161/// Represents the memory ordering for atomic instructions.
162///
163/// For an in-depth explanation of memory orderings, see the C++ documentation
164/// for [`memory_order`] or the Rust documentation for [`atomic::Ordering`].
165///
166/// [`memory_order`]: https://en.cppreference.com/w/cpp/atomic/memory_order
167/// [`atomic::Ordering`]: https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html
168#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
169pub enum Ordering {
170    /// For a load, it acquires; this orders all operations before the last
171    /// "releasing" store. For a store, it releases; this orders all operations
172    /// before it at the next "acquiring" load.
173    AcqRel,
174    /// Like `AcqRel` but all threads see all sequentially consistent operations
175    /// in the same order.
176    SeqCst,
177}
178
179macro_rules! define_operator {
180    ($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident)*) => {
181        /// Instructions as defined [here].
182        ///
183        /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html
184        #[derive(Debug, Clone, Eq, PartialEq)]
185        #[allow(missing_docs)]
186        pub enum Operator<'a> {
187            $(
188                $op $({ $($payload)* })?,
189            )*
190        }
191    }
192}
193for_each_operator!(define_operator);
194
195/// A reader for a core WebAssembly function's operators.
196#[derive(Clone)]
197pub struct OperatorsReader<'a> {
198    reader: BinaryReader<'a>,
199}
200
201impl<'a> OperatorsReader<'a> {
202    pub(crate) fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> {
203        OperatorsReader { reader }
204    }
205
206    /// Determines if the reader is at the end of the operators.
207    pub fn eof(&self) -> bool {
208        self.reader.eof()
209    }
210
211    /// Gets the original position of the reader.
212    pub fn original_position(&self) -> usize {
213        self.reader.original_position()
214    }
215
216    /// Ensures the reader is at the end.
217    ///
218    /// This function returns an error if there is extra data after the operators.
219    pub fn ensure_end(&self) -> Result<()> {
220        if self.eof() {
221            return Ok(());
222        }
223        Err(BinaryReaderError::new(
224            "unexpected data at the end of operators",
225            self.reader.original_position(),
226        ))
227    }
228
229    /// Reads an operator from the reader.
230    pub fn read(&mut self) -> Result<Operator<'a>> {
231        self.reader.read_operator()
232    }
233
234    /// Converts to an iterator of operators paired with offsets.
235    pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> {
236        OperatorsIteratorWithOffsets {
237            reader: self,
238            err: false,
239        }
240    }
241
242    /// Reads an operator with its offset.
243    pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> {
244        let pos = self.reader.original_position();
245        Ok((self.read()?, pos))
246    }
247
248    /// Visit a single operator with the specified [`VisitOperator`] instance.
249    ///
250    /// See [`BinaryReader::visit_operator`] for more information.
251    pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::Output>
252    where
253        T: VisitOperator<'a>,
254    {
255        self.reader.visit_operator(visitor)
256    }
257
258    /// Gets a binary reader from this operators reader.
259    pub fn get_binary_reader(&self) -> BinaryReader<'a> {
260        self.reader.clone()
261    }
262
263    /// Returns whether there is an `end` opcode followed by eof remaining in
264    /// this reader.
265    pub fn is_end_then_eof(&self) -> bool {
266        self.reader.is_end_then_eof()
267    }
268}
269
270impl<'a> IntoIterator for OperatorsReader<'a> {
271    type Item = Result<Operator<'a>>;
272    type IntoIter = OperatorsIterator<'a>;
273
274    /// Reads content of the code section.
275    ///
276    /// # Examples
277    /// ```
278    /// # use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
279    /// # let data: &[u8] = &[
280    /// #     0x01, 0x03, 0x00, 0x01, 0x0b];
281    /// let reader = BinaryReader::new(data, 0);
282    /// let code_reader = CodeSectionReader::new(reader).unwrap();
283    /// for body in code_reader {
284    ///     let body = body.expect("function body");
285    ///     let mut op_reader = body.get_operators_reader().expect("op reader");
286    ///     let ops = op_reader.into_iter().collect::<Result<Vec<Operator>>>().expect("ops");
287    ///     assert!(
288    ///         if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false },
289    ///         "found {:?}",
290    ///         ops
291    ///     );
292    /// }
293    /// ```
294    fn into_iter(self) -> Self::IntoIter {
295        OperatorsIterator {
296            reader: self,
297            err: false,
298        }
299    }
300}
301
302/// An iterator over a function's operators.
303pub struct OperatorsIterator<'a> {
304    reader: OperatorsReader<'a>,
305    err: bool,
306}
307
308impl<'a> Iterator for OperatorsIterator<'a> {
309    type Item = Result<Operator<'a>>;
310
311    fn next(&mut self) -> Option<Self::Item> {
312        if self.err || self.reader.eof() {
313            return None;
314        }
315        let result = self.reader.read();
316        self.err = result.is_err();
317        Some(result)
318    }
319}
320
321/// An iterator over a function's operators with offsets.
322pub struct OperatorsIteratorWithOffsets<'a> {
323    reader: OperatorsReader<'a>,
324    err: bool,
325}
326
327impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> {
328    type Item = Result<(Operator<'a>, usize)>;
329
330    /// Reads content of the code section with offsets.
331    ///
332    /// # Examples
333    /// ```
334    /// use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
335    /// # let data: &[u8] = &[
336    /// #     0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b];
337    /// let reader = BinaryReader::new(data, 20);
338    /// let code_reader = CodeSectionReader::new(reader).unwrap();
339    /// for body in code_reader {
340    ///     let body = body.expect("function body");
341    ///     let mut op_reader = body.get_operators_reader().expect("op reader");
342    ///     let ops = op_reader.into_iter_with_offsets().collect::<Result<Vec<(Operator, usize)>>>().expect("ops");
343    ///     assert!(
344    ///         if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false },
345    ///         "found {:?}",
346    ///         ops
347    ///     );
348    /// }
349    /// ```
350    fn next(&mut self) -> Option<Self::Item> {
351        if self.err || self.reader.eof() {
352            return None;
353        }
354        let result = self.reader.read_with_offset();
355        self.err = result.is_err();
356        Some(result)
357    }
358}
359
360macro_rules! define_visit_operator {
361    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
362        $(
363            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output;
364        )*
365    }
366}
367
368/// Trait implemented by types that can visit all [`Operator`] variants.
369#[allow(missing_docs)]
370pub trait VisitOperator<'a> {
371    /// The result type of the visitor.
372    type Output: 'a;
373
374    /// Visits the [`Operator`] `op` using the given `offset`.
375    ///
376    /// # Note
377    ///
378    /// This is a convenience method that is intended for non-performance
379    /// critical use cases. For performance critical implementations users
380    /// are recommended to directly use the respective `visit` methods or
381    /// implement [`VisitOperator`] on their own.
382    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
383        macro_rules! visit_operator {
384            ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
385                match op {
386                    $(
387                        Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?),
388                    )*
389                }
390            }
391
392        }
393        for_each_operator!(visit_operator)
394    }
395
396    for_each_operator!(define_visit_operator);
397}
398
399macro_rules! define_visit_operator_delegate {
400    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
401        $(
402            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
403                V::$visit(&mut *self, $($($arg),*)?)
404            }
405        )*
406    }
407}
408
409impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V {
410    type Output = V::Output;
411    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
412        V::visit_operator(*self, op)
413    }
414    for_each_operator!(define_visit_operator_delegate);
415}
416
417impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box<V> {
418    type Output = V::Output;
419    fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
420        V::visit_operator(&mut *self, op)
421    }
422    for_each_operator!(define_visit_operator_delegate);
423}
424
425/// A `try_table` entries representation.
426#[derive(Clone, Debug, Eq, PartialEq)]
427pub struct TryTable {
428    /// The block type describing the try block itself.
429    pub ty: BlockType,
430    /// Outer blocks which will receive exceptions.
431    pub catches: Vec<Catch>,
432}
433
434/// Catch clauses that can be specified in [`TryTable`].
435#[derive(Copy, Clone, Debug, Eq, PartialEq)]
436#[allow(missing_docs)]
437pub enum Catch {
438    /// Equivalent of `catch`
439    One { tag: u32, label: u32 },
440    /// Equivalent of `catch_ref`
441    OneRef { tag: u32, label: u32 },
442    /// Equivalent of `catch_all`
443    All { label: u32 },
444    /// Equivalent of `catch_all_ref`
445    AllRef { label: u32 },
446}
447
448impl<'a> FromReader<'a> for TryTable {
449    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
450        let ty = reader.read_block_type()?;
451        let catches = reader
452            .read_iter(MAX_WASM_CATCHES, "catches")?
453            .collect::<Result<_>>()?;
454        Ok(TryTable { ty, catches })
455    }
456}
457
458impl<'a> FromReader<'a> for Catch {
459    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
460        Ok(match reader.read_u8()? {
461            0x00 => Catch::One {
462                tag: reader.read_var_u32()?,
463                label: reader.read_var_u32()?,
464            },
465            0x01 => Catch::OneRef {
466                tag: reader.read_var_u32()?,
467                label: reader.read_var_u32()?,
468            },
469            0x02 => Catch::All {
470                label: reader.read_var_u32()?,
471            },
472            0x03 => Catch::AllRef {
473                label: reader.read_var_u32()?,
474            },
475
476            x => return reader.invalid_leading_byte(x, "catch"),
477        })
478    }
479}