| /* Copyright 2018 Mozilla Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| use crate::{BinaryReader, BinaryReaderError, Result}; |
| use ::core::fmt; |
| use ::core::marker; |
| use ::core::ops::Range; |
| |
| #[cfg(feature = "component-model")] |
| mod component; |
| mod core; |
| |
| #[cfg(feature = "component-model")] |
| pub use self::component::*; |
| pub use self::core::*; |
| |
| /// A trait implemented for items that can be decoded directly from a |
| /// `BinaryReader`, or that which can be parsed from the WebAssembly binary |
| /// format. |
| /// |
| /// Note that this is also accessible as a [`BinaryReader::read`] method. |
| pub trait FromReader<'a>: Sized { |
| /// Attempts to read `Self` from the provided binary reader, returning an |
| /// error if it is unable to do so. |
| fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self>; |
| } |
| |
| impl<'a> FromReader<'a> for u32 { |
| fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
| reader.read_var_u32() |
| } |
| } |
| |
| impl<'a> FromReader<'a> for &'a str { |
| fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
| reader.read_string() |
| } |
| } |
| |
| impl<'a, T, U> FromReader<'a> for (T, U) |
| where |
| T: FromReader<'a>, |
| U: FromReader<'a>, |
| { |
| fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
| Ok((reader.read()?, reader.read()?)) |
| } |
| } |
| |
| /// A generic structure for reading a section of a WebAssembly binary which has |
| /// a limited number of items within it. |
| /// |
| /// Many WebAssembly sections are a count of items followed by that many items. |
| /// This helper structure can be used to parse these sections and provides |
| /// an iteration-based API for reading the contents. |
| /// |
| /// Note that this always implements the [`Clone`] trait to represent the |
| /// ability to parse the section multiple times. |
| pub struct SectionLimited<'a, T> { |
| reader: BinaryReader<'a>, |
| count: u32, |
| _marker: marker::PhantomData<T>, |
| } |
| |
| impl<'a, T> SectionLimited<'a, T> { |
| /// Creates a new section reader from the provided contents. |
| /// |
| /// The `data` provided here is the data of the section itself that will be |
| /// parsed. The `offset` argument is the byte offset, in the original wasm |
| /// binary, that the section was found. The `offset` argument is used |
| /// for error reporting. |
| /// |
| /// # Errors |
| /// |
| /// Returns an error if a 32-bit count couldn't be read from the `data`. |
| pub fn new(mut reader: BinaryReader<'a>) -> Result<Self> { |
| let count = reader.read_var_u32()?; |
| Ok(SectionLimited { |
| reader, |
| count, |
| _marker: marker::PhantomData, |
| }) |
| } |
| |
| /// Returns the count of total items within this section. |
| pub fn count(&self) -> u32 { |
| self.count |
| } |
| |
| /// Returns whether the original byte offset of this section. |
| pub fn original_position(&self) -> usize { |
| self.reader.original_position() |
| } |
| |
| /// Returns the range, as byte offsets, of this section within the original |
| /// wasm binary. |
| pub fn range(&self) -> Range<usize> { |
| self.reader.range() |
| } |
| |
| /// Returns an iterator which yields not only each item in this section but |
| /// additionally the offset of each item within the section. |
| pub fn into_iter_with_offsets(self) -> SectionLimitedIntoIterWithOffsets<'a, T> |
| where |
| T: FromReader<'a>, |
| { |
| SectionLimitedIntoIterWithOffsets { |
| iter: self.into_iter(), |
| } |
| } |
| } |
| |
| impl<T> Clone for SectionLimited<'_, T> { |
| fn clone(&self) -> Self { |
| SectionLimited { |
| reader: self.reader.clone(), |
| count: self.count, |
| _marker: self._marker, |
| } |
| } |
| } |
| |
| impl<T> fmt::Debug for SectionLimited<'_, T> { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.debug_struct("SectionLimited") |
| .field("count", &self.count) |
| .field("range", &self.range()) |
| .finish() |
| } |
| } |
| |
| impl<'a, T> IntoIterator for SectionLimited<'a, T> |
| where |
| T: FromReader<'a>, |
| { |
| type Item = Result<T>; |
| type IntoIter = SectionLimitedIntoIter<'a, T>; |
| |
| fn into_iter(self) -> Self::IntoIter { |
| SectionLimitedIntoIter { |
| remaining: self.count, |
| section: self, |
| end: false, |
| } |
| } |
| } |
| |
| /// A consuming iterator of a [`SectionLimited`]. |
| /// |
| /// This is created via the [`IntoIterator`] `impl` for the [`SectionLimited`] |
| /// type. |
| pub struct SectionLimitedIntoIter<'a, T> { |
| section: SectionLimited<'a, T>, |
| remaining: u32, |
| end: bool, |
| } |
| |
| impl<T> SectionLimitedIntoIter<'_, T> { |
| /// Returns the current byte offset of the section within this iterator. |
| pub fn original_position(&self) -> usize { |
| self.section.reader.original_position() |
| } |
| } |
| |
| impl<'a, T> Iterator for SectionLimitedIntoIter<'a, T> |
| where |
| T: FromReader<'a>, |
| { |
| type Item = Result<T>; |
| |
| fn next(&mut self) -> Option<Result<T>> { |
| if self.end { |
| return None; |
| } |
| if self.remaining == 0 { |
| self.end = true; |
| if self.section.reader.eof() { |
| return None; |
| } |
| return Some(Err(BinaryReaderError::new( |
| "section size mismatch: unexpected data at the end of the section", |
| self.section.reader.original_position(), |
| ))); |
| } |
| let result = self.section.reader.read(); |
| self.end = result.is_err(); |
| self.remaining -= 1; |
| Some(result) |
| } |
| |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| let remaining = self.remaining as usize; |
| (remaining, Some(remaining)) |
| } |
| } |
| |
| impl<'a, T> ExactSizeIterator for SectionLimitedIntoIter<'a, T> where T: FromReader<'a> {} |
| |
| /// An iterator over a limited section iterator. |
| pub struct SectionLimitedIntoIterWithOffsets<'a, T> { |
| iter: SectionLimitedIntoIter<'a, T>, |
| } |
| |
| impl<'a, T> Iterator for SectionLimitedIntoIterWithOffsets<'a, T> |
| where |
| T: FromReader<'a>, |
| { |
| type Item = Result<(usize, T)>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| let pos = self.iter.section.reader.original_position(); |
| Some(self.iter.next()?.map(|item| (pos, item))) |
| } |
| |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| self.iter.size_hint() |
| } |
| } |
| |
| impl<'a, T> ExactSizeIterator for SectionLimitedIntoIterWithOffsets<'a, T> where T: FromReader<'a> {} |
| |
| /// A trait implemented for subsections of another outer section. |
| /// |
| /// This is currently only used for subsections within custom sections, such as |
| /// the `name` section of core wasm. |
| /// |
| /// This is used in conjunction with [`Subsections`]. |
| pub trait Subsection<'a>: Sized { |
| /// Converts the section identifier provided with the section contents into |
| /// a typed section |
| fn from_reader(id: u8, reader: BinaryReader<'a>) -> Result<Self>; |
| } |
| |
| /// Iterator/reader over the contents of a section which is composed of |
| /// subsections. |
| /// |
| /// This reader is used for the core `name` section, for example. This type |
| /// primarily implements [`Iterator`] for advancing through the sections. |
| pub struct Subsections<'a, T> { |
| reader: BinaryReader<'a>, |
| _marker: marker::PhantomData<T>, |
| } |
| |
| impl<'a, T> Subsections<'a, T> { |
| /// Creates a new reader for the specified section contents starting at |
| /// `offset` within the original wasm file. |
| pub fn new(reader: BinaryReader<'a>) -> Self { |
| Subsections { |
| reader, |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| /// Returns whether the original byte offset of this section. |
| pub fn original_position(&self) -> usize { |
| self.reader.original_position() |
| } |
| |
| /// Returns the range, as byte offsets, of this section within the original |
| /// wasm binary. |
| pub fn range(&self) -> Range<usize> { |
| self.reader.range() |
| } |
| |
| fn read(&mut self) -> Result<T> |
| where |
| T: Subsection<'a>, |
| { |
| let subsection_id = self.reader.read_u7()?; |
| let reader = self.reader.read_reader()?; |
| T::from_reader(subsection_id, reader) |
| } |
| } |
| |
| impl<T> Clone for Subsections<'_, T> { |
| fn clone(&self) -> Self { |
| Subsections { |
| reader: self.reader.clone(), |
| _marker: self._marker, |
| } |
| } |
| } |
| |
| impl<T> fmt::Debug for Subsections<'_, T> { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.debug_struct("Subsections") |
| .field("range", &self.range()) |
| .finish() |
| } |
| } |
| |
| impl<'a, T> Iterator for Subsections<'a, T> |
| where |
| T: Subsection<'a>, |
| { |
| type Item = Result<T>; |
| |
| fn next(&mut self) -> Option<Result<T>> { |
| if self.reader.eof() { |
| None |
| } else { |
| Some(self.read()) |
| } |
| } |
| } |