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