From f28ea9c7cca56031e14932876964aeee66392b12 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Thu, 30 Nov 2023 22:59:03 +0100 Subject: feat: improve module instance api Signed-off-by: Henry Gressmann --- Cargo.toml | 2 +- crates/cli/bin.rs | 5 +- crates/tinywasm/src/module/mod.rs | 89 +++++++++++++++++++++++++++++------- crates/tinywasm/src/module/reader.rs | 6 +++ crates/tinywasm/src/store.rs | 3 +- 5 files changed, 84 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb4aa02..b363050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] -members=["crates/tinywasm", "crates/cli"] +members=["crates/*"] default-members=["crates/cli"] resolver="2" diff --git a/crates/cli/bin.rs b/crates/cli/bin.rs index b06c286..b085ae1 100644 --- a/crates/cli/bin.rs +++ b/crates/cli/bin.rs @@ -68,9 +68,10 @@ fn main() -> Result<()> { } fn run(wasm: &[u8]) -> Result<()> { - let module = tinywasm::Module::try_new(wasm)?; let mut store = tinywasm::Store::default(); - let instance = tinywasm::ModuleInstance::new(&mut store, &module)?; + + let module = tinywasm::Module::try_new(wasm)?; + let instance = module.instantiate(&mut store)?; let func = instance.get_func("add").unwrap(); println!("func: {:?}", func); diff --git a/crates/tinywasm/src/module/mod.rs b/crates/tinywasm/src/module/mod.rs index 4af1a9a..a50235a 100644 --- a/crates/tinywasm/src/module/mod.rs +++ b/crates/tinywasm/src/module/mod.rs @@ -1,9 +1,9 @@ -use alloc::vec::Vec; +use alloc::{format, vec, vec::Vec}; use wasmparser::{Export, FuncType, Validator}; use crate::{ runtime::{FuncAddr, ModuleFunc}, - Error, Result, Store, + Error, Result, Store, WasmValue, }; use self::reader::ModuleReader; @@ -11,8 +11,41 @@ use self::reader::ModuleReader; pub mod reader; #[derive(Debug)] -pub struct Module<'a> { - reader: ModuleReader<'a>, +pub struct Module<'data> { + reader: ModuleReader<'data>, +} + +impl<'data> Module<'data> { + pub fn try_new(wasm: &'data [u8]) -> Result> { + let mut validator = Validator::new(); + let mut reader = ModuleReader::new(); + + for payload in wasmparser::Parser::new(0).parse_all(wasm) { + reader.process_payload(payload?, &mut validator)?; + } + if !reader.end_reached { + return Error::other("End not reached"); + } + + Ok(Self { reader }) + } + + /// Instantiate the module in the given store + /// See https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation + /// Runs the start function if it exists + /// If you want to run the start function yourself, use `ModuleInstance::new` + pub fn instantiate<'m>( + &'m self, + store: &'data mut Store<'data>, + // imports: Option<()>, + ) -> Result> + where + 'm: 'data, + { + let i = ModuleInstance::new(store, &self)?; + let _ = i.start()?; + Ok(i) + } } /// A WebAssembly Module Instance. @@ -22,6 +55,7 @@ pub struct Module<'a> { pub struct ModuleInstance<'m, 'data> { pub(crate) module: &'m Module<'data>, + pub(crate) func_start: Option, pub(crate) types: Vec, pub(crate) func_addrs: Vec, // pub table_addrs: Vec, @@ -50,6 +84,15 @@ where }) } + pub fn get_start_func(&self) -> Option { + let func_addr = self.func_addrs.get(self.func_start? as usize)?; + + Some(ModuleFunc { + code: *func_addr, + ty: self.types.get(*func_addr as usize)?.clone(), + }) + } + pub fn new(store: &'data mut Store<'data>, module: &'m Module<'data>) -> Result { let types = module .reader @@ -92,11 +135,13 @@ where }) .transpose()? .unwrap_or_default(); + let func_start = module.reader.start_func; store.initialize(&module.reader)?; Ok(Self { module, types, + func_start, func_addrs, // table_addrs, // mem_addrs, @@ -106,20 +151,32 @@ where exports, }) } -} - -impl<'a> Module<'a> { - pub fn try_new(wasm: &'a [u8]) -> Result> { - let mut validator = Validator::new(); - let mut reader = ModuleReader::new(); - for payload in wasmparser::Parser::new(0).parse_all(wasm) { - reader.process_payload(payload?, &mut validator)?; - } - if !reader.end_reached { - return Error::other("End not reached"); + pub fn call(&self, func: ModuleFunc, args: &[WasmValue]) -> Result> { + let func_type = func.ty; + let params = func_type.params(); + if params.len() != args.len() { + return Error::other(&format!( + "Function expected {} arguments, got {}", + params.len(), + args.len() + )); } - Ok(Self { reader }) + // TODO + // runtime.call(func, args) + Ok(vec![]) + } + + /// Invoke the start function of the module + /// Returns None if the module has no start function + /// https://webassembly.github.io/spec/core/syntax/modules.html#syntax-start + pub fn start(&self) -> Result> { + let Some(func) = self.get_start_func() else { + return Ok(None); + }; + + let _ = self.call(func, &[])?; + Ok(Some(())) } } diff --git a/crates/tinywasm/src/module/reader.rs b/crates/tinywasm/src/module/reader.rs index 13a42a1..082296c 100644 --- a/crates/tinywasm/src/module/reader.rs +++ b/crates/tinywasm/src/module/reader.rs @@ -12,6 +12,7 @@ use crate::{Error, Result}; #[derive(Default)] pub struct ModuleReader<'a> { pub version: Option, + pub start_func: Option, pub type_section: Option>, pub function_section: Option>, @@ -70,6 +71,11 @@ impl<'a> ModuleReader<'a> { wasmparser::Encoding::Component => return Error::other("Component"), } } + StartSection { func, range } => { + debug!("Found start section"); + validator.start_section(func, &range)?; + self.start_func = Some(func); + } TypeSection(reader) => { debug!("Found type section"); validator.type_section(&reader)?; diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs index 617e9d9..ff303e1 100644 --- a/crates/tinywasm/src/store.rs +++ b/crates/tinywasm/src/store.rs @@ -1,14 +1,13 @@ use alloc::vec::Vec; use wasmparser::FunctionBody; -use crate::{module::reader::ModuleReader, runtime::Runtime, Result}; +use crate::{module::reader::ModuleReader, Result}; /// global state that can be manipulated by WebAssembly programs /// https://webassembly.github.io/spec/core/exec/runtime.html#store #[derive(Debug, Default)] pub struct Store<'data> { pub(crate) data: StoreData<'data>, - pub(crate) engine: Runtime, } #[derive(Debug, Default)] -- cgit v1.3.1