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