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