diff options
Diffstat (limited to 'third_party/rust/wasmparser/src/readers.rs')
-rw-r--r-- | third_party/rust/wasmparser/src/readers.rs | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/third_party/rust/wasmparser/src/readers.rs b/third_party/rust/wasmparser/src/readers.rs new file mode 100644 index 0000000000..e2b25da7cf --- /dev/null +++ b/third_party/rust/wasmparser/src/readers.rs @@ -0,0 +1,316 @@ +/* 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 std::fmt; +use std::marker; +use std::ops::Range; + +mod component; +mod core; + +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(data: &'a [u8], offset: usize) -> Result<Self> { + let mut reader = BinaryReader::new_with_offset(data, offset); + 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(data: &'a [u8], offset: usize) -> Self { + Subsections { + reader: BinaryReader::new_with_offset(data, offset), + _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("unexpected end of section")?; + 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()) + } + } +} |