From 50545457d9d7439ca8ab6e3f0f88b04d208cd818 Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 15 Jul 2026 20:32:51 +0200 Subject: refactor: simplify runtime APIs and state Signed-off-by: Henry --- crates/tinywasm/src/func.rs | 50 +++++++++--------- crates/tinywasm/src/instance.rs | 77 +++++++++++++++------------- crates/tinywasm/src/interpreter/executor.rs | 2 - crates/tinywasm/src/reference.rs | 4 +- crates/tinywasm/src/store/element.rs | 2 - crates/tinywasm/src/store/global.rs | 5 +- crates/tinywasm/src/store/mod.rs | 78 ++++++++++++++--------------- 7 files changed, 110 insertions(+), 108 deletions(-) diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 76583f2..9777a47 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,7 +1,7 @@ use crate::interpreter::stack::{CallFrame, ValueStack}; use crate::reference::StoreItem; use crate::{Error, FunctionInstance, InterpreterRuntime, Result, Store, Trap}; -use alloc::{boxed::Box, format, rc::Rc, sync::Arc, vec, vec::Vec}; +use alloc::{borrow::Cow, boxed::Box, format, rc::Rc, sync::Arc, vec, vec::Vec}; use core::hint::cold_path; use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, WasmType, WasmValue}; @@ -574,31 +574,30 @@ impl<'store, R: FromWasmValues> FuncExecutionTyped<'store, R> { /// Describes the WebAssembly value types produced by a Rust value or tuple shape. pub trait ToWasmTypes { + /// Static WebAssembly types for shapes that do not require runtime concatenation. + const WASM_TYPES: Option<&'static [WasmType]>; + /// Return the flattened WebAssembly value types for this tuple shape. - fn wasm_types() -> Box<[WasmType]>; + fn wasm_types() -> Cow<'static, [WasmType]> { + Cow::Borrowed(Self::WASM_TYPES.expect("dynamic ToWasmTypes implementation must override wasm_types")) + } } /// Describes the WebAssembly value types produced by a scalar Rust type. pub trait ToWasmType { - /// Return the single WebAssembly value type for this scalar type. - fn wasm_type() -> WasmType; + /// The single WebAssembly value type for this scalar type. + const WASM_TYPE: WasmType; } macro_rules! impl_scalar_wasm_traits { ($($T:ty => $val_ty:ident),+ $(,)?) => { $( impl ToWasmType for $T { - #[inline] - fn wasm_type() -> WasmType { - WasmType::$val_ty - } + const WASM_TYPE: WasmType = WasmType::$val_ty; } impl ToWasmTypes for $T { - #[inline] - fn wasm_types() -> Box<[WasmType]> { - Box::new([WasmType::$val_ty]) - } + const WASM_TYPES: Option<&'static [WasmType]> = Some(&[WasmType::$val_ty]); } impl IntoWasmValues for $T { @@ -611,9 +610,16 @@ macro_rules! impl_scalar_wasm_traits { impl FromWasmValues for $T { #[inline] fn from_wasm_values(values: &[WasmValue]) -> Result { - let value = *values.first().ok_or(Error::other("Not enough elemennts in &[WasmValue]"))?; + let value = *values.first().ok_or_else(|| { + core::hint::cold_path(); + Error::other("Not enough elements in &[WasmValue]") + })?; + <$T>::try_from(value).map_err(|e| { - Error::Other(format!("FromWasmValues: Could not convert WasmValue to expected type: {e:?}")) + core::hint::cold_path(); + Error::Other(format!( + "FromWasmValues: Could not convert WasmValue to expected type: {e:?}" + )) }) } } @@ -627,10 +633,7 @@ macro_rules! impl_tuple_traits { where $($T: ToWasmType,)+ { - #[inline] - fn wasm_types() -> Box<[WasmType]> { - Box::new([$($T::wasm_type(),)+]) - } + const WASM_TYPES: Option<&'static [WasmType]> = Some(&[$($T::WASM_TYPE,)+]); } impl<$($T),+> IntoWasmValues for ($($T,)+) @@ -757,12 +760,14 @@ impl From<(T1, T2)> for WasmTupleChain { } impl ToWasmTypes for WasmTupleChain { + const WASM_TYPES: Option<&'static [WasmType]> = None; + #[inline] - fn wasm_types() -> Box<[WasmType]> { + fn wasm_types() -> Cow<'static, [WasmType]> { let mut types = Vec::new(); types.extend_from_slice(&T1::wasm_types()); types.extend_from_slice(&T2::wasm_types()); - types.into_boxed_slice() + Cow::Owned(types) } } @@ -788,10 +793,7 @@ impl FromWasmValues for Wa } impl ToWasmTypes for () { - #[inline] - fn wasm_types() -> Box<[WasmType]> { - Box::new([]) - } + const WASM_TYPES: Option<&'static [WasmType]> = Some(&[]); } impl IntoWasmValues for () { diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index a8cf14a..8cff310 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -210,25 +210,23 @@ impl ModuleInstance { let instance = ModuleInstance(Rc::new(instance)); store.add_instance(instance.clone()); - match (elem_trapped, data_trapped) { - (Some(trap), _) | (_, Some(trap)) => { - cold_path(); - Err(trap.into()) - } - _ => Ok(instance), + if let Some(trap) = elem_trapped.or(data_trapped) { + cold_path(); + return Err(trap.into()); } + Ok(instance) } /// Get a export by name pub fn export_addr(&self, name: &str) -> Option { - let exports = self.0.exports.iter().find(|e| *e.name == *name)?; - let addr = match exports.kind { - ExternalKind::Func => self.0.func_addrs.get(exports.index as usize)?, - ExternalKind::Table => self.0.table_addrs.get(exports.index as usize)?, - ExternalKind::Memory => self.0.mem_addrs.get(exports.index as usize)?, - ExternalKind::Global => self.0.global_addrs.get(exports.index as usize)?, + let export = self.0.exports.iter().find(|e| *e.name == *name)?; + let addr = match export.kind { + ExternalKind::Func => self.0.func_addrs.get(export.index as usize)?, + ExternalKind::Table => self.0.table_addrs.get(export.index as usize)?, + ExternalKind::Memory => self.0.mem_addrs.get(export.index as usize)?, + ExternalKind::Global => self.0.global_addrs.get(export.index as usize)?, }; - Some(ExternVal::new(exports.kind, *addr)) + Some(ExternVal::new(export.kind, *addr)) } /// Returns an iterator over all exported extern values for this instance. @@ -288,25 +286,19 @@ impl ModuleInstance { #[inline] fn require_export(&self, name: &str) -> Result { - match self.export_addr(name) { - Some(addr) => Ok(addr), - None => { - cold_path(); - Err(Error::Other(format!("Export not found: {name}"))) - } - } + self.export_addr(name).ok_or_else(|| { + cold_path(); + Error::Other(format!("Export not found: {name}")) + }) } #[inline] #[cfg(feature = "guest-debug")] fn index_addr(slice: &[T], idx: u32, kind: &str) -> Result { - match slice.get(idx as usize) { - Some(addr) => Ok(*addr), - None => { - cold_path(); - Err(Error::Other(format!("{kind} index out of bounds: {idx}"))) - } - } + slice.get(idx as usize).copied().ok_or_else(|| { + cold_path(); + Error::Other(format!("{kind} index out of bounds: {idx}")) + }) } /// Get any exported extern value by name. @@ -376,12 +368,9 @@ impl ModuleInstance { pub fn func_untyped(&self, store: &Store, name: &str) -> Result { self.validate_store(store)?; - let func_addr = match self.require_export(name)? { - ExternVal::Func(func_addr) => func_addr, - _ => { - cold_path(); - return Err(Error::Other(format!("Export is not a function: {name}"))); - } + let ExternVal::Func(func_addr) = self.require_export(name)? else { + cold_path(); + return Err(Error::Other(format!("Export is not a function: {name}"))); }; Ok(Function { @@ -444,7 +433,20 @@ impl ModuleInstance { store: &Store, name: &str, ) -> Result> { - let func = self.func_untyped(store, name)?; + self.validate_store(store)?; + + let ExternVal::Func(func_addr) = self.require_export(name)? else { + cold_path(); + return Err(Error::Other(format!("Export is not a function: {name}"))); + }; + + let func = Function { + item: StoreItem::new(self.0.store_id, func_addr), + addr: func_addr, + module_addr: self.id(), + ty: store.state.get_func(func_addr).ty().clone(), + }; + Self::validate_typed_func::(&func, name)?; Ok(FunctionTyped { func, marker: core::marker::PhantomData }) } @@ -462,14 +464,17 @@ impl ModuleInstance { Ok(FunctionTyped { func, marker: core::marker::PhantomData }) } + #[inline] fn validate_typed_func(func: &Function, func_name: &str) -> Result<()> { - if *func.ty.params() != *P::wasm_types() || *func.ty.results() != *R::wasm_types() { + let params = P::wasm_types(); + let results = R::wasm_types(); + if func.ty.params() != params.as_ref() || func.ty.results() != results.as_ref() { cold_path(); #[cfg(feature = "debug")] return Err(Error::Other(format!( "function type mismatch for {func_name}: expected {:?}, actual {:?}", - FuncType::new(&P::wasm_types(), &R::wasm_types()), + FuncType::new(¶ms, &results), func.ty ))); diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 0405546..e4bb6c5 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -1497,8 +1497,6 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { let dst = self.pop_table_operand(self.store.state.get_table(table_addr).kind.arch())?; // d let elem_addr = self.module.resolve_elem_addr(elem_index) as usize; let elem = self.store.state.elements.get(elem_addr).ok_or_else(|| Trap::Other("element not found"))?; - // Element kind storage is removed separately; table.init only depends on retained items. - let _ = &elem.kind; let items = elem.items.as_deref().unwrap_or(&[]); let Some(end) = offset.checked_add(size) else { cold_path(); diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index b8a1aa1..e46db51 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -446,7 +446,7 @@ impl Global { /// Get the current value of the global. pub fn get(&self, store: &Store) -> Result { let global = self.instance(store)?; - let value = global.value.get().attach_type(global.ty.ty); + let value = global.value.attach_type(global.ty.ty); Ok(value.unwrap_or_else(|| unreachable!("Global value type does not match global type, this is a bug"))) } @@ -461,7 +461,7 @@ impl Global { cold_path(); return Err(Error::Other("invalid global value type".to_string())); } - global.value.set(value.into()); + global.value = value.into(); Ok(()) } } diff --git a/crates/tinywasm/src/store/element.rs b/crates/tinywasm/src/store/element.rs index 940a6de..f3a8602 100644 --- a/crates/tinywasm/src/store/element.rs +++ b/crates/tinywasm/src/store/element.rs @@ -1,13 +1,11 @@ use crate::TableElement; use alloc::vec::Vec; -use tinywasm_types::*; /// A WebAssembly Element Instance /// /// See #[cfg_attr(feature = "debug", derive(Debug))] pub(crate) struct ElementInstance { - pub(crate) kind: ElementKind, pub(crate) items: Option>, // none is the element was dropped } diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs index 95af73f..9280494 100644 --- a/crates/tinywasm/src/store/global.rs +++ b/crates/tinywasm/src/store/global.rs @@ -1,5 +1,4 @@ use crate::interpreter::TinyWasmValue; -use core::cell::Cell; use tinywasm_types::*; /// A WebAssembly Global Instance @@ -7,12 +6,12 @@ use tinywasm_types::*; /// See #[cfg_attr(feature = "debug", derive(Debug))] pub(crate) struct GlobalInstance { - pub(crate) value: Cell, + pub(crate) value: TinyWasmValue, pub(crate) ty: GlobalType, } impl GlobalInstance { pub(crate) fn new(ty: GlobalType, value: TinyWasmValue) -> Self { - Self { ty, value: value.into() } + Self { ty, value } } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 875980a..1723bfe 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -85,7 +85,10 @@ impl Store { #[inline] pub(crate) fn get_module_instance_internal(&self, addr: ModuleInstanceAddr) -> ModuleInstance { - self.get_module_instance(addr).unwrap_or_else(|| unreachable!("invalid module instance: {addr}")) + self.module_instances + .get(addr as usize) + .unwrap_or_else(|| unreachable!("invalid module instance: {addr}")) + .clone() } pub(crate) fn enter_execution(&mut self) -> Result<()> { @@ -215,12 +218,12 @@ impl State { /// Get the global at the actual index in the store pub(crate) fn get_global_val(&self, addr: GlobalAddr) -> TinyWasmValue { - self.get_global(addr).value.get() + self.get_global(addr).value } /// Set the global at the actual index in the store pub(crate) fn set_global_val(&mut self, addr: GlobalAddr, value: TinyWasmValue) { - self.get_global_mut(addr).value.set(value); + self.get_global_mut(addr).value = value; } } @@ -300,14 +303,13 @@ impl Store { pub(crate) fn init_globals( &mut self, out: &mut Vec, - new_globals: &[Global], + globals: &[Global], func_addrs: &[FuncAddr], ) -> Result<()> { let start = self.state.globals.len() as Addr; - out.reserve_exact(new_globals.len()); - self.state.globals.reserve_exact(new_globals.len()); + out.extend(start..start + globals.len() as Addr); - for (i, global) in new_globals.iter().enumerate() { + for global in globals { let value = match self.eval_const(&global.init, out, func_addrs) { Ok(val) => val, Err(e) => { @@ -315,29 +317,31 @@ impl Store { return Err(e); } }; - self.state.globals.push(GlobalInstance::new(global.ty, value)); - out.push(start + i as Addr); } Ok(()) } fn elem_addr(&self, item: &ElementItem, globals: &[Addr], funcs: &[FuncAddr]) -> Result> { - let res = match item { + match item { + ElementItem::Expr(expr) => match self.eval_const(expr, globals, funcs)? { + TinyWasmValue::ValueRef(v) => Ok(v.addr()), + other => { + cold_path(); + Err(Error::Other(format!("expected ref type, got {other:?}"))) + } + }, ElementItem::Func(addr) => match funcs.get(*addr as usize) { - Some(func_addr) => Some(*func_addr), + Some(func_addr) => Ok(Some(*func_addr)), None => { cold_path(); - return Err(Error::Other(format!( + Err(Error::Other(format!( "function {addr} not found. This should have been caught by the validator" - ))); + ))) } }, - ElementItem::Expr(expr) => self.eval_ref_const(expr, globals, funcs)?, - }; - - Ok(res) + } } /// Add elements to the store, returning their addresses in the store @@ -398,7 +402,7 @@ impl Store { } }; - self.state.elements.push(ElementInstance { kind: element.kind.clone(), items }); + self.state.elements.push(ElementInstance { items }); elem_addrs.push((i + elem_count) as Addr); } @@ -428,13 +432,14 @@ impl Store { return Err(Error::Other(format!("memory {mem_addr} not found for data segment {i}"))); }; - match mem.inner.write_all(offset as usize, &data.data) { + let offset = usize::try_from(offset).unwrap_or(usize::MAX); + match mem.inner.write_all(offset, &data.data) { Some(()) => None, None => { return Ok(( data_addrs.into_boxed_slice(), Some(crate::Trap::MemoryOutOfBounds { - offset: offset as usize, + offset, len: data.data.len(), max: mem.inner.len(), }), @@ -474,6 +479,7 @@ impl Store { } /// Evaluate a constant expression + #[inline] fn eval_const( &self, const_instrs: &[tinywasm_types::ConstInstruction], @@ -495,12 +501,12 @@ impl Store { return Err(Error::Other(format!("global {addr} not found"))); }; - Ok(global.value.get()) + Ok(global.value) }; let resolve_func = |idx: u32| -> Result { - match module_func_addrs.get(idx as usize).copied() { - Some(func_addr) => Ok(func_addr), + match module_func_addrs.get(idx as usize) { + Some(func_addr) => Ok(*func_addr), None => { cold_path(); Err(Error::Other(format!( @@ -530,7 +536,7 @@ impl Store { return Ok(val); } - let mut stack = Vec::with_capacity(const_instrs.len()); + let mut stack = Vec::new(); for instr in const_instrs { match instr { I32Const(i) => stack.push(TinyWasmValue::Value32(*i as u32)), @@ -560,7 +566,10 @@ impl Store { I32Add => lhs.wrapping_add(rhs), I32Sub => lhs.wrapping_sub(rhs), I32Mul => lhs.wrapping_mul(rhs), - _ => unreachable!("invalid const instruction in i32 op"), + _ => { + cold_path(); + return Err(Error::other("invalid const instruction in i32 op")); + } }; stack.push(TinyWasmValue::Value32(out as u32)); } @@ -578,7 +587,10 @@ impl Store { I64Add => lhs.wrapping_add(rhs), I64Sub => lhs.wrapping_sub(rhs), I64Mul => lhs.wrapping_mul(rhs), - _ => unreachable!("invalid const instruction in i64 op"), + _ => { + cold_path(); + return Err(Error::other("invalid const instruction in i64 op")); + } }; stack.push(TinyWasmValue::Value64(out as u64)); } @@ -594,19 +606,7 @@ impl Store { cold_path(); return Err(Error::other("const expression did not reduce to single value")); } - Ok(value) - } - fn eval_ref_const( - &self, - const_instrs: &[tinywasm_types::ConstInstruction], - module_global_addrs: &[Addr], - module_func_addrs: &[FuncAddr], - ) -> Result> { - let value = self.eval_const(const_instrs, module_global_addrs, module_func_addrs)?; - match value { - TinyWasmValue::ValueRef(v) => Ok(v.addr()), - other => Err(Error::Other(format!("expected reference const value, got {other:?}"))), - } + Ok(value) } } -- cgit v1.3.1