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::limits::MAX_WASM_CATCHES;
17use crate::prelude::*;
18use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType};
19
20/// Represents a block type.
21#[derive(Debug, Copy, Clone, PartialEq, Eq)]
22pub enum BlockType {
23 /// The block produces consumes nor produces any values.
24 Empty,
25 /// The block produces a singular value of the given type ([] -> \[t]).
26 Type(ValType),
27 /// The block is described by a function type.
28 ///
29 /// The index is to a function type in the types section.
30 FuncType(u32),
31}
32
33/// Represents a memory immediate in a WebAssembly memory instruction.
34#[derive(Debug, Copy, Clone, Eq, PartialEq)]
35pub struct MemArg {
36 /// Alignment, stored as `n` where the actual alignment is `2^n`
37 pub align: u8,
38 /// Maximum alignment, stored as `n` where the actual alignment is `2^n`.
39 ///
40 /// Note that this field is not actually read from the binary format, it
41 /// will be a constant depending on which instruction this `MemArg` is a
42 /// payload for.
43 pub max_align: u8,
44 /// A fixed byte-offset that this memory immediate specifies.
45 ///
46 /// Note that the memory64 proposal can specify a full 64-bit byte offset
47 /// while otherwise only 32-bit offsets are allowed. Once validated
48 /// memory immediates for 32-bit memories are guaranteed to be at most
49 /// `u32::MAX` whereas 64-bit memories can use the full 64-bits.
50 pub offset: u64,
51 /// The index of the memory this immediate points to.
52 ///
53 /// Note that this points within the module's own memory index space, and
54 /// is always zero unless the multi-memory proposal of WebAssembly is
55 /// enabled.
56 pub memory: u32,
57}
58
59/// A br_table entries representation.
60#[derive(Clone)]
61pub struct BrTable<'a> {
62 pub(crate) reader: crate::BinaryReader<'a>,
63 pub(crate) cnt: u32,
64 pub(crate) default: u32,
65}
66
67impl PartialEq<Self> for BrTable<'_> {
68 fn eq(&self, other: &Self) -> bool {
69 self.cnt == other.cnt
70 && self.default == other.default
71 && self.reader.remaining_buffer() == other.reader.remaining_buffer()
72 }
73}
74
75impl Eq for BrTable<'_> {}
76
77/// An IEEE binary32 immediate floating point value, represented as a u32
78/// containing the bit pattern.
79///
80/// All bit patterns are allowed.
81#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
82pub struct Ieee32(pub(crate) u32);
83
84impl Ieee32 {
85 /// Gets the underlying bits of the 32-bit float.
86 pub fn bits(self) -> u32 {
87 self.0
88 }
89}
90
91impl From<f32> for Ieee32 {
92 fn from(value: f32) -> Self {
93 Ieee32 {
94 0: u32::from_le_bytes(value.to_le_bytes()),
95 }
96 }
97}
98
99impl From<Ieee32> for f32 {
100 fn from(bits: Ieee32) -> f32 {
101 f32::from_bits(bits.bits())
102 }
103}
104
105/// An IEEE binary64 immediate floating point value, represented as a u64
106/// containing the bit pattern.
107///
108/// All bit patterns are allowed.
109#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
110pub struct Ieee64(pub(crate) u64);
111
112impl Ieee64 {
113 /// Gets the underlying bits of the 64-bit float.
114 pub fn bits(self) -> u64 {
115 self.0
116 }
117}
118
119impl From<f64> for Ieee64 {
120 fn from(value: f64) -> Self {
121 Ieee64 {
122 0: u64::from_le_bytes(value.to_le_bytes()),
123 }
124 }
125}
126
127impl From<Ieee64> for f64 {
128 fn from(bits: Ieee64) -> f64 {
129 f64::from_bits(bits.bits())
130 }
131}
132
133/// Represents a 128-bit vector value.
134#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
135pub struct V128(pub(crate) [u8; 16]);
136
137impl V128 {
138 /// Gets the bytes of the vector value.
139 pub fn bytes(&self) -> &[u8; 16] {
140 &self.0
141 }
142
143 /// Gets a signed 128-bit integer value from the vector's bytes.
144 pub fn i128(&self) -> i128 {
145 i128::from_le_bytes(self.0)
146 }
147}
148
149impl From<V128> for i128 {
150 fn from(bits: V128) -> i128 {
151 bits.i128()
152 }
153}
154
155impl From<V128> for u128 {
156 fn from(bits: V128) -> u128 {
157 u128::from_le_bytes(bits.0)
158 }
159}
160
161/// Represents the memory ordering for atomic instructions.
162///
163/// For an in-depth explanation of memory orderings, see the C++ documentation
164/// for [`memory_order`] or the Rust documentation for [`atomic::Ordering`].
165///
166/// [`memory_order`]: https://en.cppreference.com/w/cpp/atomic/memory_order
167/// [`atomic::Ordering`]: https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html
168#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
169pub enum Ordering {
170 /// For a load, it acquires; this orders all operations before the last
171 /// "releasing" store. For a store, it releases; this orders all operations
172 /// before it at the next "acquiring" load.
173 AcqRel,
174 /// Like `AcqRel` but all threads see all sequentially consistent operations
175 /// in the same order.
176 SeqCst,
177}
178
179macro_rules! define_operator {
180 ($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident)*) => {
181 /// Instructions as defined [here].
182 ///
183 /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html
184 #[derive(Debug, Clone, Eq, PartialEq)]
185 #[allow(missing_docs)]
186 pub enum Operator<'a> {
187 $(
188 $op $({ $($payload)* })?,
189 )*
190 }
191 }
192}
193for_each_operator!(define_operator);
194
195/// A reader for a core WebAssembly function's operators.
196#[derive(Clone)]
197pub struct OperatorsReader<'a> {
198 reader: BinaryReader<'a>,
199}
200
201impl<'a> OperatorsReader<'a> {
202 pub(crate) fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> {
203 OperatorsReader { reader }
204 }
205
206 /// Determines if the reader is at the end of the operators.
207 pub fn eof(&self) -> bool {
208 self.reader.eof()
209 }
210
211 /// Gets the original position of the reader.
212 pub fn original_position(&self) -> usize {
213 self.reader.original_position()
214 }
215
216 /// Ensures the reader is at the end.
217 ///
218 /// This function returns an error if there is extra data after the operators.
219 pub fn ensure_end(&self) -> Result<()> {
220 if self.eof() {
221 return Ok(());
222 }
223 Err(BinaryReaderError::new(
224 "unexpected data at the end of operators",
225 self.reader.original_position(),
226 ))
227 }
228
229 /// Reads an operator from the reader.
230 pub fn read(&mut self) -> Result<Operator<'a>> {
231 self.reader.read_operator()
232 }
233
234 /// Converts to an iterator of operators paired with offsets.
235 pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> {
236 OperatorsIteratorWithOffsets {
237 reader: self,
238 err: false,
239 }
240 }
241
242 /// Reads an operator with its offset.
243 pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> {
244 let pos = self.reader.original_position();
245 Ok((self.read()?, pos))
246 }
247
248 /// Visit a single operator with the specified [`VisitOperator`] instance.
249 ///
250 /// See [`BinaryReader::visit_operator`] for more information.
251 pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::Output>
252 where
253 T: VisitOperator<'a>,
254 {
255 self.reader.visit_operator(visitor)
256 }
257
258 /// Gets a binary reader from this operators reader.
259 pub fn get_binary_reader(&self) -> BinaryReader<'a> {
260 self.reader.clone()
261 }
262
263 /// Returns whether there is an `end` opcode followed by eof remaining in
264 /// this reader.
265 pub fn is_end_then_eof(&self) -> bool {
266 self.reader.is_end_then_eof()
267 }
268}
269
270impl<'a> IntoIterator for OperatorsReader<'a> {
271 type Item = Result<Operator<'a>>;
272 type IntoIter = OperatorsIterator<'a>;
273
274 /// Reads content of the code section.
275 ///
276 /// # Examples
277 /// ```
278 /// # use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
279 /// # let data: &[u8] = &[
280 /// # 0x01, 0x03, 0x00, 0x01, 0x0b];
281 /// let reader = BinaryReader::new(data, 0);
282 /// let code_reader = CodeSectionReader::new(reader).unwrap();
283 /// for body in code_reader {
284 /// let body = body.expect("function body");
285 /// let mut op_reader = body.get_operators_reader().expect("op reader");
286 /// let ops = op_reader.into_iter().collect::<Result<Vec<Operator>>>().expect("ops");
287 /// assert!(
288 /// if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false },
289 /// "found {:?}",
290 /// ops
291 /// );
292 /// }
293 /// ```
294 fn into_iter(self) -> Self::IntoIter {
295 OperatorsIterator {
296 reader: self,
297 err: false,
298 }
299 }
300}
301
302/// An iterator over a function's operators.
303pub struct OperatorsIterator<'a> {
304 reader: OperatorsReader<'a>,
305 err: bool,
306}
307
308impl<'a> Iterator for OperatorsIterator<'a> {
309 type Item = Result<Operator<'a>>;
310
311 fn next(&mut self) -> Option<Self::Item> {
312 if self.err || self.reader.eof() {
313 return None;
314 }
315 let result = self.reader.read();
316 self.err = result.is_err();
317 Some(result)
318 }
319}
320
321/// An iterator over a function's operators with offsets.
322pub struct OperatorsIteratorWithOffsets<'a> {
323 reader: OperatorsReader<'a>,
324 err: bool,
325}
326
327impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> {
328 type Item = Result<(Operator<'a>, usize)>;
329
330 /// Reads content of the code section with offsets.
331 ///
332 /// # Examples
333 /// ```
334 /// use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader};
335 /// # let data: &[u8] = &[
336 /// # 0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b];
337 /// let reader = BinaryReader::new(data, 20);
338 /// let code_reader = CodeSectionReader::new(reader).unwrap();
339 /// for body in code_reader {
340 /// let body = body.expect("function body");
341 /// let mut op_reader = body.get_operators_reader().expect("op reader");
342 /// let ops = op_reader.into_iter_with_offsets().collect::<Result<Vec<(Operator, usize)>>>().expect("ops");
343 /// assert!(
344 /// if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false },
345 /// "found {:?}",
346 /// ops
347 /// );
348 /// }
349 /// ```
350 fn next(&mut self) -> Option<Self::Item> {
351 if self.err || self.reader.eof() {
352 return None;
353 }
354 let result = self.reader.read_with_offset();
355 self.err = result.is_err();
356 Some(result)
357 }
358}
359
360macro_rules! define_visit_operator {
361 ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
362 $(
363 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output;
364 )*
365 }
366}
367
368/// Trait implemented by types that can visit all [`Operator`] variants.
369#[allow(missing_docs)]
370pub trait VisitOperator<'a> {
371 /// The result type of the visitor.
372 type Output: 'a;
373
374 /// Visits the [`Operator`] `op` using the given `offset`.
375 ///
376 /// # Note
377 ///
378 /// This is a convenience method that is intended for non-performance
379 /// critical use cases. For performance critical implementations users
380 /// are recommended to directly use the respective `visit` methods or
381 /// implement [`VisitOperator`] on their own.
382 fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
383 macro_rules! visit_operator {
384 ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
385 match op {
386 $(
387 Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?),
388 )*
389 }
390 }
391
392 }
393 for_each_operator!(visit_operator)
394 }
395
396 for_each_operator!(define_visit_operator);
397}
398
399macro_rules! define_visit_operator_delegate {
400 ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
401 $(
402 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
403 V::$visit(&mut *self, $($($arg),*)?)
404 }
405 )*
406 }
407}
408
409impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V {
410 type Output = V::Output;
411 fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
412 V::visit_operator(*self, op)
413 }
414 for_each_operator!(define_visit_operator_delegate);
415}
416
417impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box<V> {
418 type Output = V::Output;
419 fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
420 V::visit_operator(&mut *self, op)
421 }
422 for_each_operator!(define_visit_operator_delegate);
423}
424
425/// A `try_table` entries representation.
426#[derive(Clone, Debug, Eq, PartialEq)]
427pub struct TryTable {
428 /// The block type describing the try block itself.
429 pub ty: BlockType,
430 /// Outer blocks which will receive exceptions.
431 pub catches: Vec<Catch>,
432}
433
434/// Catch clauses that can be specified in [`TryTable`].
435#[derive(Copy, Clone, Debug, Eq, PartialEq)]
436#[allow(missing_docs)]
437pub enum Catch {
438 /// Equivalent of `catch`
439 One { tag: u32, label: u32 },
440 /// Equivalent of `catch_ref`
441 OneRef { tag: u32, label: u32 },
442 /// Equivalent of `catch_all`
443 All { label: u32 },
444 /// Equivalent of `catch_all_ref`
445 AllRef { label: u32 },
446}
447
448impl<'a> FromReader<'a> for TryTable {
449 fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
450 let ty = reader.read_block_type()?;
451 let catches = reader
452 .read_iter(MAX_WASM_CATCHES, "catches")?
453 .collect::<Result<_>>()?;
454 Ok(TryTable { ty, catches })
455 }
456}
457
458impl<'a> FromReader<'a> for Catch {
459 fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
460 Ok(match reader.read_u8()? {
461 0x00 => Catch::One {
462 tag: reader.read_var_u32()?,
463 label: reader.read_var_u32()?,
464 },
465 0x01 => Catch::OneRef {
466 tag: reader.read_var_u32()?,
467 label: reader.read_var_u32()?,
468 },
469 0x02 => Catch::All {
470 label: reader.read_var_u32()?,
471 },
472 0x03 => Catch::AllRef {
473 label: reader.read_var_u32()?,
474 },
475
476 x => return reader.invalid_leading_byte(x, "catch"),
477 })
478 }
479}