/src/wasm-tools/crates/wasmparser/src/parser.rs
Line | Count | Source |
1 | | #[cfg(feature = "features")] |
2 | | use crate::WasmFeatures; |
3 | | use crate::binary_reader::WASM_MAGIC_NUMBER; |
4 | | use crate::offsets; |
5 | | use crate::prelude::*; |
6 | | use crate::{ |
7 | | BinaryReader, CustomSectionReader, DataSectionReader, ElementSectionReader, Error, |
8 | | ExportSectionReader, FromReader, FunctionBody, FunctionSectionReader, GlobalSectionReader, |
9 | | ImportSectionReader, MemorySectionReader, Result, TableSectionReader, TagSectionReader, |
10 | | TypeSectionReader, |
11 | | }; |
12 | | #[cfg(feature = "component-model")] |
13 | | use crate::{ |
14 | | ComponentCanonicalSectionReader, ComponentExportSectionReader, ComponentImportSectionReader, |
15 | | ComponentInstanceSectionReader, ComponentStartFunction, ComponentTypeSectionReader, |
16 | | CoreTypeSectionReader, InstanceSectionReader, SectionLimited, limits::MAX_WASM_MODULE_SIZE, |
17 | | }; |
18 | | use core::fmt; |
19 | | use core::iter; |
20 | | use core::ops::Range; |
21 | | |
22 | | pub(crate) const WASM_MODULE_VERSION: u16 = 0x1; |
23 | | |
24 | | // Note that this started at `0xa` and we're incrementing up from there. When |
25 | | // the component model is stabilized this will become 0x1. The changes here are: |
26 | | // |
27 | | // * [????-??-??] 0xa - original version |
28 | | // * [2023-01-05] 0xb - `export` introduces an alias |
29 | | // * [2023-02-06] 0xc - `export` has an optional type ascribed to it |
30 | | // * [2023-05-10] 0xd - imports/exports drop URLs, new discriminator byte which |
31 | | // allows for `(import (interface "...") ...)` syntax. |
32 | | pub(crate) const WASM_COMPONENT_VERSION: u16 = 0xd; |
33 | | |
34 | | const KIND_MODULE: u16 = 0x00; |
35 | | const KIND_COMPONENT: u16 = 0x01; |
36 | | |
37 | | /// The supported encoding formats for the parser. |
38 | | #[derive(Debug, Clone, Copy, Eq, PartialEq)] |
39 | | pub enum Encoding { |
40 | | /// The encoding format is a WebAssembly module. |
41 | | Module, |
42 | | /// The encoding format is a WebAssembly component. |
43 | | Component, |
44 | | } |
45 | | |
46 | | #[derive(Debug, Clone, Default)] |
47 | | struct ParserCounts { |
48 | | function_entries: Option<u32>, |
49 | | code_entries: Option<u32>, |
50 | | data_entries: Option<u32>, |
51 | | data_count: Option<u32>, |
52 | | #[cfg(feature = "component-model")] |
53 | | component_start_sections: bool, |
54 | | } |
55 | | |
56 | | // Section order for WebAssembly modules. |
57 | | // |
58 | | // Component sections are unordered and allow for duplicates, |
59 | | // so this isn't used for components. |
60 | | #[derive(Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Debug)] |
61 | | pub(crate) enum Order { |
62 | | #[default] |
63 | | Initial, |
64 | | Type, |
65 | | Import, |
66 | | Function, |
67 | | Table, |
68 | | Memory, |
69 | | Tag, |
70 | | Global, |
71 | | Export, |
72 | | Start, |
73 | | Element, |
74 | | DataCount, |
75 | | Code, |
76 | | Data, |
77 | | } |
78 | | |
79 | | /// An incremental parser of a binary WebAssembly module or component. |
80 | | /// |
81 | | /// This type is intended to be used to incrementally parse a WebAssembly module |
82 | | /// or component as bytes become available for the module. This can also be used |
83 | | /// to parse modules or components that are already entirely resident within memory. |
84 | | /// |
85 | | /// This primary function for a parser is the [`Parser::parse`] function which |
86 | | /// will incrementally consume input. You can also use the [`Parser::parse_all`] |
87 | | /// function to parse a module or component that is entirely resident in memory. |
88 | | #[derive(Debug, Clone)] |
89 | | pub struct Parser { |
90 | | state: State, |
91 | | offset: u64, |
92 | | max_offset: Option<u64>, |
93 | | encoding: Encoding, |
94 | | #[cfg(feature = "features")] |
95 | | features: WasmFeatures, |
96 | | counts: ParserCounts, |
97 | | order: (Order, u64), |
98 | | } |
99 | | |
100 | | #[derive(Debug, Clone)] |
101 | | enum State { |
102 | | Header { expect: Option<Encoding> }, |
103 | | SectionStart, |
104 | | FunctionBody { remaining: u32, len: u32 }, |
105 | | } |
106 | | |
107 | | /// A successful return payload from [`Parser::parse`]. |
108 | | /// |
109 | | /// On success one of two possible values can be returned, either that more data |
110 | | /// is needed to continue parsing or a chunk of the input was parsed, indicating |
111 | | /// how much of it was parsed. |
112 | | #[derive(Debug)] |
113 | | pub enum Chunk<'a> { |
114 | | /// This can be returned at any time and indicates that more data is needed |
115 | | /// to proceed with parsing. Zero bytes were consumed from the input to |
116 | | /// [`Parser::parse`]. The `usize` value here is a hint as to how many more |
117 | | /// bytes are needed to continue parsing. |
118 | | NeedMoreData(usize), |
119 | | |
120 | | /// A chunk was successfully parsed. |
121 | | Parsed { |
122 | | /// This many bytes of the `data` input to [`Parser::parse`] were |
123 | | /// consumed to produce `payload`. |
124 | | consumed: usize, |
125 | | /// The value that we actually parsed. |
126 | | payload: Payload<'a>, |
127 | | }, |
128 | | } |
129 | | |
130 | | /// Values that can be parsed from a WebAssembly module or component. |
131 | | /// |
132 | | /// This enumeration is all possible chunks of pieces that can be parsed by a |
133 | | /// [`Parser`] from a binary WebAssembly module or component. Note that for many |
134 | | /// sections the entire section is parsed all at once, whereas other functions, |
135 | | /// like the code section, are parsed incrementally. This is a distinction where some |
136 | | /// sections, like the type section, are required to be fully resident in memory |
137 | | /// (fully downloaded) before proceeding. Other sections, like the code section, |
138 | | /// can be processed in a streaming fashion where each function is extracted |
139 | | /// individually so it can possibly be shipped to another thread while you wait |
140 | | /// for more functions to get downloaded. |
141 | | /// |
142 | | /// Note that payloads, when returned, do not indicate that the module or component |
143 | | /// is valid. For example when you receive a `Payload::TypeSection` the type |
144 | | /// section itself has not yet actually been parsed. The reader returned will be |
145 | | /// able to parse it, but you'll have to actually iterate the reader to do the |
146 | | /// full parse. Each payload returned is intended to be a *window* into the |
147 | | /// original `data` passed to [`Parser::parse`] which can be further processed |
148 | | /// if necessary. |
149 | | #[non_exhaustive] |
150 | | pub enum Payload<'a> { |
151 | | /// Indicates the header of a WebAssembly module or component. |
152 | | Version { |
153 | | /// The version number found in the header. |
154 | | num: u16, |
155 | | /// The encoding format being parsed. |
156 | | encoding: Encoding, |
157 | | /// The range of bytes that were parsed to consume the header of the |
158 | | /// module or component. Note that this range is relative to the start |
159 | | /// of the byte stream. |
160 | | range: Range<u64>, |
161 | | }, |
162 | | |
163 | | /// A module type section was received and the provided reader can be |
164 | | /// used to parse the contents of the type section. |
165 | | TypeSection(TypeSectionReader<'a>), |
166 | | /// A module import section was received and the provided reader can be |
167 | | /// used to parse the contents of the import section. |
168 | | ImportSection(ImportSectionReader<'a>), |
169 | | /// A module function section was received and the provided reader can be |
170 | | /// used to parse the contents of the function section. |
171 | | FunctionSection(FunctionSectionReader<'a>), |
172 | | /// A module table section was received and the provided reader can be |
173 | | /// used to parse the contents of the table section. |
174 | | TableSection(TableSectionReader<'a>), |
175 | | /// A module memory section was received and the provided reader can be |
176 | | /// used to parse the contents of the memory section. |
177 | | MemorySection(MemorySectionReader<'a>), |
178 | | /// A module tag section was received, and the provided reader can be |
179 | | /// used to parse the contents of the tag section. |
180 | | TagSection(TagSectionReader<'a>), |
181 | | /// A module global section was received and the provided reader can be |
182 | | /// used to parse the contents of the global section. |
183 | | GlobalSection(GlobalSectionReader<'a>), |
184 | | /// A module export section was received, and the provided reader can be |
185 | | /// used to parse the contents of the export section. |
186 | | ExportSection(ExportSectionReader<'a>), |
187 | | /// A module start section was received. |
188 | | StartSection { |
189 | | /// The start function index |
190 | | func: u32, |
191 | | /// The range of bytes that specify the `func` field, specified in |
192 | | /// offsets relative to the start of the byte stream. |
193 | | range: Range<u64>, |
194 | | }, |
195 | | /// A module element section was received and the provided reader can be |
196 | | /// used to parse the contents of the element section. |
197 | | ElementSection(ElementSectionReader<'a>), |
198 | | /// A module data count section was received. |
199 | | DataCountSection { |
200 | | /// The number of data segments. |
201 | | count: u32, |
202 | | /// The range of bytes that specify the `count` field, specified in |
203 | | /// offsets relative to the start of the byte stream. |
204 | | range: Range<u64>, |
205 | | }, |
206 | | /// A module data section was received and the provided reader can be |
207 | | /// used to parse the contents of the data section. |
208 | | DataSection(DataSectionReader<'a>), |
209 | | /// Indicator of the start of the code section of a WebAssembly module. |
210 | | /// |
211 | | /// This entry is returned whenever the code section starts. The `count` |
212 | | /// field indicates how many entries are in this code section. After |
213 | | /// receiving this start marker you're guaranteed that the next `count` |
214 | | /// items will be either `CodeSectionEntry` or an error will be returned. |
215 | | /// |
216 | | /// This, unlike other sections, is intended to be used for streaming the |
217 | | /// contents of the code section. The code section is not required to be |
218 | | /// fully resident in memory when we parse it. Instead a [`Parser`] is |
219 | | /// capable of parsing piece-by-piece of a code section. |
220 | | CodeSectionStart { |
221 | | /// The number of functions in this section. |
222 | | count: u32, |
223 | | /// The range of bytes that represent this section, specified in |
224 | | /// offsets relative to the start of the byte stream. |
225 | | range: Range<u64>, |
226 | | /// The size, in bytes, of the remaining contents of this section. |
227 | | /// |
228 | | /// This can be used in combination with [`Parser::skip_section`] |
229 | | /// where the caller will know how many bytes to skip before feeding |
230 | | /// bytes into `Parser` again. |
231 | | size: u32, |
232 | | }, |
233 | | /// An entry of the code section, a function, was parsed from a WebAssembly |
234 | | /// module. |
235 | | /// |
236 | | /// This entry indicates that a function was successfully received from the |
237 | | /// code section, and the payload here is the window into the original input |
238 | | /// where the function resides. Note that the function itself has not been |
239 | | /// parsed, it's only been outlined. You'll need to process the |
240 | | /// `FunctionBody` provided to test whether it parses and/or is valid. |
241 | | CodeSectionEntry(FunctionBody<'a>), |
242 | | |
243 | | /// A core module section was received and the provided parser can be |
244 | | /// used to parse the nested module. |
245 | | /// |
246 | | /// This variant is special in that it returns a sub-`Parser`. Upon |
247 | | /// receiving a `ModuleSection` it is expected that the returned |
248 | | /// `Parser` will be used instead of the parent `Parser` until the parse has |
249 | | /// finished. You'll need to feed data into the `Parser` returned until it |
250 | | /// returns `Payload::End`. After that you'll switch back to the parent |
251 | | /// parser to resume parsing the rest of the current component. |
252 | | /// |
253 | | /// Note that binaries will not be parsed correctly if you feed the data for |
254 | | /// a nested module into the parent [`Parser`]. |
255 | | #[cfg(feature = "component-model")] |
256 | | ModuleSection { |
257 | | /// The parser for the nested module. |
258 | | parser: Parser, |
259 | | /// The range of bytes that represent the nested module in the |
260 | | /// original byte stream. |
261 | | /// |
262 | | /// Note that, to better support streaming parsing and validation, the |
263 | | /// validator does *not* check that this range is in bounds. |
264 | | unchecked_range: Range<u64>, |
265 | | }, |
266 | | /// A core instance section was received and the provided parser can be |
267 | | /// used to parse the contents of the core instance section. |
268 | | /// |
269 | | /// Currently this section is only parsed in a component. |
270 | | #[cfg(feature = "component-model")] |
271 | | InstanceSection(InstanceSectionReader<'a>), |
272 | | /// A core type section was received and the provided parser can be |
273 | | /// used to parse the contents of the core type section. |
274 | | /// |
275 | | /// Currently this section is only parsed in a component. |
276 | | #[cfg(feature = "component-model")] |
277 | | CoreTypeSection(CoreTypeSectionReader<'a>), |
278 | | /// A component section from a WebAssembly component was received and the |
279 | | /// provided parser can be used to parse the nested component. |
280 | | /// |
281 | | /// This variant is special in that it returns a sub-`Parser`. Upon |
282 | | /// receiving a `ComponentSection` it is expected that the returned |
283 | | /// `Parser` will be used instead of the parent `Parser` until the parse has |
284 | | /// finished. You'll need to feed data into the `Parser` returned until it |
285 | | /// returns `Payload::End`. After that you'll switch back to the parent |
286 | | /// parser to resume parsing the rest of the current component. |
287 | | /// |
288 | | /// Note that binaries will not be parsed correctly if you feed the data for |
289 | | /// a nested component into the parent [`Parser`]. |
290 | | #[cfg(feature = "component-model")] |
291 | | ComponentSection { |
292 | | /// The parser for the nested component. |
293 | | parser: Parser, |
294 | | /// The range of bytes that represent the nested component in the |
295 | | /// original byte stream. |
296 | | /// |
297 | | /// Note that, to better support streaming parsing and validation, the |
298 | | /// validator does *not* check that this range is in bounds. |
299 | | unchecked_range: Range<u64>, |
300 | | }, |
301 | | /// A component instance section was received and the provided reader can be |
302 | | /// used to parse the contents of the component instance section. |
303 | | #[cfg(feature = "component-model")] |
304 | | ComponentInstanceSection(ComponentInstanceSectionReader<'a>), |
305 | | /// A component alias section was received and the provided reader can be |
306 | | /// used to parse the contents of the component alias section. |
307 | | #[cfg(feature = "component-model")] |
308 | | ComponentAliasSection(SectionLimited<'a, crate::ComponentAlias<'a>>), |
309 | | /// A component type section was received and the provided reader can be |
310 | | /// used to parse the contents of the component type section. |
311 | | #[cfg(feature = "component-model")] |
312 | | ComponentTypeSection(ComponentTypeSectionReader<'a>), |
313 | | /// A component canonical section was received and the provided reader can be |
314 | | /// used to parse the contents of the component canonical section. |
315 | | #[cfg(feature = "component-model")] |
316 | | ComponentCanonicalSection(ComponentCanonicalSectionReader<'a>), |
317 | | /// A component start section was received. |
318 | | #[cfg(feature = "component-model")] |
319 | | ComponentStartSection { |
320 | | /// The start function description. |
321 | | start: ComponentStartFunction, |
322 | | /// The range of bytes that specify the `start` field. |
323 | | range: Range<u64>, |
324 | | }, |
325 | | /// A component import section was received and the provided reader can be |
326 | | /// used to parse the contents of the component import section. |
327 | | #[cfg(feature = "component-model")] |
328 | | ComponentImportSection(ComponentImportSectionReader<'a>), |
329 | | /// A component export section was received, and the provided reader can be |
330 | | /// used to parse the contents of the component export section. |
331 | | #[cfg(feature = "component-model")] |
332 | | ComponentExportSection(ComponentExportSectionReader<'a>), |
333 | | |
334 | | /// A module or component custom section was received. |
335 | | CustomSection(CustomSectionReader<'a>), |
336 | | |
337 | | /// An unknown section was found. |
338 | | /// |
339 | | /// This variant is returned for all unknown sections encountered. This |
340 | | /// likely wants to be interpreted as an error by consumers of the parser, |
341 | | /// but this can also be used to parse sections currently unsupported by |
342 | | /// the parser. |
343 | | UnknownSection { |
344 | | /// The 8-bit identifier for this section. |
345 | | id: u8, |
346 | | /// The contents of this section. |
347 | | contents: &'a [u8], |
348 | | /// The range of bytes, relative to the start of the original data |
349 | | /// stream, that the contents of this section reside in. |
350 | | range: Range<u64>, |
351 | | }, |
352 | | |
353 | | /// The end of the WebAssembly module or component was reached. |
354 | | /// |
355 | | /// The value is the offset in the input byte stream where the end |
356 | | /// was reached. |
357 | | End(u64), |
358 | | } |
359 | | |
360 | | const CUSTOM_SECTION: u8 = 0; |
361 | | const TYPE_SECTION: u8 = 1; |
362 | | const IMPORT_SECTION: u8 = 2; |
363 | | const FUNCTION_SECTION: u8 = 3; |
364 | | const TABLE_SECTION: u8 = 4; |
365 | | const MEMORY_SECTION: u8 = 5; |
366 | | const GLOBAL_SECTION: u8 = 6; |
367 | | const EXPORT_SECTION: u8 = 7; |
368 | | const START_SECTION: u8 = 8; |
369 | | const ELEMENT_SECTION: u8 = 9; |
370 | | const CODE_SECTION: u8 = 10; |
371 | | const DATA_SECTION: u8 = 11; |
372 | | const DATA_COUNT_SECTION: u8 = 12; |
373 | | const TAG_SECTION: u8 = 13; |
374 | | |
375 | | #[cfg(feature = "component-model")] |
376 | | const COMPONENT_MODULE_SECTION: u8 = 1; |
377 | | #[cfg(feature = "component-model")] |
378 | | const COMPONENT_CORE_INSTANCE_SECTION: u8 = 2; |
379 | | #[cfg(feature = "component-model")] |
380 | | const COMPONENT_CORE_TYPE_SECTION: u8 = 3; |
381 | | #[cfg(feature = "component-model")] |
382 | | const COMPONENT_SECTION: u8 = 4; |
383 | | #[cfg(feature = "component-model")] |
384 | | const COMPONENT_INSTANCE_SECTION: u8 = 5; |
385 | | #[cfg(feature = "component-model")] |
386 | | const COMPONENT_ALIAS_SECTION: u8 = 6; |
387 | | #[cfg(feature = "component-model")] |
388 | | const COMPONENT_TYPE_SECTION: u8 = 7; |
389 | | #[cfg(feature = "component-model")] |
390 | | const COMPONENT_CANONICAL_SECTION: u8 = 8; |
391 | | #[cfg(feature = "component-model")] |
392 | | const COMPONENT_START_SECTION: u8 = 9; |
393 | | #[cfg(feature = "component-model")] |
394 | | const COMPONENT_IMPORT_SECTION: u8 = 10; |
395 | | #[cfg(feature = "component-model")] |
396 | | const COMPONENT_EXPORT_SECTION: u8 = 11; |
397 | | |
398 | | impl Parser { |
399 | | /// Creates a new parser. |
400 | | /// |
401 | | /// Reports errors and ranges relative to `offset` provided, where `offset` |
402 | | /// is some logical offset within the input stream that we're parsing. |
403 | 121k | pub fn new(offset: u64) -> Parser { |
404 | 121k | Parser { |
405 | 121k | state: State::Header { expect: None }, |
406 | 121k | offset, |
407 | 121k | max_offset: None, |
408 | 121k | // Assume the encoding is a module until we know otherwise |
409 | 121k | encoding: Encoding::Module, |
410 | 121k | #[cfg(feature = "features")] |
411 | 121k | features: WasmFeatures::all(), |
412 | 121k | counts: ParserCounts::default(), |
413 | 121k | order: (Order::default(), offset), |
414 | 121k | } |
415 | 121k | } |
416 | | |
417 | | /// Tests whether `bytes` looks like a core WebAssembly module. |
418 | | /// |
419 | | /// This will inspect the first 8 bytes of `bytes` and return `true` if it |
420 | | /// starts with the standard core WebAssembly header. |
421 | 0 | pub fn is_core_wasm(bytes: &[u8]) -> bool { |
422 | | const HEADER: [u8; 8] = [ |
423 | | WASM_MAGIC_NUMBER[0], |
424 | | WASM_MAGIC_NUMBER[1], |
425 | | WASM_MAGIC_NUMBER[2], |
426 | | WASM_MAGIC_NUMBER[3], |
427 | | WASM_MODULE_VERSION.to_le_bytes()[0], |
428 | | WASM_MODULE_VERSION.to_le_bytes()[1], |
429 | | KIND_MODULE.to_le_bytes()[0], |
430 | | KIND_MODULE.to_le_bytes()[1], |
431 | | ]; |
432 | 0 | bytes.starts_with(&HEADER) |
433 | 0 | } |
434 | | |
435 | | /// Tests whether `bytes` looks like a WebAssembly component. |
436 | | /// |
437 | | /// This will inspect the first 8 bytes of `bytes` and return `true` if it |
438 | | /// starts with the standard WebAssembly component header. |
439 | 0 | pub fn is_component(bytes: &[u8]) -> bool { |
440 | | const HEADER: [u8; 8] = [ |
441 | | WASM_MAGIC_NUMBER[0], |
442 | | WASM_MAGIC_NUMBER[1], |
443 | | WASM_MAGIC_NUMBER[2], |
444 | | WASM_MAGIC_NUMBER[3], |
445 | | WASM_COMPONENT_VERSION.to_le_bytes()[0], |
446 | | WASM_COMPONENT_VERSION.to_le_bytes()[1], |
447 | | KIND_COMPONENT.to_le_bytes()[0], |
448 | | KIND_COMPONENT.to_le_bytes()[1], |
449 | | ]; |
450 | 0 | bytes.starts_with(&HEADER) |
451 | 0 | } |
452 | | |
453 | | /// Returns the currently active set of wasm features that this parser is |
454 | | /// using while parsing. |
455 | | /// |
456 | | /// The default set of features is [`WasmFeatures::all()`] for new parsers. |
457 | | /// |
458 | | /// For more information see [`BinaryReader::new`]. |
459 | | #[cfg(feature = "features")] |
460 | 0 | pub fn features(&self) -> WasmFeatures { |
461 | 0 | self.features |
462 | 0 | } |
463 | | |
464 | | /// Sets the wasm features active while parsing to the `features` specified. |
465 | | /// |
466 | | /// The default set of features is [`WasmFeatures::all()`] for new parsers. |
467 | | /// |
468 | | /// For more information see [`BinaryReader::new`]. |
469 | | #[cfg(feature = "features")] |
470 | 21.4k | pub fn set_features(&mut self, features: WasmFeatures) { |
471 | 21.4k | self.features = features; |
472 | 21.4k | } |
473 | | |
474 | | /// Returns the original offset that this parser is currently at. |
475 | 26.3k | pub fn offset(&self) -> u64 { |
476 | 26.3k | self.offset |
477 | 26.3k | } |
478 | | |
479 | | /// Attempts to parse a chunk of data. |
480 | | /// |
481 | | /// This method will attempt to parse the next incremental portion of a |
482 | | /// WebAssembly binary. Data available for the module or component is |
483 | | /// provided as `data`, and the data can be incomplete if more data has yet |
484 | | /// to arrive. The `eof` flag indicates whether more data will ever be received. |
485 | | /// |
486 | | /// There are two ways parsing can succeed with this method: |
487 | | /// |
488 | | /// * `Chunk::NeedMoreData` - this indicates that there is not enough bytes |
489 | | /// in `data` to parse a payload. The caller needs to wait for more data to |
490 | | /// be available in this situation before calling this method again. It is |
491 | | /// guaranteed that this is only returned if `eof` is `false`. |
492 | | /// |
493 | | /// * `Chunk::Parsed` - this indicates that a chunk of the input was |
494 | | /// successfully parsed. The payload is available in this variant of what |
495 | | /// was parsed, and this also indicates how many bytes of `data` was |
496 | | /// consumed. It's expected that the caller will not provide these bytes |
497 | | /// back to the [`Parser`] again. |
498 | | /// |
499 | | /// Note that all `Chunk` return values are connected, with a lifetime, to |
500 | | /// the input buffer. Each parsed chunk borrows the input buffer and is a |
501 | | /// view into it for successfully parsed chunks. |
502 | | /// |
503 | | /// It is expected that you'll call this method until `Payload::End` is |
504 | | /// reached, at which point you're guaranteed that the parse has completed. |
505 | | /// Note that complete parsing, for the top-level module or component, |
506 | | /// implies that `data` is empty and `eof` is `true`. |
507 | | /// |
508 | | /// # Errors |
509 | | /// |
510 | | /// Parse errors are returned as an `Err`. Errors can happen when the |
511 | | /// structure of the data is unexpected or if sections are too large for |
512 | | /// example. Note that errors are not returned for malformed *contents* of |
513 | | /// sections here. Sections are generally not individually parsed and each |
514 | | /// returned [`Payload`] needs to be iterated over further to detect all |
515 | | /// errors. |
516 | | /// |
517 | | /// # Examples |
518 | | /// |
519 | | /// An example of reading a wasm file from a stream (`std::io::Read`) and |
520 | | /// incrementally parsing it. |
521 | | /// |
522 | | /// ``` |
523 | | /// use std::io::Read; |
524 | | /// use anyhow::Result; |
525 | | /// use wasmparser::{Parser, Chunk, Payload::*}; |
526 | | /// |
527 | | /// fn parse(mut reader: impl Read) -> Result<()> { |
528 | | /// let mut buf = Vec::new(); |
529 | | /// let mut cur = Parser::new(0); |
530 | | /// let mut eof = false; |
531 | | /// let mut stack = Vec::new(); |
532 | | /// |
533 | | /// loop { |
534 | | /// let (payload, consumed) = match cur.parse(&buf, eof)? { |
535 | | /// Chunk::NeedMoreData(hint) => { |
536 | | /// assert!(!eof); // otherwise an error would be returned |
537 | | /// |
538 | | /// // Use the hint to preallocate more space, then read |
539 | | /// // some more data into our buffer. |
540 | | /// // |
541 | | /// // Note that the buffer management here is not ideal, |
542 | | /// // but it's compact enough to fit in an example! |
543 | | /// let len = buf.len(); |
544 | | /// buf.extend((0..hint).map(|_| 0u8)); |
545 | | /// let n = reader.read(&mut buf[len..])?; |
546 | | /// buf.truncate(len + n); |
547 | | /// eof = n == 0; |
548 | | /// continue; |
549 | | /// } |
550 | | /// |
551 | | /// Chunk::Parsed { consumed, payload } => (payload, consumed), |
552 | | /// }; |
553 | | /// |
554 | | /// match payload { |
555 | | /// // Sections for WebAssembly modules |
556 | | /// Version { .. } => { /* ... */ } |
557 | | /// TypeSection(_) => { /* ... */ } |
558 | | /// ImportSection(_) => { /* ... */ } |
559 | | /// FunctionSection(_) => { /* ... */ } |
560 | | /// TableSection(_) => { /* ... */ } |
561 | | /// MemorySection(_) => { /* ... */ } |
562 | | /// TagSection(_) => { /* ... */ } |
563 | | /// GlobalSection(_) => { /* ... */ } |
564 | | /// ExportSection(_) => { /* ... */ } |
565 | | /// StartSection { .. } => { /* ... */ } |
566 | | /// ElementSection(_) => { /* ... */ } |
567 | | /// DataCountSection { .. } => { /* ... */ } |
568 | | /// DataSection(_) => { /* ... */ } |
569 | | /// |
570 | | /// // Here we know how many functions we'll be receiving as |
571 | | /// // `CodeSectionEntry`, so we can prepare for that, and |
572 | | /// // afterwards we can parse and handle each function |
573 | | /// // individually. |
574 | | /// CodeSectionStart { .. } => { /* ... */ } |
575 | | /// CodeSectionEntry(body) => { |
576 | | /// // here we can iterate over `body` to parse the function |
577 | | /// // and its locals |
578 | | /// } |
579 | | /// |
580 | | /// // Sections for WebAssembly components |
581 | | /// InstanceSection(_) => { /* ... */ } |
582 | | /// CoreTypeSection(_) => { /* ... */ } |
583 | | /// ComponentInstanceSection(_) => { /* ... */ } |
584 | | /// ComponentAliasSection(_) => { /* ... */ } |
585 | | /// ComponentTypeSection(_) => { /* ... */ } |
586 | | /// ComponentCanonicalSection(_) => { /* ... */ } |
587 | | /// ComponentStartSection { .. } => { /* ... */ } |
588 | | /// ComponentImportSection(_) => { /* ... */ } |
589 | | /// ComponentExportSection(_) => { /* ... */ } |
590 | | /// |
591 | | /// ModuleSection { parser, .. } |
592 | | /// | ComponentSection { parser, .. } => { |
593 | | /// stack.push(cur.clone()); |
594 | | /// cur = parser.clone(); |
595 | | /// } |
596 | | /// |
597 | | /// CustomSection(_) => { /* ... */ } |
598 | | /// |
599 | | /// // Once we've reached the end of a parser we either resume |
600 | | /// // at the parent parser or we break out of the loop because |
601 | | /// // we're done. |
602 | | /// End(_) => { |
603 | | /// if let Some(parent_parser) = stack.pop() { |
604 | | /// cur = parent_parser; |
605 | | /// } else { |
606 | | /// break; |
607 | | /// } |
608 | | /// } |
609 | | /// |
610 | | /// // most likely you'd return an error here |
611 | | /// _ => { /* ... */ } |
612 | | /// } |
613 | | /// |
614 | | /// // once we're done processing the payload we can forget the |
615 | | /// // original. |
616 | | /// buf.drain(..consumed); |
617 | | /// } |
618 | | /// |
619 | | /// Ok(()) |
620 | | /// } |
621 | | /// |
622 | | /// # parse(&b"\0asm\x01\0\0\0"[..]).unwrap(); |
623 | | /// ``` |
624 | 3.45M | pub fn parse<'a>(&mut self, data: &'a [u8], eof: bool) -> Result<Chunk<'a>> { |
625 | 3.45M | let max_offset = self.max_offset.unwrap_or(u64::MAX); |
626 | 3.45M | debug_assert!(self.offset <= max_offset, "inverted offset range"); |
627 | 3.45M | let max_len = offsets::max_data_len(self.offset, max_offset); |
628 | 3.45M | let (data, eof) = if max_len < data.len() { |
629 | 182k | if self.max_offset.is_none() { |
630 | 0 | return Err(offsets::err_too_many_bytes( |
631 | 0 | self.offset, |
632 | 0 | data.len(), |
633 | 0 | max_len, |
634 | 0 | )); |
635 | 182k | } |
636 | 182k | (&data[..max_len], true) |
637 | | } else { |
638 | 3.27M | (data, eof) |
639 | | }; |
640 | 3.45M | let mut reader = BinaryReader::new(data, self.offset); |
641 | | #[cfg(feature = "features")] |
642 | 3.45M | { |
643 | 3.45M | reader.set_features(self.features); |
644 | 3.45M | } |
645 | 3.45M | match self.parse_reader(&mut reader, eof) { |
646 | 1.88M | Ok(payload) => { |
647 | | // Be sure to update our offset with how far we got in the reader |
648 | 1.88M | let consumed = reader.current_position(); |
649 | 1.88M | self.offset += consumed as u64; |
650 | 1.88M | Ok(Chunk::Parsed { |
651 | 1.88M | consumed: consumed, |
652 | 1.88M | payload, |
653 | 1.88M | }) |
654 | | } |
655 | 1.57M | Err(e) => { |
656 | | // If we're at EOF then there's no way we can recover from any |
657 | | // error, so continue to propagate it. |
658 | 1.57M | if eof { |
659 | 128 | return Err(e); |
660 | 1.57M | } |
661 | | |
662 | | // If our error doesn't look like it can be resolved with more |
663 | | // data being pulled down, then propagate it, otherwise switch |
664 | | // the error to "feed me please" |
665 | 1.57M | match e.needed_hint() { |
666 | 1.57M | Some(hint) => Ok(Chunk::NeedMoreData(hint)), |
667 | 32 | None => Err(e), |
668 | | } |
669 | | } |
670 | | } |
671 | 3.45M | } |
672 | | |
673 | 490k | fn update_order(&mut self, order: Order, pos: u64) -> Result<()> { |
674 | 490k | if self.encoding == Encoding::Module { |
675 | 490k | match self.order { |
676 | 490k | (last_order, last_pos) if last_order >= order && last_pos < pos => { |
677 | 0 | bail!(pos, "section out of order") |
678 | | } |
679 | 490k | _ => (), |
680 | | } |
681 | 0 | } |
682 | | |
683 | 490k | self.order = (order, pos); |
684 | | |
685 | 490k | Ok(()) |
686 | 490k | } |
687 | | |
688 | 3.50M | fn parse_reader<'a>( |
689 | 3.50M | &mut self, |
690 | 3.50M | reader: &mut BinaryReader<'a>, |
691 | 3.50M | eof: bool, |
692 | 3.50M | ) -> Result<Payload<'a>> { |
693 | | use Payload::*; |
694 | | |
695 | 3.50M | match self.state { |
696 | 205k | State::Header { expect } => { |
697 | 205k | let start = reader.original_position(); |
698 | 205k | let header_version = reader.read_header_version()?; |
699 | 121k | let num = header_version as u16; |
700 | 121k | self.encoding = match (num, (header_version >> 16) as u16) { |
701 | 29.3k | (WASM_MODULE_VERSION, KIND_MODULE) => match expect { |
702 | 73.1k | None | Some(Encoding::Module) => Encoding::Module, |
703 | | Some(Encoding::Component) => { |
704 | 0 | bail!(start, "expected a version header for a component") |
705 | | } |
706 | | }, |
707 | 11.3k | (WASM_COMPONENT_VERSION, KIND_COMPONENT) => match expect { |
708 | 48.1k | None | Some(Encoding::Component) => Encoding::Component, |
709 | | Some(Encoding::Module) => { |
710 | 0 | bail!(start, "expected a version header for a module") |
711 | | } |
712 | | }, |
713 | 2 | _ => bail!(start + 4, "unknown binary version: {header_version:#10x}"), |
714 | | }; |
715 | 121k | self.state = State::SectionStart; |
716 | 121k | Ok(Version { |
717 | 121k | num, |
718 | 121k | encoding: self.encoding, |
719 | 121k | range: start..reader.original_position(), |
720 | 121k | }) |
721 | | } |
722 | | State::SectionStart => { |
723 | | // If we're at eof and there are no bytes in our buffer, then |
724 | | // that means we reached the end of the data since it's |
725 | | // just a bunch of sections concatenated after the header. |
726 | 2.55M | if eof && reader.bytes_remaining() == 0 { |
727 | 123k | self.check_function_code_counts(reader.original_position())?; |
728 | 123k | self.check_data_count(reader.original_position())?; |
729 | 123k | return Ok(Payload::End(reader.original_position())); |
730 | 2.43M | } |
731 | | |
732 | | // Corrupted binaries containing multiple modules or |
733 | | // components will fail because a section can never start with |
734 | | // the magic number: 0 is custom section, 'a' is section len |
735 | | // of 97, `s` is section name string len of 115, at which |
736 | | // point validation will fail because name string is bigger |
737 | | // than section. Report a better error instead: |
738 | 2.43M | match reader.peek_bytes(4) { |
739 | 988k | Ok(peek) if peek == WASM_MAGIC_NUMBER => { |
740 | 0 | return Err(Error::new( |
741 | 0 | "expected section, got wasm magic number", |
742 | 0 | reader.original_position(), |
743 | 0 | )); |
744 | | } |
745 | 2.43M | _ => {} |
746 | | } |
747 | | |
748 | 2.43M | let id_pos = reader.original_position(); |
749 | 2.43M | let id = reader.read_u8()?; |
750 | 1.95M | if id & 0x80 != 0 { |
751 | 0 | return Err(Error::new("malformed section id", id_pos)); |
752 | 1.95M | } |
753 | 1.95M | let len_pos = reader.original_position(); |
754 | 1.95M | let mut len = reader.read_var_u32()?; |
755 | | |
756 | | // Test to make sure that this section actually fits within |
757 | | // `Parser::max_size`. This doesn't matter for top-level modules |
758 | | // but it is required for nested modules/components to correctly ensure |
759 | | // that all sections live entirely within their section of the |
760 | | // file. |
761 | 1.46M | let section_start = reader.original_position(); |
762 | 1.46M | let max_offset = self.max_offset.unwrap_or(u64::MAX); |
763 | 1.46M | let Some(section_end) = section_start |
764 | 1.46M | .checked_add(u64::from(len)) |
765 | 1.46M | .and_then(|section_end| (section_end <= max_offset).then_some(section_end)) |
766 | | else { |
767 | 0 | return Err(Error::new( |
768 | 0 | &format!("section too large, {len} goes past 0x{max_offset:x}"), |
769 | 0 | len_pos, |
770 | 0 | )); |
771 | | }; |
772 | | |
773 | 1.46M | match (self.encoding, id) { |
774 | | // Custom sections for both modules and components. |
775 | 165k | (_, 0) => section(reader, len, CustomSectionReader::new, CustomSection), |
776 | | |
777 | | // Module sections |
778 | | (Encoding::Module, TYPE_SECTION) => { |
779 | 95.3k | self.update_order(Order::Type, section_start)?; |
780 | 95.3k | section(reader, len, TypeSectionReader::new, TypeSection) |
781 | | } |
782 | | (Encoding::Module, IMPORT_SECTION) => { |
783 | 54.8k | self.update_order(Order::Import, section_start)?; |
784 | 54.8k | section(reader, len, ImportSectionReader::new, ImportSection) |
785 | | } |
786 | | (Encoding::Module, FUNCTION_SECTION) => { |
787 | 67.8k | self.update_order(Order::Function, section_start)?; |
788 | 67.8k | let s = section(reader, len, FunctionSectionReader::new, FunctionSection)?; |
789 | 56.3k | match &s { |
790 | 56.3k | FunctionSection(f) => self.counts.function_entries = Some(f.count()), |
791 | 0 | _ => unreachable!(), |
792 | | } |
793 | 56.3k | Ok(s) |
794 | | } |
795 | | (Encoding::Module, TABLE_SECTION) => { |
796 | 15.5k | self.update_order(Order::Table, section_start)?; |
797 | 15.5k | section(reader, len, TableSectionReader::new, TableSection) |
798 | | } |
799 | | (Encoding::Module, MEMORY_SECTION) => { |
800 | 53.4k | self.update_order(Order::Memory, section_start)?; |
801 | 53.4k | section(reader, len, MemorySectionReader::new, MemorySection) |
802 | | } |
803 | | (Encoding::Module, GLOBAL_SECTION) => { |
804 | 23.3k | self.update_order(Order::Global, section_start)?; |
805 | 23.3k | section(reader, len, GlobalSectionReader::new, GlobalSection) |
806 | | } |
807 | | (Encoding::Module, EXPORT_SECTION) => { |
808 | 57.5k | self.update_order(Order::Export, section_start)?; |
809 | 57.5k | section(reader, len, ExportSectionReader::new, ExportSection) |
810 | | } |
811 | | (Encoding::Module, START_SECTION) => { |
812 | 23.8k | self.update_order(Order::Start, section_start)?; |
813 | 23.8k | let (func, range) = single_item(reader, section_end, "start")?; |
814 | 15.1k | Ok(StartSection { func, range }) |
815 | | } |
816 | | (Encoding::Module, ELEMENT_SECTION) => { |
817 | 13.3k | self.update_order(Order::Element, section_start)?; |
818 | 13.3k | section(reader, len, ElementSectionReader::new, ElementSection) |
819 | | } |
820 | | (Encoding::Module, CODE_SECTION) => { |
821 | 67.7k | self.update_order(Order::Code, section_start)?; |
822 | 67.7k | let count = delimited(reader, &mut len, |r| r.read_var_u32())?; |
823 | 56.2k | self.counts.code_entries = Some(count); |
824 | 56.2k | self.check_function_code_counts(section_start)?; |
825 | 56.2k | let range = section_start..section_end; |
826 | 56.2k | self.state = State::FunctionBody { |
827 | 56.2k | remaining: count, |
828 | 56.2k | len, |
829 | 56.2k | }; |
830 | 56.2k | Ok(CodeSectionStart { |
831 | 56.2k | count, |
832 | 56.2k | range, |
833 | 56.2k | size: len, |
834 | 56.2k | }) |
835 | | } |
836 | | (Encoding::Module, DATA_SECTION) => { |
837 | 8.59k | self.update_order(Order::Data, section_start)?; |
838 | 8.59k | let s = section(reader, len, DataSectionReader::new, DataSection)?; |
839 | 8.59k | match &s { |
840 | 8.59k | DataSection(d) => self.counts.data_entries = Some(d.count()), |
841 | 0 | _ => unreachable!(), |
842 | | } |
843 | 8.59k | self.check_data_count(reader.original_position())?; |
844 | 8.59k | Ok(s) |
845 | | } |
846 | | (Encoding::Module, DATA_COUNT_SECTION) => { |
847 | 6.01k | self.update_order(Order::DataCount, section_start)?; |
848 | 6.01k | let (count, range) = single_item(reader, section_end, "data count")?; |
849 | 6.01k | self.counts.data_count = Some(count); |
850 | 6.01k | Ok(DataCountSection { count, range }) |
851 | | } |
852 | | (Encoding::Module, TAG_SECTION) => { |
853 | 3.10k | self.update_order(Order::Tag, section_start)?; |
854 | 3.10k | section(reader, len, TagSectionReader::new, TagSection) |
855 | | } |
856 | | |
857 | | // Component sections |
858 | | #[cfg(feature = "component-model")] |
859 | | (Encoding::Component, COMPONENT_MODULE_SECTION) |
860 | | | (Encoding::Component, COMPONENT_SECTION) => { |
861 | 40.7k | if len > MAX_WASM_MODULE_SIZE { |
862 | 0 | bail!( |
863 | 0 | len_pos, |
864 | | "{} section is too large", |
865 | 0 | if id == COMPONENT_MODULE_SECTION { |
866 | 0 | "module" |
867 | | } else { |
868 | 0 | "component" |
869 | | } |
870 | | ); |
871 | 40.7k | } |
872 | | |
873 | 40.7k | let range = section_start..section_end; |
874 | | // Do no consume these bytes from the reader. The parse function will |
875 | | // additionally bump this by the consumed amount, which will land us |
876 | | // at section_end. |
877 | 40.7k | self.offset += u64::from(len); |
878 | 40.7k | let mut parser = Parser::new(section_start); |
879 | | parser.state = State::Header { |
880 | 40.7k | expect: Some(if id == COMPONENT_MODULE_SECTION { |
881 | 29.3k | Encoding::Module |
882 | | } else { |
883 | 11.3k | Encoding::Component |
884 | | }), |
885 | | }; |
886 | | #[cfg(feature = "features")] |
887 | 40.7k | { |
888 | 40.7k | parser.features = self.features; |
889 | 40.7k | } |
890 | 40.7k | parser.max_offset = Some(section_end); |
891 | | |
892 | 40.7k | Ok(match id { |
893 | 29.3k | COMPONENT_MODULE_SECTION => ModuleSection { |
894 | 29.3k | parser, |
895 | 29.3k | unchecked_range: range, |
896 | 29.3k | }, |
897 | 11.3k | COMPONENT_SECTION => ComponentSection { |
898 | 11.3k | parser, |
899 | 11.3k | unchecked_range: range, |
900 | 11.3k | }, |
901 | 0 | _ => unreachable!(), |
902 | | }) |
903 | | } |
904 | | #[cfg(feature = "component-model")] |
905 | | (Encoding::Component, COMPONENT_CORE_INSTANCE_SECTION) => { |
906 | 56.2k | section(reader, len, InstanceSectionReader::new, InstanceSection) |
907 | | } |
908 | | #[cfg(feature = "component-model")] |
909 | | (Encoding::Component, COMPONENT_CORE_TYPE_SECTION) => { |
910 | 0 | section(reader, len, CoreTypeSectionReader::new, CoreTypeSection) |
911 | | } |
912 | | #[cfg(feature = "component-model")] |
913 | 18.9k | (Encoding::Component, COMPONENT_INSTANCE_SECTION) => section( |
914 | 18.9k | reader, |
915 | 18.9k | len, |
916 | 18.9k | ComponentInstanceSectionReader::new, |
917 | 18.9k | ComponentInstanceSection, |
918 | | ), |
919 | | #[cfg(feature = "component-model")] |
920 | | (Encoding::Component, COMPONENT_ALIAS_SECTION) => { |
921 | 72.5k | section(reader, len, SectionLimited::new, ComponentAliasSection) |
922 | | } |
923 | | #[cfg(feature = "component-model")] |
924 | 276k | (Encoding::Component, COMPONENT_TYPE_SECTION) => section( |
925 | 276k | reader, |
926 | 276k | len, |
927 | 276k | ComponentTypeSectionReader::new, |
928 | 276k | ComponentTypeSection, |
929 | | ), |
930 | | #[cfg(feature = "component-model")] |
931 | 60.3k | (Encoding::Component, COMPONENT_CANONICAL_SECTION) => section( |
932 | 60.3k | reader, |
933 | 60.3k | len, |
934 | 60.3k | ComponentCanonicalSectionReader::new, |
935 | 60.3k | ComponentCanonicalSection, |
936 | | ), |
937 | | #[cfg(feature = "component-model")] |
938 | | (Encoding::Component, COMPONENT_START_SECTION) => { |
939 | 0 | match self.counts.component_start_sections { |
940 | 0 | false => self.counts.component_start_sections = true, |
941 | | true => { |
942 | 0 | bail!( |
943 | 0 | reader.original_position(), |
944 | | "component cannot have more than one start function" |
945 | | ) |
946 | | } |
947 | | } |
948 | 0 | let (start, range) = single_item(reader, section_end, "component start")?; |
949 | 0 | Ok(ComponentStartSection { start, range }) |
950 | | } |
951 | | #[cfg(feature = "component-model")] |
952 | 42.3k | (Encoding::Component, COMPONENT_IMPORT_SECTION) => section( |
953 | 42.3k | reader, |
954 | 42.3k | len, |
955 | 42.3k | ComponentImportSectionReader::new, |
956 | 42.3k | ComponentImportSection, |
957 | | ), |
958 | | #[cfg(feature = "component-model")] |
959 | 240k | (Encoding::Component, COMPONENT_EXPORT_SECTION) => section( |
960 | 240k | reader, |
961 | 240k | len, |
962 | 240k | ComponentExportSectionReader::new, |
963 | 240k | ComponentExportSection, |
964 | | ), |
965 | 0 | (_, id) => { |
966 | 0 | let offset = reader.original_position(); |
967 | 0 | let contents = reader.read_bytes(len as usize)?; |
968 | 0 | let range = offset..section_end; |
969 | 0 | Ok(UnknownSection { |
970 | 0 | id, |
971 | 0 | contents, |
972 | 0 | range, |
973 | 0 | }) |
974 | | } |
975 | | } |
976 | | } |
977 | | |
978 | | // Once we hit 0 remaining incrementally parsed items, with 0 |
979 | | // remaining bytes in each section, we're done and can switch back |
980 | | // to parsing sections. |
981 | | State::FunctionBody { |
982 | | remaining: 0, |
983 | | len: 0, |
984 | | } => { |
985 | 50.4k | self.state = State::SectionStart; |
986 | 50.4k | self.parse_reader(reader, eof) |
987 | | } |
988 | | |
989 | | // ... otherwise trailing bytes with no remaining entries in these |
990 | | // sections indicates an error. |
991 | 0 | State::FunctionBody { remaining: 0, len } => { |
992 | 0 | debug_assert!(len > 0); |
993 | 0 | let offset = reader.original_position(); |
994 | 0 | Err(Error::new("trailing bytes at end of section", offset)) |
995 | | } |
996 | | |
997 | | // Functions are relatively easy to parse when we know there's at |
998 | | // least one remaining and at least one byte available to read |
999 | | // things. |
1000 | | // |
1001 | | // We use the remaining length try to read a u32 size of the |
1002 | | // function, and using that size we require the entire function be |
1003 | | // resident in memory. This means that we're reading whole chunks of |
1004 | | // functions at a time. |
1005 | | // |
1006 | | // Limiting via `Parser::max_size` (nested parsing) happens above in |
1007 | | // `fn parse`, and limiting by our section size happens via |
1008 | | // `delimited`. Actual parsing of the function body is delegated to |
1009 | | // the caller to iterate over the `FunctionBody` structure. |
1010 | 698k | State::FunctionBody { remaining, mut len } => { |
1011 | 698k | let body = delimited(reader, &mut len, |r| { |
1012 | 698k | Ok(FunctionBody::new(r.read_reader()?)) |
1013 | 698k | })?; |
1014 | 601k | self.state = State::FunctionBody { |
1015 | 601k | remaining: remaining - 1, |
1016 | 601k | len, |
1017 | 601k | }; |
1018 | 601k | Ok(CodeSectionEntry(body)) |
1019 | | } |
1020 | | } |
1021 | 3.50M | } |
1022 | | |
1023 | | /// Convenience function that can be used to parse a module or component |
1024 | | /// that is entirely resident in memory. |
1025 | | /// |
1026 | | /// This function will parse the `data` provided as a WebAssembly module |
1027 | | /// or component. |
1028 | | /// |
1029 | | /// Note that when this function yields sections that provide parsers, |
1030 | | /// no further action is required for those sections as payloads from |
1031 | | /// those parsers will be automatically returned. |
1032 | | /// |
1033 | | /// # Examples |
1034 | | /// |
1035 | | /// An example of reading a wasm file from a stream (`std::io::Read`) into |
1036 | | /// a buffer and then parsing it. |
1037 | | /// |
1038 | | /// ``` |
1039 | | /// use std::io::Read; |
1040 | | /// use anyhow::Result; |
1041 | | /// use wasmparser::{Parser, Chunk, Payload::*}; |
1042 | | /// |
1043 | | /// fn parse(mut reader: impl Read) -> Result<()> { |
1044 | | /// let mut buf = Vec::new(); |
1045 | | /// reader.read_to_end(&mut buf)?; |
1046 | | /// let parser = Parser::new(0); |
1047 | | /// |
1048 | | /// for payload in parser.parse_all(&buf) { |
1049 | | /// match payload? { |
1050 | | /// // Sections for WebAssembly modules |
1051 | | /// Version { .. } => { /* ... */ } |
1052 | | /// TypeSection(_) => { /* ... */ } |
1053 | | /// ImportSection(_) => { /* ... */ } |
1054 | | /// FunctionSection(_) => { /* ... */ } |
1055 | | /// TableSection(_) => { /* ... */ } |
1056 | | /// MemorySection(_) => { /* ... */ } |
1057 | | /// TagSection(_) => { /* ... */ } |
1058 | | /// GlobalSection(_) => { /* ... */ } |
1059 | | /// ExportSection(_) => { /* ... */ } |
1060 | | /// StartSection { .. } => { /* ... */ } |
1061 | | /// ElementSection(_) => { /* ... */ } |
1062 | | /// DataCountSection { .. } => { /* ... */ } |
1063 | | /// DataSection(_) => { /* ... */ } |
1064 | | /// |
1065 | | /// // Here we know how many functions we'll be receiving as |
1066 | | /// // `CodeSectionEntry`, so we can prepare for that, and |
1067 | | /// // afterwards we can parse and handle each function |
1068 | | /// // individually. |
1069 | | /// CodeSectionStart { .. } => { /* ... */ } |
1070 | | /// CodeSectionEntry(body) => { |
1071 | | /// // here we can iterate over `body` to parse the function |
1072 | | /// // and its locals |
1073 | | /// } |
1074 | | /// |
1075 | | /// // Sections for WebAssembly components |
1076 | | /// ModuleSection { .. } => { /* ... */ } |
1077 | | /// InstanceSection(_) => { /* ... */ } |
1078 | | /// CoreTypeSection(_) => { /* ... */ } |
1079 | | /// ComponentSection { .. } => { /* ... */ } |
1080 | | /// ComponentInstanceSection(_) => { /* ... */ } |
1081 | | /// ComponentAliasSection(_) => { /* ... */ } |
1082 | | /// ComponentTypeSection(_) => { /* ... */ } |
1083 | | /// ComponentCanonicalSection(_) => { /* ... */ } |
1084 | | /// ComponentStartSection { .. } => { /* ... */ } |
1085 | | /// ComponentImportSection(_) => { /* ... */ } |
1086 | | /// ComponentExportSection(_) => { /* ... */ } |
1087 | | /// |
1088 | | /// CustomSection(_) => { /* ... */ } |
1089 | | /// |
1090 | | /// // Once we've reached the end of a parser we either resume |
1091 | | /// // at the parent parser or the payload iterator is at its |
1092 | | /// // end and we're done. |
1093 | | /// End(_) => {} |
1094 | | /// |
1095 | | /// // most likely you'd return an error here, but if you want |
1096 | | /// // you can also inspect the raw contents of unknown sections |
1097 | | /// other => { |
1098 | | /// match other.as_section() { |
1099 | | /// Some((id, range)) => { /* ... */ } |
1100 | | /// None => { /* ... */ } |
1101 | | /// } |
1102 | | /// } |
1103 | | /// } |
1104 | | /// } |
1105 | | /// |
1106 | | /// Ok(()) |
1107 | | /// } |
1108 | | /// |
1109 | | /// # parse(&b"\0asm\x01\0\0\0"[..]).unwrap(); |
1110 | | /// ``` |
1111 | 55.4k | pub fn parse_all(self, mut data: &[u8]) -> impl Iterator<Item = Result<Payload<'_>>> { |
1112 | 55.4k | let mut stack = Vec::new(); |
1113 | 55.4k | let mut cur = self; |
1114 | 55.4k | let mut done = false; |
1115 | 1.10M | iter::from_fn(move || { |
1116 | 1.10M | if done { |
1117 | 42.1k | return None; |
1118 | 1.06M | } |
1119 | 1.06M | let payload = match cur.parse(data, true) { |
1120 | | // Propagate all errors |
1121 | 77 | Err(e) => { |
1122 | 77 | done = true; |
1123 | 77 | return Some(Err(e)); |
1124 | | } |
1125 | | |
1126 | | // This isn't possible because `eof` is always true. |
1127 | 0 | Ok(Chunk::NeedMoreData(_)) => unreachable!(), |
1128 | | |
1129 | 1.06M | Ok(Chunk::Parsed { payload, consumed }) => { |
1130 | 1.06M | data = &data[consumed..]; |
1131 | 1.06M | payload |
1132 | | } |
1133 | | }; |
1134 | | |
1135 | 1.06M | match &payload { |
1136 | | #[cfg(feature = "component-model")] |
1137 | 9.78k | Payload::ModuleSection { parser, .. } |
1138 | 13.5k | | Payload::ComponentSection { parser, .. } => { |
1139 | 13.5k | stack.push(cur.clone()); |
1140 | 13.5k | cur = parser.clone(); |
1141 | 13.5k | } |
1142 | 64.5k | Payload::End(_) => match stack.pop() { |
1143 | 13.5k | Some(p) => cur = p, |
1144 | 50.9k | None => done = true, |
1145 | | }, |
1146 | | |
1147 | 988k | _ => {} |
1148 | | } |
1149 | | |
1150 | 1.06M | Some(Ok(payload)) |
1151 | 1.10M | }) |
1152 | 55.4k | } |
1153 | | |
1154 | | /// Skip parsing the code section entirely. |
1155 | | /// |
1156 | | /// This function can be used to indicate, after receiving |
1157 | | /// `CodeSectionStart`, that the section will not be parsed. |
1158 | | /// |
1159 | | /// The caller will be responsible for skipping `size` bytes (found in the |
1160 | | /// `CodeSectionStart` payload). Bytes should only be fed into `parse` |
1161 | | /// after the `size` bytes have been skipped. |
1162 | | /// |
1163 | | /// # Panics |
1164 | | /// |
1165 | | /// This function will panic if the parser is not in a state where it's |
1166 | | /// parsing the code section. |
1167 | | /// |
1168 | | /// # Examples |
1169 | | /// |
1170 | | /// ``` |
1171 | | /// use wasmparser::{Result, Parser, Chunk, Payload::*}; |
1172 | | /// use core::ops::Range; |
1173 | | /// |
1174 | | /// fn objdump_headers(mut wasm: &[u8]) -> Result<()> { |
1175 | | /// let mut parser = Parser::new(0); |
1176 | | /// loop { |
1177 | | /// let payload = match parser.parse(wasm, true)? { |
1178 | | /// Chunk::Parsed { consumed, payload } => { |
1179 | | /// wasm = &wasm[consumed..]; |
1180 | | /// payload |
1181 | | /// } |
1182 | | /// // this state isn't possible with `eof = true` |
1183 | | /// Chunk::NeedMoreData(_) => unreachable!(), |
1184 | | /// }; |
1185 | | /// match payload { |
1186 | | /// TypeSection(s) => print_range("type section", &s.range()), |
1187 | | /// ImportSection(s) => print_range("import section", &s.range()), |
1188 | | /// // .. other sections |
1189 | | /// |
1190 | | /// // Print the range of the code section we see, but don't |
1191 | | /// // actually iterate over each individual function. |
1192 | | /// CodeSectionStart { range, size, .. } => { |
1193 | | /// print_range("code section", &range); |
1194 | | /// parser.skip_section(); |
1195 | | /// wasm = &wasm[size as usize..]; |
1196 | | /// } |
1197 | | /// End(_) => break, |
1198 | | /// _ => {} |
1199 | | /// } |
1200 | | /// } |
1201 | | /// Ok(()) |
1202 | | /// } |
1203 | | /// |
1204 | | /// fn print_range(section: &str, range: &Range<u64>) { |
1205 | | /// println!("{:>40}: {:#010x} - {:#010x}", section, range.start, range.end); |
1206 | | /// } |
1207 | | /// ``` |
1208 | 5.69k | pub fn skip_section(&mut self) { |
1209 | 5.69k | let skip = match self.state { |
1210 | 5.69k | State::FunctionBody { remaining: _, len } => len, |
1211 | 0 | _ => panic!("wrong state to call `skip_section`"), |
1212 | | }; |
1213 | 5.69k | self.offset += u64::from(skip); |
1214 | 5.69k | self.state = State::SectionStart; |
1215 | 5.69k | } |
1216 | | |
1217 | 179k | fn check_function_code_counts(&self, pos: u64) -> Result<()> { |
1218 | 179k | match (self.counts.function_entries, self.counts.code_entries) { |
1219 | 112k | (Some(n), Some(m)) if n != m => { |
1220 | 0 | bail!(pos, "function and code section have inconsistent lengths") |
1221 | | } |
1222 | 0 | (Some(n), None) if n > 0 => bail!( |
1223 | 0 | pos, |
1224 | | "function section has non-zero count but code section is absent" |
1225 | | ), |
1226 | 0 | (None, Some(m)) if m > 0 => bail!( |
1227 | 0 | pos, |
1228 | | "function section is absent but code section has non-zero count" |
1229 | | ), |
1230 | 179k | _ => Ok(()), |
1231 | | } |
1232 | 179k | } |
1233 | | |
1234 | 131k | fn check_data_count(&self, pos: u64) -> Result<()> { |
1235 | 131k | match (self.counts.data_count, self.counts.data_entries) { |
1236 | 11.9k | (Some(n), Some(m)) if n != m => { |
1237 | 0 | bail!(pos, "data count and data section have inconsistent lengths") |
1238 | | } |
1239 | 0 | (Some(n), None) if n > 0 => { |
1240 | 0 | bail!(pos, "data count is non-zero but data section is absent") |
1241 | | } |
1242 | 131k | _ => Ok(()), |
1243 | | } |
1244 | 131k | } |
1245 | | } |
1246 | | |
1247 | | /// Parses an entire section resident in memory into a `Payload`. |
1248 | | /// |
1249 | | /// Requires that `len` bytes are resident in `reader` and uses `ctor`/`variant` |
1250 | | /// to construct the section to return. |
1251 | 1.32M | fn section<'a, T>( |
1252 | 1.32M | reader: &mut BinaryReader<'a>, |
1253 | 1.32M | len: u32, |
1254 | 1.32M | ctor: fn(BinaryReader<'a>) -> Result<T>, |
1255 | 1.32M | variant: fn(T) -> Payload<'a>, |
1256 | 1.32M | ) -> Result<Payload<'a>> { |
1257 | 1.32M | let reader = reader.skip(|r| { |
1258 | 1.32M | r.read_bytes(len as usize)?; |
1259 | 918k | Ok(()) |
1260 | 1.32M | })?; wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::data::Data>>::{closure#0}Line | Count | Source | 1257 | 8.59k | let reader = reader.skip(|r| { | 1258 | 8.59k | r.read_bytes(len as usize)?; | 1259 | 8.59k | Ok(()) | 1260 | 8.59k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::types::MemoryType>>::{closure#0}Line | Count | Source | 1257 | 53.4k | let reader = reader.skip(|r| { | 1258 | 53.4k | r.read_bytes(len as usize)?; | 1259 | 44.6k | Ok(()) | 1260 | 53.4k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::types::TagType>>::{closure#0}Line | Count | Source | 1257 | 3.10k | let reader = reader.skip(|r| { | 1258 | 3.10k | r.read_bytes(len as usize)?; | 1259 | 3.10k | Ok(()) | 1260 | 3.10k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::types::RecGroup>>::{closure#0}Line | Count | Source | 1257 | 95.3k | let reader = reader.skip(|r| { | 1258 | 95.3k | r.read_bytes(len as usize)?; | 1259 | 75.7k | Ok(()) | 1260 | 95.3k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::tables::Table>>::{closure#0}Line | Count | Source | 1257 | 15.5k | let reader = reader.skip(|r| { | 1258 | 15.5k | r.read_bytes(len as usize)?; | 1259 | 14.2k | Ok(()) | 1260 | 15.5k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::exports::Export>>::{closure#0}Line | Count | Source | 1257 | 57.5k | let reader = reader.skip(|r| { | 1258 | 57.5k | r.read_bytes(len as usize)?; | 1259 | 46.7k | Ok(()) | 1260 | 57.5k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::globals::Global>>::{closure#0}Line | Count | Source | 1257 | 23.3k | let reader = reader.skip(|r| { | 1258 | 23.3k | r.read_bytes(len as usize)?; | 1259 | 22.6k | Ok(()) | 1260 | 23.3k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::imports::Imports>>::{closure#0}Line | Count | Source | 1257 | 54.8k | let reader = reader.skip(|r| { | 1258 | 54.8k | r.read_bytes(len as usize)?; | 1259 | 42.7k | Ok(()) | 1260 | 54.8k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::elements::Element>>::{closure#0}Line | Count | Source | 1257 | 13.3k | let reader = reader.skip(|r| { | 1258 | 13.3k | r.read_bytes(len as usize)?; | 1259 | 11.3k | Ok(()) | 1260 | 13.3k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::canonicals::CanonicalFunction>>::{closure#0}Line | Count | Source | 1257 | 60.3k | let reader = reader.skip(|r| { | 1258 | 60.3k | r.read_bytes(len as usize)?; | 1259 | 36.2k | Ok(()) | 1260 | 60.3k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::types::ComponentType>>::{closure#0}Line | Count | Source | 1257 | 276k | let reader = reader.skip(|r| { | 1258 | 276k | r.read_bytes(len as usize)?; | 1259 | 174k | Ok(()) | 1260 | 276k | })?; |
Unexecuted instantiation: wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::types::CoreType>>::{closure#0}wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::aliases::ComponentAlias>>::{closure#0}Line | Count | Source | 1257 | 72.5k | let reader = reader.skip(|r| { | 1258 | 72.5k | r.read_bytes(len as usize)?; | 1259 | 43.5k | Ok(()) | 1260 | 72.5k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::exports::ComponentExport>>::{closure#0}Line | Count | Source | 1257 | 240k | let reader = reader.skip(|r| { | 1258 | 240k | r.read_bytes(len as usize)?; | 1259 | 152k | Ok(()) | 1260 | 240k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::imports::ComponentImport>>::{closure#0}Line | Count | Source | 1257 | 42.3k | let reader = reader.skip(|r| { | 1258 | 42.3k | r.read_bytes(len as usize)?; | 1259 | 25.4k | Ok(()) | 1260 | 42.3k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::instances::ComponentInstance>>::{closure#0}Line | Count | Source | 1257 | 18.9k | let reader = reader.skip(|r| { | 1258 | 18.9k | r.read_bytes(len as usize)?; | 1259 | 11.3k | Ok(()) | 1260 | 18.9k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::instances::Instance>>::{closure#0}Line | Count | Source | 1257 | 56.2k | let reader = reader.skip(|r| { | 1258 | 56.2k | r.read_bytes(len as usize)?; | 1259 | 33.7k | Ok(()) | 1260 | 56.2k | })?; |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<u32>>::{closure#0}Line | Count | Source | 1257 | 67.8k | let reader = reader.skip(|r| { | 1258 | 67.8k | r.read_bytes(len as usize)?; | 1259 | 56.3k | Ok(()) | 1260 | 67.8k | })?; |
wasmparser::parser::section::<wasmparser::readers::core::custom::CustomSectionReader>::{closure#0}Line | Count | Source | 1257 | 165k | let reader = reader.skip(|r| { | 1258 | 165k | r.read_bytes(len as usize)?; | 1259 | 114k | Ok(()) | 1260 | 165k | })?; |
|
1261 | | // clear the hint for "need this many more bytes" here because we already |
1262 | | // read all the bytes, so it's not possible to read more bytes if this |
1263 | | // fails. |
1264 | 918k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; |
1265 | 918k | Ok(variant(reader)) |
1266 | 1.32M | } wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::data::Data>> Line | Count | Source | 1251 | 8.59k | fn section<'a, T>( | 1252 | 8.59k | reader: &mut BinaryReader<'a>, | 1253 | 8.59k | len: u32, | 1254 | 8.59k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 8.59k | variant: fn(T) -> Payload<'a>, | 1256 | 8.59k | ) -> Result<Payload<'a>> { | 1257 | 8.59k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 0 | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 8.59k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 8.59k | Ok(variant(reader)) | 1266 | 8.59k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::types::MemoryType>> Line | Count | Source | 1251 | 53.4k | fn section<'a, T>( | 1252 | 53.4k | reader: &mut BinaryReader<'a>, | 1253 | 53.4k | len: u32, | 1254 | 53.4k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 53.4k | variant: fn(T) -> Payload<'a>, | 1256 | 53.4k | ) -> Result<Payload<'a>> { | 1257 | 53.4k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 8.77k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 44.6k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 44.6k | Ok(variant(reader)) | 1266 | 53.4k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::types::TagType>> Line | Count | Source | 1251 | 3.10k | fn section<'a, T>( | 1252 | 3.10k | reader: &mut BinaryReader<'a>, | 1253 | 3.10k | len: u32, | 1254 | 3.10k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 3.10k | variant: fn(T) -> Payload<'a>, | 1256 | 3.10k | ) -> Result<Payload<'a>> { | 1257 | 3.10k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 0 | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 3.10k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 3.10k | Ok(variant(reader)) | 1266 | 3.10k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::types::RecGroup>> Line | Count | Source | 1251 | 95.3k | fn section<'a, T>( | 1252 | 95.3k | reader: &mut BinaryReader<'a>, | 1253 | 95.3k | len: u32, | 1254 | 95.3k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 95.3k | variant: fn(T) -> Payload<'a>, | 1256 | 95.3k | ) -> Result<Payload<'a>> { | 1257 | 95.3k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 19.5k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 75.7k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 75.7k | Ok(variant(reader)) | 1266 | 95.3k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::tables::Table>> Line | Count | Source | 1251 | 15.5k | fn section<'a, T>( | 1252 | 15.5k | reader: &mut BinaryReader<'a>, | 1253 | 15.5k | len: u32, | 1254 | 15.5k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 15.5k | variant: fn(T) -> Payload<'a>, | 1256 | 15.5k | ) -> Result<Payload<'a>> { | 1257 | 15.5k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 1.31k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 14.2k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 14.2k | Ok(variant(reader)) | 1266 | 15.5k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::exports::Export>> Line | Count | Source | 1251 | 57.5k | fn section<'a, T>( | 1252 | 57.5k | reader: &mut BinaryReader<'a>, | 1253 | 57.5k | len: u32, | 1254 | 57.5k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 57.5k | variant: fn(T) -> Payload<'a>, | 1256 | 57.5k | ) -> Result<Payload<'a>> { | 1257 | 57.5k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 10.8k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 46.7k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 46.7k | Ok(variant(reader)) | 1266 | 57.5k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::globals::Global>> Line | Count | Source | 1251 | 23.3k | fn section<'a, T>( | 1252 | 23.3k | reader: &mut BinaryReader<'a>, | 1253 | 23.3k | len: u32, | 1254 | 23.3k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 23.3k | variant: fn(T) -> Payload<'a>, | 1256 | 23.3k | ) -> Result<Payload<'a>> { | 1257 | 23.3k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 716 | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 22.6k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 22.6k | Ok(variant(reader)) | 1266 | 23.3k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::imports::Imports>> Line | Count | Source | 1251 | 54.8k | fn section<'a, T>( | 1252 | 54.8k | reader: &mut BinaryReader<'a>, | 1253 | 54.8k | len: u32, | 1254 | 54.8k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 54.8k | variant: fn(T) -> Payload<'a>, | 1256 | 54.8k | ) -> Result<Payload<'a>> { | 1257 | 54.8k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 12.0k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 42.7k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 42.7k | Ok(variant(reader)) | 1266 | 54.8k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::core::elements::Element>> Line | Count | Source | 1251 | 13.3k | fn section<'a, T>( | 1252 | 13.3k | reader: &mut BinaryReader<'a>, | 1253 | 13.3k | len: u32, | 1254 | 13.3k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 13.3k | variant: fn(T) -> Payload<'a>, | 1256 | 13.3k | ) -> Result<Payload<'a>> { | 1257 | 13.3k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 2.03k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 11.3k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 11.3k | Ok(variant(reader)) | 1266 | 13.3k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::canonicals::CanonicalFunction>> Line | Count | Source | 1251 | 60.3k | fn section<'a, T>( | 1252 | 60.3k | reader: &mut BinaryReader<'a>, | 1253 | 60.3k | len: u32, | 1254 | 60.3k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 60.3k | variant: fn(T) -> Payload<'a>, | 1256 | 60.3k | ) -> Result<Payload<'a>> { | 1257 | 60.3k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 24.1k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 36.2k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 36.2k | Ok(variant(reader)) | 1266 | 60.3k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::types::ComponentType>> Line | Count | Source | 1251 | 276k | fn section<'a, T>( | 1252 | 276k | reader: &mut BinaryReader<'a>, | 1253 | 276k | len: u32, | 1254 | 276k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 276k | variant: fn(T) -> Payload<'a>, | 1256 | 276k | ) -> Result<Payload<'a>> { | 1257 | 276k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 102k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 174k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 174k | Ok(variant(reader)) | 1266 | 276k | } |
Unexecuted instantiation: wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::types::CoreType>> wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::aliases::ComponentAlias>> Line | Count | Source | 1251 | 72.5k | fn section<'a, T>( | 1252 | 72.5k | reader: &mut BinaryReader<'a>, | 1253 | 72.5k | len: u32, | 1254 | 72.5k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 72.5k | variant: fn(T) -> Payload<'a>, | 1256 | 72.5k | ) -> Result<Payload<'a>> { | 1257 | 72.5k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 29.0k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 43.5k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 43.5k | Ok(variant(reader)) | 1266 | 72.5k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::exports::ComponentExport>> Line | Count | Source | 1251 | 240k | fn section<'a, T>( | 1252 | 240k | reader: &mut BinaryReader<'a>, | 1253 | 240k | len: u32, | 1254 | 240k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 240k | variant: fn(T) -> Payload<'a>, | 1256 | 240k | ) -> Result<Payload<'a>> { | 1257 | 240k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 87.8k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 152k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 152k | Ok(variant(reader)) | 1266 | 240k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::imports::ComponentImport>> Line | Count | Source | 1251 | 42.3k | fn section<'a, T>( | 1252 | 42.3k | reader: &mut BinaryReader<'a>, | 1253 | 42.3k | len: u32, | 1254 | 42.3k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 42.3k | variant: fn(T) -> Payload<'a>, | 1256 | 42.3k | ) -> Result<Payload<'a>> { | 1257 | 42.3k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 16.9k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 25.4k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 25.4k | Ok(variant(reader)) | 1266 | 42.3k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::instances::ComponentInstance>> Line | Count | Source | 1251 | 18.9k | fn section<'a, T>( | 1252 | 18.9k | reader: &mut BinaryReader<'a>, | 1253 | 18.9k | len: u32, | 1254 | 18.9k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 18.9k | variant: fn(T) -> Payload<'a>, | 1256 | 18.9k | ) -> Result<Payload<'a>> { | 1257 | 18.9k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 7.57k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 11.3k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 11.3k | Ok(variant(reader)) | 1266 | 18.9k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<wasmparser::readers::component::instances::Instance>> Line | Count | Source | 1251 | 56.2k | fn section<'a, T>( | 1252 | 56.2k | reader: &mut BinaryReader<'a>, | 1253 | 56.2k | len: u32, | 1254 | 56.2k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 56.2k | variant: fn(T) -> Payload<'a>, | 1256 | 56.2k | ) -> Result<Payload<'a>> { | 1257 | 56.2k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 22.5k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 33.7k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 33.7k | Ok(variant(reader)) | 1266 | 56.2k | } |
wasmparser::parser::section::<wasmparser::readers::SectionLimited<u32>> Line | Count | Source | 1251 | 67.8k | fn section<'a, T>( | 1252 | 67.8k | reader: &mut BinaryReader<'a>, | 1253 | 67.8k | len: u32, | 1254 | 67.8k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 67.8k | variant: fn(T) -> Payload<'a>, | 1256 | 67.8k | ) -> Result<Payload<'a>> { | 1257 | 67.8k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 11.5k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 56.3k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 56.3k | Ok(variant(reader)) | 1266 | 67.8k | } |
wasmparser::parser::section::<wasmparser::readers::core::custom::CustomSectionReader> Line | Count | Source | 1251 | 165k | fn section<'a, T>( | 1252 | 165k | reader: &mut BinaryReader<'a>, | 1253 | 165k | len: u32, | 1254 | 165k | ctor: fn(BinaryReader<'a>) -> Result<T>, | 1255 | 165k | variant: fn(T) -> Payload<'a>, | 1256 | 165k | ) -> Result<Payload<'a>> { | 1257 | 165k | let reader = reader.skip(|r| { | 1258 | | r.read_bytes(len as usize)?; | 1259 | | Ok(()) | 1260 | 51.1k | })?; | 1261 | | // clear the hint for "need this many more bytes" here because we already | 1262 | | // read all the bytes, so it's not possible to read more bytes if this | 1263 | | // fails. | 1264 | 114k | let reader = ctor(reader).map_err(Error::without_needed_hint)?; | 1265 | 114k | Ok(variant(reader)) | 1266 | 165k | } |
|
1267 | | |
1268 | | /// Reads a section that is represented by a single uleb-encoded `u32`. |
1269 | 29.9k | fn single_item<'a, T>( |
1270 | 29.9k | reader: &mut BinaryReader<'a>, |
1271 | 29.9k | section_end: u64, |
1272 | 29.9k | desc: &str, |
1273 | 29.9k | ) -> Result<(T, Range<u64>)> |
1274 | 29.9k | where |
1275 | 29.9k | T: FromReader<'a>, |
1276 | | { |
1277 | 29.9k | let range = reader.original_position()..section_end; |
1278 | 29.9k | let mut content = reader.skip(|r| { |
1279 | | // length is guaranteed to fit into a u32 |
1280 | 29.9k | r.read_bytes((range.end - range.start) as u32 as usize)?; |
1281 | 21.1k | Ok(()) |
1282 | 29.9k | })?; Unexecuted instantiation: wasmparser::parser::single_item::<wasmparser::readers::component::start::ComponentStartFunction>::{closure#0}wasmparser::parser::single_item::<u32>::{closure#0}Line | Count | Source | 1278 | 29.9k | let mut content = reader.skip(|r| { | 1279 | | // length is guaranteed to fit into a u32 | 1280 | 29.9k | r.read_bytes((range.end - range.start) as u32 as usize)?; | 1281 | 21.1k | Ok(()) | 1282 | 29.9k | })?; |
|
1283 | | // We can't recover from "unexpected eof" here because our entire section is |
1284 | | // already resident in memory, so clear the hint for how many more bytes are |
1285 | | // expected. |
1286 | 21.1k | let ret = content.read().map_err(Error::without_needed_hint)?; |
1287 | 21.1k | if !content.eof() { |
1288 | 0 | bail!( |
1289 | 0 | content.original_position(), |
1290 | | "unexpected content in the {desc} section", |
1291 | | ); |
1292 | 21.1k | } |
1293 | 21.1k | Ok((ret, range)) |
1294 | 29.9k | } Unexecuted instantiation: wasmparser::parser::single_item::<wasmparser::readers::component::start::ComponentStartFunction> wasmparser::parser::single_item::<u32> Line | Count | Source | 1269 | 29.9k | fn single_item<'a, T>( | 1270 | 29.9k | reader: &mut BinaryReader<'a>, | 1271 | 29.9k | section_end: u64, | 1272 | 29.9k | desc: &str, | 1273 | 29.9k | ) -> Result<(T, Range<u64>)> | 1274 | 29.9k | where | 1275 | 29.9k | T: FromReader<'a>, | 1276 | | { | 1277 | 29.9k | let range = reader.original_position()..section_end; | 1278 | 29.9k | let mut content = reader.skip(|r| { | 1279 | | // length is guaranteed to fit into a u32 | 1280 | | r.read_bytes((range.end - range.start) as u32 as usize)?; | 1281 | | Ok(()) | 1282 | 8.77k | })?; | 1283 | | // We can't recover from "unexpected eof" here because our entire section is | 1284 | | // already resident in memory, so clear the hint for how many more bytes are | 1285 | | // expected. | 1286 | 21.1k | let ret = content.read().map_err(Error::without_needed_hint)?; | 1287 | 21.1k | if !content.eof() { | 1288 | 0 | bail!( | 1289 | 0 | content.original_position(), | 1290 | | "unexpected content in the {desc} section", | 1291 | | ); | 1292 | 21.1k | } | 1293 | 21.1k | Ok((ret, range)) | 1294 | 29.9k | } |
|
1295 | | |
1296 | | /// Attempts to parse using `f`. |
1297 | | /// |
1298 | | /// This will update `*len` with the number of bytes consumed, and it will cause |
1299 | | /// a failure to be returned instead of the number of bytes consumed exceeds |
1300 | | /// what `*len` currently is. |
1301 | 765k | fn delimited<'a, T>( |
1302 | 765k | reader: &mut BinaryReader<'a>, |
1303 | 765k | len: &mut u32, |
1304 | 765k | f: impl FnOnce(&mut BinaryReader<'a>) -> Result<T>, |
1305 | 765k | ) -> Result<T> { |
1306 | 765k | let start = reader.original_position(); |
1307 | 765k | let ret = f(reader)?; |
1308 | 657k | *len = match (reader.original_position() - start) |
1309 | 657k | .try_into() |
1310 | 657k | .ok() |
1311 | 657k | .and_then(|i| len.checked_sub(i)) wasmparser::parser::delimited::<wasmparser::readers::core::code::FunctionBody, <wasmparser::parser::Parser>::parse_reader::{closure#2}>::{closure#0}Line | Count | Source | 1311 | 601k | .and_then(|i| len.checked_sub(i)) |
wasmparser::parser::delimited::<u32, <wasmparser::parser::Parser>::parse_reader::{closure#1}>::{closure#0}Line | Count | Source | 1311 | 56.2k | .and_then(|i| len.checked_sub(i)) |
|
1312 | | { |
1313 | 657k | Some(i) => i, |
1314 | 0 | None => return Err(Error::new("unexpected end-of-file", start)), |
1315 | | }; |
1316 | 657k | Ok(ret) |
1317 | 765k | } wasmparser::parser::delimited::<wasmparser::readers::core::code::FunctionBody, <wasmparser::parser::Parser>::parse_reader::{closure#2}>Line | Count | Source | 1301 | 698k | fn delimited<'a, T>( | 1302 | 698k | reader: &mut BinaryReader<'a>, | 1303 | 698k | len: &mut u32, | 1304 | 698k | f: impl FnOnce(&mut BinaryReader<'a>) -> Result<T>, | 1305 | 698k | ) -> Result<T> { | 1306 | 698k | let start = reader.original_position(); | 1307 | 698k | let ret = f(reader)?; | 1308 | 601k | *len = match (reader.original_position() - start) | 1309 | 601k | .try_into() | 1310 | 601k | .ok() | 1311 | 601k | .and_then(|i| len.checked_sub(i)) | 1312 | | { | 1313 | 601k | Some(i) => i, | 1314 | 0 | None => return Err(Error::new("unexpected end-of-file", start)), | 1315 | | }; | 1316 | 601k | Ok(ret) | 1317 | 698k | } |
wasmparser::parser::delimited::<u32, <wasmparser::parser::Parser>::parse_reader::{closure#1}>Line | Count | Source | 1301 | 67.7k | fn delimited<'a, T>( | 1302 | 67.7k | reader: &mut BinaryReader<'a>, | 1303 | 67.7k | len: &mut u32, | 1304 | 67.7k | f: impl FnOnce(&mut BinaryReader<'a>) -> Result<T>, | 1305 | 67.7k | ) -> Result<T> { | 1306 | 67.7k | let start = reader.original_position(); | 1307 | 67.7k | let ret = f(reader)?; | 1308 | 56.2k | *len = match (reader.original_position() - start) | 1309 | 56.2k | .try_into() | 1310 | 56.2k | .ok() | 1311 | 56.2k | .and_then(|i| len.checked_sub(i)) | 1312 | | { | 1313 | 56.2k | Some(i) => i, | 1314 | 0 | None => return Err(Error::new("unexpected end-of-file", start)), | 1315 | | }; | 1316 | 56.2k | Ok(ret) | 1317 | 67.7k | } |
|
1318 | | |
1319 | | impl Default for Parser { |
1320 | 575 | fn default() -> Parser { |
1321 | 575 | Parser::new(0) |
1322 | 575 | } |
1323 | | } |
1324 | | |
1325 | | impl Payload<'_> { |
1326 | | /// If this `Payload` represents a section in the original wasm module then |
1327 | | /// the section's id and range within the original wasm binary are returned. |
1328 | | /// |
1329 | | /// Not all payloads refer to entire sections, such as the `Version` and |
1330 | | /// `CodeSectionEntry` variants. These variants will return `None` from this |
1331 | | /// function. |
1332 | | /// |
1333 | | /// Otherwise this function will return `Some` where the first element is |
1334 | | /// the byte identifier for the section and the second element is the range |
1335 | | /// of the contents of the section within the original wasm binary. |
1336 | | /// |
1337 | | /// The purpose of this method is to enable tools to easily iterate over |
1338 | | /// entire sections if necessary and handle sections uniformly, for example |
1339 | | /// dropping custom sections while preserving all other sections. |
1340 | 152k | pub fn as_section(&self) -> Option<(u8, Range<u64>)> { |
1341 | | use Payload::*; |
1342 | | |
1343 | 152k | match self { |
1344 | 13.1k | Version { .. } => None, |
1345 | 13.1k | TypeSection(s) => Some((TYPE_SECTION, s.range())), |
1346 | 3.26k | ImportSection(s) => Some((IMPORT_SECTION, s.range())), |
1347 | 13.1k | FunctionSection(s) => Some((FUNCTION_SECTION, s.range())), |
1348 | 0 | TableSection(s) => Some((TABLE_SECTION, s.range())), |
1349 | 13.1k | MemorySection(s) => Some((MEMORY_SECTION, s.range())), |
1350 | 0 | TagSection(s) => Some((TAG_SECTION, s.range())), |
1351 | 0 | GlobalSection(s) => Some((GLOBAL_SECTION, s.range())), |
1352 | 13.1k | ExportSection(s) => Some((EXPORT_SECTION, s.range())), |
1353 | 0 | ElementSection(s) => Some((ELEMENT_SECTION, s.range())), |
1354 | 0 | DataSection(s) => Some((DATA_SECTION, s.range())), |
1355 | 0 | StartSection { range, .. } => Some((START_SECTION, range.clone())), |
1356 | 0 | DataCountSection { range, .. } => Some((DATA_COUNT_SECTION, range.clone())), |
1357 | 13.1k | CodeSectionStart { range, .. } => Some((CODE_SECTION, range.clone())), |
1358 | 61.6k | CodeSectionEntry(_) => None, |
1359 | | |
1360 | | #[cfg(feature = "component-model")] |
1361 | | ModuleSection { |
1362 | 0 | unchecked_range: range, |
1363 | | .. |
1364 | 0 | } => Some((COMPONENT_MODULE_SECTION, range.clone())), |
1365 | | #[cfg(feature = "component-model")] |
1366 | 0 | InstanceSection(s) => Some((COMPONENT_CORE_INSTANCE_SECTION, s.range())), |
1367 | | #[cfg(feature = "component-model")] |
1368 | 0 | CoreTypeSection(s) => Some((COMPONENT_CORE_TYPE_SECTION, s.range())), |
1369 | | #[cfg(feature = "component-model")] |
1370 | | ComponentSection { |
1371 | 0 | unchecked_range: range, |
1372 | | .. |
1373 | 0 | } => Some((COMPONENT_SECTION, range.clone())), |
1374 | | #[cfg(feature = "component-model")] |
1375 | 0 | ComponentInstanceSection(s) => Some((COMPONENT_INSTANCE_SECTION, s.range())), |
1376 | | #[cfg(feature = "component-model")] |
1377 | 0 | ComponentAliasSection(s) => Some((COMPONENT_ALIAS_SECTION, s.range())), |
1378 | | #[cfg(feature = "component-model")] |
1379 | 0 | ComponentTypeSection(s) => Some((COMPONENT_TYPE_SECTION, s.range())), |
1380 | | #[cfg(feature = "component-model")] |
1381 | 0 | ComponentCanonicalSection(s) => Some((COMPONENT_CANONICAL_SECTION, s.range())), |
1382 | | #[cfg(feature = "component-model")] |
1383 | 0 | ComponentStartSection { range, .. } => Some((COMPONENT_START_SECTION, range.clone())), |
1384 | | #[cfg(feature = "component-model")] |
1385 | 0 | ComponentImportSection(s) => Some((COMPONENT_IMPORT_SECTION, s.range())), |
1386 | | #[cfg(feature = "component-model")] |
1387 | 0 | ComponentExportSection(s) => Some((COMPONENT_EXPORT_SECTION, s.range())), |
1388 | | |
1389 | 0 | CustomSection(c) => Some((CUSTOM_SECTION, c.range())), |
1390 | | |
1391 | 0 | UnknownSection { id, range, .. } => Some((*id, range.clone())), |
1392 | | |
1393 | 8.77k | End(_) => None, |
1394 | | } |
1395 | 152k | } |
1396 | | } |
1397 | | |
1398 | | impl fmt::Debug for Payload<'_> { |
1399 | 159 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1400 | | use Payload::*; |
1401 | 159 | match self { |
1402 | | Version { |
1403 | 0 | num, |
1404 | 0 | encoding, |
1405 | 0 | range, |
1406 | 0 | } => f |
1407 | 0 | .debug_struct("Version") |
1408 | 0 | .field("num", num) |
1409 | 0 | .field("encoding", encoding) |
1410 | 0 | .field("range", range) |
1411 | 0 | .finish(), |
1412 | | |
1413 | | // Module sections |
1414 | 0 | TypeSection(_) => f.debug_tuple("TypeSection").field(&"...").finish(), |
1415 | 0 | ImportSection(_) => f.debug_tuple("ImportSection").field(&"...").finish(), |
1416 | 0 | FunctionSection(_) => f.debug_tuple("FunctionSection").field(&"...").finish(), |
1417 | 0 | TableSection(_) => f.debug_tuple("TableSection").field(&"...").finish(), |
1418 | 0 | MemorySection(_) => f.debug_tuple("MemorySection").field(&"...").finish(), |
1419 | 159 | TagSection(_) => f.debug_tuple("TagSection").field(&"...").finish(), |
1420 | 0 | GlobalSection(_) => f.debug_tuple("GlobalSection").field(&"...").finish(), |
1421 | 0 | ExportSection(_) => f.debug_tuple("ExportSection").field(&"...").finish(), |
1422 | 0 | ElementSection(_) => f.debug_tuple("ElementSection").field(&"...").finish(), |
1423 | 0 | DataSection(_) => f.debug_tuple("DataSection").field(&"...").finish(), |
1424 | 0 | StartSection { func, range } => f |
1425 | 0 | .debug_struct("StartSection") |
1426 | 0 | .field("func", func) |
1427 | 0 | .field("range", range) |
1428 | 0 | .finish(), |
1429 | 0 | DataCountSection { count, range } => f |
1430 | 0 | .debug_struct("DataCountSection") |
1431 | 0 | .field("count", count) |
1432 | 0 | .field("range", range) |
1433 | 0 | .finish(), |
1434 | 0 | CodeSectionStart { count, range, size } => f |
1435 | 0 | .debug_struct("CodeSectionStart") |
1436 | 0 | .field("count", count) |
1437 | 0 | .field("range", range) |
1438 | 0 | .field("size", size) |
1439 | 0 | .finish(), |
1440 | 0 | CodeSectionEntry(_) => f.debug_tuple("CodeSectionEntry").field(&"...").finish(), |
1441 | | |
1442 | | // Component sections |
1443 | | #[cfg(feature = "component-model")] |
1444 | | ModuleSection { |
1445 | | parser: _, |
1446 | 0 | unchecked_range: range, |
1447 | 0 | } => f |
1448 | 0 | .debug_struct("ModuleSection") |
1449 | 0 | .field("range", range) |
1450 | 0 | .finish(), |
1451 | | #[cfg(feature = "component-model")] |
1452 | 0 | InstanceSection(_) => f.debug_tuple("InstanceSection").field(&"...").finish(), |
1453 | | #[cfg(feature = "component-model")] |
1454 | 0 | CoreTypeSection(_) => f.debug_tuple("CoreTypeSection").field(&"...").finish(), |
1455 | | #[cfg(feature = "component-model")] |
1456 | | ComponentSection { |
1457 | | parser: _, |
1458 | 0 | unchecked_range: range, |
1459 | 0 | } => f |
1460 | 0 | .debug_struct("ComponentSection") |
1461 | 0 | .field("range", range) |
1462 | 0 | .finish(), |
1463 | | #[cfg(feature = "component-model")] |
1464 | 0 | ComponentInstanceSection(_) => f |
1465 | 0 | .debug_tuple("ComponentInstanceSection") |
1466 | 0 | .field(&"...") |
1467 | 0 | .finish(), |
1468 | | #[cfg(feature = "component-model")] |
1469 | 0 | ComponentAliasSection(_) => f |
1470 | 0 | .debug_tuple("ComponentAliasSection") |
1471 | 0 | .field(&"...") |
1472 | 0 | .finish(), |
1473 | | #[cfg(feature = "component-model")] |
1474 | 0 | ComponentTypeSection(_) => f.debug_tuple("ComponentTypeSection").field(&"...").finish(), |
1475 | | #[cfg(feature = "component-model")] |
1476 | 0 | ComponentCanonicalSection(_) => f |
1477 | 0 | .debug_tuple("ComponentCanonicalSection") |
1478 | 0 | .field(&"...") |
1479 | 0 | .finish(), |
1480 | | #[cfg(feature = "component-model")] |
1481 | 0 | ComponentStartSection { .. } => f |
1482 | 0 | .debug_tuple("ComponentStartSection") |
1483 | 0 | .field(&"...") |
1484 | 0 | .finish(), |
1485 | | #[cfg(feature = "component-model")] |
1486 | 0 | ComponentImportSection(_) => f |
1487 | 0 | .debug_tuple("ComponentImportSection") |
1488 | 0 | .field(&"...") |
1489 | 0 | .finish(), |
1490 | | #[cfg(feature = "component-model")] |
1491 | 0 | ComponentExportSection(_) => f |
1492 | 0 | .debug_tuple("ComponentExportSection") |
1493 | 0 | .field(&"...") |
1494 | 0 | .finish(), |
1495 | | |
1496 | 0 | CustomSection(c) => f.debug_tuple("CustomSection").field(c).finish(), |
1497 | | |
1498 | 0 | UnknownSection { id, range, .. } => f |
1499 | 0 | .debug_struct("UnknownSection") |
1500 | 0 | .field("id", id) |
1501 | 0 | .field("range", range) |
1502 | 0 | .finish(), |
1503 | | |
1504 | 0 | End(offset) => f.debug_tuple("End").field(offset).finish(), |
1505 | | } |
1506 | 159 | } |
1507 | | } |
1508 | | |
1509 | | #[cfg(test)] |
1510 | | mod tests { |
1511 | | use super::*; |
1512 | | |
1513 | | macro_rules! assert_matches { |
1514 | | ($a:expr, $b:pat $(,)?) => { |
1515 | | match $a { |
1516 | | $b => {} |
1517 | | a => panic!("`{:?}` doesn't match `{}`", a, stringify!($b)), |
1518 | | } |
1519 | | }; |
1520 | | } |
1521 | | |
1522 | | #[test] |
1523 | | fn header() { |
1524 | | assert!(Parser::default().parse(&[], true).is_err()); |
1525 | | assert_matches!( |
1526 | | Parser::default().parse(&[], false), |
1527 | | Ok(Chunk::NeedMoreData(4)), |
1528 | | ); |
1529 | | assert_matches!( |
1530 | | Parser::default().parse(b"\0", false), |
1531 | | Ok(Chunk::NeedMoreData(3)), |
1532 | | ); |
1533 | | assert_matches!( |
1534 | | Parser::default().parse(b"\0asm", false), |
1535 | | Ok(Chunk::NeedMoreData(4)), |
1536 | | ); |
1537 | | assert_matches!( |
1538 | | Parser::default().parse(b"\0asm\x01\0\0\0", false), |
1539 | | Ok(Chunk::Parsed { |
1540 | | consumed: 8, |
1541 | | payload: Payload::Version { num: 1, .. }, |
1542 | | }), |
1543 | | ); |
1544 | | } |
1545 | | |
1546 | | #[test] |
1547 | | fn header_iter() { |
1548 | | for _ in Parser::default().parse_all(&[]) {} |
1549 | | for _ in Parser::default().parse_all(b"\0") {} |
1550 | | for _ in Parser::default().parse_all(b"\0asm") {} |
1551 | | for _ in Parser::default().parse_all(b"\0asm\x01\x01\x01\x01") {} |
1552 | | } |
1553 | | |
1554 | | fn parser_after_header() -> Parser { |
1555 | | let mut p = Parser::default(); |
1556 | | assert_matches!( |
1557 | | p.parse(b"\0asm\x01\0\0\0", false), |
1558 | | Ok(Chunk::Parsed { |
1559 | | consumed: 8, |
1560 | | payload: Payload::Version { |
1561 | | num: WASM_MODULE_VERSION, |
1562 | | encoding: Encoding::Module, |
1563 | | .. |
1564 | | }, |
1565 | | }), |
1566 | | ); |
1567 | | p |
1568 | | } |
1569 | | |
1570 | | fn parser_after_component_header() -> Parser { |
1571 | | let mut p = Parser::default(); |
1572 | | assert_matches!( |
1573 | | p.parse(b"\0asm\x0d\0\x01\0", false), |
1574 | | Ok(Chunk::Parsed { |
1575 | | consumed: 8, |
1576 | | payload: Payload::Version { |
1577 | | num: WASM_COMPONENT_VERSION, |
1578 | | encoding: Encoding::Component, |
1579 | | .. |
1580 | | }, |
1581 | | }), |
1582 | | ); |
1583 | | p |
1584 | | } |
1585 | | |
1586 | | #[test] |
1587 | | fn start_section() { |
1588 | | assert_matches!( |
1589 | | parser_after_header().parse(&[], false), |
1590 | | Ok(Chunk::NeedMoreData(1)), |
1591 | | ); |
1592 | | assert!(parser_after_header().parse(&[8], true).is_err()); |
1593 | | assert!(parser_after_header().parse(&[8, 1], true).is_err()); |
1594 | | assert!(parser_after_header().parse(&[8, 2], true).is_err()); |
1595 | | assert_matches!( |
1596 | | parser_after_header().parse(&[8], false), |
1597 | | Ok(Chunk::NeedMoreData(1)), |
1598 | | ); |
1599 | | assert_matches!( |
1600 | | parser_after_header().parse(&[8, 1], false), |
1601 | | Ok(Chunk::NeedMoreData(1)), |
1602 | | ); |
1603 | | assert_matches!( |
1604 | | parser_after_header().parse(&[8, 2], false), |
1605 | | Ok(Chunk::NeedMoreData(2)), |
1606 | | ); |
1607 | | assert_matches!( |
1608 | | parser_after_header().parse(&[8, 1, 1], false), |
1609 | | Ok(Chunk::Parsed { |
1610 | | consumed: 3, |
1611 | | payload: Payload::StartSection { func: 1, .. }, |
1612 | | }), |
1613 | | ); |
1614 | | assert!(parser_after_header().parse(&[8, 2, 1, 1], false).is_err()); |
1615 | | assert!(parser_after_header().parse(&[8, 0], false).is_err()); |
1616 | | } |
1617 | | |
1618 | | #[test] |
1619 | | fn end_works() { |
1620 | | assert_matches!( |
1621 | | parser_after_header().parse(&[], true), |
1622 | | Ok(Chunk::Parsed { |
1623 | | consumed: 0, |
1624 | | payload: Payload::End(8), |
1625 | | }), |
1626 | | ); |
1627 | | } |
1628 | | |
1629 | | #[test] |
1630 | | fn type_section() { |
1631 | | assert!(parser_after_header().parse(&[1], true).is_err()); |
1632 | | assert!(parser_after_header().parse(&[1, 0], false).is_err()); |
1633 | | assert!(parser_after_header().parse(&[8, 2], true).is_err()); |
1634 | | assert_matches!( |
1635 | | parser_after_header().parse(&[1], false), |
1636 | | Ok(Chunk::NeedMoreData(1)), |
1637 | | ); |
1638 | | assert_matches!( |
1639 | | parser_after_header().parse(&[1, 1], false), |
1640 | | Ok(Chunk::NeedMoreData(1)), |
1641 | | ); |
1642 | | assert_matches!( |
1643 | | parser_after_header().parse(&[1, 1, 1], false), |
1644 | | Ok(Chunk::Parsed { |
1645 | | consumed: 3, |
1646 | | payload: Payload::TypeSection(_), |
1647 | | }), |
1648 | | ); |
1649 | | assert_matches!( |
1650 | | parser_after_header().parse(&[1, 1, 1, 2, 3, 4], false), |
1651 | | Ok(Chunk::Parsed { |
1652 | | consumed: 3, |
1653 | | payload: Payload::TypeSection(_), |
1654 | | }), |
1655 | | ); |
1656 | | } |
1657 | | |
1658 | | #[test] |
1659 | | fn custom_section() { |
1660 | | assert!(parser_after_header().parse(&[0], true).is_err()); |
1661 | | assert!(parser_after_header().parse(&[0, 0], false).is_err()); |
1662 | | assert!(parser_after_header().parse(&[0, 1, 1], false).is_err()); |
1663 | | assert_matches!( |
1664 | | parser_after_header().parse(&[0, 2, 1], false), |
1665 | | Ok(Chunk::NeedMoreData(1)), |
1666 | | ); |
1667 | | assert_custom( |
1668 | | parser_after_header().parse(&[0, 1, 0], false).unwrap(), |
1669 | | 3, |
1670 | | "", |
1671 | | 11, |
1672 | | b"", |
1673 | | Range { start: 10, end: 11 }, |
1674 | | ); |
1675 | | assert_custom( |
1676 | | parser_after_header() |
1677 | | .parse(&[0, 2, 1, b'a'], false) |
1678 | | .unwrap(), |
1679 | | 4, |
1680 | | "a", |
1681 | | 12, |
1682 | | b"", |
1683 | | Range { start: 10, end: 12 }, |
1684 | | ); |
1685 | | assert_custom( |
1686 | | parser_after_header() |
1687 | | .parse(&[0, 2, 0, b'a'], false) |
1688 | | .unwrap(), |
1689 | | 4, |
1690 | | "", |
1691 | | 11, |
1692 | | b"a", |
1693 | | Range { start: 10, end: 12 }, |
1694 | | ); |
1695 | | } |
1696 | | |
1697 | | fn assert_custom( |
1698 | | chunk: Chunk<'_>, |
1699 | | expected_consumed: usize, |
1700 | | expected_name: &str, |
1701 | | expected_data_offset: u64, |
1702 | | expected_data: &[u8], |
1703 | | expected_range: Range<u64>, |
1704 | | ) { |
1705 | | let (consumed, s) = match chunk { |
1706 | | Chunk::Parsed { |
1707 | | consumed, |
1708 | | payload: Payload::CustomSection(s), |
1709 | | } => (consumed, s), |
1710 | | _ => panic!("not a custom section payload"), |
1711 | | }; |
1712 | | assert_eq!(consumed, expected_consumed); |
1713 | | assert_eq!(s.name(), expected_name); |
1714 | | assert_eq!(s.data_offset(), expected_data_offset); |
1715 | | assert_eq!(s.data(), expected_data); |
1716 | | assert_eq!(s.range(), expected_range); |
1717 | | } |
1718 | | |
1719 | | #[test] |
1720 | | fn function_section() { |
1721 | | assert!(parser_after_header().parse(&[10], true).is_err()); |
1722 | | assert!(parser_after_header().parse(&[10, 0], true).is_err()); |
1723 | | assert!(parser_after_header().parse(&[10, 1], true).is_err()); |
1724 | | assert_matches!( |
1725 | | parser_after_header().parse(&[10], false), |
1726 | | Ok(Chunk::NeedMoreData(1)) |
1727 | | ); |
1728 | | assert_matches!( |
1729 | | parser_after_header().parse(&[10, 1], false), |
1730 | | Ok(Chunk::NeedMoreData(1)) |
1731 | | ); |
1732 | | let mut p = parser_after_header(); |
1733 | | assert_matches!( |
1734 | | p.parse(&[10, 1, 0], false), |
1735 | | Ok(Chunk::Parsed { |
1736 | | consumed: 3, |
1737 | | payload: Payload::CodeSectionStart { count: 0, .. }, |
1738 | | }), |
1739 | | ); |
1740 | | assert_matches!( |
1741 | | p.parse(&[], true), |
1742 | | Ok(Chunk::Parsed { |
1743 | | consumed: 0, |
1744 | | payload: Payload::End(11), |
1745 | | }), |
1746 | | ); |
1747 | | let mut p = parser_after_header(); |
1748 | | assert_matches!( |
1749 | | p.parse(&[3, 2, 1, 0], false), |
1750 | | Ok(Chunk::Parsed { |
1751 | | consumed: 4, |
1752 | | payload: Payload::FunctionSection { .. }, |
1753 | | }), |
1754 | | ); |
1755 | | assert_matches!( |
1756 | | p.parse(&[10, 2, 1, 0], false), |
1757 | | Ok(Chunk::Parsed { |
1758 | | consumed: 3, |
1759 | | payload: Payload::CodeSectionStart { count: 1, .. }, |
1760 | | }), |
1761 | | ); |
1762 | | assert_matches!( |
1763 | | p.parse(&[0], false), |
1764 | | Ok(Chunk::Parsed { |
1765 | | consumed: 1, |
1766 | | payload: Payload::CodeSectionEntry(_), |
1767 | | }), |
1768 | | ); |
1769 | | assert_matches!( |
1770 | | p.parse(&[], true), |
1771 | | Ok(Chunk::Parsed { |
1772 | | consumed: 0, |
1773 | | payload: Payload::End(16), |
1774 | | }), |
1775 | | ); |
1776 | | |
1777 | | // 1 byte section with 1 function can't read the function body because |
1778 | | // the section is too small |
1779 | | let mut p = parser_after_header(); |
1780 | | assert_matches!( |
1781 | | p.parse(&[3, 2, 1, 0], false), |
1782 | | Ok(Chunk::Parsed { |
1783 | | consumed: 4, |
1784 | | payload: Payload::FunctionSection { .. }, |
1785 | | }), |
1786 | | ); |
1787 | | assert_matches!( |
1788 | | p.parse(&[10, 1, 1], false), |
1789 | | Ok(Chunk::Parsed { |
1790 | | consumed: 3, |
1791 | | payload: Payload::CodeSectionStart { count: 1, .. }, |
1792 | | }), |
1793 | | ); |
1794 | | assert_eq!( |
1795 | | p.parse(&[0], false).unwrap_err().message(), |
1796 | | "unexpected end-of-file" |
1797 | | ); |
1798 | | |
1799 | | // section with 2 functions but section is cut off |
1800 | | let mut p = parser_after_header(); |
1801 | | assert_matches!( |
1802 | | p.parse(&[3, 2, 2, 0], false), |
1803 | | Ok(Chunk::Parsed { |
1804 | | consumed: 4, |
1805 | | payload: Payload::FunctionSection { .. }, |
1806 | | }), |
1807 | | ); |
1808 | | assert_matches!( |
1809 | | p.parse(&[10, 2, 2], false), |
1810 | | Ok(Chunk::Parsed { |
1811 | | consumed: 3, |
1812 | | payload: Payload::CodeSectionStart { count: 2, .. }, |
1813 | | }), |
1814 | | ); |
1815 | | assert_matches!( |
1816 | | p.parse(&[0], false), |
1817 | | Ok(Chunk::Parsed { |
1818 | | consumed: 1, |
1819 | | payload: Payload::CodeSectionEntry(_), |
1820 | | }), |
1821 | | ); |
1822 | | assert_matches!(p.parse(&[], false), Ok(Chunk::NeedMoreData(1))); |
1823 | | assert_eq!( |
1824 | | p.parse(&[0], false).unwrap_err().message(), |
1825 | | "unexpected end-of-file", |
1826 | | ); |
1827 | | |
1828 | | // trailing data is bad |
1829 | | let mut p = parser_after_header(); |
1830 | | assert_matches!( |
1831 | | p.parse(&[3, 2, 1, 0], false), |
1832 | | Ok(Chunk::Parsed { |
1833 | | consumed: 4, |
1834 | | payload: Payload::FunctionSection { .. }, |
1835 | | }), |
1836 | | ); |
1837 | | assert_matches!( |
1838 | | p.parse(&[10, 3, 1], false), |
1839 | | Ok(Chunk::Parsed { |
1840 | | consumed: 3, |
1841 | | payload: Payload::CodeSectionStart { count: 1, .. }, |
1842 | | }), |
1843 | | ); |
1844 | | assert_matches!( |
1845 | | p.parse(&[0], false), |
1846 | | Ok(Chunk::Parsed { |
1847 | | consumed: 1, |
1848 | | payload: Payload::CodeSectionEntry(_), |
1849 | | }), |
1850 | | ); |
1851 | | assert_eq!( |
1852 | | p.parse(&[0], false).unwrap_err().message(), |
1853 | | "trailing bytes at end of section", |
1854 | | ); |
1855 | | } |
1856 | | |
1857 | | #[test] |
1858 | | fn single_module() { |
1859 | | let mut p = parser_after_component_header(); |
1860 | | assert_matches!(p.parse(&[4], false), Ok(Chunk::NeedMoreData(1))); |
1861 | | |
1862 | | // A module that's 8 bytes in length |
1863 | | let mut sub = match p.parse(&[1, 8], false) { |
1864 | | Ok(Chunk::Parsed { |
1865 | | consumed: 2, |
1866 | | payload: Payload::ModuleSection { parser, .. }, |
1867 | | }) => parser, |
1868 | | other => panic!("bad parse {other:?}"), |
1869 | | }; |
1870 | | |
1871 | | // Parse the header of the submodule with the sub-parser. |
1872 | | assert_matches!(sub.parse(&[], false), Ok(Chunk::NeedMoreData(4))); |
1873 | | assert_matches!(sub.parse(b"\0asm", false), Ok(Chunk::NeedMoreData(4))); |
1874 | | assert_matches!( |
1875 | | sub.parse(b"\0asm\x01\0\0\0", false), |
1876 | | Ok(Chunk::Parsed { |
1877 | | consumed: 8, |
1878 | | payload: Payload::Version { |
1879 | | num: 1, |
1880 | | encoding: Encoding::Module, |
1881 | | .. |
1882 | | }, |
1883 | | }), |
1884 | | ); |
1885 | | |
1886 | | // The sub-parser should be byte-limited so the next byte shouldn't get |
1887 | | // consumed, it's intended for the parent parser. |
1888 | | assert_matches!( |
1889 | | sub.parse(&[10], false), |
1890 | | Ok(Chunk::Parsed { |
1891 | | consumed: 0, |
1892 | | payload: Payload::End(18), |
1893 | | }), |
1894 | | ); |
1895 | | |
1896 | | // The parent parser should now be back to resuming, and we simulate it |
1897 | | // being done with bytes to ensure that it's safely at the end, |
1898 | | // completing the module code section. |
1899 | | assert_matches!(p.parse(&[], false), Ok(Chunk::NeedMoreData(1))); |
1900 | | assert_matches!( |
1901 | | p.parse(&[], true), |
1902 | | Ok(Chunk::Parsed { |
1903 | | consumed: 0, |
1904 | | payload: Payload::End(18), |
1905 | | }), |
1906 | | ); |
1907 | | } |
1908 | | |
1909 | | #[test] |
1910 | | fn nested_section_too_big() { |
1911 | | let mut p = parser_after_component_header(); |
1912 | | |
1913 | | // A module that's 10 bytes in length |
1914 | | let mut sub = match p.parse(&[1, 10], false) { |
1915 | | Ok(Chunk::Parsed { |
1916 | | consumed: 2, |
1917 | | payload: Payload::ModuleSection { parser, .. }, |
1918 | | }) => parser, |
1919 | | other => panic!("bad parse {other:?}"), |
1920 | | }; |
1921 | | |
1922 | | // use 8 bytes to parse the header, leaving 2 remaining bytes in our |
1923 | | // module. |
1924 | | assert_matches!( |
1925 | | sub.parse(b"\0asm\x01\0\0\0", false), |
1926 | | Ok(Chunk::Parsed { |
1927 | | consumed: 8, |
1928 | | payload: Payload::Version { num: 1, .. }, |
1929 | | }), |
1930 | | ); |
1931 | | |
1932 | | // We can't parse a section which declares its bigger than the outer |
1933 | | // module. This is a custom section, one byte big, with one content byte. The |
1934 | | // content byte, however, lives outside of the parent's module code |
1935 | | // section. |
1936 | | assert!( |
1937 | | sub.parse(&[0, 1, 0], false) |
1938 | | .unwrap_err() |
1939 | | .message() |
1940 | | .starts_with("section too large") |
1941 | | ); |
1942 | | } |
1943 | | } |