summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md18
-rw-r--r--crates/cli/src/bin.rs2
-rw-r--r--crates/parser/src/conversion.rs4
-rw-r--r--crates/parser/src/module.rs4
-rw-r--r--crates/tinywasm/benches/argon2id.rs2
-rw-r--r--crates/tinywasm/benches/fibonacci.rs2
-rw-r--r--crates/tinywasm/benches/tinywasm.rs6
-rw-r--r--crates/tinywasm/benches/tinywasm_modes.rs14
-rw-r--r--crates/tinywasm/src/func.rs301
-rw-r--r--crates/tinywasm/src/imports.rs487
-rw-r--r--crates/tinywasm/src/instance.rs257
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs13
-rw-r--r--crates/tinywasm/src/lib.rs6
-rw-r--r--crates/tinywasm/src/reference.rs390
-rw-r--r--crates/tinywasm/src/store/function.rs27
-rw-r--r--crates/tinywasm/src/store/mod.rs37
-rw-r--r--crates/tinywasm/tests/host_func_signature_check.rs68
-rw-r--r--crates/tinywasm/tests/import_linking.rs2
-rw-r--r--crates/tinywasm/tests/imported_table_init.rs12
-rw-r--r--crates/tinywasm/tests/internal_refs.rs45
-rw-r--r--crates/tinywasm/tests/memory_ref_api.rs8
-rw-r--r--crates/tinywasm/tests/resume_execution.rs12
-rw-r--r--crates/tinywasm/tests/store_ownership.rs5
-rw-r--r--crates/tinywasm/tests/testsuite/run.rs73
-rw-r--r--crates/tinywasm/tests/testsuite/util.rs4
-rw-r--r--crates/tinywasm/tests/typed_lookup.rs10
-rw-r--r--crates/types/src/lib.rs56
-rw-r--r--examples/archive.rs2
-rw-r--r--examples/funcref_callbacks.rs37
-rw-r--r--examples/linking.rs2
-rw-r--r--examples/resumable.rs2
-rw-r--r--examples/rust/src/tinywasm.rs4
-rw-r--r--examples/rust/src/tinywasm_no_std.rs4
-rw-r--r--examples/simple.rs2
-rw-r--r--examples/simple2.rs2
-rw-r--r--examples/wasm-rust.rs73
36 files changed, 984 insertions, 1009 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a6b138..42b2220 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,12 +21,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `engine::FuelPolicy` and `engine::Config::fuel_policy` for fuel accounting behavior
- New `canonicalize_nans` feature flag to enable canonicalizing NaN values in the `f32`, `f64`, and `v128` types
- Public API rework for runtime object access:
- - export lookups: `func`, `func_typed`, `memory`, `memory_mut`
- - table/global access: `table`, `table_mut`, `global`, `global_mut`, `global_get`, `global_set`
- - generic export access: `extern_item`, `extern_item_mut`
+ - export lookups: `func_untyped`/`func`, `memory`, `table`, `global`
+ - table/global value access: `global_get`, `global_set`
+ - generic export access: `extern_item` and `ExternItem`
- export iteration: `ModuleInstance::exports`
- module descriptors: `Module::imports`, `Module::exports`
- - raw memory data access: `MemoryRef::data`/`data_size`, `MemoryRefMut::data`/`data_mut`/`data_size`
+ - handle-based runtime objects with explicit store access: `Memory`, `Table`, `Global`, `Function`
### Changed
@@ -50,10 +50,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Increased MSRV to 1.90
- `Error::ParseError` was renamed to `Error::Parser`, and `Error::Twasm` was added
- `ModuleInstance` export lookup APIs were renamed:
- - `exported_func_untyped` -> `func`
- - `exported_func` -> `func_typed`
+ - `exported_func_untyped` -> `func_untyped`
+ - `exported_func` -> `func`
- `exported_memory` -> `memory`
- - `exported_memory_mut` -> `memory_mut`
+- `ModuleInstance` mutable export lookup variants were removed:
+ - `memory_mut`, `table_mut`, `global_mut`
+ - `extern_item_mut`
+- `FuncHandle` / `FuncHandleTyped` were renamed to `Function` / `FunctionTyped`
+- `HostFunction::new` / `HostFunction::func` / `HostFunction::typed` now require `&mut Store`
- `Imports::link_module` now takes a `ModuleInstance` instead of a raw module instance id
- `func_typed` now validates the exact wasm signature at lookup time and fails immediately on mismatches
diff --git a/crates/cli/src/bin.rs b/crates/cli/src/bin.rs
index 814edd4..5fd50f9 100644
--- a/crates/cli/src/bin.rs
+++ b/crates/cli/src/bin.rs
@@ -109,7 +109,7 @@ fn run(module: Module, func: Option<String>, args: &[WasmValue]) -> Result<()> {
let instance = module.instantiate(&mut store, None)?;
if let Some(func) = func {
- let func = instance.func(&store, &func)?;
+ let func = instance.func_untyped(&store, &func)?;
let res = func.call(&mut store, args)?;
info!("{res:?}");
}
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index dfd5415..63a42b2 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -86,7 +86,7 @@ pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result<Im
}),
wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)),
wasmparser::TypeRef::Global(ty) => {
- ImportKind::Global(GlobalType { mutable: ty.mutable, ty: convert_valtype(&ty.content_type) })
+ ImportKind::Global(GlobalType::new(convert_valtype(&ty.content_type), ty.mutable))
}
wasmparser::TypeRef::Tag(ty) => {
return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {ty:?}")));
@@ -140,7 +140,7 @@ pub(crate) fn convert_module_globals(
let global = global?;
let ty = convert_valtype(&global.ty.content_type);
let ops = global.init_expr.get_operators_reader();
- Ok(Global { init: process_const_operators(ops)?, ty: GlobalType { mutable: global.ty.mutable, ty } })
+ Ok(Global { init: process_const_operators(ops)?, ty: GlobalType::new(ty, global.ty.mutable) })
})
.collect::<Result<Vec<_>>>()
}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index b53dfd6..8405c6d 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -158,9 +158,9 @@ impl ModuleReader {
validator.end(offset)?;
self.end_reached = true;
}
- Payload::CustomSection(_reader) => {
+ Payload::CustomSection(reader) => {
debug!("Found custom section");
- debug!("Skipping custom section: {:?}", _reader.name());
+ debug!("Skipping custom section: {:?}", reader.name());
}
Payload::UnknownSection { .. } => return Err(ParseError::UnsupportedSection("Unknown section".into())),
section => return Err(ParseError::UnsupportedSection(format!("Unsupported section: {section:?}"))),
diff --git a/crates/tinywasm/benches/argon2id.rs b/crates/tinywasm/benches/argon2id.rs
index 5d1719f..277b904 100644
--- a/crates/tinywasm/benches/argon2id.rs
+++ b/crates/tinywasm/benches/argon2id.rs
@@ -24,7 +24,7 @@ fn argon2id_from_twasm(twasm: &[u8]) -> Result<TinyWasmModule> {
fn argon2id_run(module: TinyWasmModule) -> Result<()> {
let mut store = Store::default();
let instance = ModuleInstance::instantiate(&mut store, module.into(), None)?;
- let argon2 = instance.func_typed::<(i32, i32, i32), i32>(&store, "argon2id")?;
+ let argon2 = instance.func::<(i32, i32, i32), i32>(&store, "argon2id")?;
argon2.call(&mut store, (1000, 2, 1))?;
Ok(())
}
diff --git a/crates/tinywasm/benches/fibonacci.rs b/crates/tinywasm/benches/fibonacci.rs
index 2865594..51bfa52 100644
--- a/crates/tinywasm/benches/fibonacci.rs
+++ b/crates/tinywasm/benches/fibonacci.rs
@@ -23,7 +23,7 @@ fn fibonacci_from_twasm(twasm: &[u8]) -> Result<TinyWasmModule> {
fn fibonacci_run(module: TinyWasmModule, recursive: bool, n: i32) -> Result<()> {
let mut store = Store::default();
let instance = ModuleInstance::instantiate(&mut store, module.into(), None)?;
- let argon2 = instance.func_typed::<i32, i32>(
+ let argon2 = instance.func::<i32, i32>(
&store,
match recursive {
true => "fibonacci_recursive",
diff --git a/crates/tinywasm/benches/tinywasm.rs b/crates/tinywasm/benches/tinywasm.rs
index 7ab9587..8de1838 100644
--- a/crates/tinywasm/benches/tinywasm.rs
+++ b/crates/tinywasm/benches/tinywasm.rs
@@ -1,6 +1,6 @@
use criterion::{Criterion, criterion_group, criterion_main};
use eyre::Result;
-use tinywasm::{Extern, FuncContext, Imports, ModuleInstance, Store, types};
+use tinywasm::{FuncContext, HostFunction, Imports, ModuleInstance, Store, types};
use types::TinyWasmModule;
const WASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm");
@@ -24,9 +24,9 @@ fn tinywasm_from_twasm(twasm: &[u8]) -> Result<TinyWasmModule> {
fn tinywasm_run(module: TinyWasmModule) -> Result<()> {
let mut store = Store::default();
let mut imports = Imports::default();
- imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(()))).expect("define");
+ imports.define("env", "printi32", HostFunction::from(&mut store, |_: FuncContext<'_>, _: i32| Ok(())));
let instance = ModuleInstance::instantiate(&mut store, module.into(), Some(imports)).expect("instantiate");
- let hello = instance.func_typed::<(), ()>(&store, "hello").expect("func_typed");
+ let hello = instance.func::<(), ()>(&store, "hello").expect("func_typed");
hello.call(&mut store, ()).expect("call");
Ok(())
}
diff --git a/crates/tinywasm/benches/tinywasm_modes.rs b/crates/tinywasm/benches/tinywasm_modes.rs
index 54f8033..f5f70d0 100644
--- a/crates/tinywasm/benches/tinywasm_modes.rs
+++ b/crates/tinywasm/benches/tinywasm_modes.rs
@@ -2,7 +2,7 @@ use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use eyre::Result;
use tinywasm::engine::{Config, FuelPolicy};
use tinywasm::types::TinyWasmModule;
-use tinywasm::{Engine, ExecProgress, Extern, FuncContext, FuncHandleTyped, Imports, ModuleInstance, Store};
+use tinywasm::{Engine, ExecProgress, FuncContext, FunctionTyped, HostFunction, Imports, ModuleInstance, Store};
const WASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm");
const FUEL_PER_ROUND: u32 = 512;
@@ -14,26 +14,26 @@ fn tinywasm_parse() -> Result<TinyWasmModule> {
Ok(parser.parse_module_bytes(WASM)?)
}
-fn setup_typed_func(module: TinyWasmModule, engine: Option<Engine>) -> Result<(Store, FuncHandleTyped<(), ()>)> {
+fn setup_typed_func(module: TinyWasmModule, engine: Option<Engine>) -> Result<(Store, FunctionTyped<(), ()>)> {
let mut store = match engine {
Some(engine) => Store::new(engine),
None => Store::default(),
};
let mut imports = Imports::default();
- imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(())))?;
+ imports.define("env", "printi32", HostFunction::from(&mut store, |_: FuncContext<'_>, _: i32| Ok(())));
let instance = ModuleInstance::instantiate(&mut store, module.into(), Some(imports))?;
- let func = instance.func_typed::<(), ()>(&store, "hello")?;
+ let func = instance.func::<(), ()>(&store, "hello")?;
Ok((store, func))
}
-fn run_call(store: &mut Store, func: &FuncHandleTyped<(), ()>) -> Result<()> {
+fn run_call(store: &mut Store, func: &FunctionTyped<(), ()>) -> Result<()> {
func.call(store, ())?;
Ok(())
}
-fn run_resume_with_fuel(store: &mut Store, func: &FuncHandleTyped<(), ()>) -> Result<()> {
+fn run_resume_with_fuel(store: &mut Store, func: &FunctionTyped<(), ()>) -> Result<()> {
let mut execution = func.call_resumable(store, ())?;
loop {
match execution.resume_with_fuel(FUEL_PER_ROUND)? {
@@ -43,7 +43,7 @@ fn run_resume_with_fuel(store: &mut Store, func: &FuncHandleTyped<(), ()>) -> Re
}
}
-fn run_resume_with_time_budget(store: &mut Store, func: &FuncHandleTyped<(), ()>) -> Result<()> {
+fn run_resume_with_time_budget(store: &mut Store, func: &FunctionTyped<(), ()>) -> Result<()> {
let mut execution = func.call_resumable(store, ())?;
loop {
match execution.resume_with_time_budget(TIME_BUDGET_PER_ROUND)? {
diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs
index 51ac9b0..18bcff9 100644
--- a/crates/tinywasm/src/func.rs
+++ b/crates/tinywasm/src/func.rs
@@ -1,71 +1,25 @@
use crate::interpreter::stack::CallFrame;
-use crate::{Error, FuncContext, InterpreterRuntime, Result, Store};
-use crate::{Function, unlikely};
+use crate::reference::StoreItem;
+use crate::{Error, FunctionDef, InterpreterRuntime, Result, Store, unlikely};
+use alloc::rc::Rc;
use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec};
use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, ValType, WasmValue};
-#[derive(Debug, Clone, PartialEq, Eq)]
-/// Progress for fuel-limited function execution.
-pub enum ExecProgress<T> {
- /// Execution completed and produced a result.
- Completed(T),
- /// Execution suspended after exhausting fuel or time budget.
- Suspended,
-}
-
-#[derive(Clone)]
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub(crate) struct ExecutionState {
- pub(crate) callframe: CallFrame,
-}
-
-/// A function handle
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct FuncHandle {
- pub(crate) store_id: usize,
- pub(crate) module_addr: ModuleInstanceAddr,
- pub(crate) addr: u32,
- pub(crate) ty: FuncType,
-}
-
-/// Resumable execution for an untyped function call.
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct FuncExecution<'store> {
- store: &'store mut Store,
- state: FuncExecutionState,
-}
-
-#[cfg_attr(feature = "debug", derive(Debug))]
-enum FuncExecutionState {
- Running { exec_state: ExecutionState, root_func_addr: u32 },
- Completed { result: Option<Vec<WasmValue>> },
-}
-
-/// Resumable execution for a typed function call.
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct FuncExecutionTyped<'store, R> {
- execution: FuncExecution<'store>,
- marker: core::marker::PhantomData<R>,
-}
-
-impl FuncHandle {
+impl Function {
/// Call a function (Invocation)
///
/// See <https://webassembly.github.io/spec/core/exec/modules.html#invocation>
#[inline]
pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result<Vec<WasmValue>> {
- if self.store_id != store.id() {
- return Err(Error::InvalidStore);
- }
-
+ self.item.validate_store(store)?;
validate_call_params(&self.ty, params)?;
let func_inst = store.state.get_func(self.addr);
let wasm_func = match &func_inst.func {
- Function::Host(host_func) => {
+ FunctionDef::Host(host_func) => {
return host_func.clone().call(FuncContext { store, module_addr: self.module_addr }, params);
}
- Function::Wasm(wasm_func) => wasm_func.clone(),
+ FunctionDef::Wasm(wasm_func) => wasm_func.clone(),
};
// Reset stack, push args, allocate locals, create entry frame.
@@ -91,10 +45,7 @@ impl FuncHandle {
store: &'store mut Store,
params: &[WasmValue],
) -> Result<FuncExecution<'store>> {
- if self.store_id != store.id() {
- return Err(Error::InvalidStore);
- }
-
+ self.item.validate_store(store)?;
validate_call_params(&self.ty, params)?;
let func_inst = store.state.get_func(self.addr);
@@ -102,11 +53,11 @@ impl FuncHandle {
let func = func_inst.func.clone();
match func {
- Function::Host(host_func) => {
+ FunctionDef::Host(host_func) => {
let result = host_func.call(FuncContext { store, module_addr: self.module_addr }, params)?;
Ok(FuncExecution { store, state: FuncExecutionState::Completed { result: Some(result) } })
}
- Function::Wasm(wasm_func) => {
+ FunctionDef::Wasm(wasm_func) => {
store.stack.clear();
store.stack.values.extend_from_wasmvalues(params)?;
let locals_base = store.stack.values.enter_locals(&wasm_func.params, &wasm_func.locals)?;
@@ -125,6 +76,225 @@ impl FuncHandle {
}
}
+#[derive(Clone, PartialEq, Eq)]
+/// Progress for fuel-limited function execution.
+pub enum ExecProgress<T> {
+ /// Execution completed and produced a result.
+ Completed(T),
+ /// Execution suspended after exhausting fuel or time budget.
+ Suspended,
+}
+
+#[derive(Clone)]
+#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
+pub(crate) struct ExecutionState {
+ pub(crate) callframe: CallFrame,
+}
+
+/// A function handle
+#[derive(Clone)]
+#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
+pub struct Function {
+ pub(crate) item: StoreItem,
+ pub(crate) module_addr: ModuleInstanceAddr,
+ pub(crate) addr: u32,
+ pub(crate) ty: FuncType,
+}
+
+/// A typed function handle
+#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
+pub struct FunctionTyped<P, R> {
+ /// The underlying function handle
+ pub func: Function,
+ pub(crate) marker: core::marker::PhantomData<(P, R)>,
+}
+
+/// A host function
+pub struct HostFunction {
+ pub(crate) ty: tinywasm_types::FuncType,
+ pub(crate) func: HostFuncInner,
+}
+
+impl HostFunction {
+ /// Get the function's type
+ pub fn ty(&self) -> &tinywasm_types::FuncType {
+ &self.ty
+ }
+
+ /// Call the function
+ pub fn call(&self, ctx: FuncContext<'_>, args: &[WasmValue]) -> Result<Vec<WasmValue>> {
+ (self.func)(ctx, args)
+ }
+
+ /// Create a new untyped host function import.
+ pub fn from_untyped(
+ store: &mut Store,
+ ty: &tinywasm_types::FuncType,
+ func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>> + 'static,
+ ) -> Function {
+ let ty_inner = ty.clone();
+ let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
+ let ty = ty_inner.clone();
+ let result = func(ctx, args)?;
+
+ if result.len() != ty.results.len() {
+ return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result });
+ };
+
+ result.iter().zip(ty.results.iter()).try_for_each(|(val, res_ty)| {
+ if val.val_type() != *res_ty {
+ return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result.clone() });
+ }
+ Ok(())
+ })?;
+
+ Ok(result)
+ };
+
+ let addr = store.add_func(FunctionDef::Host(Rc::new(Self { func: Box::new(inner_func), ty: ty.clone() })), 0);
+ Function { item: crate::StoreItem::new(store.id(), addr), module_addr: 0, addr, ty: ty.clone() }
+ }
+
+ /// Create a new typed host function import.
+ pub fn from<P, R>(store: &mut Store, func: impl Fn(FuncContext<'_>, P) -> Result<R> + 'static) -> Function
+ where
+ P: FromWasmValueTuple + ValTypesFromTuple,
+ R: IntoWasmValueTuple + ValTypesFromTuple,
+ {
+ let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
+ let args = P::from_wasm_value_tuple(args)?;
+ let result = func(ctx, args)?;
+ Ok(result.into_wasm_value_tuple())
+ };
+
+ let results = R::val_types();
+ let ty = tinywasm_types::FuncType { params: P::val_types(), results };
+ let addr = store.add_func(FunctionDef::Host(Rc::new(Self { func: Box::new(inner_func), ty: ty.clone() })), 0);
+ Function { item: crate::StoreItem::new(store.id(), addr), module_addr: 0, addr, ty }
+ }
+}
+
+pub(crate) type HostFuncInner = Box<dyn Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>>>;
+
+/// The context of a host-function call
+#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
+pub struct FuncContext<'a> {
+ pub(crate) store: &'a mut crate::Store,
+ pub(crate) module_addr: ModuleInstanceAddr,
+}
+
+impl FuncContext<'_> {
+ /// Get the store.
+ pub fn store(&self) -> &crate::Store {
+ self.store
+ }
+
+ /// Get mutable access to the store.
+ pub fn store_mut(&mut self) -> &mut crate::Store {
+ self.store
+ }
+
+ /// Get the module instance.
+ pub fn module(&self) -> crate::ModuleInstance {
+ self.store.get_module_instance(self.module_addr).unwrap_or_else(|| {
+ unreachable!("invalid module instance address in host function context: {}", self.module_addr)
+ })
+ }
+
+ /// Get a memory export.
+ pub fn memory(&self, name: &str) -> Result<crate::Memory> {
+ self.module().memory(name)
+ }
+
+ /// Get any exported extern value by name.
+ pub fn extern_item(&self, name: &str) -> Result<crate::ExternItem> {
+ self.module().extern_item(name)
+ }
+
+ /// Get a table export.
+ pub fn table(&self, name: &str) -> Result<crate::Table> {
+ self.module().table(name)
+ }
+
+ /// Get the value of a global export.
+ pub fn global_get(&self, name: &str) -> Result<WasmValue> {
+ self.module().global_get(self.store, name)
+ }
+
+ /// Get a global export.
+ pub fn global(&self, name: &str) -> Result<crate::Global> {
+ self.module().global(name)
+ }
+
+ /// Set the value of a mutable global export.
+ pub fn global_set(&mut self, name: &str, value: WasmValue) -> Result<()> {
+ self.module().global_set(self.store, name, value)
+ }
+
+ /// Charge additional fuel from the currently running resumable invocation.
+ ///
+ /// This is a no-op when the current invocation is not using fuel-based
+ /// resumption.
+ pub fn charge_fuel(&mut self, fuel: u32) {
+ self.store.execution_fuel = self.store.execution_fuel.saturating_sub(fuel);
+ }
+
+ /// Get remaining fuel for the current invocation.
+ ///
+ /// Returns `0` when fuel-based resumption is not active.
+ pub fn remaining_fuel(&self) -> u32 {
+ self.store.execution_fuel
+ }
+}
+
+impl core::ops::Deref for FuncContext<'_> {
+ type Target = crate::Store;
+
+ fn deref(&self) -> &Self::Target {
+ self.store
+ }
+}
+
+impl core::ops::DerefMut for FuncContext<'_> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.store
+ }
+}
+
+impl<'a> FuncContext<'a> {
+ /// Create a new host function context.
+ pub const fn new(store: &'a mut crate::Store, module_addr: ModuleInstanceAddr) -> Self {
+ Self { store, module_addr }
+ }
+}
+
+#[cfg(feature = "debug")]
+impl core::fmt::Debug for HostFunction {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_struct("HostFunction").field("ty", &self.ty).field("func", &"...").finish()
+ }
+}
+
+/// Resumable execution for an untyped function call.
+#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
+pub struct FuncExecution<'store> {
+ store: &'store mut Store,
+ state: FuncExecutionState,
+}
+
+#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
+enum FuncExecutionState {
+ Running { exec_state: ExecutionState, root_func_addr: u32 },
+ Completed { result: Option<Vec<WasmValue>> },
+}
+
+/// Resumable execution for a typed function call.
+#[cfg_attr(feature = "debug", derive(core::fmt::Debug))]
+pub struct FuncExecutionTyped<'store, R> {
+ execution: FuncExecution<'store>,
+ marker: core::marker::PhantomData<R>,
+}
+
impl<'store> FuncExecution<'store> {
/// Resume execution with up to `fuel` units of fuel.
///
@@ -214,21 +384,12 @@ fn validate_call_params(func_ty: &FuncType, params: &[WasmValue]) -> Result<()>
}
fn collect_call_results(store: &mut Store, func_ty: &FuncType) -> Result<Vec<WasmValue>> {
- // m values are on the top of the stack (Ensured by validation)
- debug_assert!(store.stack.values.len() >= func_ty.results.len());
+ debug_assert!(store.stack.values.len() >= func_ty.results.len()); // m values are on the top of the stack (Ensured by validation)
let mut res: Vec<_> = store.stack.values.pop_types(func_ty.results.iter().rev()).collect(); // pop in reverse order since the stack is LIFO
res.reverse(); // reverse to get the original order
Ok(res)
}
-/// A typed function handle
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct FuncHandleTyped<P, R> {
- /// The underlying function handle
- pub func: FuncHandle,
- pub(crate) marker: core::marker::PhantomData<(P, R)>,
-}
-
pub trait IntoWasmValueTuple {
fn into_wasm_value_tuple(self) -> Vec<WasmValue>;
}
@@ -239,7 +400,7 @@ pub trait FromWasmValueTuple {
Self: Sized;
}
-impl<P: IntoWasmValueTuple, R: FromWasmValueTuple> FuncHandleTyped<P, R> {
+impl<P: IntoWasmValueTuple, R: FromWasmValueTuple> FunctionTyped<P, R> {
/// Call a typed function
pub fn call(&self, store: &mut Store, params: P) -> Result<R> {
// Convert params into Vec<WasmValue>
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index 18b8f0a..b8dee06 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -1,252 +1,47 @@
-use alloc::boxed::Box;
use alloc::collections::BTreeMap;
-use alloc::rc::Rc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt::Debug;
-use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple};
-use crate::instance::{ExternItemRef, ExternItemRefMut};
-use crate::{GlobalRef, GlobalRefMut, LinkingError, MemoryRef, MemoryRefMut, Result, TableRef, TableRefMut, log};
+use crate::{Function, Global, LinkingError, Memory, Result, Table, log};
use tinywasm_types::*;
-/// The internal representation of a function
-#[derive(Clone)]
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub enum Function {
- /// A host function
- Host(Rc<HostFunction>),
-
- /// A pointer to a WebAssembly function
- Wasm(Rc<WasmFunction>),
-}
-
-impl Function {
- pub(crate) fn ty(&self) -> &FuncType {
- match self {
- Self::Host(f) => &f.ty,
- Self::Wasm(f) => &f.ty,
- }
- }
-}
-
-/// A host function
-pub struct HostFunction {
- pub(crate) ty: tinywasm_types::FuncType,
- pub(crate) func: HostFuncInner,
-}
-
-impl HostFunction {
- /// Get the function's type
- pub fn ty(&self) -> &tinywasm_types::FuncType {
- &self.ty
- }
-
- /// Call the function
- pub fn call(&self, ctx: FuncContext<'_>, args: &[WasmValue]) -> Result<Vec<WasmValue>> {
- (self.func)(ctx, args)
- }
-}
-
-pub(crate) type HostFuncInner = Box<dyn Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>>>;
-
-/// The context of a host-function call
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct FuncContext<'a> {
- pub(crate) store: &'a mut crate::Store,
- pub(crate) module_addr: ModuleInstanceAddr,
-}
-
-impl FuncContext<'_> {
- /// Get a reference to the store
- pub fn store(&self) -> &crate::Store {
- self.store
- }
-
- /// Get a mutable reference to the store
- pub fn store_mut(&mut self) -> &mut crate::Store {
- self.store
- }
-
- /// Get a reference to the module instance
- pub fn module(&self) -> crate::ModuleInstance {
- self.store.get_module_instance(self.module_addr).unwrap_or_else(|| {
- unreachable!("invalid module instance address in host function context: {}", self.module_addr)
- })
- }
-
- /// Get a reference to a memory export.
- pub fn memory(&self, name: &str) -> Result<MemoryRef<'_>> {
- self.module().memory(self.store, name)
- }
-
- /// Get a mutable reference to a memory export.
- pub fn memory_mut(&mut self, name: &str) -> Result<MemoryRefMut<'_>> {
- self.module().memory_mut(self.store, name)
- }
-
- /// Get any exported extern value by name.
- pub fn extern_item(&self, name: &str) -> Result<ExternItemRef<'_>> {
- self.module().extern_item(self.store, name)
- }
-
- /// Get any exported extern value by name with mutable access when applicable.
- pub fn extern_item_mut(&mut self, name: &str) -> Result<ExternItemRefMut<'_>> {
- self.module().extern_item_mut(self.store, name)
- }
-
- /// Get a reference to a table export.
- pub fn table(&self, name: &str) -> Result<TableRef<'_>> {
- self.module().table(self.store, name)
- }
-
- /// Get a mutable reference to a table export.
- pub fn table_mut(&mut self, name: &str) -> Result<TableRefMut<'_>> {
- self.module().table_mut(self.store, name)
- }
-
- /// Get the value of a global export.
- pub fn global_get(&self, name: &str) -> Result<WasmValue> {
- self.module().global_get(self.store, name)
- }
-
- /// Get a reference to a global export.
- pub fn global(&self, name: &str) -> Result<GlobalRef<'_>> {
- self.module().global(self.store, name)
- }
-
- /// Get a mutable reference to a global export.
- pub fn global_mut(&mut self, name: &str) -> Result<GlobalRefMut<'_>> {
- self.module().global_mut(self.store, name)
- }
-
- /// Set the value of a mutable global export.
- pub fn global_set(&mut self, name: &str, value: WasmValue) -> Result<()> {
- self.module().global_set(self.store, name, value)
- }
-
- /// Charge additional fuel from the currently running resumable invocation.
- ///
- /// This is a no-op when the current invocation is not using fuel-based
- /// resumption.
- pub fn charge_fuel(&mut self, fuel: u32) {
- self.store.execution_fuel = self.store.execution_fuel.saturating_sub(fuel);
- }
-
- /// Get remaining fuel for the current invocation.
- ///
- /// Returns `0` when fuel-based resumption is not active.
- pub fn remaining_fuel(&self) -> u32 {
- self.store.execution_fuel
- }
-}
-
-#[cfg(feature = "debug")]
-impl Debug for HostFunction {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- f.debug_struct("HostFunction").field("ty", &self.ty).field("func", &"...").finish()
- }
-}
-
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[non_exhaustive]
-/// An external value
+/// An external import value.
pub enum Extern {
- /// A global value
- Global {
- /// The type of the global value.
- ty: GlobalType,
- /// The actual value of the global, encapsulated in `WasmValue`.
- val: WasmValue,
- },
-
- /// A table
- Table {
- /// Defines the type of the table, including its element type and limits.
- ty: TableType,
- /// The initial value of the table.
- init: WasmValue,
- },
-
- /// A memory
- Memory {
- /// Defines the type of the memory, including its limits and the type of its pages.
- ty: MemoryType,
- },
-
- /// A function
+ /// A global instance.
+ Global(Global),
+ /// A table instance.
+ Table(Table),
+ /// A memory instance.
+ Memory(Memory),
+ /// A function import.
Function(Function),
}
-impl Extern {
- /// Create a new global import
- pub const fn global(val: WasmValue, mutable: bool) -> Self {
- Self::Global { ty: GlobalType { ty: val.val_type(), mutable }, val }
- }
-
- /// Create a new table import
- pub const fn table(ty: TableType, init: WasmValue) -> Self {
- Self::Table { ty, init }
+impl From<Global> for Extern {
+ fn from(value: Global) -> Self {
+ Self::Global(value)
}
+}
- /// Create a new memory import
- pub const fn memory(ty: MemoryType) -> Self {
- Self::Memory { ty }
- }
-
- /// Create a new function import
- pub fn func(
- ty: &tinywasm_types::FuncType,
- func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>> + 'static,
- ) -> Self {
- let ty_inner = ty.clone();
- let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
- let ty = ty_inner.clone();
- let result = func(ctx, args)?;
-
- if result.len() != ty.results.len() {
- return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result });
- };
-
- result.iter().zip(ty.results.iter()).try_for_each(|(val, res_ty)| {
- if val.val_type() != *res_ty {
- return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result.clone() });
- }
- Ok(())
- })?;
-
- Ok(result)
- };
-
- Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty: ty.clone() })))
+impl From<Table> for Extern {
+ fn from(value: Table) -> Self {
+ Self::Table(value)
}
+}
- /// Create a new typed function import
- pub fn typed_func<P, R>(func: impl Fn(FuncContext<'_>, P) -> Result<R> + 'static) -> Self
- where
- P: FromWasmValueTuple + ValTypesFromTuple,
- R: IntoWasmValueTuple + ValTypesFromTuple + Debug,
- {
- let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
- let args = P::from_wasm_value_tuple(args)?;
- let result = func(ctx, args)?;
- Ok(result.into_wasm_value_tuple())
- };
-
- let results = R::val_types();
- let ty = tinywasm_types::FuncType { params: P::val_types(), results };
- Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty })))
+impl From<Memory> for Extern {
+ fn from(value: Memory) -> Self {
+ Self::Memory(value)
}
+}
- /// Get the kind of the external value
- pub const fn kind(&self) -> ExternalKind {
- match self {
- Self::Global { .. } => ExternalKind::Global,
- Self::Table { .. } => ExternalKind::Table,
- Self::Memory { .. } => ExternalKind::Memory,
- Self::Function { .. } => ExternalKind::Func,
- }
+impl From<Function> for Extern {
+ fn from(value: Function) -> Self {
+ Self::Function(value)
}
}
@@ -271,8 +66,8 @@ impl From<&Import> for ExternName {
/// ```rust
/// # use log;
/// # fn main() -> tinywasm::Result<()> {
-/// use tinywasm::{Extern, Imports, Module, Store};
-/// use tinywasm::types::{ValType, TableType, MemoryType, MemoryArch, WasmValue};
+/// use tinywasm::{Global, HostFunction, Imports, Memory, Module, Store, Table};
+/// use tinywasm::types::{ValType, TableType, MemoryType, WasmValue};
/// # let wasm = wat::parse_str("(module)").expect("valid wat");
/// # let module = Module::parse_bytes(&wasm)?;
/// # let mut store = Store::default();
@@ -281,19 +76,20 @@ impl From<&Import> for ExternName {
///
/// // function args can be either a single
/// // value that implements `TryFrom<WasmValue>` or a tuple of them
-/// let print_i32 = Extern::typed_func(|_ctx: tinywasm::FuncContext<'_>, arg: i32| {
+/// let print_i32 = HostFunction::from(&mut store, |_ctx: tinywasm::FuncContext<'_>, arg: i32| {
/// log::debug!("print_i32: {}", arg);
/// Ok(())
/// });
///
-/// let table_type = TableType::new(ValType::RefFunc, 10, Some(20));
-/// let table_init = WasmValue::default_for(ValType::RefFunc);
+/// let table = Table::new(&mut store, TableType::new(ValType::RefFunc, 10, Some(20)), WasmValue::default_for(ValType::RefFunc))?;
+/// let memory = Memory::new(&mut store, MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)))?;
+/// let global_i32 = Global::new(&mut store, tinywasm::types::GlobalType::default().with_ty(ValType::I32), WasmValue::I32(666))?;
///
/// imports
-/// .define("my_module", "print_i32", print_i32)?
-/// .define("my_module", "table", Extern::table(table_type, table_init))?
-/// .define("my_module", "memory", Extern::memory(MemoryType::new(MemoryArch::I32, 1, Some(2), None)))?
-/// .define("my_module", "global_i32", Extern::global(WasmValue::I32(666), false))?
+/// .define("my_module", "print_i32", print_i32)
+/// .define("my_module", "table", table)
+/// .define("my_module", "memory", memory)
+/// .define("my_module", "global_i32", global_i32)
/// .link_module("my_other_module", my_other_instance)?;
/// # Ok(())
/// # }
@@ -302,15 +98,13 @@ impl From<&Import> for ExternName {
#[derive(Default, Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Imports {
- values: BTreeMap<ExternName, Extern>,
+ globals: BTreeMap<ExternName, Global>,
+ tables: BTreeMap<ExternName, Table>,
+ memories: BTreeMap<ExternName, Memory>,
+ function_handles: BTreeMap<ExternName, Function>,
modules: BTreeMap<String, crate::ModuleInstance>,
}
-pub(crate) enum ResolvedExtern<S, V> {
- Store(S), // already in the store
- Extern(V), // needs to be added to the store, provided value
-}
-
pub(crate) struct ResolvedImports {
pub(crate) globals: Vec<GlobalAddr>,
pub(crate) tables: Vec<TableAddr>,
@@ -327,12 +121,21 @@ impl ResolvedImports {
impl Imports {
/// Create a new empty import set
pub const fn new() -> Self {
- Self { values: BTreeMap::new(), modules: BTreeMap::new() }
+ Self {
+ globals: BTreeMap::new(),
+ tables: BTreeMap::new(),
+ memories: BTreeMap::new(),
+ function_handles: BTreeMap::new(),
+ modules: BTreeMap::new(),
+ }
}
/// Merge two import sets
pub fn merge(mut self, other: Self) -> Self {
- self.values.extend(other.values);
+ self.globals.extend(other.globals);
+ self.tables.extend(other.tables);
+ self.memories.extend(other.memories);
+ self.function_handles.extend(other.function_handles);
self.modules.extend(other.modules);
self
}
@@ -345,30 +148,41 @@ impl Imports {
Ok(self)
}
- /// Define an import
- pub fn define(&mut self, module: &str, name: &str, value: Extern) -> Result<&mut Self> {
- self.values.insert(ExternName { module: module.to_string(), name: name.to_string() }, value);
- Ok(self)
+ /// Define an import value.
+ pub fn define(&mut self, module: &str, name: &str, value: impl Into<Extern>) -> &mut Self {
+ let name = ExternName { module: module.to_string(), name: name.to_string() };
+ match value.into() {
+ Extern::Global(v) => {
+ self.globals.insert(name, v);
+ }
+ Extern::Table(v) => {
+ self.tables.insert(name, v);
+ }
+ Extern::Memory(v) => {
+ self.memories.insert(name, v);
+ }
+ Extern::Function(v) => {
+ self.function_handles.insert(name, v);
+ }
+ }
+ self
}
- pub(crate) fn take(
- &mut self,
- store: &mut crate::Store,
- import: &Import,
- ) -> Result<Option<ResolvedExtern<ExternVal, Extern>>> {
+ pub(crate) fn take_defined(&self, import: &Import) -> Option<Extern> {
let name = ExternName::from(import);
- if let Some(v) = self.values.get(&name) {
- return Ok(Some(ResolvedExtern::Extern(v.clone())));
+ if let Some(v) = self.globals.get(&name) {
+ return Some(Extern::Global(*v));
}
- if let Some(instance) = self.modules.get(&name.module) {
- if instance.0.store_id != store.id() {
- return Err(crate::Error::InvalidStore);
- }
-
- return Ok(instance.export_addr(&import.name).map(ResolvedExtern::Store));
+ if let Some(v) = self.tables.get(&name) {
+ return Some(Extern::Table(*v));
}
-
- Ok(None)
+ if let Some(v) = self.memories.get(&name) {
+ return Some(Extern::Memory(*v));
+ }
+ if let Some(v) = self.function_handles.get(&name) {
+ return Some(Extern::Function(v.clone()));
+ }
+ None
}
#[cfg(not(feature = "debug"))]
@@ -408,12 +222,11 @@ impl Imports {
import: &Import,
expected: &MemoryType,
actual: &MemoryType,
- real_size: Option<usize>,
+ real_size: usize,
) -> Result<()> {
Self::compare_types(import, &expected.arch(), &actual.arch())?;
- if actual.page_count_initial() > expected.page_count_initial()
- && real_size.is_none_or(|size| actual.page_count_initial() > size as u64)
+ if actual.page_count_initial() > expected.page_count_initial() && actual.page_count_initial() > real_size as u64
{
return Err(LinkingError::incompatible_import_type(import).into());
}
@@ -430,82 +243,106 @@ impl Imports {
}
pub(crate) fn link(
- mut self,
+ self,
store: &mut crate::Store,
module: &crate::Module,
- idx: ModuleInstanceAddr,
+ _idx: ModuleInstanceAddr,
) -> Result<ResolvedImports> {
let mut imports = ResolvedImports::new();
for import in &*module.0.imports {
- match self.take(store, import)?.ok_or_else(|| LinkingError::unknown_import(import))? {
- // A link to something that needs to be added to the store
- ResolvedExtern::Extern(ex) => match (ex, &import.kind) {
- (Extern::Global { ty, val }, ImportKind::Global(import_ty)) => {
- Self::compare_types(import, &ty, import_ty)?;
- imports.globals.push(store.add_global(ty, val.into(), idx));
+ if let Some(defined) = self.take_defined(import) {
+ match defined {
+ Extern::Global(global) => {
+ let ImportKind::Global(import_ty) = &import.kind else {
+ return Err(LinkingError::incompatible_import_type(import).into());
+ };
+ let global_instance = store.state.get_global(global.0.addr);
+ Self::compare_types(import, &global_instance.ty, import_ty)?;
+ imports.globals.push(global.0.addr);
}
- (Extern::Table { ty, init }, ImportKind::Table(import_ty)) => {
- Self::compare_table_types(import, &ty, import_ty)?;
- Self::compare_types(import, &ty.element_type, &init.val_type())?;
- imports.tables.push(store.add_table(ty, init, idx)?);
+ Extern::Table(table) => {
+ let ImportKind::Table(import_ty) = &import.kind else {
+ return Err(LinkingError::incompatible_import_type(import).into());
+ };
+ let table_instance = store.state.get_table(table.0.addr);
+ let mut kind = table_instance.kind.clone();
+ kind.size_initial = table_instance.size() as u32;
+ Self::compare_table_types(import, &kind, import_ty)?;
+ imports.tables.push(table.0.addr);
}
- (Extern::Memory { ty }, ImportKind::Memory(import_ty)) => {
- Self::compare_memory_types(import, &ty, import_ty, None)?;
- imports.memories.push(store.add_mem(ty, idx)?);
+ Extern::Memory(memory) => {
+ let ImportKind::Memory(import_ty) = &import.kind else {
+ return Err(LinkingError::incompatible_import_type(import).into());
+ };
+ let mem = store.state.get_mem(memory.0.addr);
+ let (size, kind) = { (mem.page_count, mem.kind) };
+ Self::compare_memory_types(import, &kind, import_ty, size)?;
+ imports.memories.push(memory.0.addr);
}
- (Extern::Function(extern_func), ImportKind::Function(ty)) => {
+ Extern::Function(func_handle) => {
+ let ImportKind::Function(ty) = &import.kind else {
+ 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))?;
-
- Self::compare_types(import, extern_func.ty(), import_func_type)?;
- imports.funcs.push(store.add_func(extern_func, idx));
+ func_handle.item.validate_store(store)?;
+ Self::compare_types(import, &func_handle.ty, import_func_type)?;
+ imports.funcs.push(func_handle.addr);
}
- _ => return Err(LinkingError::incompatible_import_type(import).into()),
- },
+ }
+ continue;
+ }
- // A link to something already in the store
- ResolvedExtern::Store(val) => {
- // check if the kind matches
- if val.kind() != (&import.kind).into() {
- return Err(LinkingError::incompatible_import_type(import).into());
- }
+ let name = ExternName::from(import);
+ let Some(instance) = self.modules.get(&name.module) else {
+ return Err(LinkingError::unknown_import(import).into());
+ };
+ if instance.0.store_id != store.id() {
+ return Err(crate::Error::InvalidStore);
+ }
+ let val = instance.export_addr(&import.name).ok_or_else(|| LinkingError::unknown_import(import))?;
- match (val, &import.kind) {
- (ExternVal::Global(global_addr), ImportKind::Global(ty)) => {
- let global = store.state.get_global(global_addr);
- Self::compare_types(import, &global.ty, ty)?;
- imports.globals.push(global_addr);
- }
- (ExternVal::Table(table_addr), ImportKind::Table(ty)) => {
- let table = store.state.get_table(table_addr);
- let mut kind = table.kind.clone();
- kind.size_initial = table.size() as u32;
- Self::compare_table_types(import, &kind, ty)?;
- imports.tables.push(table_addr);
- }
- (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => {
- let mem = store.state.get_mem(memory_addr);
- let (size, kind) = { (mem.page_count, mem.kind) };
- Self::compare_memory_types(import, &kind, ty, Some(size))?;
- imports.memories.push(memory_addr);
- }
- (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))?;
+ {
+ // check if the kind matches
+ if val.kind() != (&import.kind).into() {
+ return Err(LinkingError::incompatible_import_type(import).into());
+ }
+
+ match (val, &import.kind) {
+ (ExternVal::Global(global_addr), ImportKind::Global(ty)) => {
+ let global = store.state.get_global(global_addr);
+ Self::compare_types(import, &global.ty, ty)?;
+ imports.globals.push(global_addr);
+ }
+ (ExternVal::Table(table_addr), ImportKind::Table(ty)) => {
+ let table = store.state.get_table(table_addr);
+ let mut kind = table.kind.clone();
+ kind.size_initial = table.size() as u32;
+ Self::compare_table_types(import, &kind, ty)?;
+ imports.tables.push(table_addr);
+ }
+ (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => {
+ let mem = store.state.get_mem(memory_addr);
+ let (size, kind) = { (mem.page_count, mem.kind) };
+ Self::compare_memory_types(import, &kind, ty, size)?;
+ imports.memories.push(memory_addr);
+ }
+ (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))?;
- Self::compare_types(import, func.func.ty(), import_func_type)?;
- imports.funcs.push(func_addr);
- }
- _ => return Err(LinkingError::incompatible_import_type(import).into()),
+ Self::compare_types(import, func.func.ty(), import_func_type)?;
+ imports.funcs.push(func_addr);
}
+ _ => return Err(LinkingError::incompatible_import_type(import).into()),
}
}
}
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index bb9ca28..12148db 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -3,33 +3,18 @@ use alloc::{format, rc::Rc};
use tinywasm_types::*;
use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple};
-use crate::{
- Error, FuncHandle, FuncHandleTyped, GlobalRef, GlobalRefMut, Imports, MemoryRef, MemoryRefMut, Module, Result,
- Store, TableRef, TableRefMut,
-};
+use crate::{Error, Function, FunctionTyped, Global, Imports, Memory, Module, Result, Store, Table};
-/// A typed borrowed view over an exported extern value.
-pub enum ExternItemRef<'a> {
+/// A typed view over an exported extern value.
+pub enum ExternItem {
/// Exported function handle.
- Func(FuncHandle),
+ Func(Function),
/// Exported memory reference.
- Memory(MemoryRef<'a>),
+ Memory(Memory),
/// Exported table reference.
- Table(TableRef<'a>),
+ Table(Table),
/// Exported global reference.
- Global(GlobalRef<'a>),
-}
-
-/// A typed mutable borrowed view over an exported extern value.
-pub enum ExternItemRefMut<'a> {
- /// Exported function handle.
- Func(FuncHandle),
- /// Exported mutable memory reference.
- Memory(MemoryRefMut<'a>),
- /// Exported mutable table reference.
- Table(TableRefMut<'a>),
- /// Exported mutable global reference.
- Global(GlobalRefMut<'a>),
+ Global(Global),
}
/// An instantiated WebAssembly module
@@ -191,10 +176,8 @@ impl ModuleInstance {
}
/// Returns an iterator over all exported extern values for this instance.
- pub fn exports<'a>(&'a self, store: &'a Store) -> Result<impl Iterator<Item = (&'a str, ExternItemRef<'a>)> + 'a> {
- self.validate_store(store)?;
-
- Ok(self.0.exports.iter().map(move |export| {
+ pub fn exports(&self) -> impl Iterator<Item = (&str, ExternItem)> + '_ {
+ self.0.exports.iter().map(move |export| {
let name = export.name.as_ref();
let item = match export.kind {
ExternalKind::Func => {
@@ -204,12 +187,12 @@ impl ModuleInstance {
.func_addrs
.get(idx)
.unwrap_or_else(|| unreachable!("invalid function export index: {}", export.index));
- let ty = store.state.get_func(func_addr).func.ty();
- ExternItemRef::Func(FuncHandle {
- store_id: self.0.store_id,
+ let ty = self.0.func_ty(export.index).clone();
+ ExternItem::Func(Function {
+ item: crate::StoreItem::new(self.0.store_id, func_addr),
module_addr: self.id(),
addr: func_addr,
- ty: ty.clone(),
+ ty,
})
}
ExternalKind::Table => {
@@ -219,7 +202,7 @@ impl ModuleInstance {
.table_addrs
.get(idx)
.unwrap_or_else(|| unreachable!("invalid table export index: {}", export.index));
- ExternItemRef::Table(TableRef(store.state.get_table(table_addr)))
+ ExternItem::Table(Table::from_store_addr(self.0.store_id, table_addr))
}
ExternalKind::Memory => {
let idx = export.index as usize;
@@ -228,7 +211,7 @@ impl ModuleInstance {
.mem_addrs
.get(idx)
.unwrap_or_else(|| unreachable!("invalid memory export index: {}", export.index));
- ExternItemRef::Memory(MemoryRef(store.state.get_mem(mem_addr)))
+ ExternItem::Memory(Memory::from_store_addr(self.0.store_id, mem_addr))
}
ExternalKind::Global => {
let idx = export.index as usize;
@@ -237,12 +220,12 @@ impl ModuleInstance {
.global_addrs
.get(idx)
.unwrap_or_else(|| unreachable!("invalid global export index: {}", export.index));
- ExternItemRef::Global(GlobalRef(store.state.get_global(global_addr)))
+ ExternItem::Global(Global::from_store_addr(self.0.store_id, global_addr))
}
};
(name, item)
- }))
+ })
}
#[inline]
@@ -257,35 +240,32 @@ impl ModuleInstance {
}
/// Get any exported extern value by name.
- pub fn extern_item<'a>(&self, store: &'a Store, name: &str) -> Result<ExternItemRef<'a>> {
- self.validate_store(store)?;
- match self.require_export(name)? {
- ExternVal::Func(_) => self.func(store, name).map(ExternItemRef::Func),
- ExternVal::Memory(mem_addr) => Ok(ExternItemRef::Memory(MemoryRef(store.state.get_mem(mem_addr)))),
- ExternVal::Table(table_addr) => Ok(ExternItemRef::Table(TableRef(store.state.get_table(table_addr)))),
- ExternVal::Global(global_addr) => Ok(ExternItemRef::Global(GlobalRef(store.state.get_global(global_addr)))),
- }
- }
-
- /// Get any exported extern value by name with mutable access when applicable.
- pub fn extern_item_mut<'a>(&self, store: &'a mut Store, name: &str) -> Result<ExternItemRefMut<'a>> {
- self.validate_store(store)?;
+ pub fn extern_item(&self, name: &str) -> Result<ExternItem> {
match self.require_export(name)? {
- ExternVal::Func(_) => self.func(store, name).map(ExternItemRefMut::Func),
- ExternVal::Memory(mem_addr) => {
- Ok(ExternItemRefMut::Memory(MemoryRefMut(store.state.get_mem_mut(mem_addr))))
- }
- ExternVal::Table(table_addr) => {
- Ok(ExternItemRefMut::Table(TableRefMut(store.state.get_table_mut(table_addr))))
+ ExternVal::Func(func_addr) => {
+ let export = self
+ .0
+ .exports
+ .iter()
+ .find(|e| e.name == name.into())
+ .ok_or_else(|| Error::Other(format!("Export not found: {name}")))?;
+ Ok(ExternItem::Func(Function {
+ item: crate::StoreItem::new(self.0.store_id, func_addr),
+ module_addr: self.id(),
+ addr: func_addr,
+ ty: self.0.func_ty(export.index).clone(),
+ }))
}
+ ExternVal::Memory(mem_addr) => Ok(ExternItem::Memory(Memory::from_store_addr(self.0.store_id, mem_addr))),
+ ExternVal::Table(table_addr) => Ok(ExternItem::Table(Table::from_store_addr(self.0.store_id, table_addr))),
ExternVal::Global(global_addr) => {
- Ok(ExternItemRefMut::Global(GlobalRefMut(store.state.get_global_mut(global_addr))))
+ Ok(ExternItem::Global(Global::from_store_addr(self.0.store_id, global_addr)))
}
}
}
/// Get a function export by name.
- pub fn func(&self, store: &Store, name: &str) -> Result<FuncHandle> {
+ pub fn func_untyped(&self, store: &Store, name: &str) -> Result<Function> {
self.validate_store(store)?;
let export = self.require_export(name)?;
@@ -294,7 +274,12 @@ impl ModuleInstance {
};
let ty = store.state.get_func(func_addr).func.ty();
- Ok(FuncHandle { store_id: self.0.store_id, addr: func_addr, module_addr: self.id(), ty: ty.clone() })
+ Ok(Function {
+ item: crate::StoreItem::new(self.0.store_id, func_addr),
+ addr: func_addr,
+ module_addr: self.id(),
+ ty: ty.clone(),
+ })
}
/// Get a function by its module-local index.
@@ -305,23 +290,28 @@ impl ModuleInstance {
/// module author did not expose as part of the public API.
#[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))]
#[cfg(feature = "guest_debug")]
- pub fn func_by_index(&self, store: &Store, func_index: FuncAddr) -> Result<FuncHandle> {
+ pub fn func_by_index(&self, store: &Store, func_index: FuncAddr) -> Result<Function> {
self.validate_store(store)?;
let func_addr = Self::index_addr(&self.0.func_addrs, func_index, "function")?;
let ty = store.state.get_func(func_addr).func.ty();
- Ok(FuncHandle { store_id: self.0.store_id, addr: func_addr, module_addr: self.id(), ty: ty.clone() })
+ Ok(Function {
+ item: crate::StoreItem::new(self.0.store_id, func_addr),
+ addr: func_addr,
+ module_addr: self.id(),
+ ty: ty.clone(),
+ })
}
/// Get a typed function export by name.
- pub fn func_typed<P: IntoWasmValueTuple + ValTypesFromTuple, R: FromWasmValueTuple + ValTypesFromTuple>(
+ pub fn func<P: IntoWasmValueTuple + ValTypesFromTuple, R: FromWasmValueTuple + ValTypesFromTuple>(
&self,
store: &Store,
name: &str,
- ) -> Result<FuncHandleTyped<P, R>> {
- let func = self.func(store, name)?;
+ ) -> Result<FunctionTyped<P, R>> {
+ let func = self.func_untyped(store, name)?;
Self::validate_typed_func::<P, R>(&func, name)?;
- Ok(FuncHandleTyped { func, marker: core::marker::PhantomData })
+ Ok(FunctionTyped { func, marker: core::marker::PhantomData })
}
/// Get a typed function by its module-local index.
@@ -331,16 +321,13 @@ impl ModuleInstance {
&self,
store: &Store,
func_index: FuncAddr,
- ) -> Result<FuncHandleTyped<P, R>> {
+ ) -> Result<FunctionTyped<P, R>> {
let func = self.func_by_index(store, func_index)?;
Self::validate_typed_func::<P, R>(&func, &format!("function index {func_index}"))?;
- Ok(FuncHandleTyped { func, marker: core::marker::PhantomData })
+ Ok(FunctionTyped { func, marker: core::marker::PhantomData })
}
- fn validate_typed_func<P: ValTypesFromTuple, R: ValTypesFromTuple>(
- func: &FuncHandle,
- func_name: &str,
- ) -> Result<()> {
+ fn validate_typed_func<P: ValTypesFromTuple, R: ValTypesFromTuple>(func: &Function, func_name: &str) -> Result<()> {
let expected = FuncType { params: P::val_types(), results: R::val_types() };
if func.ty != expected {
#[cfg(feature = "debug")]
@@ -356,25 +343,11 @@ impl ModuleInstance {
}
/// Get a memory export by name.
- pub fn memory<'a>(&self, store: &'a Store, name: &str) -> Result<MemoryRef<'a>> {
- self.validate_store(store)?;
-
- let export = self.require_export(name)?;
- let ExternVal::Memory(mem_addr) = export else {
- return Err(Error::Other(format!("Export is not a memory: {name}")));
- };
- Ok(MemoryRef(store.state.get_mem(mem_addr)))
- }
-
- /// Get a mutable memory export by name.
- pub fn memory_mut<'a>(&self, store: &'a mut Store, name: &str) -> Result<MemoryRefMut<'a>> {
- self.validate_store(store)?;
-
- let export = self.require_export(name)?;
- let ExternVal::Memory(mem_addr) = export else {
+ pub fn memory(&self, name: &str) -> Result<Memory> {
+ let ExternVal::Memory(mem_addr) = self.require_export(name)? else {
return Err(Error::Other(format!("Export is not a memory: {name}")));
};
- Ok(MemoryRefMut(store.state.get_mem_mut(mem_addr)))
+ Ok(Memory::from_store_addr(self.0.store_id, mem_addr))
}
/// Get a memory by its module-local index.
@@ -385,46 +358,17 @@ impl ModuleInstance {
/// that are not part of the module's public API.
#[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))]
#[cfg(feature = "guest_debug")]
- pub fn memory_by_index<'a>(&self, store: &'a Store, memory_index: MemAddr) -> Result<MemoryRef<'a>> {
- self.validate_store(store)?;
- let mem_addr = Self::index_addr(&self.0.mem_addrs, memory_index, "memory")?;
- Ok(MemoryRef(store.state.get_mem(mem_addr)))
- }
-
- /// Get a mutable memory by its module-local index.
- ///
- /// This exposes an internal module-owned memory directly and bypasses the
- /// normal export boundary. It is mainly intended for tooling and
- /// inspection. Mutating a private memory can change module behavior in ways
- /// that are not part of the module's public API.
- #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))]
- #[cfg(feature = "guest_debug")]
- pub fn memory_mut_by_index<'a>(&self, store: &'a mut Store, memory_index: MemAddr) -> Result<MemoryRefMut<'a>> {
- self.validate_store(store)?;
- let mem_addr = Self::index_addr(&self.0.mem_addrs, memory_index, "memory")?;
- Ok(MemoryRefMut(store.state.get_mem_mut(mem_addr)))
+ pub fn memory_by_index(&self, memory_index: MemAddr) -> Result<Memory> {
+ Ok(Memory::from_store_addr(self.0.store_id, Self::index_addr(&self.0.mem_addrs, memory_index, "memory")?))
}
/// Get a table export by name.
- pub fn table<'a>(&self, store: &'a Store, name: &str) -> Result<TableRef<'a>> {
- self.validate_store(store)?;
-
- let export = self.require_export(name)?;
- let ExternVal::Table(table_addr) = export else {
- return Err(Error::Other(format!("Export is not a table: {name}")));
- };
- Ok(TableRef(store.state.get_table(table_addr)))
- }
-
- /// Get a mutable table export by name.
- pub fn table_mut<'a>(&self, store: &'a mut Store, name: &str) -> Result<TableRefMut<'a>> {
- self.validate_store(store)?;
-
+ pub fn table(&self, name: &str) -> Result<Table> {
let export = self.require_export(name)?;
let ExternVal::Table(table_addr) = export else {
return Err(Error::Other(format!("Export is not a table: {name}")));
};
- Ok(TableRefMut(store.state.get_table_mut(table_addr)))
+ Ok(Table::from_store_addr(self.0.store_id, table_addr))
}
/// Get a table by its module-local index.
@@ -435,76 +379,31 @@ impl ModuleInstance {
/// that are not part of the module's public API.
#[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))]
#[cfg(feature = "guest_debug")]
- pub fn table_by_index<'a>(&self, store: &'a Store, table_index: TableAddr) -> Result<TableRef<'a>> {
- self.validate_store(store)?;
- let table_addr = Self::index_addr(&self.0.table_addrs, table_index, "table")?;
- Ok(TableRef(store.state.get_table(table_addr)))
- }
-
- /// Get a mutable table by its module-local index.
- ///
- /// This exposes an internal module-owned table directly and bypasses the
- /// normal export boundary. It is mainly intended for tooling and
- /// inspection. Mutating a private table can change module behavior in ways
- /// that are not part of the module's public API.
- #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))]
- #[cfg(feature = "guest_debug")]
- pub fn table_mut_by_index<'a>(&self, store: &'a mut Store, table_index: TableAddr) -> Result<TableRefMut<'a>> {
- self.validate_store(store)?;
- let table_addr = Self::index_addr(&self.0.table_addrs, table_index, "table")?;
- Ok(TableRefMut(store.state.get_table_mut(table_addr)))
+ pub fn table_by_index(&self, table_index: TableAddr) -> Result<Table> {
+ Ok(Table::from_store_addr(self.0.store_id, Self::index_addr(&self.0.table_addrs, table_index, "table")?))
}
/// Get the value of a global export by name.
pub fn global_get(&self, store: &Store, name: &str) -> Result<WasmValue> {
- self.global(store, name).map(|global| global.get())
- }
-
- /// Get a reference to a global export by name.
- pub fn global<'a>(&self, store: &'a Store, name: &str) -> Result<GlobalRef<'a>> {
- self.validate_store(store)?;
-
- let export = self.require_export(name)?;
- let ExternVal::Global(global_addr) = export else {
- return Err(Error::Other(format!("Export is not a global: {name}")));
- };
-
- Ok(GlobalRef(store.state.get_global(global_addr)))
+ self.global(name)?.get(store)
}
- /// Get a mutable reference to a global export by name.
- pub fn global_mut<'a>(&self, store: &'a mut Store, name: &str) -> Result<GlobalRefMut<'a>> {
- self.validate_store(store)?;
-
+ /// Get a global export by name.
+ pub fn global(&self, name: &str) -> Result<Global> {
let export = self.require_export(name)?;
let ExternVal::Global(global_addr) = export else {
return Err(Error::Other(format!("Export is not a global: {name}")));
};
- Ok(GlobalRefMut(store.state.get_global_mut(global_addr)))
+ Ok(Global::from_store_addr(self.0.store_id, global_addr))
}
/// Set the value of a mutable global export by name.
pub fn global_set(&self, store: &mut Store, name: &str, value: WasmValue) -> Result<()> {
- self.global_mut(store, name)?.set(value)
- }
-
- /// Get a reference to a global by its module-local index.
- ///
- /// This exposes an internal module-owned global directly and bypasses the
- /// normal export boundary. It is mainly intended for tooling and
- /// inspection. Mutating a private global can change module behavior in ways
- /// that are not part of the module's public API.
- #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))]
- #[cfg(feature = "guest_debug")]
- pub fn global_by_index<'a>(&self, store: &'a Store, global_index: GlobalAddr) -> Result<GlobalRef<'a>> {
- self.validate_store(store)?;
- let global_addr = Self::index_addr(&self.0.global_addrs, global_index, "global")?;
-
- Ok(GlobalRef(store.state.get_global(global_addr)))
+ self.global(name)?.set(store, value)
}
- /// Get a mutable reference to a global by its module-local index.
+ /// Get a global by its module-local index.
///
/// This exposes an internal module-owned global directly and bypasses the
/// normal export boundary. It is mainly intended for tooling and
@@ -512,11 +411,8 @@ impl ModuleInstance {
/// that are not part of the module's public API.
#[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))]
#[cfg(feature = "guest_debug")]
- pub fn global_mut_by_index<'a>(&self, store: &'a mut Store, global_index: GlobalAddr) -> Result<GlobalRefMut<'a>> {
- self.validate_store(store)?;
- let global_addr = Self::index_addr(&self.0.global_addrs, global_index, "global")?;
-
- Ok(GlobalRefMut(store.state.get_global_mut(global_addr)))
+ pub fn global_by_index(&self, global_index: GlobalAddr) -> Result<Global> {
+ Ok(Global::from_store_addr(self.0.store_id, Self::index_addr(&self.0.global_addrs, global_index, "global")?))
}
/// Get the start function of the module
@@ -525,7 +421,7 @@ impl ModuleInstance {
/// If no start function is specified, also checks for a `_start` function in the exports
///
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#start-function>
- pub fn start_func(&self, store: &Store) -> Result<Option<FuncHandle>> {
+ pub fn start_func(&self, store: &Store) -> Result<Option<Function>> {
self.validate_store(store)?;
let func_index = match self.0.func_start {
@@ -542,7 +438,12 @@ impl ModuleInstance {
let func_addr = self.0.resolve_func_addr(func_index);
let ty = store.state.get_func(func_addr).func.ty();
- Ok(Some(FuncHandle { store_id: self.0.store_id, module_addr: self.id(), addr: func_addr, ty: ty.clone() }))
+ Ok(Some(Function {
+ item: crate::StoreItem::new(self.0.store_id, func_addr),
+ module_addr: self.id(),
+ addr: func_addr,
+ ty: ty.clone(),
+ }))
}
/// Invoke the start function of the module
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index 3236c0f..d4dafd6 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -12,6 +12,7 @@ use super::ExecState;
use super::num_helpers::*;
use super::values::*;
use crate::engine::FuelPolicy;
+use crate::func::{FuncContext, HostFunction};
use crate::instance::ModuleInstanceInner;
use crate::interpreter::Value128;
use crate::*;
@@ -299,7 +300,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
I64Popcnt => stack_op!(unary i64, |v| i64::from(v.count_ones())),
// Reference types
- RefFunc(func_idx) => self.exec_const::<ValueRef>(Some(*func_idx))?,
+ RefFunc(func_idx) => self.exec_const::<ValueRef>(Some(self.module.resolve_func_addr(*func_idx)))?,
RefNull(_) => self.exec_const::<ValueRef>(None)?,
RefIsNull => self.exec_ref_is_null()?,
MemorySize(addr) => self.exec_memory_size(*addr)?,
@@ -737,7 +738,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
Ok(())
}
- fn exec_call_host(&mut self, host_func: Rc<imports::HostFunction>) -> Result<()> {
+ fn exec_call_host(&mut self, host_func: Rc<HostFunction>) -> Result<()> {
let params = self.store.stack.values.pop_types(&host_func.ty.params).collect::<Box<_>>();
let res = host_func.call(FuncContext { store: self.store, module_addr: self.module.idx }, &params)?;
self.store.stack.values.extend_from_wasmvalues(&res)?;
@@ -749,10 +750,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
let addr = self.module.resolve_func_addr(v);
let func_inst = self.store.state.get_func(addr);
match &func_inst.func {
- crate::Function::Wasm(wasm_func) => {
+ crate::FunctionDef::Wasm(wasm_func) => {
self.exec_call::<IS_RETURN_CALL>(wasm_func.clone(), addr, func_inst.owner)
}
- crate::Function::Host(host_func) => self.exec_call_host(host_func.clone()),
+ crate::FunctionDef::Host(host_func) => self.exec_call_host(host_func.clone()),
}
}
@@ -793,7 +794,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
let call_ty = self.module.func_ty(type_addr);
match &func_inst.func {
- crate::Function::Wasm(wasm_func) => {
+ crate::FunctionDef::Wasm(wasm_func) => {
if wasm_func.ty != *call_ty {
return Err(Trap::IndirectCallTypeMismatch {
actual: wasm_func.ty.clone(),
@@ -804,7 +805,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
self.exec_call::<IS_RETURN_CALL>(wasm_func.clone(), func_ref, func_inst.owner)
}
- crate::Function::Host(host_func) => {
+ crate::FunctionDef::Host(host_func) => {
if host_func.ty != *call_ty {
return Err(Trap::IndirectCallTypeMismatch {
actual: host_func.ty.clone(),
diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs
index 6847680..37f0013 100644
--- a/crates/tinywasm/src/lib.rs
+++ b/crates/tinywasm/src/lib.rs
@@ -57,7 +57,7 @@
//! // Get a typed handle to the exported "add" function
//! // Alternatively, you can use `instance.func` to get an untyped handle
//! // that takes and returns [`WasmValue`]s
-//! let func = instance.func_typed::<(i32, i32), i32>(&mut store, "add")?;
+//! let func = instance.func::<(i32, i32), i32>(&mut store, "add")?;
//! let res = func.call(&mut store, (1, 2))?;
//!
//! assert_eq!(res, 3);
@@ -96,9 +96,9 @@ pub(crate) mod log {
mod error;
pub use error::*;
-pub use func::{ExecProgress, FuncExecution, FuncExecutionTyped, FuncHandle, FuncHandleTyped};
+pub use func::{ExecProgress, FuncContext, FuncExecution, FuncExecutionTyped, Function, FunctionTyped, HostFunction};
pub use imports::*;
-pub use instance::{ExternItemRef, ExternItemRefMut, ModuleInstance};
+pub use instance::{ExternItem, ModuleInstance};
pub use module::{ExportType, ImportType, Module, ModuleExport, ModuleImport};
pub use reference::*;
pub use store::*;
diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs
index 4deac5f..ad0ff84 100644
--- a/crates/tinywasm/src/reference.rs
+++ b/crates/tinywasm/src/reference.rs
@@ -4,280 +4,306 @@ use alloc::string::{String, ToString};
use alloc::{ffi::CString, format};
use crate::store::{GlobalInstance, TableElement, TableInstance};
-use crate::{Error, MemoryInstance, Result};
-use tinywasm_types::{ExternRef, FuncRef, GlobalType, TableAddr, TableType, ValType, WasmValue};
+use crate::{Error, MemoryInstance, Result, Store};
+use tinywasm_types::{
+ Addr, ExternRef, FuncRef, GlobalAddr, GlobalType, MemAddr, MemoryArch, MemoryType, TableAddr, TableType, ValType,
+ WasmValue,
+};
-// This module essentially contains the public APIs to interact with the data stored in the store
-
-/// A reference to a memory instance
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct MemoryRef<'a>(pub(crate) &'a MemoryInstance);
+pub(crate) struct StoreItem {
+ pub(crate) store_id: usize,
+ pub(crate) addr: Addr,
+}
-/// A mutable reference to a memory instance.
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct MemoryRefMut<'a>(pub(crate) &'a mut MemoryInstance);
+impl StoreItem {
+ #[inline]
+ pub(crate) const fn new(store_id: usize, addr: Addr) -> Self {
+ Self { store_id, addr }
+ }
-/// A reference to a table instance.
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct TableRef<'a>(pub(crate) &'a TableInstance);
+ #[inline]
+ pub(crate) fn validate_store(&self, store: &Store) -> Result<()> {
+ if self.store_id != store.id() {
+ return Err(Error::InvalidStore);
+ }
+ Ok(())
+ }
+}
-/// A mutable reference to a table instance.
+/// A memory instance in a store.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct TableRefMut<'a>(pub(crate) &'a mut TableInstance);
+pub struct Memory(pub(crate) StoreItem);
-/// A reference to a global instance.
+/// A table instance in a store.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct GlobalRef<'a>(pub(crate) &'a GlobalInstance);
+pub struct Table(pub(crate) StoreItem);
-/// A mutable reference to a global instance.
+/// A global instance in a store.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct GlobalRefMut<'a>(pub(crate) &'a mut GlobalInstance);
+pub struct Global(pub(crate) StoreItem);
-fn table_element_to_value(element_type: ValType, element: TableElement) -> WasmValue {
- match element_type {
- ValType::RefFunc => WasmValue::RefFunc(FuncRef::new(element.addr())),
- ValType::RefExtern => WasmValue::RefExtern(ExternRef::new(element.addr())),
- _ => unreachable!("table element type must be a reference type"),
+impl Memory {
+ #[inline]
+ pub(crate) const fn from_store_addr(store_id: usize, addr: MemAddr) -> Self {
+ Self(StoreItem::new(store_id, addr))
}
-}
-fn table_value_to_element(element_type: ValType, value: WasmValue) -> Result<TableElement> {
- match (element_type, value) {
- (ValType::RefFunc, WasmValue::RefFunc(func_ref)) => Ok(TableElement::from(func_ref.addr())),
- (ValType::RefExtern, WasmValue::RefExtern(extern_ref)) => Ok(TableElement::from(extern_ref.addr())),
- _ => Err(Error::Other("invalid table value type".to_string())),
+ /// Create a new memory in the given store.
+ pub fn new(store: &mut Store, ty: MemoryType) -> Result<Self> {
+ if let MemoryArch::I64 = ty.arch() {
+ return Err(Error::UnsupportedFeature("64-bit memories".to_string()));
+ }
+ let addr = store.state.memories.len() as MemAddr;
+ store.state.memories.push(MemoryInstance::new(ty));
+ Ok(Self::from_store_addr(store.id(), addr))
}
-}
-impl MemoryRefLoad for MemoryRef<'_> {
- /// Load a slice of memory
- fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.0.load(offset, len)
+ #[inline]
+ fn instance<'a>(&self, store: &'a Store) -> Result<&'a MemoryInstance> {
+ self.0.validate_store(store)?;
+ Ok(store.state.get_mem(self.0.addr))
}
-}
-impl MemoryRefLoad for MemoryRefMut<'_> {
- /// Load a slice of memory
- fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.0.load(offset, len)
+ #[inline]
+ fn instance_mut<'a>(&self, store: &'a mut Store) -> Result<&'a mut MemoryInstance> {
+ self.0.validate_store(store)?;
+ Ok(store.state.get_mem_mut(self.0.addr))
}
-}
-impl MemoryRef<'_> {
/// Returns the full raw memory data.
- pub fn data(&self) -> &[u8] {
- &self.0.data
+ pub fn data<'a>(&self, store: &'a Store) -> Result<&'a [u8]> {
+ Ok(&self.instance(store)?.data)
+ }
+
+ /// Returns the full raw mutable memory data.
+ pub fn data_mut<'a>(&self, store: &'a mut Store) -> Result<&'a mut [u8]> {
+ Ok(&mut self.instance_mut(store)?.data)
}
/// Returns the raw memory byte length.
- pub fn data_size(&self) -> usize {
- self.0.data.len()
+ pub fn data_size(&self, store: &Store) -> Result<usize> {
+ Ok(self.instance(store)?.data.len())
}
- /// Load a slice of memory
- pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.0.load(offset, len)
+ /// Load a slice of memory.
+ pub fn load<'a>(&self, store: &'a Store, offset: usize, len: usize) -> Result<&'a [u8]> {
+ self.instance(store)?.load(offset, len)
}
-}
-impl MemoryRefMut<'_> {
- /// Returns the full raw memory data.
- pub fn data(&self) -> &[u8] {
- &self.0.data
+ /// Grow the memory by the given number of pages.
+ pub fn grow(&self, store: &mut Store, delta_pages: i64) -> Result<Option<i64>> {
+ Ok(self.instance_mut(store)?.grow(delta_pages))
}
- /// Returns the full raw mutable memory data.
- pub fn data_mut(&mut self) -> &mut [u8] {
- &mut self.0.data
+ /// Get the current size of the memory in pages.
+ pub fn page_count(&self, store: &Store) -> Result<usize> {
+ Ok(self.instance(store)?.page_count)
}
- /// Returns the raw memory byte length.
- pub fn data_size(&self) -> usize {
- self.0.data.len()
+ /// Copy a slice of memory to another place in memory.
+ pub fn copy_within(&self, store: &mut Store, src: usize, dst: usize, len: usize) -> Result<()> {
+ self.instance_mut(store)?.copy_within(dst, src, len)
}
- /// Load a slice of memory
- pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.0.load(offset, len)
+ /// Fill a slice of memory with a value.
+ pub fn fill(&self, store: &mut Store, offset: usize, len: usize, val: u8) -> Result<()> {
+ self.instance_mut(store)?.fill(offset, len, val)
}
- /// Grow the memory by the given number of pages
- pub fn grow(&mut self, delta_pages: i64) -> Option<i64> {
- self.0.grow(delta_pages)
+ /// Store a slice of memory.
+ pub fn store(&self, store: &mut Store, offset: usize, len: usize, data: &[u8]) -> Result<()> {
+ self.instance_mut(store)?.store(offset, len, data)
}
- /// Get the current size of the memory in pages
- pub fn page_count(&mut self) -> usize {
- self.0.page_count
+ /// Load a C-style string from memory.
+ pub fn load_cstr<'a>(&self, store: &'a Store, offset: usize, len: usize) -> Result<&'a CStr> {
+ CStr::from_bytes_with_nul(self.load(store, offset, len)?)
+ .map_err(|e| crate::Error::Other(format!("Invalid C-style string: {e}")))
}
- /// Copy a slice of memory to another place in memory
- pub fn copy_within(&mut self, src: usize, dst: usize, len: usize) -> Result<()> {
- self.0.copy_within(dst, src, len)
+ /// Load a C-style string from memory, stopping at the first nul byte.
+ pub fn load_cstr_until_nul<'a>(&self, store: &'a Store, offset: usize, max_len: usize) -> Result<&'a CStr> {
+ CStr::from_bytes_until_nul(self.load(store, offset, max_len)?)
+ .map_err(|e| crate::Error::Other(format!("Invalid C-style string: {e}")))
}
- /// Fill a slice of memory with a value
- pub fn fill(&mut self, offset: usize, len: usize, val: u8) -> Result<()> {
- self.0.fill(offset, len, val)
+ /// Load a UTF-8 string from memory.
+ pub fn load_string(&self, store: &Store, offset: usize, len: usize) -> Result<String> {
+ String::from_utf8(self.load(store, offset, len)?.to_vec())
+ .map_err(|e| crate::Error::Other(format!("Invalid UTF-8 string: {e}")))
}
- /// Store a slice of memory
- pub fn store(&mut self, offset: usize, len: usize, data: &[u8]) -> Result<()> {
- self.0.store(offset, len, data)
+ /// Load a C-style string from memory.
+ pub fn load_cstring(&self, store: &Store, offset: usize, len: usize) -> Result<CString> {
+ Ok(CString::from(self.load_cstr(store, offset, len)?))
}
-}
-impl TableRef<'_> {
- /// Get the type of the table.
- pub fn ty(&self) -> TableType {
- self.0.kind.clone()
+ /// Load a C-style string from memory, stopping at the first nul byte.
+ pub fn load_cstring_until_nul(&self, store: &Store, offset: usize, max_len: usize) -> Result<CString> {
+ Ok(CString::from(self.load_cstr_until_nul(store, offset, max_len)?))
}
- /// Get the current number of elements in the table.
- pub fn size(&self) -> usize {
- self.0.size() as usize
+ /// Load a JavaScript-style utf-16 string from memory.
+ pub fn load_js_string(&self, store: &Store, offset: usize, len: usize) -> Result<String> {
+ let bytes = self.load(store, offset, len)?;
+ let mut string = String::new();
+ for i in 0..(len / 2) {
+ let c = u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]);
+ string.push(
+ char::from_u32(u32::from(c)).ok_or_else(|| crate::Error::Other("Invalid UTF-16 string".to_string()))?,
+ );
+ }
+ Ok(string)
}
+}
- /// Get a table element as a wasm reference value.
- pub fn get(&self, index: TableAddr) -> Result<WasmValue> {
- self.0.get_wasm_val(index)
+fn table_element_to_value(element_type: ValType, element: TableElement) -> WasmValue {
+ match element_type {
+ ValType::RefFunc => WasmValue::RefFunc(FuncRef::new(element.addr())),
+ ValType::RefExtern => WasmValue::RefExtern(ExternRef::new(element.addr())),
+ _ => unreachable!("table element type must be a reference type"),
}
+}
- /// Load a range of table elements and iterate over wasm reference values.
- pub fn load(&self, offset: usize, len: usize) -> Result<impl Iterator<Item = WasmValue> + '_> {
- let element_type = self.0.kind.element_type;
- let elements = self.0.load(offset, len)?;
- Ok(elements.iter().copied().map(move |element| table_element_to_value(element_type, element)))
+fn table_value_to_element(element_type: ValType, value: WasmValue) -> Result<TableElement> {
+ match (element_type, value) {
+ (ValType::RefFunc, WasmValue::RefFunc(func_ref)) => Ok(TableElement::from(func_ref.addr())),
+ (ValType::RefExtern, WasmValue::RefExtern(extern_ref)) => Ok(TableElement::from(extern_ref.addr())),
+ _ => Err(Error::Other("invalid table value type".to_string())),
}
}
-impl TableRefMut<'_> {
+impl Table {
+ #[inline]
+ pub(crate) const fn from_store_addr(store_id: usize, addr: TableAddr) -> Self {
+ Self(StoreItem::new(store_id, addr))
+ }
+
+ /// Create a new table in the given store.
+ pub fn new(store: &mut Store, ty: TableType, init: WasmValue) -> Result<Self> {
+ let init = match (ty.element_type, init) {
+ (ValType::RefFunc, WasmValue::RefFunc(func_ref)) => TableElement::from(func_ref.addr()),
+ (ValType::RefExtern, WasmValue::RefExtern(extern_ref)) => TableElement::from(extern_ref.addr()),
+ _ => return Err(Error::Other("invalid table init value".to_string())),
+ };
+ let addr = store.state.tables.len() as TableAddr;
+ store.state.tables.push(TableInstance::new_with_init(ty, init));
+ Ok(Self::from_store_addr(store.id(), addr))
+ }
+
+ #[inline]
+ fn instance<'a>(&self, store: &'a Store) -> Result<&'a TableInstance> {
+ self.0.validate_store(store)?;
+ Ok(store.state.get_table(self.0.addr))
+ }
+
+ #[inline]
+ fn instance_mut<'a>(&self, store: &'a mut Store) -> Result<&'a mut TableInstance> {
+ self.0.validate_store(store)?;
+ Ok(store.state.get_table_mut(self.0.addr))
+ }
+
/// Get the type of the table.
- pub fn ty(&self) -> TableType {
- self.0.kind.clone()
+ pub fn ty(&self, store: &Store) -> Result<TableType> {
+ Ok(self.instance(store)?.kind.clone())
}
/// Get the current number of elements in the table.
- pub fn size(&self) -> usize {
- self.0.size() as usize
+ pub fn size(&self, store: &Store) -> Result<usize> {
+ Ok(self.instance(store)?.size() as usize)
}
/// Get a table element as a wasm reference value.
- pub fn get(&self, index: TableAddr) -> Result<WasmValue> {
- self.0.get_wasm_val(index)
+ pub fn get(&self, store: &Store, index: TableAddr) -> Result<WasmValue> {
+ self.instance(store)?.get_wasm_val(index)
}
/// Load a range of table elements and iterate over wasm reference values.
- pub fn load(&self, offset: usize, len: usize) -> Result<impl Iterator<Item = WasmValue> + '_> {
- let element_type = self.0.kind.element_type;
- let elements = self.0.load(offset, len)?;
- Ok(elements.iter().copied().map(move |element| table_element_to_value(element_type, element)))
+ pub fn load(&self, store: &Store, offset: usize, len: usize) -> Result<alloc::vec::IntoIter<WasmValue>> {
+ let table = self.instance(store)?;
+ let element_type = table.kind.element_type;
+ let elements = table.load(offset, len)?;
+ Ok(elements
+ .iter()
+ .copied()
+ .map(move |element| table_element_to_value(element_type, element))
+ .collect::<alloc::vec::Vec<_>>()
+ .into_iter())
}
/// Set a table element.
- pub fn set(&mut self, index: TableAddr, value: WasmValue) -> Result<()> {
- let value = table_value_to_element(self.0.kind.element_type, value)?;
- self.0.set(index, value)
+ pub fn set(&self, store: &mut Store, index: TableAddr, value: WasmValue) -> Result<()> {
+ let table = self.instance_mut(store)?;
+ let value = table_value_to_element(table.kind.element_type, value)?;
+ table.set(index, value)
}
/// Copy elements within the same table.
- pub fn copy_within(&mut self, src: usize, dst: usize, len: usize) -> Result<()> {
- self.0.copy_within(dst, src, len)
+ pub fn copy_within(&self, store: &mut Store, src: usize, dst: usize, len: usize) -> Result<()> {
+ self.instance_mut(store)?.copy_within(dst, src, len)
}
/// Grow the table and return the previous size.
- pub fn grow(&mut self, delta: i32, init: WasmValue) -> Result<usize> {
- let old_size = self.size();
- let init = table_value_to_element(self.0.kind.element_type, init)?;
- self.0.grow(delta, init)?;
+ pub fn grow(&self, store: &mut Store, delta: i32, init: WasmValue) -> Result<usize> {
+ let table = self.instance_mut(store)?;
+ let old_size = table.size() as usize;
+ let init = table_value_to_element(table.kind.element_type, init)?;
+ table.grow(delta, init)?;
Ok(old_size)
}
}
-impl GlobalRef<'_> {
- /// Get the type of the global.
- pub fn ty(&self) -> GlobalType {
- self.0.ty
+impl Global {
+ #[inline]
+ pub(crate) const fn from_store_addr(store_id: usize, addr: GlobalAddr) -> Self {
+ Self(StoreItem::new(store_id, addr))
}
- /// Get the current value of the global.
- pub fn get(&self) -> WasmValue {
- self.0.value.get().attach_type(self.0.ty.ty)
+ /// Create a new global in the given store.
+ pub fn new(store: &mut Store, ty: GlobalType, value: WasmValue) -> Result<Self> {
+ let addr = store.state.globals.len() as GlobalAddr;
+ store.state.globals.push(GlobalInstance::new(ty, value.into()));
+ Ok(Self::from_store_addr(store.id(), addr))
+ }
+
+ #[inline]
+ fn instance<'a>(&self, store: &'a Store) -> Result<&'a GlobalInstance> {
+ self.0.validate_store(store)?;
+ Ok(store.state.get_global(self.0.addr))
+ }
+
+ #[inline]
+ fn instance_mut<'a>(&self, store: &'a mut Store) -> Result<&'a mut GlobalInstance> {
+ self.0.validate_store(store)?;
+ Ok(store.state.get_global_mut(self.0.addr))
}
-}
-impl GlobalRefMut<'_> {
/// Get the type of the global.
- pub fn ty(&self) -> GlobalType {
- self.0.ty
+ pub fn ty(&self, store: &Store) -> Result<GlobalType> {
+ Ok(self.instance(store)?.ty)
}
/// Get the current value of the global.
- pub fn get(&self) -> WasmValue {
- self.0.value.get().attach_type(self.0.ty.ty)
+ pub fn get(&self, store: &Store) -> Result<WasmValue> {
+ let global = self.instance(store)?;
+ Ok(global.value.get().attach_type(global.ty.ty))
}
/// Set the current value of the global.
- pub fn set(&mut self, value: WasmValue) -> Result<()> {
- if !self.0.ty.mutable {
+ pub fn set(&self, store: &mut Store, value: WasmValue) -> Result<()> {
+ let global = self.instance_mut(store)?;
+ if !global.ty.mutable {
return Err(Error::Other("global is immutable".to_string()));
}
- if value.val_type() != self.0.ty.ty {
+ if value.val_type() != global.ty.ty {
return Err(Error::Other("invalid global value type".to_string()));
}
- self.0.value.set(value.into());
+ global.value.set(value.into());
Ok(())
}
}
-
-#[doc(hidden)]
-pub trait MemoryRefLoad {
- fn load(&self, offset: usize, len: usize) -> Result<&[u8]>;
-}
-
-/// Convenience methods for loading strings from memory
-pub trait MemoryStringExt: MemoryRefLoad {
- /// Load a C-style string from memory
- fn load_cstr(&self, offset: usize, len: usize) -> Result<&CStr> {
- let bytes = self.load(offset, len)?;
- CStr::from_bytes_with_nul(bytes).map_err(|e| crate::Error::Other(format!("Invalid C-style string: {e}")))
- }
-
- /// Load a C-style string from memory, stopping at the first nul byte
- fn load_cstr_until_nul(&self, offset: usize, max_len: usize) -> Result<&CStr> {
- let bytes = self.load(offset, max_len)?;
- CStr::from_bytes_until_nul(bytes).map_err(|e| crate::Error::Other(format!("Invalid C-style string: {e}")))
- }
-
- /// Load a UTF-8 string from memory
- fn load_string(&self, offset: usize, len: usize) -> Result<String> {
- let bytes = self.load(offset, len)?;
- String::from_utf8(bytes.to_vec()).map_err(|e| crate::Error::Other(format!("Invalid UTF-8 string: {e}")))
- }
-
- /// Load a C-style string from memory
- fn load_cstring(&self, offset: usize, len: usize) -> Result<CString> {
- Ok(CString::from(self.load_cstr(offset, len)?))
- }
-
- /// Load a C-style string from memory, stopping at the first nul byte
- fn load_cstring_until_nul(&self, offset: usize, max_len: usize) -> Result<CString> {
- Ok(CString::from(self.load_cstr_until_nul(offset, max_len)?))
- }
-
- /// Load a JavaScript-style utf-16 string from memory
- fn load_js_string(&self, offset: usize, len: usize) -> Result<String> {
- let bytes = self.load(offset, len)?;
- let mut string = String::new();
- for i in 0..(len / 2) {
- let c = u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]);
- string.push(
- char::from_u32(u32::from(c)).ok_or_else(|| crate::Error::Other("Invalid UTF-16 string".to_string()))?,
- );
- }
- Ok(string)
- }
-}
-
-impl MemoryStringExt for MemoryRef<'_> {}
-impl MemoryStringExt for MemoryRefMut<'_> {}
diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs
index 3067a8e..b8edf67 100644
--- a/crates/tinywasm/src/store/function.rs
+++ b/crates/tinywasm/src/store/function.rs
@@ -1,19 +1,40 @@
-use crate::Function;
use alloc::rc::Rc;
use tinywasm_types::*;
+use crate::func::HostFunction;
+
/// A WebAssembly Function Instance
///
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances>
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct FunctionInstance {
- pub(crate) func: Function,
+ pub(crate) func: FunctionDef,
pub(crate) owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions
}
+/// The internal representation of a function
+#[derive(Clone)]
+#[cfg_attr(feature = "debug", derive(Debug))]
+pub(crate) enum FunctionDef {
+ /// A host function
+ Host(Rc<HostFunction>),
+
+ /// A pointer to a WebAssembly function
+ Wasm(Rc<WasmFunction>),
+}
+
+impl FunctionDef {
+ pub(crate) fn ty(&self) -> &FuncType {
+ match self {
+ Self::Host(f) => &f.ty,
+ Self::Wasm(f) => &f.ty,
+ }
+ }
+}
+
impl FunctionInstance {
pub(crate) fn new_wasm(func: WasmFunction, owner: ModuleInstanceAddr) -> Self {
- Self { func: Function::Wasm(Rc::new(func)), owner }
+ Self { func: FunctionDef::Wasm(Rc::new(func)), owner }
}
}
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index 8c6289c..f7bd0e9 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -6,7 +6,7 @@ use tinywasm_types::*;
use crate::instance::ModuleInstanceInner;
use crate::interpreter::TinyWasmValue;
use crate::interpreter::stack::Stack;
-use crate::{Engine, Error, Function, ModuleInstance, Result, Trap};
+use crate::{Engine, Error, ModuleInstance, Result, Trap};
mod data;
mod element;
@@ -118,8 +118,8 @@ impl State {
pub(crate) fn get_wasm_func(&self, addr: FuncAddr) -> &Rc<WasmFunction> {
match self.funcs.get(addr as usize) {
Some(func) => match &func.func {
- Function::Wasm(wasm_func) => wasm_func,
- Function::Host(_) => unreachable!(
+ FunctionDef::Wasm(wasm_func) => wasm_func,
+ FunctionDef::Host(_) => unreachable!(
"expected a wasm function at address {addr}, but found a host function. This should be unreachable"
),
},
@@ -423,36 +423,7 @@ impl Store {
Ok((data_addrs.into_boxed_slice(), None))
}
- pub(crate) fn add_global(&mut self, ty: GlobalType, value: TinyWasmValue, _idx: ModuleInstanceAddr) -> Addr {
- self.state.globals.push(GlobalInstance::new(ty, value));
- self.state.globals.len() as Addr - 1
- }
-
- pub(crate) fn add_table(
- &mut self,
- table: TableType,
- init: WasmValue,
- _idx: ModuleInstanceAddr,
- ) -> Result<TableAddr> {
- let init = match (table.element_type, init) {
- (ValType::RefFunc, WasmValue::RefFunc(func_ref)) => TableElement::from(func_ref.addr()),
- (ValType::RefExtern, WasmValue::RefExtern(extern_ref)) => TableElement::from(extern_ref.addr()),
- _ => return Err(Error::Other("invalid table init value".to_string())),
- };
-
- self.state.tables.push(TableInstance::new_with_init(table, init));
- Ok(self.state.tables.len() as TableAddr - 1)
- }
-
- pub(crate) fn add_mem(&mut self, mem: MemoryType, _idx: ModuleInstanceAddr) -> Result<MemAddr> {
- if let MemoryArch::I64 = mem.arch() {
- return Err(Error::UnsupportedFeature("64-bit memories".to_string()));
- }
- self.state.memories.push(MemoryInstance::new(mem));
- Ok(self.state.memories.len() as MemAddr - 1)
- }
-
- pub(crate) fn add_func(&mut self, func: Function, idx: ModuleInstanceAddr) -> FuncAddr {
+ pub(crate) fn add_func(&mut self, func: FunctionDef, idx: ModuleInstanceAddr) -> FuncAddr {
self.state.funcs.push(FunctionInstance { func, owner: idx });
self.state.funcs.len() as FuncAddr - 1
}
diff --git a/crates/tinywasm/tests/host_func_signature_check.rs b/crates/tinywasm/tests/host_func_signature_check.rs
index 4590e77..6667e7a 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::{
- Extern, FuncContext, Imports, Module, Store,
+ FuncContext, HostFunction, Imports, Module, Store,
types::{FuncType, ValType, WasmValue},
};
use tinywasm_types::ExternRef;
@@ -34,18 +34,17 @@ fn module_cases() -> Vec<(Module, FuncType, Vec<WasmValue>)> {
fn test_return_invalid_type() -> Result<()> {
let cases = module_cases();
- for (module, func_ty, args) in cases {
+ for (module, ty, args) in cases {
for returned_values in VAL_LISTS {
let mut store = Store::default();
let mut imports = Imports::new();
- imports
- .define("host", "hfn", Extern::func(&func_ty, |_: FuncContext<'_>, _| Ok(returned_values.to_vec())))
- .unwrap();
+ 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 caller = instance.func(&store, "call_hfn").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(WasmValue::val_type).eq(func_ty.results.iter().copied());
+ let should_succeed = returned_values.iter().map(WasmValue::val_type).eq(ty.results.iter().copied());
let call_res = caller.call(&mut store, &args);
assert_eq!(call_res.is_ok(), should_succeed);
}
@@ -58,15 +57,15 @@ fn test_return_invalid_type() -> Result<()> {
fn test_linking_invalid_untyped_func() -> Result<()> {
let cases = module_cases();
for (module, expected_func_ty, _) in &cases {
- for (_, func_ty_to_try, _) in &cases {
- let tried_fn = Extern::func(func_ty_to_try, |_: FuncContext<'_>, _| panic!("not intended to be called"));
+ for (_, ty, _) in &cases {
let mut store = Store::default();
+ let tried_fn =
+ HostFunction::from_untyped(&mut store, ty, |_: FuncContext<'_>, _| panic!("not intended to be called"));
let mut imports = Imports::new();
- imports.define("host", "hfn", tried_fn).unwrap();
+ imports.define("host", "hfn", tried_fn);
- let should_succeed = func_ty_to_try == expected_func_ty;
+ let should_succeed = ty == expected_func_ty;
let link_res = module.clone().instantiate(&mut store, Some(imports));
-
assert_eq!(link_res.is_ok(), should_succeed);
}
}
@@ -80,26 +79,37 @@ fn test_linking_invalid_typed_func() -> Result<()> {
type NonMatchingTuple = (f64, i32, i32);
const DONT_CALL: &str = "not meant to be called";
- // None of these typed host signatures are produced by module_cases().
- let matching_none = vec![
- Extern::typed_func(|_, _: NonMatchingTuple| -> tinywasm::Result<Existing> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: NonMatchingTuple| -> tinywasm::Result<()> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result<Existing> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result<()> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: Existing| -> tinywasm::Result<NonMatchingTuple> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: Existing| -> tinywasm::Result<NonMatchingSingle> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: ()| -> tinywasm::Result<NonMatchingSingle> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: ()| -> tinywasm::Result<NonMatchingTuple> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result<NonMatchingTuple> { panic!("{DONT_CALL}") }),
- Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result<NonMatchingSingle> { panic!("{DONT_CALL}") }),
- ];
-
let cases = module_cases();
for (module, _, _) in cases {
- for typed_fn in matching_none.iter().cloned() {
- let mut store = Store::default();
+ let mut store = Store::default();
+ let matching_none = vec![
+ HostFunction::from(&mut store, |_, _: NonMatchingTuple| -> tinywasm::Result<Existing> {
+ panic!("{DONT_CALL}")
+ }),
+ HostFunction::from(&mut store, |_, _: NonMatchingTuple| -> tinywasm::Result<()> { panic!("{DONT_CALL}") }),
+ HostFunction::from(&mut store, |_, _: NonMatchingSingle| -> tinywasm::Result<Existing> {
+ panic!("{DONT_CALL}")
+ }),
+ HostFunction::from(&mut store, |_, _: NonMatchingSingle| -> tinywasm::Result<()> { panic!("{DONT_CALL}") }),
+ HostFunction::from(&mut store, |_, _: Existing| -> tinywasm::Result<NonMatchingTuple> {
+ panic!("{DONT_CALL}")
+ }),
+ HostFunction::from(&mut store, |_, _: Existing| -> tinywasm::Result<NonMatchingSingle> {
+ panic!("{DONT_CALL}")
+ }),
+ HostFunction::from(&mut store, |_, _: ()| -> tinywasm::Result<NonMatchingSingle> { panic!("{DONT_CALL}") }),
+ HostFunction::from(&mut store, |_, _: ()| -> tinywasm::Result<NonMatchingTuple> { panic!("{DONT_CALL}") }),
+ HostFunction::from(&mut store, |_, _: NonMatchingSingle| -> tinywasm::Result<NonMatchingTuple> {
+ panic!("{DONT_CALL}")
+ }),
+ HostFunction::from(&mut store, |_, _: NonMatchingSingle| -> tinywasm::Result<NonMatchingSingle> {
+ panic!("{DONT_CALL}")
+ }),
+ ];
+
+ for typed_fn in matching_none {
let mut imports = Imports::new();
- imports.define("host", "hfn", typed_fn).unwrap();
+ imports.define("host", "hfn", typed_fn);
let link_failure = module.clone().instantiate(&mut store, Some(imports));
assert!(link_failure.is_err(), "Expected linking to fail for mismatched typed func, but it succeeded");
}
diff --git a/crates/tinywasm/tests/import_linking.rs b/crates/tinywasm/tests/import_linking.rs
index 85c41ed..3ab13ff 100644
--- a/crates/tinywasm/tests/import_linking.rs
+++ b/crates/tinywasm/tests/import_linking.rs
@@ -35,7 +35,7 @@ fn link_module_links_same_store_instance() -> Result<()> {
imports.link_module("adder", add_instance)?;
let instance = import_module.instantiate(&mut store, Some(imports))?;
- let main = instance.func_typed::<(), i32>(&store, "main")?;
+ let main = instance.func::<(), i32>(&store, "main")?;
assert_eq!(main.call(&mut store, ())?, 3);
Ok(())
}
diff --git a/crates/tinywasm/tests/imported_table_init.rs b/crates/tinywasm/tests/imported_table_init.rs
index c040394..8b07000 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, ValType, WasmValue};
-use tinywasm::{Extern, Imports, Module, Store};
+use tinywasm::{Imports, Module, Store, Table};
#[test]
fn imported_table_uses_provided_init_value() -> Result<()> {
@@ -19,14 +19,12 @@ fn imported_table_uses_provided_init_value() -> Result<()> {
let module = Module::parse_bytes(&wasm)?;
let mut store = Store::default();
let mut imports = Imports::new();
- imports.define(
- "host",
- "table",
- Extern::table(TableType::new(ValType::RefFunc, 3, None), WasmValue::RefFunc(FuncRef::new(Some(0)))),
- )?;
+ let table =
+ Table::new(&mut store, TableType::new(ValType::RefFunc, 3, None), WasmValue::RefFunc(FuncRef::new(Some(0))))?;
+ imports.define("host", "table", table);
let instance = module.instantiate(&mut store, Some(imports))?;
- let slot_is_null = instance.func_typed::<i32, i32>(&store, "slot_is_null")?;
+ let slot_is_null = instance.func::<i32, i32>(&store, "slot_is_null")?;
assert_eq!(slot_is_null.call(&mut store, 0)?, 0);
assert_eq!(slot_is_null.call(&mut store, 1)?, 0);
diff --git a/crates/tinywasm/tests/internal_refs.rs b/crates/tinywasm/tests/internal_refs.rs
index bd5561f..98dc702 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::{ExternItemRef, ExternItemRefMut, Module, Store};
+use tinywasm::{ExternItem, Module, Store};
#[test]
#[cfg(feature = "guest_debug")]
@@ -25,16 +25,16 @@ fn private_items_are_accessible_by_index() -> Result<()> {
let func = instance.func_by_index(&store, 0)?;
assert_eq!(func.call(&mut store, &[])?, vec![WasmValue::I32(7)]);
- instance.memory_mut_by_index(&mut store, 0)?.store(0, 4, &[1, 2, 3, 4])?;
- assert_eq!(instance.memory_by_index(&store, 0)?.load(0, 4)?, &[1, 2, 3, 4]);
+ instance.memory_by_index(0)?.store(&mut store, 0, 4, &[1, 2, 3, 4])?;
+ assert_eq!(instance.memory_by_index(0)?.load(&store, 0, 4)?, &[1, 2, 3, 4]);
- assert_eq!(instance.table_by_index(&store, 0)?.size(), 2);
- assert_eq!(instance.table_by_index(&store, 0)?.get(0)?, WasmValue::RefFunc(FuncRef::new(Some(0))));
- assert!(matches!(instance.table_by_index(&store, 0)?.get(1)?, WasmValue::RefFunc(func_ref) if func_ref.is_null()));
+ assert_eq!(instance.table_by_index(0)?.size(&store)?, 2);
+ assert_eq!(instance.table_by_index(0)?.get(&store, 0)?, WasmValue::RefFunc(FuncRef::new(Some(0))));
+ assert!(matches!(instance.table_by_index(0)?.get(&store, 1)?, WasmValue::RefFunc(func_ref) if func_ref.is_null()));
- assert_eq!(instance.global_by_index(&store, 0)?.get(), WasmValue::I32(11));
- instance.global_mut_by_index(&mut store, 0)?.set(WasmValue::I32(23))?;
- assert_eq!(instance.global_by_index(&store, 0)?.get(), WasmValue::I32(23));
+ assert_eq!(instance.global_by_index(0)?.get(&store)?, WasmValue::I32(11));
+ instance.global_by_index(0)?.set(&mut store, WasmValue::I32(23))?;
+ assert_eq!(instance.global_by_index(0)?.get(&store)?, WasmValue::I32(23));
Ok(())
}
@@ -55,17 +55,17 @@ fn exported_tables_and_globals_have_handle_and_helper_apis() -> Result<()> {
let instance = module.instantiate(&mut store, None)?;
assert_eq!(instance.global_get(&store, "g")?, WasmValue::I32(3));
- assert_eq!(instance.global(&store, "g")?.get(), WasmValue::I32(3));
+ assert_eq!(instance.global("g")?.get(&store)?, WasmValue::I32(3));
instance.global_set(&mut store, "g", WasmValue::I32(9))?;
- assert_eq!(instance.global_mut(&mut store, "g")?.get(), WasmValue::I32(9));
+ assert_eq!(instance.global("g")?.get(&store)?, WasmValue::I32(9));
- let table = instance.table(&store, "t")?;
- assert_eq!(table.size(), 1);
- assert!(matches!(table.get(0)?, WasmValue::RefFunc(func_ref) if func_ref.is_null()));
+ let table = instance.table("t")?;
+ assert_eq!(table.size(&store)?, 1);
+ assert!(matches!(table.get(&store, 0)?, WasmValue::RefFunc(func_ref) if func_ref.is_null()));
- let old_size = instance.table_mut(&mut store, "t")?.grow(1, WasmValue::RefFunc(FuncRef::null()))?;
+ let old_size = instance.table("t")?.grow(&mut store, 1, WasmValue::RefFunc(FuncRef::null()))?;
assert_eq!(old_size, 1);
- assert_eq!(instance.table(&store, "t")?.size(), 2);
+ assert_eq!(instance.table("t")?.size(&store)?, 2);
Ok(())
}
@@ -87,15 +87,10 @@ fn extern_item_lookup_returns_expected_kinds() -> Result<()> {
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- assert!(matches!(instance.extern_item(&store, "f")?, ExternItemRef::Func(_)));
- assert!(matches!(instance.extern_item(&store, "m")?, ExternItemRef::Memory(_)));
- assert!(matches!(instance.extern_item(&store, "t")?, ExternItemRef::Table(_)));
- assert!(matches!(instance.extern_item(&store, "g")?, ExternItemRef::Global(_)));
-
- assert!(matches!(instance.extern_item_mut(&mut store, "f")?, ExternItemRefMut::Func(_)));
- assert!(matches!(instance.extern_item_mut(&mut store, "m")?, ExternItemRefMut::Memory(_)));
- assert!(matches!(instance.extern_item_mut(&mut store, "t")?, ExternItemRefMut::Table(_)));
- assert!(matches!(instance.extern_item_mut(&mut store, "g")?, ExternItemRefMut::Global(_)));
+ assert!(matches!(instance.extern_item("f")?, ExternItem::Func(_)));
+ assert!(matches!(instance.extern_item("m")?, ExternItem::Memory(_)));
+ assert!(matches!(instance.extern_item("t")?, ExternItem::Table(_)));
+ assert!(matches!(instance.extern_item("g")?, ExternItem::Global(_)));
Ok(())
}
diff --git a/crates/tinywasm/tests/memory_ref_api.rs b/crates/tinywasm/tests/memory_ref_api.rs
index 9e57b6b..dd2d01e 100644
--- a/crates/tinywasm/tests/memory_ref_api.rs
+++ b/crates/tinywasm/tests/memory_ref_api.rs
@@ -15,11 +15,11 @@ fn memory_ref_mut_copy_within_uses_src_then_dst_order() -> Result<()> {
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- let mut memory = instance.memory_mut(&mut store, "memory")?;
- memory.store(0, 4, &[1, 2, 3, 4])?;
- memory.copy_within(0, 4, 4)?;
+ let memory = instance.memory("memory")?;
+ memory.store(&mut store, 0, 4, &[1, 2, 3, 4])?;
+ memory.copy_within(&mut store, 0, 4, 4)?;
- assert_eq!(memory.load(0, 8)?, &[1, 2, 3, 4, 1, 2, 3, 4]);
+ assert_eq!(memory.load(&store, 0, 8)?, &[1, 2, 3, 4, 1, 2, 3, 4]);
Ok(())
}
diff --git a/crates/tinywasm/tests/resume_execution.rs b/crates/tinywasm/tests/resume_execution.rs
index 5a58f81..a1e2bc6 100644
--- a/crates/tinywasm/tests/resume_execution.rs
+++ b/crates/tinywasm/tests/resume_execution.rs
@@ -14,12 +14,12 @@ fn typed_resume_matches_non_budgeted_call() -> Result<()> {
let mut store_full = tinywasm::Store::default();
let instance_full = module.clone().instantiate(&mut store_full, None)?;
- let func_full = instance_full.func_typed::<i32, i32>(&store_full, "fibonacci_recursive")?;
+ let func_full = instance_full.func::<i32, i32>(&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 func_budgeted = instance_budgeted.func_typed::<i32, i32>(&store_budgeted, "fibonacci_recursive")?;
+ let func_budgeted = instance_budgeted.func::<i32, i32>(&store_budgeted, "fibonacci_recursive")?;
let mut exec = func_budgeted.call_resumable(&mut store_budgeted, 20)?;
let mut saw_suspended = false;
@@ -41,7 +41,7 @@ fn untyped_resume_supports_zero_fuel() -> Result<()> {
let module = Module::parse_bytes(ADD_WASM)?;
let mut store = tinywasm::Store::default();
let instance = module.instantiate(&mut store, None)?;
- let func = instance.func(&store, "add")?;
+ let func = instance.func_untyped(&store, "add")?;
let mut exec = func.call_resumable(&mut store, &[WasmValue::I32(20), WasmValue::I32(22)])?;
assert!(matches!(exec.resume_with_fuel(0)?, ExecProgress::Suspended));
@@ -62,12 +62,12 @@ fn weighted_call_fuel_requires_more_rounds() -> Result<()> {
let mut per_instr_store = tinywasm::Store::default();
let instance_per_instr = module.clone().instantiate(&mut per_instr_store, None)?;
- let func_per_instr = instance_per_instr.func_typed::<i32, i32>(&per_instr_store, "fibonacci_recursive")?;
+ let func_per_instr = instance_per_instr.func::<i32, i32>(&per_instr_store, "fibonacci_recursive")?;
let mut weighted_store =
tinywasm::Store::new(tinywasm::Engine::new(Config::new().fuel_policy(FuelPolicy::Weighted)));
let instance_weighted = module.instantiate(&mut weighted_store, None)?;
- let func_weighted = instance_weighted.func_typed::<i32, i32>(&weighted_store, "fibonacci_recursive")?;
+ let func_weighted = instance_weighted.func::<i32, i32>(&weighted_store, "fibonacci_recursive")?;
let fuel = 64;
let n = 20;
@@ -104,7 +104,7 @@ fn time_budget_zero_suspends_then_completes() -> Result<()> {
let module = Module::parse_bytes(ADD_WASM)?;
let mut store = tinywasm::Store::default();
let instance = module.instantiate(&mut store, None)?;
- let func = instance.func_typed::<(i32, i32), i32>(&store, "add")?;
+ let func = instance.func::<(i32, i32), i32>(&store, "add")?;
let mut exec = func.call_resumable(&mut store, (20, 22))?;
assert!(matches!(exec.resume_with_time_budget(Duration::ZERO)?, ExecProgress::Suspended));
diff --git a/crates/tinywasm/tests/store_ownership.rs b/crates/tinywasm/tests/store_ownership.rs
index a9cbad4..b8c3729 100644
--- a/crates/tinywasm/tests/store_ownership.rs
+++ b/crates/tinywasm/tests/store_ownership.rs
@@ -18,7 +18,7 @@ fn func_handle_rejects_wrong_store() -> Result<()> {
let mut owner_store = Store::default();
let instance = module.instantiate(&mut owner_store, None)?;
- let func = instance.func(&owner_store, "add")?;
+ let func = instance.func_untyped(&owner_store, "add")?;
let mut other_store = Store::default();
let err = func.call(&mut other_store, &[1.into(), 2.into()]).unwrap_err();
@@ -35,8 +35,9 @@ fn memory_access_rejects_wrong_store() -> Result<()> {
let mut owner_store = Store::default();
let instance = module.instantiate(&mut owner_store, None)?;
+ let memory = instance.memory("memory")?;
let other_store = Store::default();
- let err = instance.memory(&other_store, "memory").unwrap_err();
+ let err = memory.data(&other_store).unwrap_err();
assert!(matches!(err, Error::InvalidStore));
Ok(())
diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs
index e8c35c2..b45baa4 100644
--- a/crates/tinywasm/tests/testsuite/run.rs
+++ b/crates/tinywasm/tests/testsuite/run.rs
@@ -5,7 +5,7 @@ use super::TestSuite;
use eyre::{Result, eyre};
use indexmap::IndexMap;
use log::{debug, error, info};
-use tinywasm::{Extern, Imports, ModuleInstance};
+use tinywasm::{Global, HostFunction, Imports, Memory, ModuleInstance, Table};
use tinywasm_types::{ExternVal, MemoryType, ModuleInstanceAddr, TableType, ValType, WasmValue};
use wasm_testsuite::data::TestFile;
use wasm_testsuite::wast;
@@ -87,65 +87,72 @@ impl TestSuite {
Ok(())
}
- fn imports(modules: &HashMap<std::string::String, ModuleInstance>) -> Result<Imports> {
+ fn imports(store: &mut tinywasm::Store, modules: &HashMap<std::string::String, ModuleInstance>) -> Result<Imports> {
let mut imports = Imports::new();
- let table =
- Extern::table(TableType::new(ValType::RefFunc, 10, Some(20)), WasmValue::default_for(ValType::RefFunc));
+ let table = Table::new(
+ store,
+ TableType::new(ValType::RefFunc, 10, Some(20)),
+ WasmValue::default_for(ValType::RefFunc),
+ )?;
- let print = Extern::typed_func(|_ctx: tinywasm::FuncContext, (): ()| {
+ let print = HostFunction::from(store, |_ctx: tinywasm::FuncContext, (): ()| {
log::debug!("print");
Ok(())
});
- let print_i32 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: i32| {
+ let print_i32 = HostFunction::from(store, |_ctx: tinywasm::FuncContext, arg: i32| {
log::debug!("print_i32: {arg}");
Ok(())
});
- let print_i64 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: i64| {
+ let print_i64 = HostFunction::from(store, |_ctx: tinywasm::FuncContext, arg: i64| {
log::debug!("print_i64: {arg}");
Ok(())
});
- let print_f32 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: f32| {
+ let print_f32 = HostFunction::from(store, |_ctx: tinywasm::FuncContext, arg: f32| {
log::debug!("print_f32: {arg}");
Ok(())
});
- let print_f64 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: f64| {
+ let print_f64 = HostFunction::from(store, |_ctx: tinywasm::FuncContext, arg: f64| {
log::debug!("print_f64: {arg}");
Ok(())
});
- let print_i32_f32 = Extern::typed_func(|_ctx: tinywasm::FuncContext, args: (i32, f32)| {
+ let print_i32_f32 = HostFunction::from(store, |_ctx: tinywasm::FuncContext, args: (i32, f32)| {
log::debug!("print_i32_f32: {}, {}", args.0, args.1);
Ok(())
});
- let print_f64_f64 = Extern::typed_func(|_ctx: tinywasm::FuncContext, args: (f64, f64)| {
+ let print_f64_f64 = HostFunction::from(store, |_ctx: tinywasm::FuncContext, args: (f64, f64)| {
log::debug!("print_f64_f64: {}, {}", args.0, args.1);
Ok(())
});
+ let memory = Memory::new(store, MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)))?;
+ let global_i32 = Global::new(store, tinywasm_types::GlobalType::new(ValType::I32, false), WasmValue::I32(666))?;
+ let global_i64 = Global::new(store, tinywasm_types::GlobalType::new(ValType::I64, false), WasmValue::I64(666))?;
+ let global_f32 =
+ Global::new(store, tinywasm_types::GlobalType::new(ValType::F32, false), WasmValue::F32(666.6))?;
+ let global_f64 =
+ Global::new(store, tinywasm_types::GlobalType::new(ValType::F64, false), WasmValue::F64(666.6))?;
+
imports
- .define(
- "spectest",
- "memory",
- Extern::memory(MemoryType::new(tinywasm_types::MemoryArch::I32, 1, Some(2), None)),
- )?
- .define("spectest", "table", table)?
- .define("spectest", "global_i32", Extern::global(WasmValue::I32(666), false))?
- .define("spectest", "global_i64", Extern::global(WasmValue::I64(666), false))?
- .define("spectest", "global_f32", Extern::global(WasmValue::F32(666.6), false))?
- .define("spectest", "global_f64", Extern::global(WasmValue::F64(666.6), false))?
- .define("spectest", "print", print)?
- .define("spectest", "print_i32", print_i32)?
- .define("spectest", "print_i64", print_i64)?
- .define("spectest", "print_f32", print_f32)?
- .define("spectest", "print_f64", print_f64)?
- .define("spectest", "print_i32_f32", print_i32_f32)?
- .define("spectest", "print_f64_f64", print_f64_f64)?;
+ .define("spectest", "memory", memory)
+ .define("spectest", "table", table)
+ .define("spectest", "global_i32", global_i32)
+ .define("spectest", "global_i64", global_i64)
+ .define("spectest", "global_f32", global_f32)
+ .define("spectest", "global_f64", global_f64)
+ .define("spectest", "print", print)
+ .define("spectest", "print_i32", print_i32)
+ .define("spectest", "print_i64", print_i64)
+ .define("spectest", "print_f32", print_f32)
+ .define("spectest", "print_f64", print_f64)
+ .define("spectest", "print_i32_f32", print_i32_f32)
+ .define("spectest", "print_f64_f64", print_f64_f64);
for (name, module) in modules {
log::debug!("registering module: {name}");
@@ -208,8 +215,9 @@ impl TestSuite {
let (name, bytes) = encode_quote_wat(module);
let m = 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(Self::imports(module_registry.modules()).unwrap()))
+ .instantiate(&mut store, Some(imports))
.expect("failed to instantiate module");
(name, module_instance)
@@ -310,8 +318,8 @@ impl TestSuite {
let module = parse_module_bytes(&wat.encode().expect("failed to encode module"))
.expect("failed to parse module");
let module = tinywasm::Module::from(module);
- module
- .instantiate(&mut store, Some(Self::imports(module_registry.modules()).unwrap()))?;
+ let imports = Self::imports(&mut store, module_registry.modules()).unwrap();
+ module.instantiate(&mut store, Some(imports))?;
return Ok(());
}
wast::WastExecute::Get { module: _, global: _, .. } => {
@@ -361,7 +369,8 @@ impl TestSuite {
let module = parse_module_bytes(&module.encode().expect("failed to encode module"))
.expect("failed to parse module");
let module = tinywasm::Module::from(module);
- module.instantiate(&mut store, Some(Self::imports(module_registry.modules()).unwrap()))
+ let imports = Self::imports(&mut store, module_registry.modules()).unwrap();
+ module.instantiate(&mut store, Some(imports))
});
match res {
diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs
index 000bb1a..4b3e001 100644
--- a/crates/tinywasm/tests/testsuite/util.rs
+++ b/crates/tinywasm/tests/testsuite/util.rs
@@ -26,7 +26,7 @@ pub fn exec_fn_instance(
return Err(tinywasm::Error::Other("no instance found".to_string()));
};
- let func = instance.func(store, name)?;
+ let func = instance.func_untyped(store, name)?;
func.call(store, args)
}
@@ -43,7 +43,7 @@ pub fn exec_fn(
let mut store = tinywasm::Store::default();
let module = tinywasm::Module::from(module);
let instance = module.instantiate(&mut store, imports)?;
- instance.func(&store, name)?.call(&mut store, args)
+ instance.func_untyped(&store, name)?.call(&mut store, args)
}
pub fn catch_unwind_silent<R>(f: impl FnOnce() -> R) -> std::thread::Result<R> {
diff --git a/crates/tinywasm/tests/typed_lookup.rs b/crates/tinywasm/tests/typed_lookup.rs
index 7bfc26d..acc6a7a 100644
--- a/crates/tinywasm/tests/typed_lookup.rs
+++ b/crates/tinywasm/tests/typed_lookup.rs
@@ -18,9 +18,9 @@ fn func_typed_rejects_wrong_param_or_result_types() -> Result<()> {
let mut store = tinywasm::Store::default();
let instance = module.instantiate(&mut store, None)?;
- assert!(instance.func_typed::<(i32, i32), i32>(&store, "add").is_ok());
- assert!(instance.func_typed::<i32, i32>(&store, "add").is_err());
- assert!(instance.func_typed::<(i32, i32), ()>(&store, "add").is_err());
+ assert!(instance.func::<(i32, i32), i32>(&store, "add").is_ok());
+ assert!(instance.func::<i32, i32>(&store, "add").is_err());
+ assert!(instance.func::<(i32, i32), ()>(&store, "add").is_err());
Ok(())
}
@@ -41,8 +41,8 @@ fn func_typed_rejects_partial_multi_value_results() -> Result<()> {
let mut store = tinywasm::Store::default();
let instance = module.instantiate(&mut store, None)?;
- assert!(instance.func_typed::<(), (i32, i32)>(&store, "pair").is_ok());
- assert!(instance.func_typed::<(), i32>(&store, "pair").is_err());
+ assert!(instance.func::<(), (i32, i32)>(&store, "pair").is_ok());
+ assert!(instance.func::<(), i32>(&store, "pair").is_err());
Ok(())
}
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 737c04e..f74336c 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -336,6 +336,31 @@ pub struct GlobalType {
pub ty: ValType,
}
+impl GlobalType {
+ /// Create a new global type.
+ pub const fn new(ty: ValType, mutable: bool) -> Self {
+ Self { mutable, ty }
+ }
+
+ /// Set a different value type.
+ pub const fn with_ty(mut self, ty: ValType) -> Self {
+ self.ty = ty;
+ self
+ }
+
+ /// Set global mutability.
+ pub const fn with_mutable(mut self, mutable: bool) -> Self {
+ self.mutable = mutable;
+ self
+ }
+}
+
+impl Default for GlobalType {
+ fn default() -> Self {
+ Self::new(ValType::I32, false)
+ }
+}
+
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
@@ -367,6 +392,7 @@ pub struct MemoryType {
}
impl MemoryType {
+ /// Create a new memory type.
pub const fn new(
arch: MemoryArch,
page_count_initial: u64,
@@ -405,6 +431,36 @@ impl MemoryType {
pub const fn max_size(&self) -> u64 {
self.page_count_max() * self.page_size()
}
+
+ /// Set a different memory architecture.
+ pub const fn with_arch(mut self, arch: MemoryArch) -> Self {
+ self.arch = arch;
+ self
+ }
+
+ /// Set a different initial page count.
+ pub const fn with_page_count_initial(mut self, page_count_initial: u64) -> Self {
+ self.page_count_initial = page_count_initial;
+ self
+ }
+
+ /// Set a different maximum page count.
+ pub const fn with_page_count_max(mut self, page_count_max: Option<u64>) -> Self {
+ self.page_count_max = page_count_max;
+ self
+ }
+
+ /// Set a different page size.
+ pub const fn with_page_size(mut self, page_size: Option<u64>) -> Self {
+ self.page_size = page_size;
+ self
+ }
+}
+
+impl Default for MemoryType {
+ fn default() -> Self {
+ Self::new(MemoryArch::I32, 0, None, None)
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
diff --git a/examples/archive.rs b/examples/archive.rs
index 7025fba..f8d309c 100644
--- a/examples/archive.rs
+++ b/examples/archive.rs
@@ -21,7 +21,7 @@ fn main() -> Result<()> {
let module: Module = TinyWasmModule::from_twasm(&twasm)?.into();
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- let add = instance.func_typed::<(i32, i32), i32>(&store, "add")?;
+ let add = instance.func::<(i32, i32), i32>(&store, "add")?;
assert_eq!(add.call(&mut store, (1, 2))?, 3);
diff --git a/examples/funcref_callbacks.rs b/examples/funcref_callbacks.rs
index 6e1d3ef..f517e30 100644
--- a/examples/funcref_callbacks.rs
+++ b/examples/funcref_callbacks.rs
@@ -1,5 +1,5 @@
use eyre::Result;
-use tinywasm::{Extern, FuncContext, Imports, Module, Store, types::FuncRef};
+use tinywasm::{FuncContext, HostFunction, Imports, Module, Store, types::FuncRef};
const LHS: i32 = 5;
const RHS: i32 = 3;
@@ -49,27 +49,21 @@ fn run_passed_funcref_example() -> Result<()> {
let wasm = wat::parse_str(WASM).expect("failed to parse wat");
let module = Module::parse_bytes(&wasm)?;
let mut store = Store::default();
- let mut imports = Imports::new();
- imports.define(
- "host",
- "call_this",
- Extern::typed_func(|mut ctx: FuncContext<'_>, func_ref: FuncRef| -> tinywasm::Result<()> {
+ let mul = HostFunction::from(&mut store, |_, (lhs, rhs): (i32, i32)| -> tinywasm::Result<i32> { Ok(lhs * rhs) });
+ let call_this =
+ HostFunction::from(&mut store, |mut ctx: FuncContext<'_>, func_ref: FuncRef| -> tinywasm::Result<()> {
// Host cannot call a funcref directly, so it routes through Wasm.
- let call_by_ref = ctx.module().func_typed::<(FuncRef, i32, i32), i32>(ctx.store(), "call_binop_by_ref")?;
+ let call_by_ref = ctx.module().func::<(FuncRef, i32, i32), i32>(ctx.store(), "call_binop_by_ref")?;
let _result = call_by_ref.call(ctx.store_mut(), (func_ref, LHS, RHS))?;
Ok(())
- }),
- )?;
+ });
- imports.define(
- "host",
- "mul",
- Extern::typed_func(|_, (lhs, rhs): (i32, i32)| -> tinywasm::Result<i32> { Ok(lhs * rhs) }),
- )?;
+ let mut imports = Imports::new();
+ imports.define("host", "call_this", call_this).define("host", "mul", mul);
let instance = module.instantiate(&mut store, Some(imports))?;
- let caller = instance.func_typed::<(), ()>(&store, "tell_host_to_call")?;
+ let caller = instance.func::<(), ()>(&store, "tell_host_to_call")?;
caller.call(&mut store, ())?;
@@ -113,21 +107,16 @@ fn run_returned_funcref_example() -> Result<()> {
let mut store = Store::default();
let mut imports = Imports::new();
- imports.define(
- "host",
- "mul",
- Extern::typed_func(|_, (lhs, rhs): (i32, i32)| -> tinywasm::Result<i32> { Ok(lhs * rhs) }),
- )?;
+ let mul = HostFunction::from(&mut store, |_, (lhs, rhs): (i32, i32)| -> tinywasm::Result<i32> { Ok(lhs * rhs) });
+ imports.define("host", "mul", mul);
let instance = module.instantiate(&mut store, Some(imports))?;
-
let (add_ref, sub_ref, mul_ref) = {
- let get_funcrefs = instance.func_typed::<(), (FuncRef, FuncRef, FuncRef)>(&store, "what_should_host_call")?;
+ let get_funcrefs = instance.func::<(), (FuncRef, FuncRef, FuncRef)>(&store, "what_should_host_call")?;
get_funcrefs.call(&mut store, ())?
};
- let call_by_ref = instance.func_typed::<(FuncRef, i32, i32), i32>(&store, "call_binop_by_ref")?;
-
+ let call_by_ref = instance.func::<(FuncRef, i32, i32), i32>(&store, "call_binop_by_ref")?;
for func_ref in [add_ref, sub_ref, mul_ref] {
let _result = call_by_ref.call(&mut store, (func_ref, LHS, RHS))?;
}
diff --git a/examples/linking.rs b/examples/linking.rs
index ce9748b..4da7fec 100644
--- a/examples/linking.rs
+++ b/examples/linking.rs
@@ -43,7 +43,7 @@ fn main() -> Result<()> {
let import_instance = import_module.instantiate(&mut store, Some(imports))?;
// Call the `main` function, which uses the imported `add` function.
- let main = import_instance.func_typed::<(), i32>(&store, "main")?;
+ let main = import_instance.func::<(), i32>(&store, "main")?;
assert_eq!(main.call(&mut store, ())?, 3);
Ok(())
diff --git a/examples/resumable.rs b/examples/resumable.rs
index f3ebc89..fef01dd 100644
--- a/examples/resumable.rs
+++ b/examples/resumable.rs
@@ -27,7 +27,7 @@ fn main() -> Result<()> {
let module = Module::parse_bytes(&wasm)?;
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- let count_down = instance.func_typed::<i32, i32>(&store, "count_down")?;
+ let count_down = instance.func::<i32, i32>(&store, "count_down")?;
let mut execution = count_down.call_resumable(&mut store, 10_000)?;
let fuel_per_round = 128;
diff --git a/examples/rust/src/tinywasm.rs b/examples/rust/src/tinywasm.rs
index e06cf81..7de0dbc 100644
--- a/examples/rust/src/tinywasm.rs
+++ b/examples/rust/src/tinywasm.rs
@@ -1,5 +1,5 @@
#![no_main]
-use tinywasm::{Extern, FuncContext};
+use tinywasm::{FuncContext, HostFunction};
#[link(wasm_import_module = "env")]
unsafe extern "C" {
@@ -19,7 +19,7 @@ fn run() -> tinywasm::Result<()> {
imports.define(
"env",
"printi32",
- Extern::typed_func(|_: FuncContext<'_>, v: i32| {
+ HostFunction::from(&mut store, |_: FuncContext<'_>, v: i32| {
unsafe { printi32(v) }
Ok(())
}),
diff --git a/examples/rust/src/tinywasm_no_std.rs b/examples/rust/src/tinywasm_no_std.rs
index 9a032dd..5444280 100644
--- a/examples/rust/src/tinywasm_no_std.rs
+++ b/examples/rust/src/tinywasm_no_std.rs
@@ -1,7 +1,7 @@
#![no_main]
#![no_std]
use lol_alloc::{AssumeSingleThreaded, FreeListAllocator};
-use tinywasm::{Extern, FuncContext};
+use tinywasm::{FuncContext, HostFunction};
extern crate alloc;
@@ -36,7 +36,7 @@ fn run() -> tinywasm::Result<()> {
imports.define(
"env",
"printi32",
- Extern::typed_func(|_: FuncContext<'_>, v: i32| {
+ HostFunction::from(&mut store, |_: FuncContext<'_>, v: i32| {
unsafe { printi32(v) }
Ok(())
}),
diff --git a/examples/simple.rs b/examples/simple.rs
index 8371992..38839c9 100644
--- a/examples/simple.rs
+++ b/examples/simple.rs
@@ -15,7 +15,7 @@ fn main() -> Result<()> {
let module = Module::parse_bytes(&wasm)?;
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- let add = instance.func_typed::<(i32, i32), i32>(&store, "add")?;
+ let add = instance.func::<(i32, i32), i32>(&store, "add")?;
assert_eq!(add.call(&mut store, (1, 2))?, 3);
Ok(())
diff --git a/examples/simple2.rs b/examples/simple2.rs
index 66e85b3..f0d566e 100644
--- a/examples/simple2.rs
+++ b/examples/simple2.rs
@@ -14,7 +14,7 @@ fn main() -> Result<()> {
let module = Module::parse_bytes(&wasm)?;
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- let add = instance.func_typed::<(i32, i64), (i32, i64)>(&store, "return")?;
+ let add = instance.func::<(i32, i64), (i32, i64)>(&store, "return")?;
assert_eq!(add.call(&mut store, (1, 2))?, (1, 2));
Ok(())
diff --git a/examples/wasm-rust.rs b/examples/wasm-rust.rs
index 0ffa499..4386531 100644
--- a/examples/wasm-rust.rs
+++ b/examples/wasm-rust.rs
@@ -1,7 +1,7 @@
use std::hint::black_box;
use eyre::{Result, eyre};
-use tinywasm::{Extern, FuncContext, Imports, MemoryStringExt, Module, Store};
+use tinywasm::{FuncContext, HostFunction, Imports, Module, Store};
/// Examples of using WebAssembly compiled from Rust with tinywasm.
///
@@ -76,10 +76,10 @@ fn tinywasm() -> Result<()> {
let mut store = Store::default();
let mut imports = Imports::new();
- imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _x: i32| Ok(())))?;
+ imports.define("env", "printi32", HostFunction::from(&mut store, |_: FuncContext<'_>, _x: i32| Ok(())));
let instance = module.instantiate(&mut store, Some(black_box(imports)))?;
- let hello = instance.func_typed::<(), ()>(&store, "hello")?;
+ let hello = instance.func::<(), ()>(&store, "hello")?;
hello.call(&mut store, black_box(()))?;
hello.call(&mut store, black_box(()))?;
hello.call(&mut store, black_box(()))?;
@@ -91,10 +91,10 @@ fn tinywasm_no_std() -> Result<()> {
let mut store = Store::default();
let mut imports = Imports::new();
- imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _x: i32| Ok(())))?;
+ imports.define("env", "printi32", HostFunction::from(&mut store, |_: FuncContext<'_>, _x: i32| Ok(())));
let instance = module.instantiate(&mut store, Some(black_box(imports)))?;
- let hello = instance.func_typed::<(), ()>(&store, "hello")?;
+ let hello = instance.func::<(), ()>(&store, "hello")?;
hello.call(&mut store, black_box(()))?;
hello.call(&mut store, black_box(()))?;
hello.call(&mut store, black_box(()))?;
@@ -105,24 +105,22 @@ fn hello() -> Result<()> {
let module = Module::parse_file("./examples/rust/out/hello.opt.wasm")?;
let mut store = Store::default();
+ let print_utf8 = HostFunction::from(&mut store, |ctx: FuncContext<'_>, (ptr, len): (i64, i32)| {
+ let mem = ctx.memory("memory")?;
+ let string = mem.load_string(ctx.store(), ptr as usize, len as usize)?;
+ println!("{string}");
+ Ok(())
+ });
+
let mut imports = Imports::new();
- imports.define(
- "env",
- "print_utf8",
- Extern::typed_func(|ctx: FuncContext<'_>, (ptr, len): (i64, i32)| {
- let mem = ctx.memory("memory")?;
- let string = mem.load_string(ptr as usize, len as usize)?;
- println!("{string}");
- Ok(())
- }),
- )?;
+ imports.define("env", "print_utf8", print_utf8);
let instance = module.instantiate(&mut store, Some(imports))?;
- let arg_ptr = instance.func_typed::<(), i32>(&store, "arg_ptr")?.call(&mut store, ())?;
+ let arg_ptr = instance.func::<(), i32>(&store, "arg_ptr")?.call(&mut store, ())?;
let arg = b"world";
- instance.memory_mut(&mut store, "memory")?.store(arg_ptr as usize, arg.len(), arg)?;
- let hello = instance.func_typed::<i32, ()>(&store, "hello")?;
+ instance.memory("memory")?.store(&mut store, arg_ptr as usize, arg.len(), arg)?;
+ let hello = instance.func::<i32, ()>(&store, "hello")?;
hello.call(&mut store, arg.len() as i32)?;
Ok(())
@@ -131,19 +129,18 @@ fn hello() -> Result<()> {
fn host_fn() -> Result<()> {
let module = Module::parse_file("./examples/rust/out/host_fn.opt.wasm")?;
let mut store = Store::default();
+
+ let bar = HostFunction::from(&mut store, |_: FuncContext<'_>, (left, right): (i64, i32)| {
+ assert_eq!(left, 1);
+ assert_eq!(right, 2);
+ Ok(left as i32 + right)
+ });
+
let mut imports = Imports::new();
- imports.define(
- "env",
- "bar",
- Extern::typed_func(|_: FuncContext<'_>, (left, right): (i64, i32)| {
- assert_eq!(left, 1);
- assert_eq!(right, 2);
- Ok(left as i32 + right)
- }),
- )?;
+ imports.define("env", "bar", bar);
let instance = module.instantiate(&mut store, Some(imports))?;
- let host_fn = instance.func_typed::<(), i32>(&store, "foo")?;
+ let host_fn = instance.func::<(), i32>(&store, "foo")?;
assert_eq!(host_fn.call(&mut store, ())?, 3);
Ok(())
}
@@ -152,18 +149,16 @@ fn printi32() -> Result<()> {
let module = Module::parse_file("./examples/rust/out/print.opt.wasm")?;
let mut store = Store::default();
+ let printi32 = HostFunction::from(&mut store, |_: FuncContext<'_>, x: i32| {
+ println!("{x}");
+ Ok(())
+ });
+
let mut imports = Imports::new();
- imports.define(
- "env",
- "printi32",
- Extern::typed_func(|_: FuncContext<'_>, x: i32| {
- println!("{x}");
- Ok(())
- }),
- )?;
+ imports.define("env", "printi32", printi32);
let instance = module.instantiate(&mut store, Some(imports))?;
- let add_and_print = instance.func_typed::<(i32, i32), ()>(&store, "add_and_print")?;
+ let add_and_print = instance.func::<(i32, i32), ()>(&store, "add_and_print")?;
add_and_print.call(&mut store, (1, 2))?;
Ok(())
@@ -174,7 +169,7 @@ fn fibonacci() -> Result<()> {
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- let fibonacci = instance.func_typed::<i32, i32>(&store, "fibonacci_recursive")?;
+ let fibonacci = instance.func::<i32, i32>(&store, "fibonacci_recursive")?;
let n = 26;
let result = fibonacci.call(&mut store, n)?;
println!("fibonacci({n}) = {result}");
@@ -187,7 +182,7 @@ fn argon2id() -> Result<()> {
let mut store = Store::default();
let instance = module.instantiate(&mut store, None)?;
- let argon2id = instance.func_typed::<(i32, i32, i32), i32>(&store, "argon2id")?;
+ let argon2id = instance.func::<(i32, i32, i32), i32>(&store, "argon2id")?;
argon2id.call(&mut store, (1000, 2, 1))?;
Ok(())