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}