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}