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 | |
16 | use crate::limits::{MAX_WASM_CATCHES, MAX_WASM_HANDLERS}; |
17 | use crate::prelude::*; |
18 | use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType}; |
19 | |
20 | /// Represents a block type. |
21 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
22 | pub 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)] |
35 | pub 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)] |
72 | pub 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)] |
98 | pub struct BrTable<'a> { |
99 | pub(crate) reader: crate::BinaryReader<'a>, |
100 | pub(crate) cnt: u32, |
101 | pub(crate) default: u32, |
102 | } |
103 | |
104 | impl 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 | |
112 | impl 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)] |
119 | pub struct Ieee32(pub(crate) u32); |
120 | |
121 | impl Ieee32 { |
122 | /// Gets the underlying bits of the 32-bit float. |
123 | pub fn bits(self) -> u32 { |
124 | self.0 |
125 | } |
126 | } |
127 | |
128 | impl 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 | |
136 | impl 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)] |
147 | pub struct Ieee64(pub(crate) u64); |
148 | |
149 | impl Ieee64 { |
150 | /// Gets the underlying bits of the 64-bit float. |
151 | pub fn bits(self) -> u64 { |
152 | self.0 |
153 | } |
154 | } |
155 | |
156 | impl 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 | |
164 | impl 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)] |
172 | pub struct V128(pub(crate) [u8; 16]); |
173 | |
174 | impl 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 | |
186 | impl From<V128> for i128 { |
187 | fn from(bits: V128) -> i128 { |
188 | bits.i128() |
189 | } |
190 | } |
191 | |
192 | impl 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)] |
206 | pub 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 | |
216 | macro_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 | } |
230 | for_each_operator!(define_operator); |
231 | |
232 | /// A reader for a core WebAssembly function's operators. |
233 | #[derive (Clone)] |
234 | pub struct OperatorsReader<'a> { |
235 | reader: BinaryReader<'a>, |
236 | } |
237 | |
238 | impl<'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 | |
307 | impl<'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. |
340 | pub struct OperatorsIterator<'a> { |
341 | reader: OperatorsReader<'a>, |
342 | err: bool, |
343 | } |
344 | |
345 | impl<'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. |
359 | pub struct OperatorsIteratorWithOffsets<'a> { |
360 | reader: OperatorsReader<'a>, |
361 | err: bool, |
362 | } |
363 | |
364 | impl<'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 | |
397 | macro_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)] |
407 | pub 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 | |
436 | macro_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 | |
446 | impl<'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 | |
454 | impl<'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)] |
464 | pub 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)] |
474 | pub 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 | |
485 | impl<'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 | |
495 | impl<'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)] |
521 | pub 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)] |
530 | pub 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 | |
537 | impl ResumeTable { |
538 | /// Returns the number of entries in the table. |
539 | pub fn len(&self) -> usize { |
540 | self.handlers.len() |
541 | } |
542 | } |
543 | |
544 | impl<'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 | |
554 | impl<'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 | |