Skip to content

Commit

Permalink
Add non-streaming Wasm module creation (#1035)
Browse files Browse the repository at this point in the history
* rename Module::new[_unchecked] to new_streaming[_unchecked]

* remove unnecessary pattern matches

We are guaranteed that only Payload::CodeSectionEntry follows until the code section is done.

* fix doc test

* clean-up module parsing

* properly process invalid payloads everywhere needed

* move streaming parser into its own submodule

* re-rename ModuleStreamingParser back to ModuleParser

It will be handling both streaming and buffered Wasm module parsing.

* update docs of streaming parsing

* remove convenience methods

* update docs

* add non-streaming Wasm module creation

* fix internal doc links

* use non-streaming Module creation everywhere

It should be the new default when applicable.

* fix bug in non-streaming parser

* apply clippy suggestions

* fix buffered parsing

* apply more clippy suggestions

* test streaming Wasm module compilation in spec testsuite
  • Loading branch information
Robbepop committed May 13, 2024
1 parent 9c7b899 commit 0288016
Show file tree
Hide file tree
Showing 12 changed files with 602 additions and 375 deletions.
2 changes: 1 addition & 1 deletion crates/cli/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl Context {
config.compilation_mode(compilation_mode);
let engine = wasmi::Engine::new(&config);
let wasm_bytes = utils::read_wasm_or_wat(wasm_file)?;
let module = wasmi::Module::new(&engine, &mut &wasm_bytes[..]).map_err(|error| {
let module = wasmi::Module::new(&engine, &wasm_bytes[..]).map_err(|error| {
anyhow!("failed to parse and validate Wasm module {wasm_file:?}: {error}")
})?;
let mut store = wasmi::Store::new(&engine, wasi_ctx);
Expand Down
84 changes: 64 additions & 20 deletions crates/wasmi/src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use self::{
export::ExternIdx,
global::Global,
import::{ExternTypeIdx, Import},
parser::{parse, parse_unchecked},
parser::ModuleParser,
};
pub(crate) use self::{
data::{DataSegment, DataSegments, InitDataSegment, PassiveDataSegmentBytes},
Expand Down Expand Up @@ -186,49 +186,93 @@ impl ModuleImports {
}

impl Module {
/// Creates a new Wasm [`Module`] from the given byte stream.
/// Creates a new Wasm [`Module`] from the given Wasm bytecode buffer.
///
/// # Note
///
/// This parses, validates and translates the buffered Wasm bytecode.
///
/// # Errors
///
/// - If the Wasm bytecode is malformed or fails to validate.
/// - If the Wasm bytecode violates restrictions
/// set in the [`Config`] used by the `engine`.
/// - If Wasmi cannot translate the Wasm bytecode.
///
/// [`Config`]: crate::Config
pub fn new(engine: &Engine, wasm: &[u8]) -> Result<Self, Error> {
ModuleParser::new(engine).parse_buffered(wasm)
}

/// Creates a new Wasm [`Module`] from the given Wasm bytecode stream.
///
/// # Note
///
/// This parses, validates and translates the Wasm bytecode yielded by `stream`.
///
/// # Errors
///
/// - If the `stream` cannot be parsed as a valid Wasm module.
/// - If the Wasm bytecode yielded by `stream` is not valid.
/// - If the Wasm bytecode yielded by `stream` violates restrictions
/// - If the Wasm bytecode is malformed or fails to validate.
/// - If the Wasm bytecode violates restrictions
/// set in the [`Config`] used by the `engine`.
/// - If Wasmi cannot translate the Wasm bytecode yielded by `stream`.
/// - If Wasmi cannot translate the Wasm bytecode.
///
/// [`Config`]: crate::Config
pub fn new_streaming(engine: &Engine, stream: impl Read) -> Result<Self, Error> {
ModuleParser::new(engine).parse_streaming(stream)
}

/// Creates a new Wasm [`Module`] from the given Wasm bytecode buffer.
///
/// # Note
///
/// This parses and translates the buffered Wasm bytecode.
///
/// # Safety
///
/// - This does _not_ validate the Wasm bytecode.
/// - It is the caller's responsibility that the Wasm bytecode is valid.
/// - It is the caller's responsibility that the Wasm bytecode adheres
/// to the restrictions set by the used [`Config`] of the `engine`.
/// - Violating the above rules is undefined behavior.
///
/// # Errors
///
/// - If the Wasm bytecode is malformed or contains invalid sections.
/// - If the Wasm bytecode fails to be compiled by Wasmi.
///
/// [`Config`]: crate::Config
pub fn new(engine: &Engine, stream: impl Read) -> Result<Self, Error> {
parse(engine, stream).map_err(Into::into)
pub unsafe fn new_unchecked(engine: &Engine, wasm: &[u8]) -> Result<Self, Error> {
let parser = ModuleParser::new(engine);
unsafe { parser.parse_buffered_unchecked(wasm) }
}

/// Creates a new Wasm [`Module`] from the given byte stream.
///
/// # Note
///
/// - This parses and translates the Wasm bytecode yielded by `stream`.
/// - This still validates Wasm bytecode outside of function bodies.
/// This parses and translates the Wasm bytecode yielded by `stream`.
///
/// # Safety
///
/// - This does _not_ fully validate the Wasm bytecode yielded by `stream`.
/// - It is the caller's responsibility to call this function only with
/// a `stream` that yields fully valid Wasm bytecode.
/// - Additionally it is the caller's responsibility that the Wasm bytecode
/// yielded by `stream` must adhere to the restrictions set by the used
/// [`Config`] of the `engine`.
/// - Violating these rules may lead to undefined behavior.
/// - This does _not_ validate the Wasm bytecode.
/// - It is the caller's responsibility that the Wasm bytecode is valid.
/// - It is the caller's responsibility that the Wasm bytecode adheres
/// to the restrictions set by the used [`Config`] of the `engine`.
/// - Violating the above rules is undefined behavior.
///
/// # Errors
///
/// If the `stream` cannot be parsed as a valid Wasm module.
/// - If the Wasm bytecode is malformed or contains invalid sections.
/// - If the Wasm bytecode fails to be compiled by Wasmi.
///
/// [`Config`]: crate::Config
pub unsafe fn new_unchecked(engine: &Engine, stream: impl Read) -> Result<Self, Error> {
unsafe { parse_unchecked(engine, stream).map_err(Into::into) }
pub unsafe fn new_streaming_unchecked(
engine: &Engine,
stream: impl Read,
) -> Result<Self, Error> {
let parser = ModuleParser::new(engine);
unsafe { parser.parse_streaming_unchecked(stream) }
}

/// Returns the [`Engine`] used during creation of the [`Module`].
Expand Down
Loading

0 comments on commit 0288016

Please sign in to comment.