From cff17ced4464acb6b0b4af96f093653c1eb24da0 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Mon, 4 Dec 2023 17:01:05 +0100 Subject: feat: add store ids Signed-off-by: Henry Gressmann --- crates/cli/Cargo.toml | 2 +- crates/tinywasm/src/error.rs | 3 +++ crates/tinywasm/src/instance.rs | 17 ++++++++++++++-- crates/tinywasm/src/module.rs | 1 + crates/tinywasm/src/store.rs | 44 +++++++++++++++++++++++++++++++++++++---- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 60c3ad8..d34aeff 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -12,7 +12,7 @@ path="src/bin.rs" [dependencies] tinywasm={path="../tinywasm"} argh="0.1" -color-eyre={default-features=false} +color-eyre={version="0.6", default-features=false} log="0.4" pretty_env_logger="0.5" wast={version="69.0", optional=true} diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 084a49c..82f00b9 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -11,6 +11,8 @@ pub enum Error { FuncDidNotReturn, StackUnderflow, + InvalidStore, + #[cfg(feature = "std")] Io(crate::std::io::Error), } @@ -23,6 +25,7 @@ impl Display for Error { Self::ParseError(err) => write!(f, "error parsing module: {:?}", err), Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {}", feature), Self::Other(message) => write!(f, "unknown error: {}", message), + Self::InvalidStore => write!(f, "invalid store"), #[cfg(feature = "std")] Self::Io(err) => write!(f, "I/O error: {}", err), } diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index c90cec6..598dba1 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -3,17 +3,20 @@ use tinywasm_types::{Export, FuncAddr, FuncType, ModuleInstanceAddr}; use crate::{ func::{FromWasmValueTuple, IntoWasmValueTuple}, - ExportInstance, FuncHandle, Result, Store, TypedFuncHandle, + Error, ExportInstance, FuncHandle, Result, Store, TypedFuncHandle, }; -/// A WebAssembly Module Instance. +/// A WebAssembly Module Instance +/// /// Addrs are indices into the store's data structures. +/// /// See https://webassembly.github.io/spec/core/exec/runtime.html#module-instances #[derive(Debug, Clone)] pub struct ModuleInstance(Arc); #[derive(Debug)] struct ModuleInstanceInner { + pub(crate) store_id: usize, pub(crate) _idx: ModuleInstanceAddr, pub(crate) func_start: Option, pub(crate) types: Box<[FuncType]>, @@ -34,8 +37,10 @@ impl ModuleInstance { exports: Box<[Export]>, func_addrs: Vec, idx: ModuleInstanceAddr, + store_id: usize, ) -> Self { Self(Arc::new(ModuleInstanceInner { + store_id, _idx: idx, types, func_start, @@ -46,6 +51,10 @@ impl ModuleInstance { /// Get an exported function by name pub fn get_func(&self, store: &Store, name: &str) -> Result { + if self.0.store_id != store.id() { + return Err(Error::InvalidStore); + } + let export = self.0.exports.func(name)?; let func_addr = self.0.func_addrs[export.index as usize]; let func = store.get_func(func_addr as usize)?; @@ -78,6 +87,10 @@ impl ModuleInstance { /// (which is not part of the spec, but used by llvm) /// https://webassembly.github.io/spec/core/syntax/modules.html#start-function pub fn get_start_func(&mut self, store: &Store) -> Result> { + if self.0.store_id != store.id() { + return Err(Error::InvalidStore); + } + let func_index = match self.0.func_start { Some(func_index) => func_index, None => { diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs index 8e258fd..04320a2 100644 --- a/crates/tinywasm/src/module.rs +++ b/crates/tinywasm/src/module.rs @@ -52,6 +52,7 @@ impl Module { self.data.exports, func_addrs, idx, + store.id(), ); store.add_instance(instance.clone())?; diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs index a74163e..0e73ece 100644 --- a/crates/tinywasm/src/store.rs +++ b/crates/tinywasm/src/store.rs @@ -1,13 +1,25 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; + use alloc::{format, vec::Vec}; use tinywasm_types::{FuncAddr, Function, Instruction, ModuleInstanceAddr, TypeAddr, ValType}; use crate::{runtime::Runtime, Error, ModuleInstance, Result}; -/// global state that can be manipulated by WebAssembly programs -/// data should only be addressable by the module that owns it -/// https://webassembly.github.io/spec/core/exec/runtime.html#store -#[derive(Debug, Default)] +// global store id counter +static STORE_ID: AtomicUsize = AtomicUsize::new(0); + +/// Global state that can be manipulated by WebAssembly programs +/// +/// Data should only be addressable by the module that owns it +/// +/// Note that the state doesn't do any garbage collection - so it will grow +/// indefinitely if you keep adding modules to it. When calling temporary +/// functions, you should create a new store and then drop it when you're done (e.g. in a request handler) +/// +/// See also: https://webassembly.github.io/spec/core/exec/runtime.html#store +#[derive(Debug)] pub struct Store { + id: usize, module_instances: Vec, module_instance_count: usize, @@ -15,6 +27,26 @@ pub struct Store { pub(crate) runtime: Runtime, } +impl PartialEq for Store { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Default for Store { + fn default() -> Self { + let id = STORE_ID.fetch_add(1, Ordering::Relaxed); + + Self { + id, + module_instances: Vec::new(), + module_instance_count: 0, + data: StoreData::default(), + runtime: Runtime::default(), + } + } +} + #[derive(Debug)] pub struct FunctionInstance { pub(crate) func: Function, @@ -50,6 +82,10 @@ pub struct StoreData { } impl Store { + pub fn id(&self) -> usize { + self.id + } + pub(crate) fn next_module_instance_idx(&self) -> ModuleInstanceAddr { self.module_instance_count as ModuleInstanceAddr } -- cgit v1.3.1