summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorHenry <mail@henrygressmann.de>2026-06-27 13:39:36 +0200
committerHenry <mail@henrygressmann.de>2026-06-27 13:39:36 +0200
commit5f74d5bc5cd0870a42342210c62d7257a0dd8458 (patch)
tree0884f1dc580adc2b5f72552bc6b2ecc9048db6c2 /crates
parent8ec1231d818884ee69570e6987908a18a8762a26 (diff)
feat: blocking host reentrant calls
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates')
-rw-r--r--crates/tinywasm/src/error.rs6
-rw-r--r--crates/tinywasm/src/func.rs249
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs9
-rw-r--r--crates/tinywasm/src/interpreter/mod.rs8
-rw-r--r--crates/tinywasm/src/interpreter/stack/call_stack.rs14
-rw-r--r--crates/tinywasm/src/interpreter/stack/value_stack.rs9
-rw-r--r--crates/tinywasm/src/reference.rs2
-rw-r--r--crates/tinywasm/src/store/mod.rs35
8 files changed, 226 insertions, 106 deletions
diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs
index b26da42..4f187e5 100644
--- a/crates/tinywasm/src/error.rs
+++ b/crates/tinywasm/src/error.rs
@@ -96,6 +96,12 @@ impl LinkingError {
}
}
+impl Error {
+ pub(crate) fn other(message: impl Into<String>) -> Self {
+ Self::Other(message.into())
+ }
+}
+
/// A WebAssembly trap
///
/// See <https://webassembly.github.io/spec/core/intro/overview.html#trap>
diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs
index 82b329d..cb95945 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, unlikely};
-use alloc::{boxed::Box, format, rc::Rc, string::ToString, sync::Arc, vec, vec::Vec};
+use alloc::{boxed::Box, format, rc::Rc, sync::Arc, vec, vec::Vec};
use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, WasmType, WasmValue};
impl Function {
@@ -10,26 +10,36 @@ impl Function {
/// See <https://webassembly.github.io/spec/core/exec/modules.html#invocation>
#[inline]
pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result<Vec<WasmValue>> {
+ #[inline]
+ fn call_inner(func: &Function, store: &mut Store, params: &[WasmValue]) -> Result<Vec<WasmValue>> {
+ let func_instance = store.state.get_func(func.addr);
+ let wasm_func = match func_instance {
+ FunctionInstance::Host(host_func) => {
+ return host_func.clone().call(FuncContext { store, module_addr: func.module_addr }, params);
+ }
+ FunctionInstance::Wasm(wasm_func) => wasm_func,
+ };
+
+ // Reset stack, push args, allocate locals, create entry frame.
+ store.call_stack.clear();
+ store.value_stack.clear();
+ store.value_stack.extend_from_wasmvalues(params)?;
+ let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?;
+ let callframe = CallFrame::new(func.addr, locals_base, wasm_func.func.locals);
+
+ // Execute until completion and then collect result values from the stack.
+ InterpreterRuntime::exec(store, callframe, 0)?;
+ collect_call_results(&mut store.value_stack, &func.ty)
+ }
+
self.item.validate_store(store)?;
validate_call_params(&self.ty, params)?;
- let wasm_func = match store.state.get_func(self.addr) {
- FunctionInstance::Host(host_func) => {
- return host_func.clone().call(FuncContext { store, module_addr: self.module_addr }, params);
- }
- FunctionInstance::Wasm(wasm_func) => wasm_func,
- };
-
- // Reset stack, push args, allocate locals, create entry frame.
- store.call_stack.clear();
- store.value_stack.clear();
- store.value_stack.extend_from_wasmvalues(params)?;
- let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?;
- let callframe = CallFrame::new(self.addr, locals_base, wasm_func.func.locals);
+ store.enter_execution()?;
+ let result = call_inner(self, store, params);
+ store.exit_execution();
- // Execute until completion and then collect result values from the stack.
- InterpreterRuntime::exec(store, callframe)?;
- collect_call_results(&mut store.value_stack, &self.ty)
+ result
}
/// Call a function and return a resumable execution handle.
@@ -42,30 +52,41 @@ impl Function {
store: &'store mut Store,
params: &[WasmValue],
) -> Result<FuncExecution<'store>> {
- self.item.validate_store(store)?;
- validate_call_params(&self.ty, params)?;
-
- match store.state.get_func(self.addr) {
- FunctionInstance::Host(host_func) => {
- let result = host_func.clone().call(FuncContext { store, module_addr: self.module_addr }, params)?;
- Ok(FuncExecution { store, state: FuncExecutionState::Completed { result: Some(result) } })
- }
- FunctionInstance::Wasm(wasm_func) => {
- store.call_stack.clear();
- store.value_stack.clear();
- store.value_stack.extend_from_wasmvalues(params)?;
- let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?;
- let callframe = CallFrame::new(self.addr, locals_base, wasm_func.func.locals);
+ #[inline]
+ fn call_resumable_inner(
+ func: &Function,
+ store: &mut Store,
+ params: &[WasmValue],
+ ) -> Result<FuncExecutionState> {
+ let func_instance = store.state.get_func(func.addr);
+ match func_instance {
+ FunctionInstance::Host(host_func) => host_func
+ .clone()
+ .call(FuncContext { store, module_addr: func.module_addr }, params)
+ .map(|result| FuncExecutionState::Completed { result: Some(result) }),
+ FunctionInstance::Wasm(wasm_func) => {
+ store.call_stack.clear();
+ store.value_stack.clear();
+ store.value_stack.extend_from_wasmvalues(params)?;
+ let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?;
+ let callframe = CallFrame::new(func.addr, locals_base, wasm_func.func.locals);
- Ok(FuncExecution {
- store,
- state: FuncExecutionState::Running {
+ Ok(FuncExecutionState::Running {
exec_state: ExecutionState { callframe },
- root_func_addr: self.addr,
- },
- })
+ root_func_addr: func.addr,
+ })
+ }
}
}
+
+ self.item.validate_store(store)?;
+ validate_call_params(&self.ty, params)?;
+
+ store.enter_execution()?;
+ let result = call_resumable_inner(self, store, params);
+ store.exit_execution();
+
+ Ok(FuncExecution { store, state: result? })
}
}
@@ -157,27 +178,24 @@ impl HostFunction {
func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>> + 'static,
) -> Function {
let ty = Arc::new(ty.clone());
- let ty_inner = ty.clone();
+ let host_ty = ty.clone();
+
let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
- let ty = ty_inner.clone();
let result = func(ctx, args)?;
+ let expected = host_ty.results();
- if result.len() != ty.results().len() {
- return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result });
- };
+ let valid = result.len() == expected.len()
+ && result.iter().zip(expected).all(|(val, ty)| WasmType::from(val) == *ty);
- result.iter().zip(ty.results().iter()).try_for_each(|(val, res_ty)| {
- if WasmType::from(val) != *res_ty {
- return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result.clone() });
- }
- Ok(())
- })?;
+ if !valid {
+ return Err(crate::Error::InvalidHostFnReturn { expected: Arc::clone(&host_ty), actual: result });
+ }
Ok(result)
};
let addr = store.add_func(FunctionInstance::Host(Rc::new(Self { func: Box::new(inner_func), ty: ty.clone() })));
- Function { item: crate::StoreItem::new(store.id(), addr), module_addr: 0, addr, ty: ty.clone() }
+ Function { item: crate::StoreItem::new(store.id(), addr), module_addr: 0, addr, ty }
}
/// Create a new typed host function import.
@@ -211,9 +229,7 @@ impl HostFunction {
R: IntoWasmValues + ToWasmTypes,
{
let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
- let args = P::from_wasm_values(args)?;
- let result = func(ctx, args)?;
- Ok(result.into_wasm_values())
+ Ok(func(ctx, P::from_wasm_values(args)?)?.into_wasm_values())
};
let ty = Arc::new(tinywasm_types::FuncType::new(&P::wasm_types(), &R::wasm_types()));
@@ -293,6 +309,66 @@ impl FuncContext<'_> {
pub fn remaining_fuel(&self) -> u32 {
self.store.execution_fuel
}
+
+ /// Call a function from within the current host-function invocation.
+ ///
+ /// This is the safe way for host functions to perform blocking reentrant
+ /// calls into Wasm. Unlike [`Function::call`], it preserves the active
+ /// invocation's stacks and resumes the host caller after the nested call
+ /// completes.
+ ///
+ /// Nested calls are currently blocking only. If the surrounding invocation
+ /// is resumed with fuel or a time budget, this method does not suspend and
+ /// later continue the host function in the middle of the nested call.
+ pub fn call_untyped(&mut self, func: &Function, args: &[WasmValue]) -> Result<Vec<WasmValue>> {
+ if !self.store.execution_active {
+ return Err(Error::other("FuncContext::call requires an active host-function invocation"));
+ }
+
+ func.item.validate_store(self.store)?;
+ validate_call_params(&func.ty, args)?;
+
+ let func_instance = self.store.state.get_func(func.addr).clone();
+ match func_instance {
+ FunctionInstance::Host(host_func) => {
+ host_func.call(FuncContext { store: &mut *self.store, module_addr: func.module_addr }, args)
+ }
+ FunctionInstance::Wasm(wasm_func) => {
+ let call_stack_base = self.store.call_stack.len();
+ let value_stack_base = self.store.value_stack.base();
+
+ self.store.value_stack.extend_from_wasmvalues(args).inspect_err(|_| {
+ self.store.value_stack.truncate_to_base(value_stack_base);
+ })?;
+
+ let locals_base = self
+ .store
+ .value_stack
+ .enter_locals(&wasm_func.func.params, &wasm_func.func.locals)
+ .inspect_err(|_| self.store.value_stack.truncate_to_base(value_stack_base))?;
+
+ let callframe = CallFrame::new(func.addr, locals_base, wasm_func.func.locals);
+ InterpreterRuntime::exec(self.store, callframe, call_stack_base).inspect_err(|_| {
+ self.store.call_stack.truncate_to(call_stack_base);
+ self.store.value_stack.truncate_to_base(value_stack_base);
+ })?;
+
+ collect_call_results(&mut self.store.value_stack, &func.ty)
+ }
+ }
+ }
+
+ /// Call a typed function from within the current host-function invocation.
+ ///
+ /// See [`Self::call_untyped`] for reentrancy and resumable-execution
+ /// limitations.
+ pub fn call<P, R>(&mut self, func: &FunctionTyped<P, R>, params: P) -> Result<R>
+ where
+ P: IntoWasmValues,
+ R: FromWasmValues,
+ {
+ R::from_wasm_values(&self.call_untyped(&func.func, &params.into_wasm_values())?)
+ }
}
impl core::ops::Deref for FuncContext<'_> {
@@ -352,23 +428,30 @@ impl<'store> FuncExecution<'store> {
/// Returns [`ExecProgress::Suspended`] when fuel is exhausted, or
/// [`ExecProgress::Completed`] with the final values once the invocation
/// returns.
+ ///
+ /// Reentrant calls made by host functions through [`FuncContext::call`] are
+ /// currently blocking. They do not suspend and later resume the host
+ /// function in the middle of the nested call.
pub fn resume_with_fuel(&mut self, fuel: u32) -> Result<ExecProgress<Vec<WasmValue>>> {
let FuncExecutionState::Running { exec_state, root_func_addr } = &mut self.state else {
let FuncExecutionState::Completed { result } = &mut self.state else {
unreachable!("invalid function execution state")
};
- return result
- .take()
- .map(ExecProgress::Completed)
- .ok_or_else(|| Error::Other("execution already completed".to_string()));
+ return match result.take() {
+ Some(res) => Ok(ExecProgress::Completed(res)),
+ None => Err(Error::other("execution already completed")),
+ };
};
- match InterpreterRuntime::exec_with_fuel(self.store, exec_state.callframe, fuel)? {
+ self.store.enter_execution()?;
+ let result = InterpreterRuntime::exec_with_fuel(self.store, exec_state.callframe, fuel);
+ self.store.exit_execution();
+
+ match result? {
crate::interpreter::ExecState::Completed => {
let result_ty = self.store.state.get_func(*root_func_addr).ty().clone();
- let result = collect_call_results(&mut self.store.value_stack, &result_ty)?;
self.state = FuncExecutionState::Completed { result: None };
- Ok(ExecProgress::Completed(result))
+ Ok(ExecProgress::Completed(collect_call_results(&mut self.store.value_stack, &result_ty)?))
}
crate::interpreter::ExecState::Suspended(callframe) => {
exec_state.callframe = callframe;
@@ -386,6 +469,10 @@ impl<'store> FuncExecution<'store> {
/// Returns [`ExecProgress::Suspended`] when the budget is exhausted, or
/// [`ExecProgress::Completed`] with the final values once the invocation
/// returns.
+ ///
+ /// Reentrant calls made by host functions through [`FuncContext::call`] are
+ /// currently blocking. They do not suspend and later resume the host
+ /// function in the middle of the nested call.
pub fn resume_with_time_budget(
&mut self,
time_budget: crate::std::time::Duration,
@@ -394,18 +481,21 @@ impl<'store> FuncExecution<'store> {
let FuncExecutionState::Completed { result } = &mut self.state else {
unreachable!("invalid function execution state")
};
- return result
- .take()
- .map(ExecProgress::Completed)
- .ok_or_else(|| Error::Other("execution already completed".to_string()));
+ return match result.take() {
+ Some(res) => Ok(ExecProgress::Completed(res)),
+ None => Err(Error::other("execution already completed")),
+ };
};
- match InterpreterRuntime::exec_with_time_budget(self.store, exec_state.callframe, time_budget)? {
+ self.store.enter_execution()?;
+ let result = InterpreterRuntime::exec_with_time_budget(self.store, exec_state.callframe, time_budget);
+ self.store.exit_execution();
+
+ match result? {
crate::interpreter::ExecState::Completed => {
let result_ty = self.store.state.get_func(*root_func_addr).ty().clone();
- let result = collect_call_results(&mut self.store.value_stack, &result_ty)?;
self.state = FuncExecutionState::Completed { result: None };
- Ok(ExecProgress::Completed(result))
+ Ok(ExecProgress::Completed(collect_call_results(&mut self.store.value_stack, &result_ty)?))
}
crate::interpreter::ExecState::Suspended(callframe) => {
exec_state.callframe = callframe;
@@ -425,7 +515,7 @@ fn validate_call_params(func_ty: &FuncType, params: &[WasmValue]) -> Result<()>
}
if !(func_ty.params().iter().zip(params).all(|(ty, param)| ty == &param.into())) {
- return Err(Error::Other("Type mismatch".into()));
+ return Err(Error::other("Type mismatch"));
}
Ok(())
@@ -533,14 +623,9 @@ macro_rules! impl_scalar_wasm_traits {
impl FromWasmValues for $T {
#[inline]
fn from_wasm_values(values: &[WasmValue]) -> Result<Self> {
- let value = *values
- .first()
- .ok_or(Error::Other("Not enough values in WasmValue vector".to_string()))?;
+ let value = *values.first().ok_or(Error::other("Not enough elemennts in &[WasmValue]"))?;
<$T>::try_from(value).map_err(|e| {
- Error::Other(format!(
- "FromWasmValues: Could not convert WasmValue to expected type: {:?}",
- e
- ))
+ Error::Other(format!("FromWasmValues: Could not convert WasmValue to expected type: {e:?}"))
})
}
}
@@ -580,18 +665,10 @@ macro_rules! impl_tuple_traits {
fn from_wasm_values(values: &[WasmValue]) -> Result<Self> {
let mut iter = values.iter();
- Ok((
- $(
- $T::try_from(
- *iter.next()
- .ok_or(Error::Other("Not enough values in WasmValue vector".to_string()))?
- )
- .map_err(|e| Error::Other(format!(
- "FromWasmValues: Could not convert WasmValue to expected type: {:?}",
- e,
- )))?,
- )+
- ))
+ Ok(($(
+ $T::try_from(*iter.next().ok_or(Error::other("Not enough values in WasmValue vector"))?)
+ .map_err(|e| Error::Other(format!("FromWasmValues: Could not convert WasmValue to expected type: {e:?}")))?,
+ )+))
}
}
}
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index dd45f74..f0d995d 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -27,13 +27,14 @@ pub(crate) struct Executor<'store, const BUDGETED: bool> {
func: Arc<WasmFunction>,
module: ModuleInstance,
store: &'store mut Store,
+ call_stack_base: u32,
}
impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
- pub(crate) fn new(store: &'store mut Store, cf: CallFrame) -> Self {
+ pub(crate) fn new(store: &'store mut Store, cf: CallFrame, call_stack_base: u32) -> Self {
let wasm_func = store.state.get_wasm_func(cf.func_addr);
let module = store.get_module_instance_internal(wasm_func.owner);
- Self { module, cf, func: wasm_func.func.clone(), store }
+ Self { module, cf, func: wasm_func.func.clone(), store, call_stack_base }
}
#[inline(always)]
@@ -1078,7 +1079,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
fn exec_return(&mut self) -> bool {
self.store.value_stack.truncate_keep_counts(self.cf.locals_base, self.func.results);
- let Some(caller) = self.store.call_stack.pop() else {
+ let Some(caller) = self.store.call_stack.pop_frame(self.call_stack_base) else {
return true;
};
if caller.func_addr == self.cf.func_addr {
@@ -1096,7 +1097,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
#[inline(always)]
fn finish_return(&mut self) -> bool {
- let Some(caller) = self.store.call_stack.pop() else {
+ let Some(caller) = self.store.call_stack.pop_frame(self.call_stack_base) else {
return true;
};
if caller.func_addr == self.cf.func_addr {
diff --git a/crates/tinywasm/src/interpreter/mod.rs b/crates/tinywasm/src/interpreter/mod.rs
index db094e0..cece36a 100644
--- a/crates/tinywasm/src/interpreter/mod.rs
+++ b/crates/tinywasm/src/interpreter/mod.rs
@@ -26,12 +26,12 @@ pub(crate) enum ExecState {
pub(crate) struct InterpreterRuntime;
impl InterpreterRuntime {
- pub(crate) fn exec(store: &mut Store, cf: CallFrame) -> Result<(), Trap> {
- executor::Executor::<false>::new(store, cf).run_to_completion()
+ pub(crate) fn exec(store: &mut Store, cf: CallFrame, call_stack_base: u32) -> Result<(), Trap> {
+ executor::Executor::<false>::new(store, cf, call_stack_base).run_to_completion()
}
pub(crate) fn exec_with_fuel(store: &mut Store, cf: CallFrame, fuel: u32) -> Result<ExecState, Trap> {
- executor::Executor::<true>::new(store, cf).run_with_fuel(fuel)
+ executor::Executor::<true>::new(store, cf, 0).run_with_fuel(fuel)
}
#[cfg(feature = "std")]
@@ -40,6 +40,6 @@ impl InterpreterRuntime {
cf: CallFrame,
time_budget: core::time::Duration,
) -> Result<ExecState, Trap> {
- executor::Executor::<false>::new(store, cf).run_with_time_budget(time_budget)
+ executor::Executor::<false>::new(store, cf, 0).run_with_time_budget(time_budget)
}
}
diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs
index a307b50..ad71302 100644
--- a/crates/tinywasm/src/interpreter/stack/call_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs
@@ -22,8 +22,18 @@ impl CallStack {
}
#[inline(always)]
- pub(crate) fn pop(&mut self) -> Option<CallFrame> {
- self.stack.pop()
+ pub(crate) fn len(&self) -> u32 {
+ self.stack.len() as u32
+ }
+
+ pub(crate) fn truncate_to(&mut self, len: u32) {
+ debug_assert!(len as usize <= self.stack.len());
+ self.stack.truncate(len as usize);
+ }
+
+ #[inline(always)]
+ pub(crate) fn pop_frame(&mut self, base: u32) -> Option<CallFrame> {
+ if self.len() == base { None } else { self.stack.pop() }
}
#[inline(always)]
diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs
index 014f7a3..ad16467 100644
--- a/crates/tinywasm/src/interpreter/stack/value_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs
@@ -195,6 +195,15 @@ impl ValueStack {
}
#[inline(always)]
+ pub(crate) fn base(&self) -> StackBase {
+ StackBase {
+ s32: self.stack_32.len() as u32,
+ s64: self.stack_64.len() as u32,
+ s128: self.stack_128.len() as u32,
+ }
+ }
+
+ #[inline(always)]
pub(crate) fn len(&self) -> usize {
self.stack_32.len() + self.stack_64.len() + self.stack_128.len()
}
diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs
index 51e0cee..036eaf1 100644
--- a/crates/tinywasm/src/reference.rs
+++ b/crates/tinywasm/src/reference.rs
@@ -353,7 +353,7 @@ impl Table {
let init = match (ty.element_type, init) {
(WasmType::RefFunc, WasmValue::RefFunc(func_ref)) => TableElement::from(func_ref.addr()),
(WasmType::RefExtern, WasmValue::RefExtern(extern_ref)) => TableElement::from(extern_ref.addr()),
- _ => return Err(Error::Other("invalid table init value".to_string())),
+ _ => return Err(Error::other("invalid table init value")),
};
let addr = store.state.tables.len() as TableAddr;
store.state.tables.push(TableInstance::new_with_init(ty, init));
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index 446c9ed..1599d81 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -1,5 +1,5 @@
use alloc::sync::Arc;
-use alloc::{boxed::Box, format, string::ToString, vec::Vec};
+use alloc::{boxed::Box, format, vec::Vec};
use core::hint::cold_path;
use core::sync::atomic::{AtomicUsize, Ordering};
use tinywasm_types::*;
@@ -45,6 +45,7 @@ pub struct Store {
pub(crate) engine: Engine,
pub(crate) execution_fuel: u32,
+ pub(crate) execution_active: bool,
pub(crate) state: State,
pub(crate) call_stack: CallStack,
pub(crate) value_stack: ValueStack,
@@ -73,6 +74,7 @@ impl Store {
value_stack: ValueStack::new(engine.config()),
engine,
execution_fuel: 0,
+ execution_active: false,
}
}
@@ -91,6 +93,21 @@ impl Store {
}
}
}
+
+ pub(crate) fn enter_execution(&mut self) -> Result<()> {
+ if self.execution_active {
+ return Err(Trap::Other(
+ "cannot call a function while another invocation is active; use FuncContext::call from host functions",
+ )
+ .into());
+ }
+ self.execution_active = true;
+ Ok(())
+ }
+
+ pub(crate) fn exit_execution(&mut self) {
+ self.execution_active = false;
+ }
}
impl PartialEq for Store {
@@ -569,7 +586,7 @@ impl Store {
RefFunc(Some(idx)) => TinyWasmValue::ValueRef(ValueRef::from_addr(Some(resolve_func(*idx)?))),
_ => {
cold_path();
- return Err(Error::Other("unsupported const instruction".to_string()));
+ return Err(Error::other("unsupported const instruction"));
}
};
@@ -591,14 +608,14 @@ impl Store {
}
RefExtern(Some(_)) => {
cold_path();
- return Err(Error::Other("ref.extern constants are not supported in init expressions".to_string()));
+ return Err(Error::other("ref.extern constants are not supported in init expressions"));
}
I32Add | I32Sub | I32Mul => {
- let rhs = stack.pop().ok_or_else(|| Error::Other("const stack underflow".to_string()))?;
- let lhs = stack.pop().ok_or_else(|| Error::Other("const stack underflow".to_string()))?;
+ let rhs = stack.pop().ok_or_else(|| Error::other("const stack underflow"))?;
+ let lhs = stack.pop().ok_or_else(|| Error::other("const stack underflow"))?;
let (TinyWasmValue::Value32(lhs), TinyWasmValue::Value32(rhs)) = (lhs, rhs) else {
cold_path();
- return Err(Error::Other("type mismatch in const i32 op".to_string()));
+ return Err(Error::other("type mismatch in const i32 op"));
};
let lhs = lhs as i32;
let rhs = rhs as i32;
@@ -615,7 +632,7 @@ impl Store {
let lhs = stack.pop();
let (Some(TinyWasmValue::Value64(lhs)), Some(TinyWasmValue::Value64(rhs))) = (lhs, rhs) else {
cold_path();
- return Err(Error::Other("type mismatch in const i64 op".to_string()));
+ return Err(Error::other("type mismatch in const i64 op"));
};
let lhs = lhs as i64;
@@ -636,12 +653,12 @@ impl Store {
let Some(value) = stack.pop() else {
cold_path();
- return Err(Error::Other("empty const expression".to_string()));
+ return Err(Error::other("empty const expression"));
};
if !stack.is_empty() {
cold_path();
- return Err(Error::Other("const expression did not reduce to single value".to_string()));
+ return Err(Error::other("const expression did not reduce to single value"));
}
Ok(value)
}