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