use super::{
CustomSectionsBuilder,
ModuleBuilder,
ModuleHeader,
ModuleHeaderBuilder,
ModuleParser,
};
use crate::{Error, Module};
use wasmparser::{Chunk, Payload, Validator};
impl ModuleParser {
/// Starts parsing and validating the Wasm bytecode stream.
///
/// Returns the compiled and validated Wasm [`Module`] upon success.
///
/// # Errors
///
/// If the Wasm bytecode stream fails to validate.
pub fn parse_buffered(mut self, buffer: &[u8]) -> Result<Module, Error> {
let features = self.engine.config().wasm_features();
self.validator = Some(Validator::new_with_features(features));
// SAFETY: we just pre-populated the Wasm module parser with a validator
// thus calling this method is safe.
unsafe { self.parse_buffered_impl(buffer) }
}
/// Starts parsing and validating the Wasm bytecode stream.
///
/// Returns the compiled and validated Wasm [`Module`] upon success.
///
/// # Safety
///
/// The caller is responsible to make sure that the provided
/// `stream` yields valid WebAssembly bytecode.
///
/// # Errors
///
/// If the Wasm bytecode stream fails to validate.
pub unsafe fn parse_buffered_unchecked(self, buffer: &[u8]) -> Result<Module, Error> {
unsafe { self.parse_buffered_impl(buffer) }
}
/// Starts parsing and validating the Wasm bytecode stream.
///
/// Returns the compiled and validated Wasm [`Module`] upon success.
///
/// # Safety
///
/// The caller is responsible to either
///
/// 1) Populate the [`ModuleParser`] with a [`Validator`] prior to calling this method, OR;
/// 2) Make sure that the provided `stream` yields valid WebAssembly bytecode.
///
/// Otherwise this method has undefined behavior.
///
/// # Errors
///
/// If the Wasm bytecode stream fails to validate.
unsafe fn parse_buffered_impl(mut self, mut buffer: &[u8]) -> Result<Module, Error> {
let mut custom_sections = CustomSectionsBuilder::default();
let header = Self::parse_buffered_header(&mut self, &mut buffer, &mut custom_sections)?;
let builder = Self::parse_buffered_code(&mut self, &mut buffer, header, custom_sections)?;
let module = Self::parse_buffered_data(&mut self, &mut buffer, builder)?;
Ok(module)
}
/// Fetch next Wasm module payload and adust the `buffer`.
///
/// # Errors
///
/// If the parsed Wasm is malformed.
fn next_payload<'a>(&mut self, buffer: &mut &'a [u8]) -> Result<(usize, Payload<'a>), Error> {
match self.parser.parse(&buffer[..], true)? {
Chunk::Parsed { consumed, payload } => Ok((consumed, payload)),
Chunk::NeedMoreData(_hint) => {
// This is not possible since `eof` is always true.
unreachable!()
}
}
}
/// Consumes the parts of the buffer that have been processed.
fn consume_buffer<'a>(consumed: usize, buffer: &mut &'a [u8]) -> &'a [u8] {
let (consumed, remaining) = buffer.split_at(consumed);
*buffer = remaining;
consumed
}
/// Parse the Wasm module header.
///
/// - The Wasm module header is the set of all sections that appear before
/// the Wasm code section.
/// - We separate parsing of the Wasm module header since the information of
/// the Wasm module header is required for translating the Wasm code section.
///
/// # Errors
///
/// If the Wasm bytecode stream fails to parse or validate.
fn parse_buffered_header(
&mut self,
buffer: &mut &[u8],
custom_sections: &mut CustomSectionsBuilder,
) -> Result<ModuleHeader, Error> {
let mut header = ModuleHeaderBuilder::new(&self.engine);
loop {
let (consumed, payload) = self.next_payload(buffer)?;
match payload {
Payload::Version {
num,
encoding,
range,
} => self.process_version(num, encoding, range),
Payload::TypeSection(section) => self.process_types(section, &mut header),
Payload::ImportSection(section) => self.process_imports(section, &mut header),
Payload::FunctionSection(section) => self.process_functions(section, &mut header),
Payload::TableSection(section) => self.process_tables(section, &mut header),
Payload::MemorySection(section) => self.process_memories(section, &mut header),
Payload::GlobalSection(section) => self.process_globals(section, &mut header),
Payload::ExportSection(section) => self.process_exports(section, &mut header),
Payload::StartSection { func, range } => {
self.process_start(func, range, &mut header)
}
Payload::ElementSection(section) => self.process_element(section, &mut header),
Payload::DataCountSection { count, range } => self.process_data_count(count, range),
Payload::CodeSectionStart { count, range, size } => {
self.process_code_start(count, range, size)?;
Self::consume_buffer(consumed, buffer);
break;
}
Payload::DataSection(_) => break,
Payload::End(_) => break,
Payload::CustomSection(reader) => {
self.process_custom_section(custom_sections, reader)
}
unexpected => self.process_invalid_payload(unexpected),
}?;
Self::consume_buffer(consumed, buffer);
}
Ok(header.finish())
}
/// Parse the Wasm code section entries.
///
/// We separate parsing of the Wasm code section since most of a Wasm module
/// is made up of code section entries which we can parse and validate more efficiently
/// by serving them with a specialized routine.
///
/// # Errors
///
/// If the Wasm bytecode stream fails to parse or validate.
fn parse_buffered_code(
&mut self,
buffer: &mut &[u8],
header: ModuleHeader,
custom_sections: CustomSectionsBuilder,
) -> Result<ModuleBuilder, Error> {
loop {
let (consumed, payload) = self.next_payload(buffer)?;
match payload {
Payload::CodeSectionEntry(func_body) => {
// Note: Unfortunately the `wasmparser` crate is missing an API
// to return the byte slice for the respective code section
// entry payload. Please remove this work around as soon as
// such an API becomes available.
Self::consume_buffer(consumed, buffer);
let bytes = func_body.as_bytes();
self.process_code_entry(func_body, bytes, &header)?;
}
_ => break,
}
}
Ok(ModuleBuilder::new(header, custom_sections))
}
/// Parse the Wasm data section and finalize parsing.
///
/// We separate parsing of the Wasm data section since it is the only Wasm
/// section that comes after the Wasm code section that we have to separate
/// out for technical reasons.
///
/// # Errors
///
/// If the Wasm bytecode stream fails to parse or validate.
fn parse_buffered_data(
&mut self,
buffer: &mut &[u8],
mut builder: ModuleBuilder,
) -> Result<Module, Error> {
loop {
let (consumed, payload) = self.next_payload(buffer)?;
match payload {
Payload::DataSection(section) => {
self.process_data(section, &mut builder)?;
}
Payload::End(offset) => {
self.process_end(offset)?;
break;
}
Payload::CustomSection(reader) => {
self.process_custom_section(&mut builder.custom_sections, reader)?;
}
invalid => self.process_invalid_payload(invalid)?,
}
Self::consume_buffer(consumed, buffer);
}
Ok(builder.finish(&self.engine))
}
}
use ;
use crate::;
use ;