From e667ae4554e816b4081d874fc3564b07c28dbec6 Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 19 Apr 2026 17:48:32 +0200 Subject: feat: simplify module api Signed-off-by: Henry --- crates/cli/src/bin.rs | 8 +- crates/parser/src/lib.rs | 36 +++- crates/parser/src/module.rs | 83 ++++---- crates/tinywasm/benches/argon2id.rs | 15 +- crates/tinywasm/benches/fibonacci.rs | 15 +- crates/tinywasm/benches/tinywasm.rs | 16 +- crates/tinywasm/benches/tinywasm_modes.rs | 19 +- crates/tinywasm/src/imports.rs | 12 +- crates/tinywasm/src/instance.rs | 80 ++++++-- crates/tinywasm/src/interpreter/executor.rs | 9 +- crates/tinywasm/src/lib.rs | 17 +- crates/tinywasm/src/module.rs | 179 ---------------- crates/tinywasm/src/store/function.rs | 8 +- crates/tinywasm/src/store/mod.rs | 5 +- crates/tinywasm/tests/host_func_signature_check.rs | 10 +- crates/tinywasm/tests/import_linking.rs | 14 +- crates/tinywasm/tests/imported_table_init.rs | 6 +- crates/tinywasm/tests/internal_refs.rs | 14 +- crates/tinywasm/tests/memory_backends.rs | 16 +- crates/tinywasm/tests/memory_ref_api.rs | 6 +- crates/tinywasm/tests/module_descriptors.rs | 4 +- crates/tinywasm/tests/resume_execution.rs | 23 ++- crates/tinywasm/tests/store_ownership.rs | 10 +- crates/tinywasm/tests/testsuite/run.rs | 12 +- crates/tinywasm/tests/testsuite/util.rs | 10 +- crates/tinywasm/tests/typed_lookup.rs | 10 +- crates/types/Cargo.toml | 2 +- crates/types/src/archive.rs | 18 +- crates/types/src/lib.rs | 226 ++++++++++++++------- 29 files changed, 420 insertions(+), 463 deletions(-) delete mode 100644 crates/tinywasm/src/module.rs (limited to 'crates') diff --git a/crates/cli/src/bin.rs b/crates/cli/src/bin.rs index 5fd50f9..9b283c3 100644 --- a/crates/cli/src/bin.rs +++ b/crates/cli/src/bin.rs @@ -4,7 +4,7 @@ use argh::FromArgs; use args::WasmArg; use eyre::Result; use log::{debug, info}; -use tinywasm::{Module, types::WasmValue}; +use tinywasm::{Module, ModuleInstance, types::WasmValue}; use crate::args::to_wasm_args; mod args; @@ -90,11 +90,11 @@ fn main() -> Result<()> { true => { let wat = std::fs::read_to_string(path)?; let wasm = wat::wat2wasm(&wat); - tinywasm::Module::parse_bytes(&wasm)? + tinywasm::parse_bytes(&wasm)? } #[cfg(not(feature = "wat"))] true => return Err(eyre::eyre!("wat support is not enabled in this build")), - false => tinywasm::Module::parse_file(path)?, + false => tinywasm::parse_file(path)?, }; match engine { @@ -106,7 +106,7 @@ fn main() -> Result<()> { fn run(module: Module, func: Option, args: &[WasmValue]) -> Result<()> { let mut store = tinywasm::Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; if let Some(func) = func { let func = instance.func_untyped(&store, &func)?; diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 0b5af2e..7b3dfa2 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -39,7 +39,7 @@ pub use error::*; use module::ModuleReader; use wasmparser::{Validator, WasmFeatures}; -pub use tinywasm_types::TinyWasmModule; +pub use tinywasm_types::Module; /// Parser optimization and lowering options. #[non_exhaustive] @@ -113,8 +113,8 @@ impl Parser { Validator::new_with_features(features) } - /// Parse a [`TinyWasmModule`] from bytes - pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result { + /// Parse a [`Module`] from bytes + pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result { let wasm = wasm.as_ref(); let mut validator = Self::create_validator(self.options.clone()); let mut reader = ModuleReader::default(); @@ -131,16 +131,16 @@ impl Parser { } #[cfg(feature = "std")] - /// Parse a [`TinyWasmModule`] from a file. Requires `std` feature. - pub fn parse_module_file(&self, path: impl AsRef + Clone) -> Result { + /// Parse a [`Module`] from a file. Requires `std` feature. + pub fn parse_module_file(&self, path: impl AsRef + Clone) -> Result { let file = crate::std::fs::File::open(&path) .map_err(|e| ParseError::Other(alloc::format!("Error opening file {:?}: {}", path.as_ref(), e)))?; self.parse_module_stream(&mut crate::std::io::BufReader::new(file)) } #[cfg(feature = "std")] - /// Parse a [`TinyWasmModule`] from a stream. Requires `std` feature. - pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result { + /// Parse a [`Module`] from a stream. Requires `std` feature. + pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result { let mut validator = Self::create_validator(self.options.clone()); let mut reader = ModuleReader::default(); let mut buffer = alloc::vec::Vec::new(); @@ -170,10 +170,30 @@ impl Parser { } } -impl TryFrom for TinyWasmModule { +impl TryFrom for Module { type Error = ParseError; fn try_from(reader: ModuleReader) -> Result { reader.into_module(&ParserOptions::default()) } } + +/// Parse a module from bytes +pub fn parse_bytes(wasm: &[u8]) -> Result { + let data = Parser::new().parse_module_bytes(wasm)?; + Ok(data) +} + +#[cfg(feature = "std")] +/// Parse a module from a file. Requires the `std` feature. +pub fn parse_file(path: impl AsRef + Clone) -> Result { + let data = Parser::new().parse_module_file(path)?; + Ok(data) +} + +#[cfg(feature = "std")] +/// Parse a module from a stream. Requires `parser` and `std` features. +pub fn parse_stream(stream: impl crate::std::io::Read) -> Result { + let data = Parser::new().parse_module_stream(stream)?; + Ok(data) +} diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 9c4d6b4..47257fe 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -1,5 +1,6 @@ use crate::log::debug; use crate::{ParseError, ParserOptions, Result, conversion, optimize}; +use alloc::sync::Arc; use alloc::{format, string::ToString, vec::Vec}; use tinywasm_types::*; use wasmparser::{FuncValidatorAllocations, Payload, Validator}; @@ -12,7 +13,7 @@ pub(crate) struct ModuleReader { pub(crate) version: Option, pub(crate) start_func: Option, - pub(crate) func_types: Vec, + pub(crate) func_types: Vec>, pub(crate) code_type_addrs: Vec, pub(crate) exports: Vec, pub(crate) code: Vec, @@ -53,8 +54,8 @@ impl ModuleReader { validator.type_section(&reader)?; self.func_types = reader .into_iter() - .map(|t| conversion::convert_module_type(t?)) - .collect::>>()?; + .map(|t| conversion::convert_module_type(t?).map(Arc::new)) + .collect::>>>()?; } Payload::GlobalSection(reader) => { @@ -167,7 +168,7 @@ impl ModuleReader { Ok(()) } - pub(crate) fn into_module(self, options: &ParserOptions) -> Result { + pub(crate) fn into_module(self, options: &ParserOptions) -> Result { if !self.end_reached { return Err(ParseError::EndNotReached); } @@ -176,73 +177,57 @@ impl ModuleReader { return Err(ParseError::Other("Code and code type address count mismatch".to_string())); } - let Self { - start_func, - func_types, - code_type_addrs, - exports, - code, - globals, - table_types, - memory_types, - imports, - data, - elements, - .. - } = self; - - let imported_func_count = imports.iter().filter(|i| matches!(&i.kind, ImportKind::Function(_))).count(); - let imported_memory_count = imports.iter().filter(|i| matches!(&i.kind, ImportKind::Memory(_))).count() as u32; - let has_local_memory_export = - exports.iter().any(|export| export.kind == ExternalKind::Memory && export.index >= imported_memory_count); - let has_active_data_segment_on_local_memory = data.iter().any(|data| match &data.kind { - DataKind::Active { mem, .. } => *mem >= imported_memory_count, + let imported_func_count = self.imports.iter().filter(|i| matches!(&i.kind, ImportKind::Function(_))).count(); + let import_mem_count = self.imports.iter().filter(|i| matches!(&i.kind, ImportKind::Memory(_))).count() as u32; + let has_local_mem_export = + self.exports.iter().any(|export| export.kind == ExternalKind::Memory && export.index >= import_mem_count); + let has_active_data_segment_on_local_memory = self.data.iter().any(|data| match &data.kind { + DataKind::Active { mem, .. } => *mem >= import_mem_count, DataKind::Passive => false, }); let optimize_local_memory_allocation = options.optimize_local_memory_allocation(); - let mut local_memory_allocation = if memory_types.is_empty() { + let mut local_memory_allocation = if self.memory_types.is_empty() { LocalMemoryAllocation::Skip } else if !optimize_local_memory_allocation || has_active_data_segment_on_local_memory { LocalMemoryAllocation::Eager - } else if has_local_memory_export { + } else if has_local_mem_export { LocalMemoryAllocation::Lazy } else { LocalMemoryAllocation::Skip }; - let mut funcs = Vec::with_capacity(code.len()); + let mut funcs = Vec::with_capacity(self.code.len()); - for (func_idx, ((instructions, mut data, locals), ty_idx)) in code.into_iter().zip(code_type_addrs).enumerate() + for (func_idx, ((instructions, mut data, locals), ty_idx)) in + self.code.into_iter().zip(self.code_type_addrs).enumerate() { - let ty = func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(); + let ty = self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(); let params = ValueCounts::from_iter(ty.params()); let self_func = (imported_func_count + func_idx) as u32; - let optimized = optimize::optimize_instructions( - instructions, - &mut data, - self_func, - imported_memory_count, - optimize_local_memory_allocation && local_memory_allocation != LocalMemoryAllocation::Eager, - ); + let local_mem_alloc = + optimize_local_memory_allocation && local_memory_allocation != LocalMemoryAllocation::Eager; + let optimized = + optimize::optimize_instructions(instructions, &mut data, self_func, import_mem_count, local_mem_alloc); if optimized.uses_local_memory { local_memory_allocation = LocalMemoryAllocation::Eager; } - funcs.push(WasmFunction { instructions: ArcSlice::from(optimized.instructions), data, locals, params, ty }); + funcs.push(WasmFunction { instructions: optimized.instructions.into(), data, locals, params, ty }.into()); } - Ok(TinyWasmModule { + Ok(ModuleInner { funcs: funcs.into(), - func_types: func_types.into(), - globals: globals.into(), - table_types: table_types.into(), - imports: imports.into(), - start_func, - data: data.into(), - exports: exports.into(), - elements: elements.into(), - memory_types: memory_types.into(), + func_types: self.func_types.into(), + globals: self.globals.into(), + table_types: self.table_types.into(), + imports: self.imports.into(), + start_func: self.start_func, + data: self.data.into(), + exports: self.exports.into(), + elements: self.elements.into(), + memory_types: self.memory_types.into(), local_memory_allocation, - }) + } + .into()) } } diff --git a/crates/tinywasm/benches/argon2id.rs b/crates/tinywasm/benches/argon2id.rs index 277b904..dbd86e5 100644 --- a/crates/tinywasm/benches/argon2id.rs +++ b/crates/tinywasm/benches/argon2id.rs @@ -1,29 +1,29 @@ use criterion::{Criterion, criterion_group, criterion_main}; use eyre::Result; use tinywasm::{ModuleInstance, Store, types}; -use types::TinyWasmModule; +use types::Module; const WASM: &[u8] = include_bytes!("../../../examples/rust/out/argon2id.wasm"); -fn argon2id_parse() -> Result { +fn argon2id_parse() -> Result { let parser = tinywasm_parser::Parser::new(); let data = parser.parse_module_bytes(WASM)?; Ok(data) } -fn argon2id_to_twasm(module: &TinyWasmModule) -> Result> { +fn argon2id_to_twasm(module: &Module) -> Result> { let twasm = module.serialize_twasm()?; Ok(twasm) } -fn argon2id_from_twasm(twasm: &[u8]) -> Result { - let module = TinyWasmModule::from_twasm(twasm)?; +fn argon2id_from_twasm(twasm: &[u8]) -> Result { + let module = Module::try_from_twasm(twasm)?; Ok(module) } -fn argon2id_run(module: TinyWasmModule) -> Result<()> { +fn argon2id_run(module: Module) -> Result<()> { let mut store = Store::default(); - let instance = ModuleInstance::instantiate(&mut store, module.into(), None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; let argon2 = instance.func::<(i32, i32, i32), i32>(&store, "argon2id")?; argon2.call(&mut store, (1000, 2, 1))?; Ok(()) @@ -38,6 +38,7 @@ fn criterion_benchmark(c: &mut Criterion) { group.bench_function("argon2id_parse", |b| b.iter(argon2id_parse)); group.bench_function("argon2id_to_twasm", |b| b.iter(|| argon2id_to_twasm(&module))); group.bench_function("argon2id_from_twasm", |b| b.iter(|| argon2id_from_twasm(&twasm))); + group.measurement_time(std::time::Duration::from_secs(10)); group.bench_function("argon2id", |b| b.iter(|| argon2id_run(module.clone()))); } diff --git a/crates/tinywasm/benches/fibonacci.rs b/crates/tinywasm/benches/fibonacci.rs index 51bfa52..e0737da 100644 --- a/crates/tinywasm/benches/fibonacci.rs +++ b/crates/tinywasm/benches/fibonacci.rs @@ -1,28 +1,28 @@ use criterion::{Criterion, criterion_group, criterion_main}; use eyre::Result; use tinywasm::{ModuleInstance, Store, types}; -use types::TinyWasmModule; +use types::Module; const WASM: &[u8] = include_bytes!("../../../examples/rust/out/fibonacci.wasm"); -fn fibonacci_parse() -> Result { +fn fibonacci_parse() -> Result { let parser = tinywasm_parser::Parser::new(); let data = parser.parse_module_bytes(WASM)?; Ok(data) } -fn fibonacci_to_twasm(module: &TinyWasmModule) -> Result> { +fn fibonacci_to_twasm(module: &Module) -> Result> { let twasm = module.serialize_twasm()?; Ok(twasm) } -fn fibonacci_from_twasm(twasm: &[u8]) -> Result { - Ok(TinyWasmModule::from_twasm(twasm)?) +fn fibonacci_from_twasm(twasm: &[u8]) -> Result { + Ok(Module::try_from_twasm(twasm)?) } -fn fibonacci_run(module: TinyWasmModule, recursive: bool, n: i32) -> Result<()> { +fn fibonacci_run(module: Module, recursive: bool, n: i32) -> Result<()> { let mut store = Store::default(); - let instance = ModuleInstance::instantiate(&mut store, module.into(), None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; let argon2 = instance.func::( &store, match recursive { @@ -43,6 +43,7 @@ fn criterion_benchmark(c: &mut Criterion) { group.bench_function("fibonacci_parse", |b| b.iter(fibonacci_parse)); group.bench_function("fibonacci_to_twasm", |b| b.iter(|| fibonacci_to_twasm(&module))); group.bench_function("fibonacci_from_twasm", |b| b.iter(|| fibonacci_from_twasm(&twasm))); + group.measurement_time(std::time::Duration::from_secs(10)); group.bench_function("fibonacci_iterative_60", |b| b.iter(|| fibonacci_run(module.clone(), false, 60))); group.bench_function("fibonacci_recursive_26", |b| b.iter(|| fibonacci_run(module.clone(), true, 26))); diff --git a/crates/tinywasm/benches/tinywasm.rs b/crates/tinywasm/benches/tinywasm.rs index 48458d7..7d50d72 100644 --- a/crates/tinywasm/benches/tinywasm.rs +++ b/crates/tinywasm/benches/tinywasm.rs @@ -3,32 +3,32 @@ use eyre::Result; use tinywasm::{ Engine, FuncContext, HostFunction, Imports, MemoryBackend, ModuleInstance, Store, engine::Config, types, }; -use types::TinyWasmModule; +use types::Module; const WASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm"); -fn tinywasm_parse() -> Result { +fn tinywasm_parse() -> Result { let parser = tinywasm_parser::Parser::new(); let data = parser.parse_module_bytes(WASM)?; Ok(data) } -fn tinywasm_to_twasm(module: &TinyWasmModule) -> Result> { +fn tinywasm_to_twasm(module: &Module) -> Result> { let twasm = module.serialize_twasm()?; Ok(twasm) } -fn tinywasm_from_twasm(twasm: &[u8]) -> Result { - let module = TinyWasmModule::from_twasm(twasm)?; +fn tinywasm_from_twasm(twasm: &[u8]) -> Result { + let module = Module::try_from_twasm(twasm)?; Ok(module) } -fn tinywasm_run(module: TinyWasmModule) -> Result<()> { +fn tinywasm_run(module: &Module) -> Result<()> { let engine = Engine::new(Config::default().with_memory_backend(MemoryBackend::paged(64 * 1024))); let mut store = Store::new(engine); let mut imports = Imports::default(); imports.define("env", "printi32", HostFunction::from(&mut store, |_: FuncContext<'_>, _: i32| Ok(()))); - let instance = ModuleInstance::instantiate(&mut store, module.into(), Some(imports)).expect("instantiate"); + let instance = ModuleInstance::instantiate(&mut store, module, Some(imports)).expect("instantiate"); let hello = instance.func::<(), ()>(&store, "hello").expect("func_typed"); hello.call(&mut store, ()).expect("call"); Ok(()) @@ -44,7 +44,7 @@ fn criterion_benchmark(c: &mut Criterion) { group.bench_function("tinywasm_to_twasm", |b| b.iter(|| tinywasm_to_twasm(&module))); group.bench_function("tinywasm_from_twasm", |b| b.iter(|| tinywasm_from_twasm(&twasm))); group.measurement_time(std::time::Duration::from_secs(10)); - group.bench_function("tinywasm", |b| b.iter(|| tinywasm_run(module.clone()))); + group.bench_function("tinywasm", |b| b.iter(|| tinywasm_run(&module))); } criterion_group!(benches, criterion_benchmark); diff --git a/crates/tinywasm/benches/tinywasm_modes.rs b/crates/tinywasm/benches/tinywasm_modes.rs index d7636f8..ed89b7b 100644 --- a/crates/tinywasm/benches/tinywasm_modes.rs +++ b/crates/tinywasm/benches/tinywasm_modes.rs @@ -1,7 +1,7 @@ use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use eyre::Result; use tinywasm::engine::{Config, FuelPolicy}; -use tinywasm::types::TinyWasmModule; +use tinywasm::types::Module; use tinywasm::{Engine, ExecProgress, FuncContext, FunctionTyped, HostFunction, Imports, ModuleInstance, Store}; const WASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm"); @@ -9,12 +9,12 @@ const FUEL_PER_ROUND: u32 = 512; const TIME_BUDGET_PER_ROUND: core::time::Duration = core::time::Duration::from_micros(50); const BENCH_MEASUREMENT_TIME: core::time::Duration = core::time::Duration::from_secs(10); -fn tinywasm_parse() -> Result { +fn tinywasm_parse() -> Result { let parser = tinywasm_parser::Parser::new(); Ok(parser.parse_module_bytes(WASM)?) } -fn setup_typed_func(module: TinyWasmModule, engine: Option) -> Result<(Store, FunctionTyped<(), ()>)> { +fn setup_typed_func(module: &Module, engine: Option) -> Result<(Store, FunctionTyped<(), ()>)> { let mut store = match engine { Some(engine) => Store::new(engine), None => Store::default(), @@ -23,7 +23,7 @@ fn setup_typed_func(module: TinyWasmModule, engine: Option) -> Result<(S let mut imports = Imports::default(); imports.define("env", "printi32", HostFunction::from(&mut store, |_: FuncContext<'_>, _: i32| Ok(()))); - let instance = ModuleInstance::instantiate(&mut store, module.into(), Some(imports))?; + let instance = ModuleInstance::instantiate(&mut store, module, Some(imports))?; let func = instance.func::<(), ()>(&store, "hello")?; Ok((store, func)) } @@ -61,10 +61,7 @@ fn criterion_benchmark(c: &mut Criterion) { let per_instruction_engine = Engine::new(Config::new().with_fuel_policy(FuelPolicy::PerInstruction)); group.bench_function("resume_fuel_per_instruction", |b| { b.iter_batched_ref( - || { - setup_typed_func(module.clone(), Some(per_instruction_engine.clone())) - .expect("setup fuel per-instruction") - }, + || setup_typed_func(&module, Some(per_instruction_engine.clone())).expect("setup fuel per-instruction"), |(store, func)| run_resume_with_fuel(store, func).expect("run fuel per-instruction"), BatchSize::LargeInput, ) @@ -73,7 +70,7 @@ fn criterion_benchmark(c: &mut Criterion) { let weighted_engine = Engine::new(Config::new().with_fuel_policy(FuelPolicy::Weighted)); group.bench_function("resume_fuel_weighted", |b| { b.iter_batched_ref( - || setup_typed_func(module.clone(), Some(weighted_engine.clone())).expect("setup fuel weighted"), + || setup_typed_func(&module, Some(weighted_engine.clone())).expect("setup fuel weighted"), |(store, func)| run_resume_with_fuel(store, func).expect("run fuel weighted"), BatchSize::LargeInput, ) @@ -81,7 +78,7 @@ fn criterion_benchmark(c: &mut Criterion) { group.bench_function("resume_time_budget", |b| { b.iter_batched_ref( - || setup_typed_func(module.clone(), None).expect("setup time budget"), + || setup_typed_func(&module, None).expect("setup time budget"), |(store, func)| run_resume_with_time_budget(store, func).expect("run time budget"), BatchSize::LargeInput, ) @@ -89,7 +86,7 @@ fn criterion_benchmark(c: &mut Criterion) { group.bench_function("call", |b| { b.iter_batched_ref( - || setup_typed_func(module.clone(), None).expect("setup call"), + || setup_typed_func(&module, None).expect("setup call"), |(store, func)| run_call(store, func).expect("run call"), BatchSize::LargeInput, ) diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index d82b8a4..fff2a8f 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -66,12 +66,12 @@ impl From<&Import> for ExternName { /// ```rust /// # use log; /// # fn main() -> tinywasm::Result<()> { -/// use tinywasm::{Global, HostFunction, Imports, Memory, Module, Store, Table}; +/// use tinywasm::{Global, HostFunction, Imports, Memory, ModuleInstance, Store, Table}; /// use tinywasm::types::{WasmType, TableType, MemoryType, WasmValue}; /// # let wasm = wat::parse_str("(module)").expect("valid wat"); -/// # let module = Module::parse_bytes(&wasm)?; +/// # let module = tinywasm::parse_bytes(&wasm)?; /// # let mut store = Store::default(); -/// # let my_other_instance = module.instantiate(&mut store, None)?; +/// # let my_other_instance = ModuleInstance::instantiate(&mut store, &module, None)?; /// let mut imports = Imports::new(); /// /// // function args can be either a single @@ -245,12 +245,12 @@ impl Imports { pub(crate) fn link( self, store: &mut crate::Store, - module: &crate::Module, + module: &Module, _idx: ModuleInstanceAddr, ) -> Result { let mut imports = ResolvedImports::new(); - for import in &*module.0.imports { + for import in &*module.imports { if let Some(defined) = self.take_defined(import) { match defined { Extern::Global(global) => { @@ -284,7 +284,6 @@ impl Imports { return Err(LinkingError::incompatible_import_type(import).into()); }; let import_func_type = module - .0 .func_types .get(*ty as usize) .ok_or_else(|| LinkingError::incompatible_import_type(import))?; @@ -332,7 +331,6 @@ impl Imports { (ExternVal::Func(func_addr), ImportKind::Function(ty)) => { let func = store.state.get_func(func_addr); let import_func_type = module - .0 .func_types .get(*ty as usize) .ok_or_else(|| LinkingError::incompatible_import_type(import))?; diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 6b4f407..90a95e3 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -1,9 +1,10 @@ use alloc::boxed::Box; +use alloc::sync::Arc; use alloc::{format, rc::Rc}; use tinywasm_types::*; use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, WasmTypesFromTuple}; -use crate::{Error, Function, FunctionTyped, Global, Imports, Memory, Module, Result, Store, Table}; +use crate::{Error, Function, FunctionTyped, Global, Imports, Memory, Result, Store, Table}; /// A typed view over an exported extern value. pub enum ExternItem { @@ -30,7 +31,7 @@ pub struct ModuleInstance(pub(crate) Rc); pub(crate) struct ModuleInstanceInner { pub(crate) store_id: usize, pub(crate) idx: ModuleInstanceAddr, - pub(crate) types: ArcSlice, + pub(crate) types: Arc<[Arc]>, pub(crate) func_addrs: Box<[FuncAddr]>, pub(crate) table_addrs: Box<[TableAddr]>, pub(crate) mem_addrs: Box<[MemAddr]>, @@ -38,9 +39,45 @@ pub(crate) struct ModuleInstanceInner { pub(crate) elem_addrs: Box<[ElemAddr]>, pub(crate) data_addrs: Box<[DataAddr]>, pub(crate) func_start: Option, - pub(crate) exports: ArcSlice, + pub(crate) exports: Arc<[Export]>, } +// impl ModuleInstance { +// #[cfg(feature = "parser")] +// /// Parse a module from bytes. Requires `parser` feature. +// pub fn from_wasm_bytes(wasm: &[u8]) -> Result { +// let data = tinywasm_parser::Parser::new().parse_module_bytes(wasm)?; +// Ok(data.into()) +// } + +// #[cfg(all(feature = "parser", feature = "std"))] +// /// Parse a module from a file. Requires `parser` and `std` features. +// pub fn from_wasm_file(path: impl AsRef + Clone) -> Result { +// let data = tinywasm_parser::Parser::new().parse_module_file(path)?; +// Ok(data.into()) +// } + +// #[cfg(all(feature = "parser", feature = "std"))] +// /// Parse a module from a stream. Requires `parser` and `std` features. +// pub fn from_wasm_stream(stream: impl crate::std::io::Read) -> Result { +// let data = tinywasm_parser::Parser::new().parse_module_stream(stream)?; +// Ok(data.into()) +// } + +// /// Instantiate the module in the given store +// /// +// /// Runs the start function if it exists +// /// +// /// If you want to run the start function yourself, use `ModuleInstance::instantiate` +// /// +// /// See +// pub fn instantiate(self, store: &mut Store, imports: Option) -> Result { +// let instance = ModuleInstance::instantiate(store, self, imports)?; +// let _ = instance.start(store)?; +// Ok(instance) +// } +// } + impl ModuleInstanceInner { #[inline] pub(crate) fn func_ty(&self, addr: FuncAddr) -> &FuncType { @@ -127,37 +164,44 @@ impl ModuleInstance { /// Instantiate the module in the given store /// /// See - pub fn instantiate(store: &mut Store, module: Module, imports: Option) -> Result { + pub fn instantiate(store: &mut Store, module: &Module, imports: Option) -> Result { + let instance = ModuleInstance::instantiate_no_start(store, module, imports)?; + let _ = instance.start(store)?; + Ok(instance) + } + + /// Instantiate the module in the given store (without running the start function) + /// + /// See + pub fn instantiate_no_start(store: &mut Store, module: &Module, imports: Option) -> Result { let idx = store.next_module_instance_idx(); - let mut addrs = imports.unwrap_or_default().link(store, &module, idx)?; + let mut addrs = imports.unwrap_or_default().link(store, module, idx)?; - addrs.funcs.extend(store.init_funcs(&module.0.funcs, idx)); - addrs.tables.extend(store.init_tables(&module.0.table_types, idx)); - match module.0.local_memory_allocation { + addrs.funcs.extend(store.init_funcs(&module.funcs, idx)); + addrs.tables.extend(store.init_tables(&module.table_types, idx)); + match module.local_memory_allocation { LocalMemoryAllocation::Skip => {} - LocalMemoryAllocation::Lazy => { - addrs.memories.extend(store.init_lazy_memories(&module.0.memory_types, idx)?) - } - LocalMemoryAllocation::Eager => addrs.memories.extend(store.init_memories(&module.0.memory_types, idx)?), + LocalMemoryAllocation::Lazy => addrs.memories.extend(store.init_lazy_memories(&module.memory_types, idx)?), + LocalMemoryAllocation::Eager => addrs.memories.extend(store.init_memories(&module.memory_types, idx)?), } - let global_addrs = store.init_globals(addrs.globals, &module.0.globals, &addrs.funcs, idx)?; + let global_addrs = store.init_globals(addrs.globals, &module.globals, &addrs.funcs, idx)?; let (elem_addrs, elem_trapped) = - store.init_elements(&addrs.tables, &addrs.funcs, &global_addrs, &module.0.elements, idx)?; + store.init_elements(&addrs.tables, &addrs.funcs, &global_addrs, &module.elements, idx)?; let (data_addrs, data_trapped) = - store.init_data(&addrs.memories, &global_addrs, &addrs.funcs, &module.0.data, idx)?; + store.init_data(&addrs.memories, &global_addrs, &addrs.funcs, &module.data, idx)?; let instance = ModuleInstanceInner { store_id: store.id(), idx, - types: module.0.func_types.clone(), + types: module.func_types.clone(), func_addrs: addrs.funcs.into_boxed_slice(), table_addrs: addrs.tables.into_boxed_slice(), mem_addrs: addrs.memories.into_boxed_slice(), global_addrs: global_addrs.into_boxed_slice(), elem_addrs, data_addrs, - func_start: module.0.start_func, - exports: module.0.exports.clone(), + func_start: module.start_func, + exports: module.exports.clone(), }; let instance = Rc::new(instance); diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index acd14f6..fd7658b 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -7,6 +7,7 @@ use super::no_std_floats::NoStdFloatExt; use alloc::boxed::Box; use alloc::rc::Rc; +use alloc::sync::Arc; use interpreter::stack::CallFrame; use tinywasm_types::*; @@ -23,7 +24,7 @@ const FUEL_COST_CALL_TOTAL: u32 = 5; pub(crate) struct Executor<'store, const BUDGETED: bool> { cf: CallFrame, - func: Rc, + func: Arc, module: Rc, store: &'store mut Store, } @@ -113,14 +114,14 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { }}; } - let next = match self.func.instructions.0.get(self.cf.instr_ptr as usize) { + let next = match self.func.instructions.get(self.cf.instr_ptr as usize) { Some(instr) => instr, None => { cold_path(); unreachable!( "Instruction pointer out of bounds: {} ({} instructions)", self.cf.instr_ptr, - self.func.instructions.0.len() + self.func.instructions.len() ) } }; @@ -785,7 +786,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { wasm_func: WasmFunctionInstance, func_addr: FuncAddr, ) -> Result<(), Trap> { - if !Rc::ptr_eq(&self.func, &wasm_func.func) { + if !Arc::ptr_eq(&self.func, &wasm_func.func) { self.func = wasm_func.func.clone(); } diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index d352042..5f7fa5c 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -32,17 +32,17 @@ #![cfg_attr(docsrs, feature(doc_cfg))] //! //! ## Getting Started -//! The easiest way to get started is to use the [`Module::parse_bytes`] function to load a +//! The easiest way to get started is to use the [`crate::parse_bytes`] function to load a //! WebAssembly module from bytes. This will parse the module and validate it, returning //! a [`Module`] that can be used to instantiate the module. //! //! //! ```rust -//! use tinywasm::{Store, Module}; +//! use tinywasm::{ModuleInstance, Store}; //! //! // Load a module from bytes //! let wasm = include_bytes!("../../../examples/wasm/add.wasm"); -//! let module = Module::parse_bytes(wasm)?; +//! let module = tinywasm::parse_bytes(wasm)?; //! //! // Create a new store //! // Stores are used to allocate objects like functions and globals @@ -52,7 +52,7 @@ //! // This will allocate the module and its globals into the store //! // and execute the module's start function. //! // Every ModuleInstance has its own ID space for functions, globals, etc. -//! let instance = module.instantiate(&mut store, None)?; +//! let instance = ModuleInstance::instantiate(&mut store, &module, None)?; //! //! // Get a typed handle to the exported "add" function //! // Alternatively, you can use `instance.func` to get an untyped handle @@ -99,14 +99,12 @@ pub use error::*; pub use func::{ExecProgress, FuncContext, FuncExecution, FuncExecutionTyped, Function, FunctionTyped, HostFunction}; pub use imports::*; pub use instance::{ExternItem, ModuleInstance}; -pub use module::{ExportType, ImportType, Module, ModuleExport, ModuleImport}; pub use reference::*; pub use store::*; mod func; mod imports; mod instance; -mod module; mod reference; mod store; @@ -123,11 +121,18 @@ pub mod parser { pub use tinywasm_parser::*; } +#[cfg(feature = "parser")] +pub use parser::parse_bytes; +#[cfg(all(feature = "parser", feature = "std"))] +pub use parser::{parse_file, parse_stream}; + /// Re-export of [`tinywasm_types`]. pub mod types { pub use tinywasm_types::*; } +pub use tinywasm_types::Module; + pub(crate) fn unlikely(b: bool) -> bool { if b { core::hint::cold_path(); diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs deleted file mode 100644 index f725054..0000000 --- a/crates/tinywasm/src/module.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::{Imports, ModuleInstance, Result, Store}; -use tinywasm_types::{ExternalKind, FuncType, TinyWasmModule}; - -/// A module import descriptor. -pub struct ModuleImport<'a> { - /// Importing module name. - pub module: &'a str, - /// Import name. - pub name: &'a str, - /// Import type. - pub ty: ImportType<'a>, -} - -/// A module export descriptor. -pub struct ModuleExport<'a> { - /// Export name. - pub name: &'a str, - /// Export type. - pub ty: ExportType<'a>, -} - -/// Imported entity type. -pub enum ImportType<'a> { - /// Imported function type. - Func(&'a FuncType), - /// Imported table type. - Table(&'a tinywasm_types::TableType), - /// Imported memory type. - Memory(&'a tinywasm_types::MemoryType), - /// Imported global type. - Global(&'a tinywasm_types::GlobalType), -} - -/// Exported entity type. -pub enum ExportType<'a> { - /// Exported function type. - Func(&'a FuncType), - /// Exported table type. - Table(&'a tinywasm_types::TableType), - /// Exported memory type. - Memory(&'a tinywasm_types::MemoryType), - /// Exported global type. - Global(&'a tinywasm_types::GlobalType), -} - -/// A WebAssembly Module -/// -/// See -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Module(pub(crate) alloc::sync::Arc); - -impl From<&TinyWasmModule> for Module { - fn from(data: &TinyWasmModule) -> Self { - Self(alloc::sync::Arc::new(data.clone())) - } -} - -impl From for Module { - fn from(data: TinyWasmModule) -> Self { - Self(alloc::sync::Arc::new(data)) - } -} - -impl Module { - #[cfg(feature = "parser")] - /// Parse a module from bytes. Requires `parser` feature. - pub fn parse_bytes(wasm: &[u8]) -> Result { - let data = tinywasm_parser::Parser::new().parse_module_bytes(wasm)?; - Ok(data.into()) - } - - #[cfg(all(feature = "parser", feature = "std"))] - /// Parse a module from a file. Requires `parser` and `std` features. - pub fn parse_file(path: impl AsRef + Clone) -> Result { - let data = tinywasm_parser::Parser::new().parse_module_file(path)?; - Ok(data.into()) - } - - #[cfg(all(feature = "parser", feature = "std"))] - /// Parse a module from a stream. Requires `parser` and `std` features. - pub fn parse_stream(stream: impl crate::std::io::Read) -> Result { - let data = tinywasm_parser::Parser::new().parse_module_stream(stream)?; - Ok(data.into()) - } - - /// Instantiate the module in the given store - /// - /// Runs the start function if it exists - /// - /// If you want to run the start function yourself, use `ModuleInstance::instantiate` - /// - /// See - pub fn instantiate(self, store: &mut Store, imports: Option) -> Result { - let instance = ModuleInstance::instantiate(store, self, imports)?; - let _ = instance.start(store)?; - Ok(instance) - } - - /// Returns an iterator over the module's import descriptors. - /// - /// The returned data mirrors the module's import section and preserves order. - pub fn imports(&self) -> impl Iterator> { - self.0.imports.iter().filter_map(|import| { - let ty = match &import.kind { - tinywasm_types::ImportKind::Function(type_idx) => { - Some(ImportType::Func(self.0.func_types.get(*type_idx as usize)?)) - } - tinywasm_types::ImportKind::Table(table_ty) => Some(ImportType::Table(table_ty)), - tinywasm_types::ImportKind::Memory(memory_ty) => Some(ImportType::Memory(memory_ty)), - tinywasm_types::ImportKind::Global(global_ty) => Some(ImportType::Global(global_ty)), - }?; - - Some(ModuleImport { module: import.module.as_ref(), name: import.name.as_ref(), ty }) - }) - } - - /// Returns an iterator over the module's export descriptors. - /// - /// The returned data mirrors the module's export section and preserves order. - pub fn exports(&self) -> impl Iterator> { - self.0.exports.iter().filter_map(|export| { - let imports = self.0.imports.iter(); - let idx = export.index as usize; - let ty = match export.kind { - ExternalKind::Func => { - let imported_funcs = - imports.filter(|import| matches!(import.kind, tinywasm_types::ImportKind::Function(_))).count(); - if idx < imported_funcs { - ExportType::Func(imported_func_type(&self.0, idx)?) - } else { - let local_idx = idx - imported_funcs; - ExportType::Func(&self.0.funcs.get(local_idx)?.ty) - } - } - ExternalKind::Table => ExportType::Table(self.0.table_types.get(idx)?), - ExternalKind::Memory => ExportType::Memory(self.0.memory_types.get(idx)?), - ExternalKind::Global => { - let imported_globals = - imports.filter(|import| matches!(import.kind, tinywasm_types::ImportKind::Global(_))).count(); - if idx < imported_globals { - ExportType::Global(imported_global_type(&self.0, idx)?) - } else { - let local_idx = idx - imported_globals; - ExportType::Global(&self.0.globals.get(local_idx)?.ty) - } - } - }; - - Some(ModuleExport { name: export.name.as_ref(), ty }) - }) - } -} - -fn imported_func_type(module: &TinyWasmModule, function_index: usize) -> Option<&FuncType> { - let mut seen = 0usize; - for import in module.imports.iter() { - if let tinywasm_types::ImportKind::Function(type_idx) = import.kind { - if seen == function_index { - return module.func_types.get(type_idx as usize); - } - seen += 1; - } - } - None -} - -fn imported_global_type(module: &TinyWasmModule, global_index: usize) -> Option<&tinywasm_types::GlobalType> { - let mut seen = 0usize; - for import in module.imports.iter() { - if let tinywasm_types::ImportKind::Global(global_ty) = &import.kind { - if seen == global_index { - return Some(global_ty); - } - seen += 1; - } - } - None -} diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs index 56479dc..a7a22d5 100644 --- a/crates/tinywasm/src/store/function.rs +++ b/crates/tinywasm/src/store/function.rs @@ -1,4 +1,4 @@ -use alloc::rc::Rc; +use alloc::{rc::Rc, sync::Arc}; use tinywasm_types::*; use crate::func::HostFunction; @@ -27,15 +27,15 @@ impl FunctionInstance { } impl FunctionInstance { - pub(crate) fn new_wasm(func: WasmFunction, owner: ModuleInstanceAddr) -> Self { - Self::Wasm(WasmFunctionInstance { func: Rc::new(func), owner }) + pub(crate) fn new_wasm(func: Arc, owner: ModuleInstanceAddr) -> Self { + Self::Wasm(WasmFunctionInstance { func, owner }) } } #[derive(Clone)] #[cfg_attr(feature = "debug", derive(Debug))] pub(crate) struct WasmFunctionInstance { - pub(crate) func: Rc, + pub(crate) func: Arc, pub(crate) owner: ModuleInstanceAddr, } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index d26cb8a..773f4cb 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -1,4 +1,5 @@ use alloc::rc::Rc; +use alloc::sync::Arc; use alloc::{boxed::Box, format, string::ToString, vec::Vec}; use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; @@ -118,7 +119,7 @@ impl State { } /// Get a wasm function at the actual index in the store, panicking if it's a host function (which should be guaranteed by the validator) - pub(crate) fn get_wasm_func(&self, addr: FuncAddr) -> &Rc { + pub(crate) fn get_wasm_func(&self, addr: FuncAddr) -> &Arc { match self.funcs.get(addr as usize) { Some(func) => match func { FunctionInstance::Wasm(wasm_func) => &wasm_func.func, @@ -262,7 +263,7 @@ impl Store { // Linking related functions impl Store { /// Add functions to the store, returning their addresses in the store - pub(crate) fn init_funcs(&mut self, funcs: &[WasmFunction], idx: ModuleInstanceAddr) -> Vec { + pub(crate) fn init_funcs(&mut self, funcs: &[Arc], idx: ModuleInstanceAddr) -> Vec { let func_count = self.state.funcs.len(); let mut func_addrs = Vec::with_capacity(func_count); for (i, func) in funcs.iter().enumerate() { diff --git a/crates/tinywasm/tests/host_func_signature_check.rs b/crates/tinywasm/tests/host_func_signature_check.rs index 0b09a51..ef8b2a1 100644 --- a/crates/tinywasm/tests/host_func_signature_check.rs +++ b/crates/tinywasm/tests/host_func_signature_check.rs @@ -1,7 +1,7 @@ use eyre::Result; use std::fmt::Write; use tinywasm::{ - FuncContext, HostFunction, Imports, Module, Store, + FuncContext, HostFunction, Imports, Module, ModuleInstance, Store, types::{FuncType, WasmType, WasmValue}, }; use tinywasm_types::ExternRef; @@ -39,7 +39,7 @@ fn test_return_invalid_type() -> Result<()> { let hfn = HostFunction::from_untyped(&mut store, &ty, |_: FuncContext<'_>, _| Ok(returned_values.to_vec())); imports.define("host", "hfn", hfn); - let instance = module.clone().instantiate(&mut store, Some(imports)).unwrap(); + let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports)).unwrap(); let caller = instance.func_untyped(&store, "call_hfn").unwrap(); // Return-type mismatch is only observable at call time. let should_succeed = returned_values.iter().map(WasmType::from).eq(ty.results().iter().copied()); @@ -63,7 +63,7 @@ fn test_linking_invalid_untyped_func() -> Result<()> { imports.define("host", "hfn", tried_fn); let should_succeed = ty == expected_func_ty; - let link_res = module.clone().instantiate(&mut store, Some(imports)); + let link_res = ModuleInstance::instantiate(&mut store, module, Some(imports)); assert_eq!(link_res.is_ok(), should_succeed); } } @@ -108,7 +108,7 @@ fn test_linking_invalid_typed_func() -> Result<()> { for typed_fn in matching_none { let mut imports = Imports::new(); imports.define("host", "hfn", typed_fn); - let link_failure = module.clone().instantiate(&mut store, Some(imports)); + let link_failure = ModuleInstance::instantiate(&mut store, &module, Some(imports)); assert!(link_failure.is_err(), "Expected linking to fail for mismatched typed func, but it succeeded"); } } @@ -165,5 +165,5 @@ fn proxy_module(func_ty: &FuncType) -> Module { "# ); let wasm = wat::parse_str(wasm_text).expect("failed to parse wat"); - Module::parse_bytes(&wasm).expect("failed to make module") + tinywasm::parse_bytes(&wasm).expect("failed to make module") } diff --git a/crates/tinywasm/tests/import_linking.rs b/crates/tinywasm/tests/import_linking.rs index 3ab13ff..712148e 100644 --- a/crates/tinywasm/tests/import_linking.rs +++ b/crates/tinywasm/tests/import_linking.rs @@ -1,5 +1,5 @@ use eyre::Result; -use tinywasm::{Error, Imports, Module, Store}; +use tinywasm::{Error, Imports, Module, ModuleInstance, Store}; const WASM_ADD: &str = r#" (module @@ -20,8 +20,8 @@ const WASM_IMPORT: &str = r#" "#; fn parse_modules() -> Result<(Module, Module)> { - let add = Module::parse_bytes(&wat::parse_str(WASM_ADD)?)?; - let import = Module::parse_bytes(&wat::parse_str(WASM_IMPORT)?)?; + let add = tinywasm::parse_bytes(&wat::parse_str(WASM_ADD)?)?; + let import = tinywasm::parse_bytes(&wat::parse_str(WASM_IMPORT)?)?; Ok((add, import)) } @@ -30,11 +30,11 @@ fn link_module_links_same_store_instance() -> Result<()> { let (add_module, import_module) = parse_modules()?; let mut store = Store::default(); - let add_instance = add_module.instantiate(&mut store, None)?; + let add_instance = ModuleInstance::instantiate(&mut store, &add_module, None)?; let mut imports = Imports::new(); imports.link_module("adder", add_instance)?; - let instance = import_module.instantiate(&mut store, Some(imports))?; + let instance = ModuleInstance::instantiate(&mut store, &import_module, Some(imports))?; let main = instance.func::<(), i32>(&store, "main")?; assert_eq!(main.call(&mut store, ())?, 3); Ok(()) @@ -45,13 +45,13 @@ fn link_module_rejects_cross_store_instance() -> Result<()> { let (add_module, import_module) = parse_modules()?; let mut source_store = Store::default(); - let add_instance = add_module.instantiate(&mut source_store, None)?; + let add_instance = ModuleInstance::instantiate(&mut source_store, &add_module, None)?; let mut target_store = Store::default(); let mut imports = Imports::new(); imports.link_module("adder", add_instance)?; - let err = import_module.instantiate(&mut target_store, Some(imports)).unwrap_err(); + let err = ModuleInstance::instantiate(&mut target_store, &import_module, Some(imports)).unwrap_err(); assert!(matches!(err, Error::InvalidStore)); Ok(()) } diff --git a/crates/tinywasm/tests/imported_table_init.rs b/crates/tinywasm/tests/imported_table_init.rs index b92401d..4e2a587 100644 --- a/crates/tinywasm/tests/imported_table_init.rs +++ b/crates/tinywasm/tests/imported_table_init.rs @@ -1,6 +1,6 @@ use eyre::Result; use tinywasm::types::{FuncRef, TableType, WasmType, WasmValue}; -use tinywasm::{Imports, Module, Store, Table}; +use tinywasm::{Imports, ModuleInstance, Store, Table}; #[test] fn imported_table_uses_provided_init_value() -> Result<()> { @@ -16,14 +16,14 @@ fn imported_table_uses_provided_init_value() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut store = Store::default(); let mut imports = Imports::new(); let table = Table::new(&mut store, TableType::new(WasmType::RefFunc, 3, None), WasmValue::RefFunc(FuncRef::new(Some(0))))?; imports.define("host", "table", table); - let instance = module.instantiate(&mut store, Some(imports))?; + let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports))?; let slot_is_null = instance.func::(&store, "slot_is_null")?; assert_eq!(slot_is_null.call(&mut store, 0)?, 0); diff --git a/crates/tinywasm/tests/internal_refs.rs b/crates/tinywasm/tests/internal_refs.rs index 9bd091e..eb0a7c2 100644 --- a/crates/tinywasm/tests/internal_refs.rs +++ b/crates/tinywasm/tests/internal_refs.rs @@ -1,6 +1,6 @@ use eyre::Result; use tinywasm::types::{FuncRef, WasmValue}; -use tinywasm::{ExternItem, Module, Store}; +use tinywasm::{ExternItem, ModuleInstance, Store}; #[test] #[cfg(feature = "guest_debug")] @@ -18,9 +18,9 @@ fn private_items_are_accessible_by_index() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut store = Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; let func = instance.func_by_index(&store, 0)?; assert_eq!(func.call(&mut store, &[])?, vec![WasmValue::I32(7)]); @@ -50,9 +50,9 @@ fn exported_tables_and_globals_have_handle_and_helper_apis() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut store = Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; assert_eq!(instance.global_get(&store, "g")?, WasmValue::I32(3)); assert_eq!(instance.global("g")?.get(&store)?, WasmValue::I32(3)); @@ -83,9 +83,9 @@ fn extern_item_lookup_returns_expected_kinds() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut store = Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; assert!(matches!(instance.extern_item("f")?, ExternItem::Func(_))); assert!(matches!(instance.extern_item("m")?, ExternItem::Memory(_))); diff --git a/crates/tinywasm/tests/memory_backends.rs b/crates/tinywasm/tests/memory_backends.rs index cd9f770..8b7d630 100644 --- a/crates/tinywasm/tests/memory_backends.rs +++ b/crates/tinywasm/tests/memory_backends.rs @@ -7,7 +7,7 @@ use std::io::{Read, Seek, SeekFrom, Write}; use eyre::Result; use tinywasm::engine::Config; use tinywasm::types::{MemoryArch, MemoryType}; -use tinywasm::{Engine, Memory, MemoryBackend, Module, PagedMemory, Store}; +use tinywasm::{Engine, Memory, MemoryBackend, Module, ModuleInstance, PagedMemory, Store}; use tinywasm_parser::{Parser, ParserOptions}; fn instantiate_module_with_counting_backend(module: Module) -> Result { @@ -20,14 +20,14 @@ fn instantiate_module_with_counting_backend(module: Module) -> Result { let engine = Engine::new(Config::new().with_memory_backend(backend)); let mut store = Store::new(engine); - let _ = module.instantiate(&mut store, None)?; + let _ = ModuleInstance::instantiate(&mut store, &module, None)?; Ok(created.load(Ordering::Relaxed)) } fn instantiate_with_counting_backend(wat: &str) -> Result { let wasm = wat::parse_str(wat)?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; instantiate_module_with_counting_backend(module) } @@ -35,7 +35,7 @@ fn instantiate_exported_memory_with_counting_backend( wat: &str, ) -> Result<(Store, tinywasm::ModuleInstance, Arc)> { let wasm = wat::parse_str(wat)?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let created = Arc::new(AtomicUsize::new(0)); let factory_calls = created.clone(); let backend = MemoryBackend::custom(move |ty| { @@ -44,7 +44,7 @@ fn instantiate_exported_memory_with_counting_backend( }); let engine = Engine::new(Config::new().with_memory_backend(backend)); let mut store = Store::new(engine); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; Ok((store, instance, created)) } @@ -58,10 +58,10 @@ fn paged_backend_works_for_module_memories() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let config = Config::new().with_memory_backend(MemoryBackend::paged(8)); let mut store = Store::new(Engine::new(config)); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; let memory = instance.memory("memory")?; memory.copy_from_slice(&mut store, 6, &[1, 2, 3, 4, 5, 6, 7, 8])?; @@ -184,7 +184,7 @@ fn disabled_local_memory_allocation_optimization_keeps_old_behavior() -> Result< "#, )?; let parser = Parser::with_options(ParserOptions::default().with_local_memory_allocation_optimization(false)); - let module = Module::from(parser.parse_module_bytes(&wasm)?); + let module = parser.parse_module_bytes(&wasm)?; let created = instantiate_module_with_counting_backend(module)?; diff --git a/crates/tinywasm/tests/memory_ref_api.rs b/crates/tinywasm/tests/memory_ref_api.rs index 97252f4..6f72477 100644 --- a/crates/tinywasm/tests/memory_ref_api.rs +++ b/crates/tinywasm/tests/memory_ref_api.rs @@ -1,5 +1,5 @@ use eyre::Result; -use tinywasm::{Module, Store}; +use tinywasm::{ModuleInstance, Store}; #[test] fn memory_ref_mut_copy_within_uses_src_then_dst_order() -> Result<()> { @@ -11,9 +11,9 @@ fn memory_ref_mut_copy_within_uses_src_then_dst_order() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut store = Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; let memory = instance.memory("memory")?; memory.copy_from_slice(&mut store, 0, &[1, 2, 3, 4])?; diff --git a/crates/tinywasm/tests/module_descriptors.rs b/crates/tinywasm/tests/module_descriptors.rs index 6c8c1ad..3804df8 100644 --- a/crates/tinywasm/tests/module_descriptors.rs +++ b/crates/tinywasm/tests/module_descriptors.rs @@ -1,6 +1,6 @@ use eyre::Result; use tinywasm::types::WasmType; -use tinywasm::{ExportType, ImportType, Module}; +use tinywasm_types::{ExportType, ImportType}; #[test] fn module_descriptors_resolve_imported_and_local_export_types() -> Result<()> { @@ -23,7 +23,7 @@ fn module_descriptors_resolve_imported_and_local_export_types() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let imports: Vec<_> = module.imports().collect(); assert_eq!(imports.len(), 2); diff --git a/crates/tinywasm/tests/resume_execution.rs b/crates/tinywasm/tests/resume_execution.rs index 06b666f..82e8a11 100644 --- a/crates/tinywasm/tests/resume_execution.rs +++ b/crates/tinywasm/tests/resume_execution.rs @@ -1,6 +1,7 @@ use eyre::Result; +use tinywasm::ModuleInstance; use tinywasm::engine::{Config, FuelPolicy}; -use tinywasm::{ExecProgress, Module, types::WasmValue}; +use tinywasm::{ExecProgress, types::WasmValue}; #[cfg(feature = "std")] use std::time::Duration; @@ -10,15 +11,15 @@ const ADD_WASM: &[u8] = include_bytes!("../../../examples/wasm/add.wasm"); #[test] fn typed_resume_matches_non_budgeted_call() -> Result<()> { - let module = Module::parse_bytes(FIBONACCI_WASM)?; + let module = tinywasm::parse_bytes(FIBONACCI_WASM)?; let mut store_full = tinywasm::Store::default(); - let instance_full = module.clone().instantiate(&mut store_full, None)?; + let instance_full = ModuleInstance::instantiate(&mut store_full, &module, None)?; let func_full = instance_full.func::(&store_full, "fibonacci_recursive")?; let expected = func_full.call(&mut store_full, 20)?; let mut store_budgeted = tinywasm::Store::default(); - let instance_budgeted = module.instantiate(&mut store_budgeted, None)?; + let instance_budgeted = ModuleInstance::instantiate(&mut store_budgeted, &module, None)?; let func_budgeted = instance_budgeted.func::(&store_budgeted, "fibonacci_recursive")?; let mut exec = func_budgeted.call_resumable(&mut store_budgeted, 20)?; @@ -38,9 +39,9 @@ fn typed_resume_matches_non_budgeted_call() -> Result<()> { #[test] fn untyped_resume_supports_zero_fuel() -> Result<()> { - let module = Module::parse_bytes(ADD_WASM)?; + let module = tinywasm::parse_bytes(ADD_WASM)?; let mut store = tinywasm::Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; let func = instance.func_untyped(&store, "add")?; let mut exec = func.call_resumable(&mut store, &[WasmValue::I32(20), WasmValue::I32(22)])?; @@ -58,15 +59,15 @@ fn untyped_resume_supports_zero_fuel() -> Result<()> { #[test] fn weighted_call_fuel_requires_more_rounds() -> Result<()> { - let module = Module::parse_bytes(FIBONACCI_WASM)?; + let module = tinywasm::parse_bytes(FIBONACCI_WASM)?; let mut per_instr_store = tinywasm::Store::default(); - let instance_per_instr = module.clone().instantiate(&mut per_instr_store, None)?; + let instance_per_instr = ModuleInstance::instantiate(&mut per_instr_store, &module, None)?; let func_per_instr = instance_per_instr.func::(&per_instr_store, "fibonacci_recursive")?; let mut weighted_store = tinywasm::Store::new(tinywasm::Engine::new(Config::new().with_fuel_policy(FuelPolicy::Weighted))); - let instance_weighted = module.instantiate(&mut weighted_store, None)?; + let instance_weighted = ModuleInstance::instantiate(&mut weighted_store, &module, None)?; let func_weighted = instance_weighted.func::(&weighted_store, "fibonacci_recursive")?; let fuel = 64; @@ -101,9 +102,9 @@ fn weighted_call_fuel_requires_more_rounds() -> Result<()> { #[cfg(feature = "std")] #[test] fn time_budget_zero_suspends_then_completes() -> Result<()> { - let module = Module::parse_bytes(ADD_WASM)?; + let module = tinywasm::parse_bytes(ADD_WASM)?; let mut store = tinywasm::Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; let func = instance.func::<(i32, i32), i32>(&store, "add")?; let mut exec = func.call_resumable(&mut store, (20, 22))?; diff --git a/crates/tinywasm/tests/store_ownership.rs b/crates/tinywasm/tests/store_ownership.rs index 0f81ea3..acf8e42 100644 --- a/crates/tinywasm/tests/store_ownership.rs +++ b/crates/tinywasm/tests/store_ownership.rs @@ -1,5 +1,5 @@ use eyre::Result; -use tinywasm::{Module, Store}; +use tinywasm::{ModuleInstance, Store}; const MODULE_WAT: &str = r#" (module @@ -14,10 +14,10 @@ const MODULE_WAT: &str = r#" #[test] fn func_handle_rejects_wrong_store() -> Result<()> { let wasm = wat::parse_str(MODULE_WAT)?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut owner_store = Store::default(); - let instance = module.instantiate(&mut owner_store, None)?; + let instance = ModuleInstance::instantiate(&mut owner_store, &module, None)?; let func = instance.func_untyped(&owner_store, "add")?; let mut other_store = Store::default(); @@ -30,10 +30,10 @@ fn func_handle_rejects_wrong_store() -> Result<()> { #[test] fn memory_access_rejects_wrong_store() -> Result<()> { let wasm = wat::parse_str(MODULE_WAT)?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut owner_store = Store::default(); - let instance = module.instantiate(&mut owner_store, None)?; + let instance = ModuleInstance::instantiate(&mut owner_store, &module, None)?; let memory = instance.memory("memory")?; let other_store = Store::default(); diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index 5b1d11c..cfa7f54 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -215,11 +215,9 @@ impl TestSuite { debug!("got wat module"); let result = catch_unwind_silent(|| { let (name, bytes) = encode_quote_wat(module); - let m = parse_module_bytes(&bytes).expect("failed to parse module bytes"); - + let module = parse_module_bytes(&bytes).expect("failed to parse module bytes"); let imports = Self::imports(&mut store, module_registry.modules()).unwrap(); - let module_instance = tinywasm::Module::from(m) - .instantiate(&mut store, Some(imports)) + let module_instance = ModuleInstance::instantiate(&mut store, &module, Some(imports)) .expect("failed to instantiate module"); (name, module_instance) @@ -319,9 +317,8 @@ impl TestSuite { wast::WastExecute::Wat(mut wat) => { let module = parse_module_bytes(&wat.encode().expect("failed to encode module")) .expect("failed to parse module"); - let module = tinywasm::Module::from(module); let imports = Self::imports(&mut store, module_registry.modules()).unwrap(); - module.instantiate(&mut store, Some(imports))?; + ModuleInstance::instantiate(&mut store, &module, Some(imports))?; return Ok(()); } wast::WastExecute::Get { module: _, global: _, .. } => { @@ -370,9 +367,8 @@ impl TestSuite { let res = catch_unwind_silent(|| { let module = parse_module_bytes(&module.encode().expect("failed to encode module")) .expect("failed to parse module"); - let module = tinywasm::Module::from(module); let imports = Self::imports(&mut store, module_registry.modules()).unwrap(); - module.instantiate(&mut store, Some(imports)) + ModuleInstance::instantiate(&mut store, &module, Some(imports)) }); match res { diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index e03f08b..61c163e 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -1,7 +1,8 @@ use std::panic::{self, AssertUnwindSafe}; use eyre::{Result, bail, eyre}; -use tinywasm_types::{ExternRef, FuncRef, ModuleInstanceAddr, TinyWasmModule, WasmType, WasmValue}; +use tinywasm::ModuleInstance; +use tinywasm_types::{ExternRef, FuncRef, Module, ModuleInstanceAddr, WasmType, WasmValue}; use wasm_testsuite::wast; use wasm_testsuite::wast::{QuoteWat, core::AbstractHeapType}; @@ -31,7 +32,7 @@ pub fn exec_fn_instance( } pub fn exec_fn( - module: Option<&TinyWasmModule>, + module: Option<&Module>, name: &str, args: &[tinywasm_types::WasmValue], imports: Option, @@ -41,8 +42,7 @@ pub fn exec_fn( }; let mut store = tinywasm::Store::default(); - let module = tinywasm::Module::from(module); - let instance = module.instantiate(&mut store, imports)?; + let instance = ModuleInstance::instantiate(&mut store, module, imports)?; instance.func_untyped(&store, name)?.call(&mut store, args) } @@ -78,7 +78,7 @@ pub fn encode_quote_wat(module: QuoteWat) -> (Option, Vec) { } } -pub fn parse_module_bytes(bytes: &[u8]) -> Result { +pub fn parse_module_bytes(bytes: &[u8]) -> Result { let parser = tinywasm_parser::Parser::new(); Ok(parser.parse_module_bytes(bytes)?) } diff --git a/crates/tinywasm/tests/typed_lookup.rs b/crates/tinywasm/tests/typed_lookup.rs index acc6a7a..15b31b1 100644 --- a/crates/tinywasm/tests/typed_lookup.rs +++ b/crates/tinywasm/tests/typed_lookup.rs @@ -1,5 +1,5 @@ use eyre::Result; -use tinywasm::Module; +use tinywasm::ModuleInstance; #[test] fn func_typed_rejects_wrong_param_or_result_types() -> Result<()> { @@ -14,9 +14,9 @@ fn func_typed_rejects_wrong_param_or_result_types() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut store = tinywasm::Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; assert!(instance.func::<(i32, i32), i32>(&store, "add").is_ok()); assert!(instance.func::(&store, "add").is_err()); @@ -37,9 +37,9 @@ fn func_typed_rejects_partial_multi_value_results() -> Result<()> { "#, )?; - let module = Module::parse_bytes(&wasm)?; + let module = tinywasm::parse_bytes(&wasm)?; let mut store = tinywasm::Store::default(); - let instance = module.instantiate(&mut store, None)?; + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; assert!(instance.func::<(), (i32, i32)>(&store, "pair").is_ok()); assert!(instance.func::<(), i32>(&store, "pair").is_err()); diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 94832c1..77e0be7 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -13,7 +13,7 @@ categories.workspace=true [dependencies] log={workspace=true, optional=true} postcard={version="1.1", optional=true, default-features=false, features=["alloc"]} -serde={version="1.0", optional=true, default-features=false, features=["alloc"]} +serde={version="1.0", optional=true, default-features=false, features=["alloc", "rc"]} [features] default=["std", "log", "archive", "debug"] diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index dc98e30..2636230 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -2,7 +2,7 @@ use core::fmt::{Display, Formatter}; use alloc::vec::Vec; -use crate::TinyWasmModule; +use crate::Module; #[rustfmt::skip] const TWASM_MAGIC: [u8; 16] = [ TWASM_MAGIC_PREFIX[0], TWASM_MAGIC_PREFIX[1], TWASM_MAGIC_PREFIX[2], TWASM_MAGIC_PREFIX[3], TWASM_VERSION[0], TWASM_VERSION[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; @@ -47,14 +47,14 @@ extern crate std; impl core::error::Error for TwasmError {} -impl TinyWasmModule { - /// Creates a `TinyWasmModule` from a slice of bytes. - pub fn from_twasm(wasm: &[u8]) -> Result { +impl Module { + /// Creates a [`Module`] from a slice of bytes. + pub fn try_from_twasm(wasm: &[u8]) -> Result { let len = validate_magic(wasm)?; postcard::from_bytes(&wasm[len..]).map_err(TwasmError::InvalidArchive) } - /// Serializes the `TinyWasmModule` into a vector of bytes. + /// Serializes the [`Module`] into a vector of bytes. pub fn serialize_twasm(&self) -> Result, TwasmError> { let buf = Vec::from(TWASM_MAGIC); postcard::to_extend(self, buf).map_err(TwasmError::InvalidArchive) @@ -67,17 +67,17 @@ mod tests { #[test] fn test_invalid_magic() { - let wasm = TinyWasmModule::default(); + let wasm = Module::default(); let mut twasm = wasm.serialize_twasm().expect("should serialize"); twasm[0] = 0; - assert!(matches!(TinyWasmModule::from_twasm(&twasm), Err(TwasmError::InvalidMagic))); + assert!(matches!(Module::try_from_twasm(&twasm), Err(TwasmError::InvalidMagic))); } #[test] fn test_invalid_version() { - let wasm = TinyWasmModule::default(); + let wasm = Module::default(); let mut twasm = wasm.serialize_twasm().expect("should serialize"); twasm[4] = 0; - assert!(matches!(TinyWasmModule::from_twasm(&twasm), Err(TwasmError::InvalidVersion))); + assert!(matches!(Module::try_from_twasm(&twasm), Err(TwasmError::InvalidVersion))); } } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 914e8c4..6196beb 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -10,10 +10,7 @@ extern crate alloc; use alloc::{boxed::Box, sync::Arc}; -use core::{ - fmt::Debug, - ops::{Deref, Range}, -}; +use core::ops::{Deref, Range}; // Memory defaults const MEM_PAGE_SIZE: u64 = 65536; @@ -63,12 +60,31 @@ pub mod archive { /// A `TinyWasm` WebAssembly Module /// /// This is the internal representation of a WebAssembly module in `TinyWasm`. -/// `TinyWasmModules` are validated before being created, so they are guaranteed to be valid (as long as they were created by `TinyWasm`). -/// This means you should not trust a `TinyWasmModule` created by a third party to be valid. +/// [`Module`] are validated before being created, so they are guaranteed to be valid (as long as they were created by `TinyWasm`). +/// This means you should not trust a [`Module`] created by a third party to be valid. #[derive(Clone, Default, PartialEq)] #[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] -pub struct TinyWasmModule { +pub struct Module(Arc); + +impl From for Module { + fn from(inner: ModuleInner) -> Self { + Self(Arc::new(inner)) + } +} + +impl Deref for Module { + type Target = ModuleInner; + fn deref(&self) -> &ModuleInner { + &self.0 + } +} + +#[doc(hidden)] +#[derive(Clone, Default, PartialEq)] +#[cfg_attr(feature = "debug", derive(Debug))] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub struct ModuleInner { /// Optional address of the start function /// /// Corresponds to the `start` section of the original WebAssembly module. @@ -77,52 +93,174 @@ pub struct TinyWasmModule { /// Optimized and validated WebAssembly functions /// /// Contains data from to the `code`, `func`, and `type` sections of the original WebAssembly module. - pub funcs: ArcSlice, + pub funcs: Arc<[Arc]>, /// A vector of type definitions, indexed by `TypeAddr` /// /// Corresponds to the `type` section of the original WebAssembly module. - pub func_types: ArcSlice, + pub func_types: Arc<[Arc]>, /// Exported items of the WebAssembly module. /// /// Corresponds to the `export` section of the original WebAssembly module. - pub exports: ArcSlice, + pub exports: Arc<[Export]>, /// Global components of the WebAssembly module. /// /// Corresponds to the `global` section of the original WebAssembly module. - pub globals: ArcSlice, + pub globals: Arc<[Global]>, /// Table components of the WebAssembly module used to initialize tables. /// /// Corresponds to the `table` section of the original WebAssembly module. - pub table_types: ArcSlice, + pub table_types: Arc<[TableType]>, /// Memory components of the WebAssembly module used to initialize memories. /// /// Corresponds to the `memory` section of the original WebAssembly module. - pub memory_types: ArcSlice, + pub memory_types: Arc<[MemoryType]>, /// Imports of the WebAssembly module. /// /// Corresponds to the `import` section of the original WebAssembly module. - pub imports: ArcSlice, + pub imports: Arc<[Import]>, /// Data segments of the WebAssembly module. /// /// Corresponds to the `data` section of the original WebAssembly module. - pub data: ArcSlice, + pub data: Arc<[Data]>, /// Element segments of the WebAssembly module. /// /// Corresponds to the `elem` section of the original WebAssembly module. - pub elements: ArcSlice, + pub elements: Arc<[Element]>, /// How instantiation should prepare the module's local memories. pub local_memory_allocation: LocalMemoryAllocation, } +impl Module { + /// Returns an iterator over the module's import descriptors. + /// + /// The returned data mirrors the module's import section and preserves order. + pub fn imports(&self) -> impl Iterator> { + self.0.imports.iter().filter_map(|import| { + let ty = match &import.kind { + ImportKind::Function(type_idx) => Some(ImportType::Func(self.0.func_types.get(*type_idx as usize)?)), + ImportKind::Table(table_ty) => Some(ImportType::Table(table_ty)), + ImportKind::Memory(memory_ty) => Some(ImportType::Memory(memory_ty)), + ImportKind::Global(global_ty) => Some(ImportType::Global(global_ty)), + }?; + + Some(ModuleImport { module: import.module.as_ref(), name: import.name.as_ref(), ty }) + }) + } + + /// Returns an iterator over the module's export descriptors. + /// + /// The returned data mirrors the module's export section and preserves order. + pub fn exports(&self) -> impl Iterator> { + fn imported_func_type(module: &ModuleInner, function_index: usize) -> Option<&FuncType> { + let mut seen = 0usize; + for import in module.imports.iter() { + if let ImportKind::Function(type_idx) = import.kind { + if seen == function_index { + return module.func_types.get(type_idx as usize).map(|ty| &**ty); + } + seen += 1; + } + } + None + } + + fn imported_global_type(module: &Module, global_index: usize) -> Option<&GlobalType> { + let mut seen = 0usize; + for import in module.imports.iter() { + if let ImportKind::Global(global_ty) = &import.kind { + if seen == global_index { + return Some(global_ty); + } + seen += 1; + } + } + None + } + + self.0.exports.iter().filter_map(move |export| { + let imports = self.0.imports.iter(); + let idx = export.index as usize; + let ty = match export.kind { + ExternalKind::Func => { + let imported_funcs = + imports.filter(|import| matches!(import.kind, ImportKind::Function(_))).count(); + if idx < imported_funcs { + ExportType::Func(imported_func_type(&self.0, idx)?) + } else { + let local_idx = idx - imported_funcs; + ExportType::Func(&self.0.funcs.get(local_idx)?.ty) + } + } + ExternalKind::Table => ExportType::Table(self.0.table_types.get(idx)?), + ExternalKind::Memory => ExportType::Memory(self.0.memory_types.get(idx)?), + ExternalKind::Global => { + let imported_globals = + imports.filter(|import| matches!(import.kind, ImportKind::Global(_))).count(); + if idx < imported_globals { + ExportType::Global(imported_global_type(self, idx)?) + } else { + let local_idx = idx - imported_globals; + ExportType::Global(&self.0.globals.get(local_idx)?.ty) + } + } + }; + + Some(ModuleExport { name: export.name.as_ref(), ty }) + }) + } +} + +/// A module export descriptor. +pub struct ModuleExport<'a> { + /// Export name. + pub name: &'a str, + /// Export type. + pub ty: ExportType<'a>, +} + +/// A module import descriptor. +pub struct ModuleImport<'a> { + /// Importing module name. + pub module: &'a str, + /// Import name. + pub name: &'a str, + /// Import type. + pub ty: ImportType<'a>, +} + +/// Imported entity type. +pub enum ImportType<'a> { + /// Imported function type. + Func(&'a FuncType), + /// Imported table type. + Table(&'a TableType), + /// Imported memory type. + Memory(&'a MemoryType), + /// Imported global type. + Global(&'a GlobalType), +} + +/// Exported entity type. +pub enum ExportType<'a> { + /// Exported function type. + Func(&'a FuncType), + /// Exported table type. + Table(&'a TableType), + /// Exported memory type. + Memory(&'a MemoryType), + /// Exported global type. + Global(&'a GlobalType), +} + /// How instantiation should prepare local memories declared by the module. #[derive(Clone, Copy, PartialEq, Eq, Default)] #[cfg_attr(feature = "debug", derive(Debug))] @@ -275,63 +413,11 @@ impl<'a> FromIterator<&'a WasmType> for ValueCounts { #[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct WasmFunction { - pub instructions: ArcSlice, + pub instructions: Box<[Instruction]>, pub data: WasmFunctionData, pub locals: ValueCounts, pub params: ValueCounts, - pub ty: FuncType, -} - -#[doc(hidden)] -#[derive(Clone, PartialEq)] -// wrapper around Arc<[T]> to support serde serialization and deserialization -pub struct ArcSlice(pub Arc<[T]>); - -impl Debug for ArcSlice { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - self.0.as_ref().fmt(f) - } -} - -impl From> for ArcSlice { - fn from(vec: alloc::vec::Vec) -> Self { - Self(Arc::from(vec)) - } -} - -impl Default for ArcSlice { - fn default() -> Self { - Self(Arc::from([])) - } -} - -impl Deref for ArcSlice { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - self.0.as_ref() - } -} - -impl FromIterator for ArcSlice { - fn from_iter>(iter: I) -> Self { - Self(Arc::from_iter(iter)) - } -} - -#[cfg(feature = "archive")] -impl serde::Serialize for ArcSlice { - fn serialize(&self, serializer: S) -> Result { - self.0.as_ref().serialize(serializer) - } -} - -#[cfg(feature = "archive")] -impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for ArcSlice { - fn deserialize>(deserializer: D) -> Result { - let vec: alloc::vec::Vec = alloc::vec::Vec::deserialize(deserializer)?; - Ok(Self(Arc::from(vec))) - } + pub ty: Arc, } #[derive(Clone, PartialEq, Eq, Default)] -- cgit v1.3.1