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