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}