Revision control
Copy as Markdown
Other Tools
/* 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
*
*
* 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())
}
}
}