wasmparser/
readers.rs

1/* Copyright 2018 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16use crate::{BinaryReader, BinaryReaderError, Result};
17use ::core::fmt;
18use ::core::marker;
19use ::core::ops::Range;
20
21#[cfg(feature = "component-model")]
22mod component;
23mod core;
24
25#[cfg(feature = "component-model")]
26pub use self::component::*;
27pub use self::core::*;
28
29/// A trait implemented for items that can be decoded directly from a
30/// `BinaryReader`, or that which can be parsed from the WebAssembly binary
31/// format.
32///
33/// Note that this is also accessible as a [`BinaryReader::read`] method.
34pub trait FromReader<'a>: Sized {
35    /// Attempts to read `Self` from the provided binary reader, returning an
36    /// error if it is unable to do so.
37    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self>;
38}
39
40impl<'a> FromReader<'a> for bool {
41    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
42        match reader.read_u8()? {
43            0 => Ok(false),
44            1 => Ok(true),
45            _ => Err(BinaryReaderError::new(
46                "invalid boolean value",
47                reader.original_position() - 1,
48            )),
49        }
50    }
51}
52
53impl<'a> FromReader<'a> for u32 {
54    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
55        reader.read_var_u32()
56    }
57}
58
59impl<'a> FromReader<'a> for &'a str {
60    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
61        reader.read_string()
62    }
63}
64
65impl<'a, T, U> FromReader<'a> for (T, U)
66where
67    T: FromReader<'a>,
68    U: FromReader<'a>,
69{
70    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
71        Ok((reader.read()?, reader.read()?))
72    }
73}
74
75/// A generic structure for reading a section of a WebAssembly binary which has
76/// a limited number of items within it.
77///
78/// Many WebAssembly sections are a count of items followed by that many items.
79/// This helper structure can be used to parse these sections and provides
80/// an iteration-based API for reading the contents.
81///
82/// Note that this always implements the [`Clone`] trait to represent the
83/// ability to parse the section multiple times.
84pub struct SectionLimited<'a, T> {
85    reader: BinaryReader<'a>,
86    count: u32,
87    _marker: marker::PhantomData<T>,
88}
89
90impl<'a, T> SectionLimited<'a, T> {
91    /// Creates a new section reader from the provided contents.
92    ///
93    /// The `data` provided here is the data of the section itself that will be
94    /// parsed. The `offset` argument is the byte offset, in the original wasm
95    /// binary, that the section was found. The `offset` argument is used
96    /// for error reporting.
97    ///
98    /// # Errors
99    ///
100    /// Returns an error if a 32-bit count couldn't be read from the `data`.
101    pub fn new(mut reader: BinaryReader<'a>) -> Result<Self> {
102        let count = reader.read_var_u32()?;
103        Ok(SectionLimited {
104            reader,
105            count,
106            _marker: marker::PhantomData,
107        })
108    }
109
110    /// Returns the count of total items within this section.
111    pub fn count(&self) -> u32 {
112        self.count
113    }
114
115    /// Returns whether the original byte offset of this section.
116    pub fn original_position(&self) -> usize {
117        self.reader.original_position()
118    }
119
120    /// Returns the range, as byte offsets, of this section within the original
121    /// wasm binary.
122    pub fn range(&self) -> Range<usize> {
123        self.reader.range()
124    }
125
126    /// Returns an iterator which yields not only each item in this section but
127    /// additionally the offset of each item within the section.
128    pub fn into_iter_with_offsets(self) -> SectionLimitedIntoIterWithOffsets<'a, T>
129    where
130        T: FromReader<'a>,
131    {
132        SectionLimitedIntoIterWithOffsets {
133            iter: self.into_iter(),
134        }
135    }
136}
137
138impl<T> Clone for SectionLimited<'_, T> {
139    fn clone(&self) -> Self {
140        SectionLimited {
141            reader: self.reader.clone(),
142            count: self.count,
143            _marker: self._marker,
144        }
145    }
146}
147
148impl<T> fmt::Debug for SectionLimited<'_, T> {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.debug_struct("SectionLimited")
151            .field("count", &self.count)
152            .field("range", &self.range())
153            .finish()
154    }
155}
156
157impl<'a, T> IntoIterator for SectionLimited<'a, T>
158where
159    T: FromReader<'a>,
160{
161    type Item = Result<T>;
162    type IntoIter = SectionLimitedIntoIter<'a, T>;
163
164    fn into_iter(self) -> Self::IntoIter {
165        SectionLimitedIntoIter {
166            remaining: self.count,
167            section: self,
168            end: false,
169        }
170    }
171}
172
173/// A consuming iterator of a [`SectionLimited`].
174///
175/// This is created via the [`IntoIterator`] `impl` for the [`SectionLimited`]
176/// type.
177pub struct SectionLimitedIntoIter<'a, T> {
178    section: SectionLimited<'a, T>,
179    remaining: u32,
180    end: bool,
181}
182
183impl<T> SectionLimitedIntoIter<'_, T> {
184    /// Returns the current byte offset of the section within this iterator.
185    pub fn original_position(&self) -> usize {
186        self.section.reader.original_position()
187    }
188}
189
190impl<'a, T> Iterator for SectionLimitedIntoIter<'a, T>
191where
192    T: FromReader<'a>,
193{
194    type Item = Result<T>;
195
196    fn next(&mut self) -> Option<Result<T>> {
197        if self.end {
198            return None;
199        }
200        if self.remaining == 0 {
201            self.end = true;
202            if self.section.reader.eof() {
203                return None;
204            }
205            return Some(Err(BinaryReaderError::new(
206                "section size mismatch: unexpected data at the end of the section",
207                self.section.reader.original_position(),
208            )));
209        }
210        let result = self.section.reader.read();
211        self.end = result.is_err();
212        self.remaining -= 1;
213        Some(result)
214    }
215
216    fn size_hint(&self) -> (usize, Option<usize>) {
217        let remaining = self.remaining as usize;
218        (remaining, Some(remaining))
219    }
220}
221
222impl<'a, T> ExactSizeIterator for SectionLimitedIntoIter<'a, T> where T: FromReader<'a> {}
223
224/// An iterator over a limited section iterator.
225pub struct SectionLimitedIntoIterWithOffsets<'a, T> {
226    iter: SectionLimitedIntoIter<'a, T>,
227}
228
229impl<'a, T> Iterator for SectionLimitedIntoIterWithOffsets<'a, T>
230where
231    T: FromReader<'a>,
232{
233    type Item = Result<(usize, T)>;
234
235    fn next(&mut self) -> Option<Self::Item> {
236        let pos = self.iter.section.reader.original_position();
237        Some(self.iter.next()?.map(|item| (pos, item)))
238    }
239
240    fn size_hint(&self) -> (usize, Option<usize>) {
241        self.iter.size_hint()
242    }
243}
244
245impl<'a, T> ExactSizeIterator for SectionLimitedIntoIterWithOffsets<'a, T> where T: FromReader<'a> {}
246
247/// A trait implemented for subsections of another outer section.
248///
249/// This is currently only used for subsections within custom sections, such as
250/// the `name` section of core wasm.
251///
252/// This is used in conjunction with [`Subsections`].
253pub trait Subsection<'a>: Sized {
254    /// Converts the section identifier provided with the section contents into
255    /// a typed section
256    fn from_reader(id: u8, reader: BinaryReader<'a>) -> Result<Self>;
257}
258
259/// Iterator/reader over the contents of a section which is composed of
260/// subsections.
261///
262/// This reader is used for the core `name` section, for example. This type
263/// primarily implements [`Iterator`] for advancing through the sections.
264pub struct Subsections<'a, T> {
265    reader: BinaryReader<'a>,
266    _marker: marker::PhantomData<T>,
267}
268
269impl<'a, T> Subsections<'a, T> {
270    /// Creates a new reader for the specified section contents starting at
271    /// `offset` within the original wasm file.
272    pub fn new(reader: BinaryReader<'a>) -> Self {
273        Subsections {
274            reader,
275            _marker: marker::PhantomData,
276        }
277    }
278
279    /// Returns whether the original byte offset of this section.
280    pub fn original_position(&self) -> usize {
281        self.reader.original_position()
282    }
283
284    /// Returns the range, as byte offsets, of this section within the original
285    /// wasm binary.
286    pub fn range(&self) -> Range<usize> {
287        self.reader.range()
288    }
289
290    fn read(&mut self) -> Result<T>
291    where
292        T: Subsection<'a>,
293    {
294        let subsection_id = self.reader.read_u7()?;
295        let reader = self.reader.read_reader()?;
296        T::from_reader(subsection_id, reader)
297    }
298}
299
300impl<T> Clone for Subsections<'_, T> {
301    fn clone(&self) -> Self {
302        Subsections {
303            reader: self.reader.clone(),
304            _marker: self._marker,
305        }
306    }
307}
308
309impl<T> fmt::Debug for Subsections<'_, T> {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        f.debug_struct("Subsections")
312            .field("range", &self.range())
313            .finish()
314    }
315}
316
317impl<'a, T> Iterator for Subsections<'a, T>
318where
319    T: Subsection<'a>,
320{
321    type Item = Result<T>;
322
323    fn next(&mut self) -> Option<Result<T>> {
324        if self.reader.eof() {
325            None
326        } else {
327            Some(self.read())
328        }
329    }
330}