diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index 2315b9b..932c177 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -44,6 +44,7 @@ parser=["dep:tinywasm-parser"] # support for "archiving" tinywasm bytecode archive=["tinywasm-types/archive"] +test_async=[] #feels weird putting it here # canonicalize all NaN values to a single representation canonicalize_nans=[] diff --git a/crates/tinywasm/src/coro.rs b/crates/tinywasm/src/coro.rs new file mode 100644 index 0000000..41fac58 --- /dev/null +++ b/crates/tinywasm/src/coro.rs @@ -0,0 +1,240 @@ +use core::fmt::Debug; + +use crate::Result; +// use alloc::boxed::Box; +pub(crate) use tinywasm_types::{ResumeArgument, YieldedValue}; + +///"coroutine statse", "coroutine instance", "resumable". Stores info to continue a function that was paused +pub trait CoroState: Debug { + /// resumes the execution of the coroutine + fn resume(&mut self, ctx: ResumeContext, arg: ResumeArgument) -> Result>; +} + +/// explains why did execution suspend, and carries payload if needed +#[derive(Debug)] +#[non_exhaustive] // some variants are feature-gated +pub enum SuspendReason { + /// host function yielded + /// some host functions might expect resume argument when calling resume + Yield(YieldedValue), + + /// time to suspend has come, + /// host shouldn't provide resume argument when calling resume + #[cfg(feature = "std")] + SuspendedEpoch, + + /// user's should-suspend-callback returned Break, + /// host shouldn't provide resume argument when calling resume + SuspendedCallback, + + /// async should_suspend flag was set + /// host shouldn't provide resume argument when calling resume + SuspendedFlag, + // possible others: delimited continuations proposal, debugger breakpoint, out of fuel +} + +/// result of a function that might pause in the middle and yield +/// to be resumed later +#[derive(Debug)] +pub enum PotentialCoroCallResult +//where for +// State: CoroState, // can't in stable rust +{ + /// function returns normally + Return(R), + /// interpreter will be suspended and execution will return to host along with SuspendReason + Suspended(SuspendReason, State), +} + +/// result of resuming coroutine state. Unlike [`PotentialCoroCallResult`] +/// doesn't need to have state, since it's contained in self +#[derive(Debug)] +pub enum CoroStateResumeResult { + /// CoroState has finished + /// after this CoroState::resume can't be called again on that CoroState + Return(R), + + /// host function yielded + /// execution returns to host along with yielded value + Suspended(SuspendReason), +} + +impl PotentialCoroCallResult { + /// in case you expect function only to return + /// you can make Suspend into [crate::Error::UnexpectedSuspend] error + pub fn suspend_to_err(self) -> Result { + match self { + PotentialCoroCallResult::Return(r) => Ok(r), + PotentialCoroCallResult::Suspended(r, _) => Err(crate::Error::UnexpectedSuspend(r.into())), + } + } + + /// true if coro is finished + pub fn finished(&self) -> bool { + matches!(self, Self::Return(_)) + } + /// separates state from PotentialCoroCallResult, leaving CoroStateResumeResult (one without state) + pub fn split_state(self) -> (CoroStateResumeResult, Option) { + match self { + Self::Return(val) => (CoroStateResumeResult::Return(val), None), + Self::Suspended(suspend, state) => (CoroStateResumeResult::Suspended(suspend), Some(state)), + } + } + /// separates result from PotentialCoroCallResult, leaving unit type in it's place + pub fn split_result(self) -> (PotentialCoroCallResult<(), State>, Option) { + match self { + Self::Return(result) => (PotentialCoroCallResult::Return(()), Some(result)), + Self::Suspended(suspend, state) => (PotentialCoroCallResult::Suspended(suspend, state), None), + } + } + + /// transforms state + pub fn map_state(self, mapper: impl FnOnce(State) -> OutS) -> PotentialCoroCallResult { + match self { + Self::Return(val) => PotentialCoroCallResult::Return(val), + Self::Suspended(suspend, state) => PotentialCoroCallResult::Suspended(suspend, mapper(state)), + } + } + /// transform result with res_mapper or state with state_mapper + /// user_val passed to whichever is called and is guaranteed to be used + pub fn map( + self, + user_val: UserData, + res_mapper: impl FnOnce(R, UserData) -> OutR, + state_mapper: impl FnOnce(State, UserData) -> OutS, + ) -> PotentialCoroCallResult { + match self { + Self::Return(res) => PotentialCoroCallResult::Return(res_mapper(res, user_val)), + Self::Suspended(suspend, state) => { + PotentialCoroCallResult::Suspended(suspend, state_mapper(state, user_val)) + } + } + } + /// transforms result + pub fn map_result(self, mapper: impl FnOnce(R) -> OutR) -> PotentialCoroCallResult { + self.map((), |val, _| mapper(val), |s, _| s) + } +} + +impl PotentialCoroCallResult, State> { + /// turns Self, S> into Resulf, S> + pub fn propagate_err_result(self) -> core::result::Result, E> { + Ok(match self { + PotentialCoroCallResult::Return(res) => PotentialCoroCallResult::::Return(res?), + PotentialCoroCallResult::Suspended(why, state) => { + PotentialCoroCallResult::::Suspended(why, state) + } + }) + } +} +impl PotentialCoroCallResult> { + /// turns Self> into Resulf> + pub fn propagate_err_state(self) -> core::result::Result, E> { + Ok(match self { + PotentialCoroCallResult::Return(res) => PotentialCoroCallResult::::Return(res), + PotentialCoroCallResult::Suspended(why, state) => { + PotentialCoroCallResult::::Suspended(why, state?) + } + }) + } +} + +impl CoroStateResumeResult { + /// in case you expect function only to return + /// you can make Suspend into [crate::Error::UnexpectedSuspend] error + pub fn suspend_to_err(self) -> Result { + match self { + Self::Return(r) => Ok(r), + Self::Suspended(r) => Err(crate::Error::UnexpectedSuspend(r.into())), + } + } + + /// true if coro is finished + pub fn finished(&self) -> bool { + matches!(self, Self::Return(_)) + } + /// separates result from CoroStateResumeResult, leaving unit type in it's place + pub fn split_result(self) -> (CoroStateResumeResult<()>, Option) { + let (a, r) = PotentialCoroCallResult::::from(self).split_result(); + (a.into(), r) + } + /// transforms result + pub fn map_result(self, mapper: impl FnOnce(R) -> OutR) -> CoroStateResumeResult { + PotentialCoroCallResult::::from(self).map_result(mapper).into() + } + /// transform result with mapper. If there is none - calls "otherwise" + /// user_val passed to whichever is called and is guaranteed to be used + pub fn map( + self, + user_val: Usr, + mapper: impl FnOnce(R, Usr) -> OutR, + otherwise: impl FnOnce(Usr), + ) -> CoroStateResumeResult { + PotentialCoroCallResult::::from(self).map(user_val, mapper, |(), usr| otherwise(usr)).into() + } +} + +impl CoroStateResumeResult> { + /// turns Self> into Resulf> + pub fn propagate_err(self) -> core::result::Result, E> { + Ok(PotentialCoroCallResult::, ()>::from(self).propagate_err_result()?.into()) + } +} + +// convert between PotentialCoroCallResult and CoroStateResumeResult +impl From> for CoroStateResumeResult +where + DstR: From, +{ + fn from(value: PotentialCoroCallResult) -> Self { + match value { + PotentialCoroCallResult::Return(val) => Self::Return(val.into()), + PotentialCoroCallResult::Suspended(suspend, ()) => Self::Suspended(suspend), + } + } +} +impl From> for PotentialCoroCallResult { + fn from(value: CoroStateResumeResult) -> Self { + match value { + CoroStateResumeResult::Return(val) => PotentialCoroCallResult::Return(val), + CoroStateResumeResult::Suspended(suspend) => PotentialCoroCallResult::Suspended(suspend, ()), + } + } +} + +impl SuspendReason { + /// shotrhand to package val into a Box in a [SuspendReason::Yield] variant + /// you'll need to specify type explicitly, because you'll need to use exact same type when downcasting + pub fn make_yield(val: impl Into + core::any::Any) -> Self { + Self::Yield(Some(alloc::boxed::Box::new(val) as alloc::boxed::Box)) + } +} + +// same as SuspendReason, but without [tinywasm_types::YieldedValue] +// it's opaque anyway, and error has Send and Sync which aren't typically needed for yielded value +#[derive(Debug)] +pub enum UnexpectedSuspendError { + /// host function yielded + Yield, + + /// timeout, + #[cfg(feature = "std")] + SuspendedEpoch, + + /// user's should-suspend-callback returned Break, + SuspendedCallback, + + /// async should_suspend flag was set + SuspendedFlag, +} +impl From for UnexpectedSuspendError { + fn from(value: SuspendReason) -> Self { + match value { + SuspendReason::Yield(_) => Self::Yield, + #[cfg(feature = "std")] + SuspendReason::SuspendedEpoch => Self::SuspendedEpoch, + SuspendReason::SuspendedCallback => Self::SuspendedCallback, + SuspendReason::SuspendedFlag => Self::SuspendedFlag, + } + } +} diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 4b3a969..1246b60 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -7,6 +7,8 @@ use tinywasm_types::archive::TwasmError; #[cfg(feature = "parser")] pub use tinywasm_parser::ParseError; +use crate::{coro::UnexpectedSuspendError, interpreter}; + /// Errors that can occur for `TinyWasm` operations #[derive(Debug)] #[non_exhaustive] @@ -37,6 +39,16 @@ pub enum Error { /// The store is not the one that the module instance was instantiated in InvalidStore, + /// ResumeArgument of wrong type was provided + InvalidResumeArgument, + + /// Tried to resume on runtime when it's not suspended + InvalidResume, + + /// Function unexpectedly yielded instead of returning + /// (for backwards compatibility with old api) + UnexpectedSuspend(UnexpectedSuspendError), + #[cfg(feature = "std")] /// An I/O error occurred Io(crate::std::io::Error), @@ -206,6 +218,10 @@ impl Display for Error { write!(f, "invalid host function return: expected={expected:?}, actual={actual:?}") } Self::InvalidStore => write!(f, "invalid store"), + + Self::UnexpectedSuspend(_) => write!(f, "funtion yielded instead of returning"), + Self::InvalidResumeArgument => write!(f, "invalid resume argument supplied to suspended function"), + Self::InvalidResume => write!(f, "attempt to resume coroutine that has already finished"), } } } @@ -259,14 +275,14 @@ impl From for Error { pub type Result = crate::std::result::Result; pub(crate) trait Controlify { - fn to_cf(self) -> ControlFlow, T>; + fn to_cf(self) -> ControlFlow; } impl Controlify for Result { - fn to_cf(self) -> ControlFlow, T> { + fn to_cf(self) -> ControlFlow { match self { Ok(value) => ControlFlow::Continue(value), - Err(err) => ControlFlow::Break(Some(err)), + Err(err) => ControlFlow::Break(interpreter::executor::ReasonToBreak::Errored(err)), } } } diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index e8055d1..4ff33cf 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,8 +1,11 @@ +use crate::coro::CoroState; +use crate::interpreter; +use crate::interpreter::executor::SuspendedHostCoroState; use crate::interpreter::stack::{CallFrame, Stack}; use crate::{Error, FuncContext, Result, Store}; use crate::{Function, log, unlikely}; use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; -use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, ValType, WasmValue}; +use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, ResumeArgument, ValType, WasmValue}; #[derive(Debug)] /// A function handle @@ -19,8 +22,15 @@ impl FuncHandle { /// Call a function (Invocation) /// /// See + /// #[inline] pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result> { + self.call_coro(store, params)?.suspend_to_err() + } + + /// Call a function (Invocation) and anticipate possible yield instead as well as return + #[inline] + pub fn call_coro(&self, store: &mut Store, params: &[WasmValue]) -> Result { // Comments are ordered by the steps in the spec // In this implementation, some steps are combined and ordered differently for performance reasons @@ -53,7 +63,14 @@ impl FuncHandle { Function::Host(host_func) => { let host_func = host_func.clone(); let ctx = FuncContext { store, module_addr: self.module_addr }; - return host_func.call(ctx, params); + return Ok(host_func.call(ctx, params)?.map_state(|state| SuspendedFunc { + func: SuspendedFuncInner::Host(SuspendedHostCoroState { + coro_state: state, + coro_orig_function: self.addr, + }), + module_addr: self.module_addr, + store_id: store.id(), + })); } Function::Wasm(wasm_func) => wasm_func, }; @@ -63,23 +80,34 @@ impl FuncHandle { // 7. Push the frame f to the call stack // & 8. Push the values to the stack (Not needed since the call frame owns the values) - let mut stack = Stack::new(call_frame, &store.config); + + let stack = Stack::new(call_frame, &store.config); // 9. Invoke the function instance let runtime = store.runtime(); - runtime.exec(store, &mut stack)?; - - // Once the function returns: - // let result_m = func_ty.results.len(); - - // 1. Assert: m values are on the top of the stack (Ensured by validation) - // assert!(stack.values.len() >= result_m); - - // 2. Pop m values from the stack - let res = stack.values.pop_results(&func_ty.results); - - // The values are returned as the results of the invocation. - Ok(res) + let exec_outcome = runtime.exec(store, stack)?; + Ok(exec_outcome + .map_result(|mut stack| -> Vec { + // Once the function returns: + // let result_m = func_ty.results.len(); + + // 1. Assert: m values are on the top of the stack (Ensured by validation) + // assert!(stack.values.len() >= result_m); + + // 2. Pop m values from the stack + stack.values.pop_results(&func_ty.results) + // The values are returned as the results of the invocation. + }) + .map_state(|coro_state| -> SuspendedFunc { + SuspendedFunc { + func: SuspendedFuncInner::Wasm(SuspendedWasmFunc { + runtime: coro_state, + result_types: func_ty.results.clone(), + }), + module_addr: self.module_addr, + store_id: store.id(), + } + })) } } @@ -113,6 +141,92 @@ impl FuncHandleTyped { // Convert the Vec back to R R::from_wasm_value_tuple(&result) } + + /// call a typed function, anticipating possible suspension of execution + pub fn call_coro(&self, store: &mut Store, params: P) -> Result> { + // Convert params into Vec + let wasm_values = params.into_wasm_value_tuple(); + + // Call the underlying WASM function + let result = self.func.call_coro(store, &wasm_values)?; + + // Convert the Vec back to R + result + .map_result(|vals| R::from_wasm_value_tuple(&vals)) + .map_state(|state| SuspendedFuncTyped:: { func: state, _marker: core::marker::PhantomData {} }) + .propagate_err_result() + } +} + +pub(crate) type FuncHandleCallOutcome = crate::coro::PotentialCoroCallResult, SuspendedFunc>; +pub(crate) type TypedFuncHandleCallOutcome = crate::coro::PotentialCoroCallResult>; + +#[derive(Debug)] +struct SuspendedWasmFunc { + runtime: interpreter::SuspendedInterpreterRuntime, + result_types: Box<[ValType]>, +} +impl SuspendedWasmFunc { + fn resume( + &mut self, + ctx: FuncContext<'_>, + arg: ResumeArgument, + ) -> Result>> { + Ok(self.runtime.resume(ctx, arg)?.map_result(|mut stack| stack.values.pop_results(&self.result_types))) + } +} + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] // Wasm is bigger, but also much more common variant +enum SuspendedFuncInner { + Wasm(SuspendedWasmFunc), // could also have host coro-function in the stack, but with some wasm below + Host(SuspendedHostCoroState), // in case we directly called host coro-function bypassing wasm stack +} + +/// handle to function that was suspended and can be resumed +#[derive(Debug)] +pub struct SuspendedFunc { + func: SuspendedFuncInner, + module_addr: ModuleInstanceAddr, + store_id: usize, +} + +impl crate::coro::CoroState, &mut Store> for SuspendedFunc { + fn resume( + &mut self, + store: &mut Store, + arg: ResumeArgument, + ) -> Result>> { + if store.id() != self.store_id { + return Err(Error::InvalidStore); + } + + let ctx = FuncContext { store, module_addr: self.module_addr }; + match &mut self.func { + SuspendedFuncInner::Wasm(wasm) => wasm.resume(ctx, arg), + SuspendedFuncInner::Host(host) => Ok(host.coro_state.resume(ctx, arg)?), + } + } +} + +pub struct SuspendedFuncTyped { + pub func: SuspendedFunc, + pub(crate) _marker: core::marker::PhantomData, +} + +impl core::fmt::Debug for SuspendedFuncTyped { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("SuspendedFuncTyped").field("func", &self.func).finish() + } +} + +impl crate::coro::CoroState for SuspendedFuncTyped +where + R: FromWasmValueTuple, +{ + fn resume(&mut self, ctx: &mut Store, arg: ResumeArgument) -> Result> { + self.func.resume(ctx, arg)?.map_result(|vals| R::from_wasm_value_tuple(&vals)).propagate_err() + } } macro_rules! impl_into_wasm_value_tuple { diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 563e586..dac3ec1 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -5,8 +5,9 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::fmt::Debug; +use crate::coro::CoroState; use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple}; -use crate::{LinkingError, MemoryRef, MemoryRefMut, Result, log}; +use crate::{LinkingError, MemoryRef, MemoryRefMut, PotentialCoroCallResult, Result, coro, log}; use tinywasm_types::*; /// The internal representation of a function @@ -28,6 +29,11 @@ impl Function { } } +/// A "resumable" function. If a host function need to suspend wasm execution +/// it can return [`coro::PotentialCoroCallResult::Suspended`] with an object that implements this trait +pub trait HostCoroState: for<'a> CoroState, FuncContext<'a>> + core::fmt::Debug + Send {} +impl CoroState, FuncContext<'a>>> HostCoroState for T {} + /// A host function pub struct HostFunction { pub(crate) ty: tinywasm_types::FuncType, @@ -41,12 +47,14 @@ impl HostFunction { } /// Call the function - pub fn call(&self, ctx: FuncContext<'_>, args: &[WasmValue]) -> Result> { + pub fn call(&self, ctx: FuncContext<'_>, args: &[WasmValue]) -> Result { (self.func)(ctx, args) } } -pub(crate) type HostFuncInner = Box, &[WasmValue]) -> Result>>; +pub(crate) type InnerHostFunCallOutcome = coro::PotentialCoroCallResult, Box>; + +pub(crate) type HostFuncInner = Box, &[WasmValue]) -> Result>; /// The context of a host-function call #[derive(Debug)] @@ -134,13 +142,21 @@ impl Extern { Self::Memory { ty } } + /// Create a new function import + pub fn func_coro( + ty: &tinywasm_types::FuncType, + func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result + 'static, + ) -> Self { + Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(func), ty: ty.clone() }))) + } + /// Create a new function import pub fn func( ty: &tinywasm_types::FuncType, func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result> + 'static, ) -> Self { let _ty = ty.clone(); - let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result> { + let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result { let _ty = _ty.clone(); let result = func(ctx, args)?; @@ -155,22 +171,40 @@ impl Extern { Ok(()) })?; - Ok(result) + Ok(PotentialCoroCallResult::Return(result)) }; Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty: ty.clone() }))) } + /// Create a new typed function import + pub fn typed_func_coro( + func: impl Fn(FuncContext<'_>, P) -> Result>> + 'static, + ) -> Self + where + P: FromWasmValueTuple + ValTypesFromTuple, + R: IntoWasmValueTuple + ValTypesFromTuple + Debug, + { + let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result { + let args = P::from_wasm_value_tuple(args)?; + let result = func(ctx, args)?; + Ok(result.map_result(|vals| vals.into_wasm_value_tuple().to_vec())) + }; + + let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() }; + Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty }))) + } + /// Create a new typed function import pub fn typed_func(func: impl Fn(FuncContext<'_>, P) -> Result + 'static) -> Self where P: FromWasmValueTuple + ValTypesFromTuple, R: IntoWasmValueTuple + ValTypesFromTuple + Debug, { - let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result> { + let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result { let args = P::from_wasm_value_tuple(args)?; let result = func(ctx, args)?; - Ok(result.into_wasm_value_tuple()) + Ok(InnerHostFunCallOutcome::Return(result.into_wasm_value_tuple())) }; let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() }; diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index aaa3407..f44cbea 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -2,7 +2,10 @@ use alloc::{boxed::Box, format, rc::Rc, string::ToString}; use tinywasm_types::*; use crate::func::{FromWasmValueTuple, IntoWasmValueTuple}; -use crate::{Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store}; +use crate::{ + Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, PotentialCoroCallResult, Result, + Store, SuspendedFunc, +}; /// An instanciated WebAssembly module /// @@ -263,4 +266,18 @@ impl ModuleInstance { let _ = func.call(store, &[])?; Ok(Some(())) } + + /// Invoke the start function of the module + /// + /// Returns None if the module has no start function + /// If start function suspends, returns SuspededFunc. + /// Only when it finishes can this module instance be considered instantiated + pub fn start_coro(&self, store: &mut Store) -> Result>> { + let Some(func) = self.start_func(store)? else { + return Ok(None); + }; + + let res = func.call_coro(store, &[])?; + Ok(Some(res.map_result(|_| {}))) + } } diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 2e979bc..d592a85 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -2,9 +2,10 @@ #[allow(unused_imports)] use super::no_std_floats::NoStdFloatExt; +use alloc::boxed::Box; use alloc::{format, rc::Rc, string::ToString}; use core::ops::ControlFlow; - +use coro::SuspendReason; use interpreter::stack::CallFrame; use tinywasm_types::*; @@ -21,6 +22,28 @@ use super::stack::{BlockFrame, BlockType, Stack}; use super::values::*; use crate::*; +pub(crate) enum ReasonToBreak { + Errored(Error), + Suspended(SuspendReason, Option), + Finished, +} + +impl From for ControlFlow { + fn from(value: ReasonToBreak) -> Self { + ControlFlow::Break(value) + } +} + +#[derive(Debug)] +pub(crate) struct SuspendedHostCoroState { + pub(crate) coro_state: Box, + // plug into used in store.get_func to get original function + // can be used for checking returned types + #[allow(dead_code)] // not implemented yet, but knowing context is useful + pub(crate) coro_orig_function: u32, +} + +#[derive(Debug)] pub(crate) struct Executor<'store, 'stack> { pub(crate) cf: CallFrame, pub(crate) module: ModuleInstance, @@ -28,6 +51,8 @@ pub(crate) struct Executor<'store, 'stack> { pub(crate) stack: &'stack mut Stack, } +pub(crate) type ExecOutcome = coro::PotentialCoroCallResult<(), Option>; + impl<'store, 'stack> Executor<'store, 'stack> { pub(crate) fn new(store: &'store mut Store, stack: &'stack mut Stack) -> Result { let current_frame = stack.call_stack.pop().expect("no call frame, this is a bug"); @@ -36,19 +61,96 @@ impl<'store, 'stack> Executor<'store, 'stack> { } #[inline(always)] - pub(crate) fn run_to_completion(&mut self) -> Result<()> { + pub(crate) fn run_to_suspension(&mut self) -> Result { + loop { + if let ControlFlow::Break(res) = self.exec_next() { + return match res { + ReasonToBreak::Errored(e) => Err(e), + ReasonToBreak::Suspended(suspend_reason, coro_state) => { + Ok(ExecOutcome::Suspended(suspend_reason, coro_state)) + } + ReasonToBreak::Finished => Ok(ExecOutcome::Return(())), + }; + } + } + } + + // on error running suspended_coro, returns it + // so that it could be retried later + #[inline(always)] + pub(crate) fn resume( + &mut self, + res_arg: ResumeArgument, + mut suspended_coro: Option, + ) -> Result)> { + if let Some(coro_state) = suspended_coro.as_mut() { + let ctx = FuncContext { store: self.store, module_addr: self.module.id() }; + let host_res = match coro_state.coro_state.resume(ctx, res_arg) { + Ok(val) => val, + Err(err) => return Err((err, suspended_coro)), + }; + + let res = match host_res { + CoroStateResumeResult::Return(res) => res, + CoroStateResumeResult::Suspended(suspend_reason) => { + return Ok(ExecOutcome::Suspended(suspend_reason, suspended_coro)); + } + }; + self.stack.values.extend_from_wasmvalues(&res); + + // we don't know how much time we spent in host function + if let ControlFlow::Break(ReasonToBreak::Suspended(reason, coro_state)) = self.check_should_suspend() { + return Ok(ExecOutcome::Suspended(reason, coro_state)); + } + } + loop { if let ControlFlow::Break(res) = self.exec_next() { return match res { - Some(e) => Err(e), - None => Ok(()), + ReasonToBreak::Errored(e) => Err((e, None)), + ReasonToBreak::Suspended(suspend_reason, coro_state) => { + Ok(ExecOutcome::Suspended(suspend_reason, coro_state)) + } + ReasonToBreak::Finished => Ok(ExecOutcome::Return(())), }; } } } + /// for controlling how long execution spends in wasm + /// called when execution loops back, because that might happen indefinite amount of times + /// and before and after function calls, because even without loops or infinite recursion, wasm function calls + /// can mutliply time spent in execution + /// execution may not be suspended in the middle of execution the innstruction + /// so only do it before mutating any state + /// or after all state mutations including increasing instruction pointer + #[must_use = "If this returns ControlFlow::Break, the caller should propagate it"] + fn check_should_suspend(&mut self) -> ControlFlow { + if let Some(flag) = &self.store.suspend_cond.suspend_flag + && flag.load(core::sync::atomic::Ordering::Acquire) { + return ReasonToBreak::Suspended(SuspendReason::SuspendedFlag, None).into(); + + } + + #[cfg(feature = "std")] + if let Some(when) = &self.store.suspend_cond.timeout_instant + && crate::std::time::Instant::now() >= *when { + return ReasonToBreak::Suspended(SuspendReason::SuspendedEpoch, None).into(); + } + + if let Some(mut cb) = self.store.suspend_cond.suspend_cb.take() { + let should_suspend = matches!(cb(self.store), ControlFlow::Break(())); + self.store.suspend_cond.suspend_cb = Some(cb); // put it back + if should_suspend { + return ReasonToBreak::Suspended(SuspendReason::SuspendedCallback, None).into(); + } + } + + ControlFlow::Continue(()) + } + #[inline(always)] - fn exec_next(&mut self) -> ControlFlow> { + fn exec_next(&mut self) -> ControlFlow { use tinywasm_types::Instruction::*; #[rustfmt::skip] @@ -722,7 +824,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[cfg(feature = "unstable-simd")] I32x4RelaxedDotI8x16I7x16AddS => unimplemented!(), #[allow(unreachable_patterns)] - i => return ControlFlow::Break(Some(Error::UnsupportedFeature(format!("unimplemented opcode: {i:?}")))), + i => return ControlFlow::Break(ReasonToBreak::Errored(Error::UnsupportedFeature(format!("unimplemented opcode: {i:?}")))), }; self.cf.incr_instr_ptr(); @@ -730,15 +832,15 @@ impl<'store, 'stack> Executor<'store, 'stack> { } #[cold] - fn exec_unreachable(&self) -> ControlFlow> { - ControlFlow::Break(Some(Trap::Unreachable.into())) + fn exec_unreachable(&self) -> ControlFlow { + ReasonToBreak::Errored(Trap::Unreachable.into()).into() } fn exec_call( &mut self, wasm_func: Rc, owner: ModuleInstanceAddr, - ) -> ControlFlow> { + ) -> ControlFlow { let locals = self.stack.values.pop_locals(wasm_func.params, wasm_func.locals); if IS_RETURN_CALL { @@ -752,28 +854,43 @@ impl<'store, 'stack> Executor<'store, 'stack> { self.module.swap_with(self.cf.module_addr(), self.store); ControlFlow::Continue(()) } - fn exec_call_host(&mut self, host_func: Rc) -> ControlFlow> { + fn exec_call_host(&mut self, host_func: Rc, func_ref: u32) -> ControlFlow { let params = self.stack.values.pop_params(&host_func.ty.params); let res = host_func .clone() .call(FuncContext { store: self.store, module_addr: self.module.id() }, ¶ms) .to_cf()?; - self.stack.values.extend_from_wasmvalues(&res); - self.cf.incr_instr_ptr(); - ControlFlow::Continue(()) + match res { + PotentialCoroCallResult::Return(results) => { + self.stack.values.extend_from_wasmvalues(&results); + self.cf.incr_instr_ptr(); + self.check_should_suspend()?; // who knows how long we've spent in host function + ControlFlow::Continue(()) + } + PotentialCoroCallResult::Suspended(suspend_reason, coro_state) => { + self.cf.incr_instr_ptr(); // so that when execution comes back, we'll be at next instruction already + ReasonToBreak::Suspended( + suspend_reason, + Some(SuspendedHostCoroState { coro_state, coro_orig_function: func_ref }), + ) + .into() + } + } } - fn exec_call_direct(&mut self, v: u32) -> ControlFlow> { + fn exec_call_direct(&mut self, v: u32) -> ControlFlow { + self.check_should_suspend()?; // don't commit to function if we should be stopping now let func_inst = self.store.get_func(self.module.resolve_func_addr(v)); match func_inst.func.clone() { crate::Function::Wasm(wasm_func) => self.exec_call::(wasm_func, func_inst.owner), - crate::Function::Host(host_func) => self.exec_call_host(host_func), + crate::Function::Host(host_func) => self.exec_call_host(host_func, v), } } fn exec_call_indirect( &mut self, type_addr: u32, table_addr: u32, - ) -> ControlFlow> { + ) -> ControlFlow { + self.check_should_suspend()?; // check if we should suspend now before commiting to function // verify that the table is of the right type, this should be validated by the parser already let func_ref = { let table = self.store.get_table(self.module.resolve_table_addr(table_addr)); @@ -790,7 +907,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { match func_inst.func.clone() { crate::Function::Wasm(wasm_func) => { if unlikely(wasm_func.ty != *call_ty) { - return ControlFlow::Break(Some( + return ControlFlow::Break(ReasonToBreak::Errored( Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() } .into(), )); @@ -800,13 +917,14 @@ impl<'store, 'stack> Executor<'store, 'stack> { } crate::Function::Host(host_func) => { if unlikely(host_func.ty != *call_ty) { - return ControlFlow::Break(Some( + return ReasonToBreak::Errored( Trap::IndirectCallTypeMismatch { actual: host_func.ty.clone(), expected: call_ty.clone() } .into(), - )); + ) + .into(); } - self.exec_call_host(host_func) + self.exec_call_host(host_func, func_ref) } } } @@ -845,52 +963,74 @@ impl<'store, 'stack> Executor<'store, 'stack> { ty, }); } - fn exec_br(&mut self, to: u32) -> ControlFlow> { - if self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() { + fn exec_br(&mut self, to: u32) -> ControlFlow { + let block_ty = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks); + if block_ty.is_none() { return self.exec_return(); } self.cf.incr_instr_ptr(); + + if matches!(block_ty, Some(BlockType::Loop)) { + self.check_should_suspend()?; + } ControlFlow::Continue(()) } - fn exec_br_if(&mut self, to: u32) -> ControlFlow> { - if self.stack.values.pop::() != 0 - && self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() - { - return self.exec_return(); - } + fn exec_br_if(&mut self, to: u32) -> ControlFlow { + let should_check_suspend = if self.stack.values.pop::() != 0 { + // condition says we should break + let block_ty = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks); + if block_ty.is_none() { + return self.exec_return(); + } + matches!(block_ty, Some(BlockType::Loop)) + } else { + // condition says we shouldn't break + false + }; + self.cf.incr_instr_ptr(); + + if should_check_suspend { + self.check_should_suspend()?; + } ControlFlow::Continue(()) } - fn exec_brtable(&mut self, default: u32, len: u32) -> ControlFlow> { + fn exec_brtable(&mut self, default: u32, len: u32) -> ControlFlow { let start = self.cf.instr_ptr() + 1; let end = start + len as usize; if end > self.cf.instructions().len() { - return ControlFlow::Break(Some(Error::Other(format!( + return ReasonToBreak::Errored(Error::Other(format!( "br_table out of bounds: {} >= {}", end, self.cf.instructions().len() - )))); + ))) + .into(); } let idx = self.stack.values.pop::(); let to = match self.cf.instructions()[start..end].get(idx as usize) { None => default, Some(Instruction::BrLabel(to)) => *to, - _ => return ControlFlow::Break(Some(Error::Other("br_table out of bounds".to_string()))), + _ => return ReasonToBreak::Errored(Error::Other("br_table out of bounds".to_string())).into(), }; - if self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() { + let block_ty = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks); + if block_ty.is_none() { return self.exec_return(); } self.cf.incr_instr_ptr(); + + if matches!(block_ty, Some(BlockType::Loop)) { + self.check_should_suspend()?; + } ControlFlow::Continue(()) } - fn exec_return(&mut self) -> ControlFlow> { + fn exec_return(&mut self) -> ControlFlow { let old = self.cf.block_ptr(); match self.stack.call_stack.pop() { - None => return ControlFlow::Break(None), + None => return ReasonToBreak::Finished.into(), Some(cf) => self.cf = cf, } @@ -899,6 +1039,8 @@ impl<'store, 'stack> Executor<'store, 'stack> { } self.module.swap_with(self.cf.module_addr(), self.store); + + self.check_should_suspend()?; ControlFlow::Continue(()) } fn exec_end_block(&mut self) { @@ -1055,17 +1197,18 @@ impl<'store, 'stack> Executor<'store, 'stack> { mem_addr: tinywasm_types::MemAddr, offset: u64, lane: u8, - ) -> ControlFlow> { + ) -> ControlFlow { let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr)); let mut imm = self.stack.values.pop::(); let val = self.stack.values.pop::() as u64; let Some(Ok(addr)) = offset.checked_add(val).map(TryInto::try_into) else { cold(); - return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds { + return ReasonToBreak::Errored(Error::Trap(Trap::MemoryOutOfBounds { offset: val as usize, len: LOAD_SIZE, max: 0, - }))); + })) + .into(); }; let val = mem.load_as::(addr).to_cf()?; imm[lane as usize] = val; @@ -1078,7 +1221,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { mem_addr: tinywasm_types::MemAddr, offset: u64, cast: fn(LOAD) -> TARGET, - ) -> ControlFlow> { + ) -> ControlFlow { let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr)); let addr = match mem.is_64bit() { @@ -1088,7 +1231,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { let Some(Ok(addr)) = offset.checked_add(addr).map(|a| a.try_into()) else { cold(); - return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds { + return ControlFlow::Break(ReasonToBreak::Errored(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: LOAD_SIZE, max: 0, @@ -1105,7 +1248,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { mem_addr: tinywasm_types::MemAddr, offset: u64, lane: u8, - ) -> ControlFlow> { + ) -> ControlFlow { let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(mem_addr)); let val = self.stack.values.pop::(); let val = val[lane as usize].to_mem_bytes(); @@ -1116,7 +1259,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { }; if let Err(e) = mem.store((offset + addr) as usize, val.len(), &val) { - return ControlFlow::Break(Some(e)); + return ControlFlow::Break(ReasonToBreak::Errored(e)); } ControlFlow::Continue(()) @@ -1127,7 +1270,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { mem_addr: tinywasm_types::MemAddr, offset: u64, cast: fn(T) -> U, - ) -> ControlFlow> { + ) -> ControlFlow { let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(mem_addr)); let val = self.stack.values.pop::(); let val = (cast(val)).to_mem_bytes(); @@ -1138,7 +1281,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { }; if let Err(e) = mem.store((offset + addr) as usize, val.len(), &val) { - return ControlFlow::Break(Some(e)); + return ReasonToBreak::Errored(e).into(); } ControlFlow::Continue(()) diff --git a/crates/tinywasm/src/interpreter/mod.rs b/crates/tinywasm/src/interpreter/mod.rs index 0b7df2f..68ce393 100644 --- a/crates/tinywasm/src/interpreter/mod.rs +++ b/crates/tinywasm/src/interpreter/mod.rs @@ -6,7 +6,11 @@ mod values; #[cfg(not(feature = "std"))] mod no_std_floats; -use crate::{Result, Store}; +use crate::coro; +use crate::{FuncContext, ModuleInstance, Result, Store}; +use executor::{ExecOutcome, Executor, SuspendedHostCoroState}; +use stack::{CallFrame, Stack}; +use tinywasm_types::ResumeArgument; pub use values::*; /// The main `TinyWasm` runtime. @@ -15,8 +19,85 @@ pub use values::*; #[derive(Debug, Default)] pub struct InterpreterRuntime {} +#[derive(Debug)] +struct SuspendedInterpreterRuntimeBody { + host_coro: Option, + executor_stack: Stack, + executor_module: ModuleInstance, + executor_frame: CallFrame, +} + +impl SuspendedInterpreterRuntimeBody { + fn new( + host_coro: Option, + executor_stack: Stack, + executor_module: ModuleInstance, + executor_frame: CallFrame, + ) -> Self { + Self { host_coro, executor_stack, executor_module, executor_frame } + } +} + +#[derive(Debug, Default)] +pub(crate) struct SuspendedInterpreterRuntime(Option); + +pub(crate) type InterpreterRuntimeExecOutcome = coro::PotentialCoroCallResult; +pub(crate) type InterpreterRuntimeResumeOutcome = coro::CoroStateResumeResult; + +impl<'_a> coro::CoroState> for SuspendedInterpreterRuntime { + fn resume(&mut self, ctx: FuncContext<'_a>, arg: ResumeArgument) -> Result { + let body = if let Some(body) = self.0.take() { + body + } else { + // no suspended state to continue + return Result::Err(crate::Error::InvalidResume); + }; + + let SuspendedInterpreterRuntimeBody { host_coro, executor_stack, executor_module, executor_frame } = body; + + let mut stack = executor_stack; + let mut exec = + executor::Executor { cf: executor_frame, module: executor_module, store: ctx.store, stack: &mut stack }; + let res = match exec.resume(arg, host_coro) { + Ok(val) => val, + Err(e) => { + let Executor { cf, module, .. } = exec; + // pack back in case host_coro isn't corrupted and can be continued + self.0 = Some(SuspendedInterpreterRuntimeBody::new(e.1, stack, module, cf)); + return Err(e.0); + } + }; + + Ok(match res { + ExecOutcome::Return(()) => { + // we are finished + InterpreterRuntimeResumeOutcome::Return(stack) + } + ExecOutcome::Suspended(suspend_reason, host_coro) => { + // host_coro could be different host_coro than one we provided + let Executor { cf, module, .. } = exec; + self.0 = Some(SuspendedInterpreterRuntimeBody::new(host_coro, stack, module, cf)); + InterpreterRuntimeResumeOutcome::Suspended(suspend_reason) + } + }) + } +} + impl InterpreterRuntime { - pub(crate) fn exec(&self, store: &mut Store, stack: &mut stack::Stack) -> Result<()> { - executor::Executor::new(store, stack)?.run_to_completion() + pub(crate) fn exec(&self, store: &mut Store, mut stack: Stack) -> Result { + let mut executor = executor::Executor::new(store, &mut stack)?; + let result = executor.run_to_suspension()?; + Ok(match result { + ExecOutcome::Return(()) => InterpreterRuntimeExecOutcome::Return(stack), + ExecOutcome::Suspended(suspend_reason, host_coro) => { + let Executor { cf, module, .. } = executor; + InterpreterRuntimeExecOutcome::Suspended( + suspend_reason, + SuspendedInterpreterRuntime(Some(SuspendedInterpreterRuntimeBody::new( + host_coro, stack, module, cf, + ))), + ) + } + }) } } diff --git a/crates/tinywasm/src/interpreter/stack/block_stack.rs b/crates/tinywasm/src/interpreter/stack/block_stack.rs index e5d87a8..69383ae 100644 --- a/crates/tinywasm/src/interpreter/stack/block_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/block_stack.rs @@ -58,7 +58,7 @@ pub(crate) struct BlockFrame { pub(crate) ty: BlockType, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) enum BlockType { Loop, If, diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs index c7f02b6..e290abc 100644 --- a/crates/tinywasm/src/interpreter/stack/call_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs @@ -2,8 +2,9 @@ use core::ops::ControlFlow; use super::BlockType; use crate::Trap; +use crate::interpreter::executor::ReasonToBreak; use crate::interpreter::values::*; -use crate::{Error, unlikely}; +use crate::unlikely; use alloc::boxed::Box; use alloc::{rc::Rc, vec, vec::Vec}; @@ -28,9 +29,9 @@ impl CallStack { } #[inline] - pub(crate) fn push(&mut self, call_frame: CallFrame) -> ControlFlow> { + pub(crate) fn push(&mut self, call_frame: CallFrame) -> ControlFlow { if unlikely((self.stack.len() + 1) >= MAX_CALL_STACK_SIZE) { - return ControlFlow::Break(Some(Trap::CallStackOverflow.into())); + return ControlFlow::Break(ReasonToBreak::Errored(Trap::CallStackOverflow.into())); } self.stack.push(call_frame); ControlFlow::Continue(()) @@ -120,18 +121,25 @@ impl CallFrame { /// Break to a block at the given index (relative to the current frame) /// Returns `None` if there is no block at the given index (e.g. if we need to return, this is handled by the caller) + /// otherwise returns type if block it broke to + ///
+ /// if it returned Some (broke to block), + /// it expects caller to increment instruction pointer after calling it: + /// otherwise caller might exit block that's already exited or inter block caller's already in + ///
#[inline] pub(crate) fn break_to( &mut self, break_to_relative: u32, values: &mut super::ValueStack, blocks: &mut super::BlockStack, - ) -> Option<()> { + ) -> Option { let break_to = blocks.get_relative_to(break_to_relative, self.block_ptr)?; + let block_ty = break_to.ty; // instr_ptr points to the label instruction, but the next step // will increment it by 1 since we're changing the "current" instr_ptr - match break_to.ty { + match block_ty { BlockType::Loop => { // this is a loop, so we want to jump back to the start of the loop self.instr_ptr = break_to.instr_ptr; @@ -143,7 +151,7 @@ impl CallFrame { if break_to_relative != 0 { // we also want to trim the label stack to the loop (but not including the loop) blocks.truncate(blocks.len() as u32 - break_to_relative); - return Some(()); + return Some(BlockType::Loop); } } @@ -160,7 +168,7 @@ impl CallFrame { } } - Some(()) + Some(block_ty) } #[inline] diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 0037829..46304d9 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -114,14 +114,16 @@ pub(crate) mod log { } mod error; +pub use coro::{CoroState, CoroStateResumeResult, PotentialCoroCallResult, SuspendReason}; pub use error::*; -pub use func::{FuncHandle, FuncHandleTyped}; +pub use func::{FuncHandle, FuncHandleTyped, SuspendedFunc}; pub use imports::*; pub use instance::ModuleInstance; -pub use module::Module; +pub use module::{IncompleteModule, Module}; pub use reference::*; pub use store::*; +mod coro; mod func; mod imports; mod instance; diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs index 26cea9c..53e9811 100644 --- a/crates/tinywasm/src/module.rs +++ b/crates/tinywasm/src/module.rs @@ -1,5 +1,5 @@ -use crate::{Imports, ModuleInstance, Result, Store}; -use tinywasm_types::TinyWasmModule; +use crate::{CoroState, Imports, ModuleInstance, PotentialCoroCallResult, Result, Store, SuspendedFunc}; +use tinywasm_types::{ResumeArgument, TinyWasmModule}; /// A WebAssembly Module /// @@ -56,4 +56,57 @@ impl Module { let _ = instance.start(store)?; Ok(instance) } + + /// same as [Self::instantiate] but accounts for possibility of start function suspending, in which case it returns + /// [PotentialCoroCallResult::Suspended]. You can call [CoroState::resume] on it at any time to resume instantiation + pub fn instantiate_coro( + self, + store: &mut Store, + imports: Option, + ) -> Result> { + let instance = ModuleInstance::instantiate(store, self, imports)?; + let core_res = match instance.start_coro(store)? { + Some(res) => res, + None => return Ok(PotentialCoroCallResult::Return(instance)), + }; + Ok(match core_res { + crate::PotentialCoroCallResult::Return(_) => PotentialCoroCallResult::Return(instance), + crate::PotentialCoroCallResult::Suspended(suspend_reason, state) => { + PotentialCoroCallResult::Suspended(suspend_reason, IncompleteModule(Some(HitTheFloor(instance, state)))) + } + }) + } +} + +/// a corostate that results in [ModuleInstance] when finished +#[derive(Debug)] +pub struct IncompleteModule(Option); + +#[derive(Debug)] +struct HitTheFloor(ModuleInstance, SuspendedFunc); + +impl CoroState for IncompleteModule { + fn resume(&mut self, ctx: &mut Store, arg: ResumeArgument) -> Result> { + let mut body: HitTheFloor = match self.0.take() { + Some(body) => body, + None => return Err(crate::Error::InvalidResume), + }; + let coro_res = match body.1.resume(ctx, arg) { + Ok(res) => res, + Err(e) => { + self.0 = Some(body); + return Err(e); + } + }; + match coro_res { + crate::CoroStateResumeResult::Return(_) => { + let res = body.0; + Ok(crate::CoroStateResumeResult::Return(res)) + } + crate::CoroStateResumeResult::Suspended(suspend_reason) => { + self.0 = Some(body); // ...once told me + Ok(crate::CoroStateResumeResult::Suspended(suspend_reason)) + } + } + } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 8446297..b4240fd 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -11,8 +11,10 @@ mod element; mod function; mod global; mod memory; +mod suspend_conditions; mod table; +pub use suspend_conditions::*; pub(crate) use {data::*, element::*, function::*, global::*, memory::*, table::*}; // global store id counter @@ -33,7 +35,14 @@ pub struct Store { pub(crate) data: StoreData, pub(crate) runtime: Runtime, + pub(crate) config: StackConfig, + + // idk where really to put it, but it should be accessible to host environment (obviously) + // and (less obviously) to host functions called from store - for calling wasm callbacks and propagating this config to them + // (or just complying with suspend conditions themselves) + // alternatively it could be passed to function handles and passend into function context + pub(crate) suspend_cond: SuspendConditions, } impl Debug for Store { @@ -61,7 +70,14 @@ impl Store { /// Create a new store with the given stack configuration pub fn with_config(config: StackConfig) -> Self { let id = STORE_ID.fetch_add(1, Ordering::Relaxed); - Self { id, module_instances: Vec::new(), data: StoreData::default(), runtime: Runtime::Default, config } + Self { + id, + module_instances: Vec::new(), + data: StoreData::default(), + runtime: Runtime::Default, + config, + suspend_cond: SuspendConditions::default(), + } } /// Get a module instance by the internal id @@ -96,6 +112,7 @@ impl Default for Store { data: StoreData::default(), runtime: Runtime::Default, config: StackConfig::default(), + suspend_cond: SuspendConditions::default(), } } } @@ -492,3 +509,20 @@ fn get_pair_mut(slice: &mut [T], i: usize, j: usize) -> Option<(&mut T, &mut let pair = if i < j { (&mut x[0], &mut y[0]) } else { (&mut y[0], &mut x[0]) }; Some(pair) } + +// suspend_conditions-related functions +impl Store { + /// sets suspend conditions for store + pub fn set_suspend_conditions(&mut self, val: SuspendConditions) { + self.suspend_cond = val; + } + /// gets suspend conditions of store + pub fn get_suspend_conditions(&self) -> &SuspendConditions { + &self.suspend_cond + } + /// transforms suspend conditions for store using user-provided function + pub fn update_suspend_conditions(&mut self, mapper: impl FnOnce(SuspendConditions) -> SuspendConditions) { + let temp = core::mem::take(&mut self.suspend_cond); + self.suspend_cond = mapper(temp); + } +} diff --git a/crates/tinywasm/src/store/suspend_conditions.rs b/crates/tinywasm/src/store/suspend_conditions.rs new file mode 100644 index 0000000..58a51fb --- /dev/null +++ b/crates/tinywasm/src/store/suspend_conditions.rs @@ -0,0 +1,94 @@ +use crate::store::Store; +use alloc::boxed::Box; +use core::fmt::Debug; +use core::ops::ControlFlow; + +/// user callback for use in [SuspendConditions::suspend_cb] +pub type ShouldSuspendCb = Box ControlFlow<(), ()>>; + +/// used to limit execution time wasm code takes +#[derive(Default)] +#[non_exhaustive] // some fields are feature-gated, use with*-methods to construct +pub struct SuspendConditions { + /// atomic flag. when set to true it means execution should suspend + /// can be used to tell executor to stop from another thread + pub suspend_flag: Option>, + + /// instant at which execution should suspend + /// can be used to control how much time will be spent in wasm without requiring other threads + /// such as for time-slice multitasking. + /// Uses rust standard library for checking time - so not available in no-std + #[cfg(feature = "std")] + pub timeout_instant: Option, + + /// Callback that returns [`ControlFlow::Break`]` when execution should suspend. + /// Can be used when above ways are insufficient or + /// instead of [`timeout_instant`] in no-std builds, with your own clock function + pub suspend_cb: Option, +} + +impl Debug for SuspendConditions { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let stop_cb_text = if self.suspend_cb.is_some() { "" } else { "" }; + let mut f = f.debug_struct("SuspendConditions"); + f.field("stop_flag", &self.suspend_flag); + #[cfg(feature = "std")] + { + f.field("timeout_instant", &self.timeout_instant); + } + f.field("stop_cb", &stop_cb_text).finish() + } +} + +impl SuspendConditions { + /// creates suspend_conditions with every condition unset + pub fn new() -> Self { + Default::default() + } + + /// sets timeout_instant to `how_long` from now + #[cfg(feature = "std")] + pub fn set_timeout_in(&mut self, how_long: crate::std::time::Duration) -> &mut Self { + self.timeout_instant = Some(crate::std::time::Instant::now() + how_long); + self + } + /// adds timeout at specified instant + #[cfg(feature = "std")] + pub fn with_timeout_at(self, when: crate::std::time::Instant) -> Self { + Self { timeout_instant: Some(when), ..self } + } + /// adds timeout in specified duration + #[cfg(feature = "std")] + pub fn with_timeout_in(self, how_long: crate::std::time::Duration) -> Self { + Self { timeout_instant: Some(crate::std::time::Instant::now() + how_long), ..self } + } + /// removes timeout + pub fn without_timeout(self) -> Self { + #[cfg(feature = "std")] + { + Self { timeout_instant: None, ..self } + } + #[cfg(not(feature = "std"))] + { + self + } + } + + /// adds susped flag + pub fn with_suspend_flag(self, should_suspend: alloc::sync::Arc) -> Self { + Self { suspend_flag: Some(should_suspend), ..self } + } + /// removes susped flag + pub fn without_suspend_flag(self) -> Self { + Self { suspend_flag: None, ..self } + } + + /// adds suspend callback + pub fn with_suspend_callback(self, cb: ShouldSuspendCb) -> Self { + Self { suspend_cb: Some(cb), ..self } + } + /// removes suspend callback + pub fn without_suspend_callback(self) -> Self { + Self { suspend_cb: None, ..self } + } +} diff --git a/crates/tinywasm/tests/generated/wasm-1.csv b/crates/tinywasm/tests/generated/wasm-1.csv index ebb511b..d403e3b 100644 --- a/crates/tinywasm/tests/generated/wasm-1.csv +++ b/crates/tinywasm/tests/generated/wasm-1.csv @@ -5,4 +5,4 @@ 0.6.1,27572,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.7.0,27572,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.8.0,20360,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":128,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":178,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_grow.wast","passed":104,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] -0.9.0-alpha.0,19241,0,[{"name":"address.wast","passed":243,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":67,"failed":0},{"name":"block.wast","passed":171,"failed":0},{"name":"br.wast","passed":84,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":168,"failed":0},{"name":"break-drop.wast","passed":4,"failed":0},{"name":"call.wast","passed":82,"failed":0},{"name":"call_indirect.wast","passed":152,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":668,"failed":0},{"name":"conversions.wast","passed":435,"failed":0},{"name":"custom.wast","passed":10,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":55,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":82,"failed":0},{"name":"f32.wast","passed":2512,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2512,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":7,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":121,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"globals.wast","passed":78,"failed":0},{"name":"i32.wast","passed":443,"failed":0},{"name":"i64.wast","passed":389,"failed":0},{"name":"if.wast","passed":151,"failed":0},{"name":"imports.wast","passed":146,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":116,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":81,"failed":0},{"name":"memory.wast","passed":71,"failed":0},{"name":"memory_grow.wast","passed":94,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":173,"failed":0},{"name":"names.wast","passed":483,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":111,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":5,"failed":0},{"name":"start.wast","passed":19,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":62,"failed":0},{"name":"unreached-invalid.wast","passed":110,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.9.0-alpha.0,19243,0,[{"name":"address.wast","passed":243,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":79,"failed":0},{"name":"binary.wast","passed":67,"failed":0},{"name":"block.wast","passed":171,"failed":0},{"name":"br.wast","passed":84,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":168,"failed":0},{"name":"break-drop.wast","passed":4,"failed":0},{"name":"call.wast","passed":82,"failed":0},{"name":"call_indirect.wast","passed":152,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":668,"failed":0},{"name":"conversions.wast","passed":435,"failed":0},{"name":"custom.wast","passed":10,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":55,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":82,"failed":0},{"name":"f32.wast","passed":2512,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2512,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":7,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":121,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"globals.wast","passed":78,"failed":0},{"name":"i32.wast","passed":443,"failed":0},{"name":"i64.wast","passed":389,"failed":0},{"name":"if.wast","passed":151,"failed":0},{"name":"imports.wast","passed":146,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":116,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":81,"failed":0},{"name":"memory.wast","passed":71,"failed":0},{"name":"memory_grow.wast","passed":94,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":173,"failed":0},{"name":"names.wast","passed":483,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":111,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":5,"failed":0},{"name":"start.wast","passed":19,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":62,"failed":0},{"name":"unreached-invalid.wast","passed":110,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-2.csv b/crates/tinywasm/tests/generated/wasm-2.csv index 8e228b4..b0949ec 100644 --- a/crates/tinywasm/tests/generated/wasm-2.csv +++ b/crates/tinywasm/tests/generated/wasm-2.csv @@ -10,4 +10,4 @@ 0.6.1,20278,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.7.0,20278,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.8.0,28008,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":128,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":178,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":104,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] -0.9.0-alpha.0,27822,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":79,"failed":0},{"name":"binary.wast","passed":160,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":169,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":58,"failed":0},{"name":"elem.wast","passed":74,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":108,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":239,"failed":0},{"name":"imports.wast","passed":183,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":147,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":50,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":6,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.9.0-alpha.0,27826,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":81,"failed":0},{"name":"binary.wast","passed":162,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":169,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":58,"failed":0},{"name":"elem.wast","passed":74,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":108,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":239,"failed":0},{"name":"imports.wast","passed":183,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":147,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":50,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":6,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-3.csv b/crates/tinywasm/tests/generated/wasm-3.csv index f821c61..ed301e4 100644 --- a/crates/tinywasm/tests/generated/wasm-3.csv +++ b/crates/tinywasm/tests/generated/wasm-3.csv @@ -1 +1 @@ -0.9.0-alpha.0,33727,1192,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":242,"failed":0},{"name":"align.wast","passed":165,"failed":0},{"name":"align64.wast","passed":156,"failed":0},{"name":"annotations.wast","passed":74,"failed":0},{"name":"binary-leb128.wast","passed":93,"failed":0},{"name":"binary.wast","passed":127,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":119,"failed":0},{"name":"br_on_non_null.wast","passed":1,"failed":11},{"name":"br_on_null.wast","passed":1,"failed":9},{"name":"br_table.wast","passed":24,"failed":162},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":1},{"name":"call_ref.wast","passed":4,"failed":31},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":59,"failed":6},{"name":"elem.wast","passed":137,"failed":14},{"name":"endianness.wast","passed":69,"failed":0},{"name":"endianness64.wast","passed":69,"failed":0},{"name":"exports.wast","passed":97,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_memory64.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":53,"failed":71},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":169,"failed":90},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"instance.wast","passed":0,"failed":23},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":142,"failed":21},{"name":"load.wast","passed":118,"failed":0},{"name":"load64.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":98,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory.wast","passed":89,"failed":1},{"name":"memory64.wast","passed":68,"failed":1},{"name":"memory_copy.wast","passed":8628,"failed":272},{"name":"memory_fill.wast","passed":184,"failed":16},{"name":"memory_grow.wast","passed":157,"failed":0},{"name":"memory_grow64.wast","passed":49,"failed":0},{"name":"memory_init.wast","passed":439,"failed":41},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_redundancy64.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":49,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"memory_trap64.wast","passed":172,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref.wast","passed":12,"failed":1},{"name":"ref_as_non_null.wast","passed":1,"failed":6},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":2,"failed":20},{"name":"ref_null.wast","passed":0,"failed":34},{"name":"return.wast","passed":84,"failed":0},{"name":"return_call.wast","passed":47,"failed":0},{"name":"return_call_indirect.wast","passed":79,"failed":0},{"name":"return_call_ref.wast","passed":11,"failed":40},{"name":"select.wast","passed":155,"failed":2},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":111,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":1},{"name":"table.wast","passed":47,"failed":13},{"name":"table_copy.wast","passed":1750,"failed":22},{"name":"table_copy_mixed.wast","passed":4,"failed":0},{"name":"table_fill.wast","passed":45,"failed":35},{"name":"table_get.wast","passed":16,"failed":1},{"name":"table_grow.wast","passed":58,"failed":21},{"name":"table_init.wast","passed":783,"failed":93},{"name":"table_set.wast","passed":26,"failed":2},{"name":"table_size.wast","passed":39,"failed":1},{"name":"tag.wast","passed":2,"failed":8},{"name":"throw.wast","passed":3,"failed":10},{"name":"throw_ref.wast","passed":2,"failed":13},{"name":"token.wast","passed":61,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"try_table.wast","passed":11,"failed":51},{"name":"type-canon.wast","passed":0,"failed":2},{"name":"type-equivalence.wast","passed":12,"failed":20},{"name":"type-rec.wast","passed":8,"failed":15},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":2,"failed":11},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.9.0-alpha.0,32305,2591,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":0,"failed":242},{"name":"align.wast","passed":161,"failed":0},{"name":"align64.wast","passed":83,"failed":73},{"name":"annotations.wast","passed":74,"failed":0},{"name":"binary-leb128.wast","passed":92,"failed":1},{"name":"binary.wast","passed":126,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":119,"failed":0},{"name":"br_on_non_null.wast","passed":1,"failed":9},{"name":"br_on_null.wast","passed":1,"failed":9},{"name":"br_table.wast","passed":24,"failed":162},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":49,"failed":124},{"name":"call_ref.wast","passed":4,"failed":31},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":59,"failed":6},{"name":"elem.wast","passed":137,"failed":14},{"name":"endianness.wast","passed":69,"failed":0},{"name":"endianness64.wast","passed":0,"failed":69},{"name":"exports.wast","passed":97,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_memory64.wast","passed":0,"failed":90},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":53,"failed":71},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":169,"failed":90},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"instance.wast","passed":0,"failed":23},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":142,"failed":21},{"name":"load.wast","passed":118,"failed":0},{"name":"load64.wast","passed":59,"failed":38},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":98,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory.wast","passed":89,"failed":0},{"name":"memory64.wast","passed":14,"failed":53},{"name":"memory_copy.wast","passed":8385,"failed":515},{"name":"memory_fill.wast","passed":164,"failed":36},{"name":"memory_grow.wast","passed":157,"failed":0},{"name":"memory_grow64.wast","passed":0,"failed":49},{"name":"memory_init.wast","passed":307,"failed":173},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_redundancy64.wast","passed":0,"failed":8},{"name":"memory_size.wast","passed":49,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"memory_trap64.wast","passed":0,"failed":172},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref.wast","passed":12,"failed":1},{"name":"ref_as_non_null.wast","passed":1,"failed":6},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":2,"failed":20},{"name":"ref_null.wast","passed":0,"failed":34},{"name":"return.wast","passed":84,"failed":0},{"name":"return_call.wast","passed":11,"failed":34},{"name":"return_call_indirect.wast","passed":26,"failed":50},{"name":"return_call_ref.wast","passed":11,"failed":40},{"name":"select.wast","passed":155,"failed":2},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":111,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":1},{"name":"table.wast","passed":47,"failed":9},{"name":"table_copy.wast","passed":1742,"failed":30},{"name":"table_copy_mixed.wast","passed":4,"failed":0},{"name":"table_fill.wast","passed":45,"failed":35},{"name":"table_get.wast","passed":16,"failed":1},{"name":"table_grow.wast","passed":58,"failed":21},{"name":"table_init.wast","passed":780,"failed":96},{"name":"table_set.wast","passed":26,"failed":2},{"name":"table_size.wast","passed":39,"failed":1},{"name":"tag.wast","passed":1,"failed":8},{"name":"throw.wast","passed":3,"failed":10},{"name":"throw_ref.wast","passed":2,"failed":13},{"name":"token.wast","passed":61,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"try_table.wast","passed":11,"failed":51},{"name":"type-canon.wast","passed":0,"failed":2},{"name":"type-equivalence.wast","passed":12,"failed":20},{"name":"type-rec.wast","passed":6,"failed":14},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":2,"failed":11},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv b/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv index 5ba121e..7f168a1 100644 --- a/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv +++ b/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv @@ -1,2 +1,2 @@ 0.8.0,57,0,[{"name":"custom-page-sizes-invalid.wast","passed":22,"failed":0},{"name":"custom-page-sizes.wast","passed":35,"failed":0}] -0.9.0-alpha.0,76,0,[{"name":"custom-page-sizes-invalid.wast","passed":23,"failed":0},{"name":"custom-page-sizes.wast","passed":45,"failed":0},{"name":"memory_max.wast","passed":4,"failed":0},{"name":"memory_max_i64.wast","passed":4,"failed":0}] +0.9.0-alpha.0,57,0,[{"name":"custom-page-sizes-invalid.wast","passed":22,"failed":0},{"name":"custom-page-sizes.wast","passed":35,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-memory64.csv b/crates/tinywasm/tests/generated/wasm-memory64.csv index 20afc3b..6638c85 100644 --- a/crates/tinywasm/tests/generated/wasm-memory64.csv +++ b/crates/tinywasm/tests/generated/wasm-memory64.csv @@ -1,2 +1,2 @@ 0.8.0,15081,3214,[{"name":"address.wast","passed":260,"failed":0},{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"address64.wast","passed":0,"failed":242},{"name":"align.wast","passed":161,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"align64.wast","passed":83,"failed":73},{"name":"annotations.wast","passed":74,"failed":0},{"name":"array_copy.wast","passed":4,"failed":31},{"name":"array_fill.wast","passed":3,"failed":14},{"name":"array_init_data.wast","passed":2,"failed":31},{"name":"array_init_elem.wast","passed":3,"failed":20},{"name":"binary-gc.wast","passed":1,"failed":0},{"name":"binary-leb128.wast","passed":92,"failed":1},{"name":"binary.wast","passed":124,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"br_if.wast","passed":119,"failed":0},{"name":"br_on_cast.wast","passed":6,"failed":31},{"name":"br_on_cast_fail.wast","passed":6,"failed":31},{"name":"br_on_non_null.wast","passed":1,"failed":9},{"name":"br_on_null.wast","passed":1,"failed":9},{"name":"br_table.wast","passed":24,"failed":162},{"name":"call_indirect.wast","passed":47,"failed":124},{"name":"call_ref.wast","passed":4,"failed":31},{"name":"data.wast","passed":59,"failed":6},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"elem.wast","passed":137,"failed":14},{"name":"endianness64.wast","passed":0,"failed":69},{"name":"exports.wast","passed":97,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"float_memory64.wast","passed":0,"failed":90},{"name":"func.wast","passed":175,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":99,"failed":82},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking.wast","passed":122,"failed":41},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load.wast","passed":118,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"load64.wast","passed":59,"failed":38},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"local_tee.wast","passed":98,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory.wast","passed":86,"failed":0},{"name":"memory64.wast","passed":10,"failed":53},{"name":"memory64/array.wast (skipped)","passed":0,"failed":0},{"name":"memory64/extern.wast (skipped)","passed":0,"failed":0},{"name":"memory64/global.wast (skipped)","passed":0,"failed":0},{"name":"memory64/i31.wast (skipped)","passed":0,"failed":0},{"name":"memory64/ref_null.wast (skipped)","passed":0,"failed":0},{"name":"memory64/select.wast (skipped)","passed":0,"failed":0},{"name":"memory64/simd_address.wast (skipped)","passed":0,"failed":0},{"name":"memory64/simd_lane.wast (skipped)","passed":0,"failed":0},{"name":"memory64/struct.wast (skipped)","passed":0,"failed":0},{"name":"memory64/table.wast (skipped)","passed":0,"failed":0},{"name":"memory_copy.wast","passed":8385,"failed":515},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill.wast","passed":164,"failed":36},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":157,"failed":0},{"name":"memory_grow64.wast","passed":0,"failed":49},{"name":"memory_init.wast","passed":307,"failed":173},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_redundancy64.wast","passed":0,"failed":8},{"name":"memory_size.wast","passed":49,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"memory_trap64.wast","passed":0,"failed":172},{"name":"ref.wast","passed":12,"failed":1},{"name":"ref_as_non_null.wast","passed":1,"failed":6},{"name":"ref_cast.wast","passed":0,"failed":45},{"name":"ref_eq.wast","passed":6,"failed":83},{"name":"ref_is_null.wast","passed":2,"failed":20},{"name":"ref_test.wast","passed":0,"failed":71},{"name":"return_call.wast","passed":18,"failed":27},{"name":"return_call_indirect.wast","passed":31,"failed":45},{"name":"return_call_ref.wast","passed":11,"failed":40},{"name":"simd_memory-multi.wast","passed":0,"failed":1},{"name":"start0.wast","passed":9,"failed":0},{"name":"store.wast","passed":111,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"table-sub.wast","passed":2,"failed":1},{"name":"table_copy.wast","passed":1742,"failed":30},{"name":"table_copy_mixed.wast","passed":3,"failed":1},{"name":"table_fill.wast","passed":9,"failed":71},{"name":"table_get.wast","passed":5,"failed":12},{"name":"table_grow.wast","passed":36,"failed":43},{"name":"table_init.wast","passed":588,"failed":288},{"name":"table_set.wast","passed":7,"failed":21},{"name":"table_size.wast","passed":2,"failed":38},{"name":"tag.wast","passed":1,"failed":8},{"name":"throw.wast","passed":3,"failed":10},{"name":"throw_ref.wast","passed":2,"failed":13},{"name":"token.wast","passed":61,"failed":0},{"name":"traps0.wast","passed":15,"failed":0},{"name":"try_table.wast","passed":11,"failed":51},{"name":"type-canon.wast","passed":0,"failed":2},{"name":"type-equivalence.wast","passed":12,"failed":20},{"name":"type-rec.wast","passed":6,"failed":14},{"name":"type-subtyping.wast","passed":16,"failed":86},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":2,"failed":11}] -0.9.0-alpha.0,1598,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":242,"failed":0},{"name":"align64.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":93,"failed":0},{"name":"binary.wast","passed":169,"failed":0},{"name":"endianness64.wast","passed":69,"failed":0},{"name":"float_memory64.wast","passed":90,"failed":0},{"name":"load64.wast","passed":97,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory64.wast","passed":65,"failed":0},{"name":"memory_grow64.wast","passed":49,"failed":0},{"name":"memory_redundancy64.wast","passed":8,"failed":0},{"name":"memory_trap64.wast","passed":172,"failed":0},{"name":"simd_address.wast","passed":49,"failed":0}] +0.9.0-alpha.0,12505,1919,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":0,"failed":242},{"name":"align64.wast","passed":83,"failed":73},{"name":"binary-leb128.wast","passed":92,"failed":1},{"name":"binary.wast","passed":126,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"call_indirect.wast","passed":47,"failed":124},{"name":"endianness64.wast","passed":0,"failed":69},{"name":"float_memory64.wast","passed":0,"failed":90},{"name":"imports.wast","passed":169,"failed":90},{"name":"load64.wast","passed":59,"failed":38},{"name":"memory.wast","passed":86,"failed":0},{"name":"memory64.wast","passed":10,"failed":53},{"name":"memory64/simd_address.wast (skipped)","passed":0,"failed":0},{"name":"memory64/table.wast (skipped)","passed":0,"failed":0},{"name":"memory_copy.wast","passed":8385,"failed":515},{"name":"memory_fill.wast","passed":164,"failed":36},{"name":"memory_grow64.wast","passed":0,"failed":49},{"name":"memory_init.wast","passed":307,"failed":173},{"name":"memory_redundancy64.wast","passed":0,"failed":8},{"name":"memory_trap64.wast","passed":0,"failed":172},{"name":"table_copy.wast","passed":1742,"failed":30},{"name":"table_copy_mixed.wast","passed":4,"failed":0},{"name":"table_fill.wast","passed":45,"failed":35},{"name":"table_get.wast","passed":16,"failed":1},{"name":"table_grow.wast","passed":58,"failed":21},{"name":"table_init.wast","passed":780,"failed":96},{"name":"table_set.wast","passed":26,"failed":2},{"name":"table_size.wast","passed":39,"failed":1}] diff --git a/crates/tinywasm/tests/generated/wasm-multi-memory.csv b/crates/tinywasm/tests/generated/wasm-multi-memory.csv index 33debd6..ae9ba03 100644 --- a/crates/tinywasm/tests/generated/wasm-multi-memory.csv +++ b/crates/tinywasm/tests/generated/wasm-multi-memory.csv @@ -1,2 +1,2 @@ 0.8.0,1872,0,[{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"align.wast","passed":160,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"binary.wast","passed":126,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"imports.wast","passed":175,"failed":0},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load.wast","passed":118,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory.wast","passed":86,"failed":0},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":157,"failed":0},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_size.wast","passed":49,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"multi-memory/simd_memory-multi.wast (skipped)","passed":0,"failed":0},{"name":"start0.wast","passed":9,"failed":0},{"name":"store.wast","passed":111,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"traps0.wast","passed":15,"failed":0}] -0.9.0-alpha.0,1871,0,[{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"align.wast","passed":160,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"binary.wast","passed":126,"failed":0},{"name":"binary0.wast","passed":6,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"imports.wast","passed":175,"failed":0},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load.wast","passed":118,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory.wast","passed":86,"failed":0},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":157,"failed":0},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_size.wast","passed":49,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"simd_memory-multi.wast (skipped)","passed":0,"failed":0},{"name":"start0.wast","passed":9,"failed":0},{"name":"store.wast","passed":111,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"traps0.wast","passed":15,"failed":0}] +0.9.0-alpha.0,1872,0,[{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"align.wast","passed":160,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"binary.wast","passed":126,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"imports.wast","passed":175,"failed":0},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load.wast","passed":118,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory.wast","passed":86,"failed":0},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":157,"failed":0},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_size.wast","passed":49,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"simd_memory-multi.wast (skipped)","passed":0,"failed":0},{"name":"start0.wast","passed":9,"failed":0},{"name":"store.wast","passed":111,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"traps0.wast","passed":15,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv b/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv deleted file mode 100644 index a6e6228..0000000 --- a/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv +++ /dev/null @@ -1 +0,0 @@ -0.9.0-alpha.0,0,93,[{"name":"i16x8_relaxed_q15mulr_s.wast","passed":0,"failed":3},{"name":"i32x4_relaxed_trunc.wast","passed":0,"failed":17},{"name":"i8x16_relaxed_swizzle.wast","passed":0,"failed":6},{"name":"relaxed_dot_product.wast","passed":0,"failed":11},{"name":"relaxed_laneselect.wast","passed":0,"failed":12},{"name":"relaxed_madd_nmadd.wast","passed":0,"failed":19},{"name":"relaxed_min_max.wast","passed":0,"failed":25}] diff --git a/crates/tinywasm/tests/generated/wasm-simd.csv b/crates/tinywasm/tests/generated/wasm-simd.csv index 2ae5a0a..d695dfa 100644 --- a/crates/tinywasm/tests/generated/wasm-simd.csv +++ b/crates/tinywasm/tests/generated/wasm-simd.csv @@ -1,2 +1,2 @@ 0.8.0,1300,24679,[{"name":"simd_address.wast","passed":4,"failed":45},{"name":"simd_align.wast","passed":46,"failed":54},{"name":"simd_bit_shift.wast","passed":39,"failed":213},{"name":"simd_bitwise.wast","passed":28,"failed":141},{"name":"simd_boolean.wast","passed":16,"failed":261},{"name":"simd_const.wast","passed":301,"failed":456},{"name":"simd_conversions.wast","passed":48,"failed":234},{"name":"simd_f32x4.wast","passed":16,"failed":774},{"name":"simd_f32x4_arith.wast","passed":16,"failed":1806},{"name":"simd_f32x4_cmp.wast","passed":24,"failed":2583},{"name":"simd_f32x4_pmin_pmax.wast","passed":14,"failed":3873},{"name":"simd_f32x4_rounding.wast","passed":24,"failed":177},{"name":"simd_f64x2.wast","passed":8,"failed":795},{"name":"simd_f64x2_arith.wast","passed":16,"failed":1809},{"name":"simd_f64x2_cmp.wast","passed":24,"failed":2661},{"name":"simd_f64x2_pmin_pmax.wast","passed":14,"failed":3873},{"name":"simd_f64x2_rounding.wast","passed":24,"failed":177},{"name":"simd_i16x8_arith.wast","passed":11,"failed":183},{"name":"simd_i16x8_arith2.wast","passed":19,"failed":153},{"name":"simd_i16x8_cmp.wast","passed":30,"failed":435},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":4,"failed":17},{"name":"simd_i16x8_extmul_i8x16.wast","passed":12,"failed":105},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":3,"failed":27},{"name":"simd_i16x8_sat_arith.wast","passed":16,"failed":206},{"name":"simd_i32x4_arith.wast","passed":11,"failed":183},{"name":"simd_i32x4_arith2.wast","passed":26,"failed":123},{"name":"simd_i32x4_cmp.wast","passed":40,"failed":435},{"name":"simd_i32x4_dot_i16x8.wast","passed":3,"failed":27},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":4,"failed":17},{"name":"simd_i32x4_extmul_i16x8.wast","passed":12,"failed":105},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":4,"failed":103},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":4,"failed":103},{"name":"simd_i64x2_arith.wast","passed":11,"failed":189},{"name":"simd_i64x2_arith2.wast","passed":2,"failed":23},{"name":"simd_i64x2_cmp.wast","passed":10,"failed":103},{"name":"simd_i64x2_extmul_i32x4.wast","passed":12,"failed":105},{"name":"simd_i8x16_arith.wast","passed":8,"failed":123},{"name":"simd_i8x16_arith2.wast","passed":25,"failed":186},{"name":"simd_i8x16_cmp.wast","passed":30,"failed":415},{"name":"simd_i8x16_sat_arith.wast","passed":24,"failed":190},{"name":"simd_int_to_int_extend.wast","passed":24,"failed":229},{"name":"simd_lane.wast","passed":189,"failed":286},{"name":"simd_linking.wast","passed":0,"failed":3},{"name":"simd_load.wast","passed":8,"failed":31},{"name":"simd_load16_lane.wast","passed":3,"failed":33},{"name":"simd_load32_lane.wast","passed":3,"failed":21},{"name":"simd_load64_lane.wast","passed":3,"failed":13},{"name":"simd_load8_lane.wast","passed":3,"failed":49},{"name":"simd_load_extend.wast","passed":18,"failed":86},{"name":"simd_load_splat.wast","passed":12,"failed":114},{"name":"simd_load_zero.wast","passed":10,"failed":29},{"name":"simd_splat.wast","passed":23,"failed":162},{"name":"simd_store.wast","passed":9,"failed":19},{"name":"simd_store16_lane.wast","passed":3,"failed":33},{"name":"simd_store32_lane.wast","passed":3,"failed":21},{"name":"simd_store64_lane.wast","passed":3,"failed":13},{"name":"simd_store8_lane.wast","passed":3,"failed":49}] -0.9.0-alpha.0,24450,1537,[{"name":"simd_address.wast","passed":49,"failed":0},{"name":"simd_align.wast","passed":100,"failed":0},{"name":"simd_bit_shift.wast","passed":252,"failed":0},{"name":"simd_bitwise.wast","passed":169,"failed":0},{"name":"simd_boolean.wast","passed":277,"failed":0},{"name":"simd_const.wast","passed":757,"failed":0},{"name":"simd_conversions.wast","passed":50,"failed":232},{"name":"simd_f32x4.wast","passed":790,"failed":0},{"name":"simd_f32x4_arith.wast","passed":1822,"failed":0},{"name":"simd_f32x4_cmp.wast","passed":2607,"failed":0},{"name":"simd_f32x4_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f32x4_rounding.wast","passed":187,"failed":14},{"name":"simd_f64x2.wast","passed":803,"failed":0},{"name":"simd_f64x2_arith.wast","passed":1824,"failed":1},{"name":"simd_f64x2_cmp.wast","passed":2685,"failed":0},{"name":"simd_f64x2_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f64x2_rounding.wast","passed":187,"failed":14},{"name":"simd_i16x8_arith.wast","passed":194,"failed":0},{"name":"simd_i16x8_arith2.wast","passed":142,"failed":30},{"name":"simd_i16x8_cmp.wast","passed":436,"failed":29},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":5,"failed":16},{"name":"simd_i16x8_extmul_i8x16.wast","passed":13,"failed":104},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":30,"failed":0},{"name":"simd_i16x8_sat_arith.wast","passed":222,"failed":0},{"name":"simd_i32x4_arith.wast","passed":194,"failed":0},{"name":"simd_i32x4_arith2.wast","passed":149,"failed":0},{"name":"simd_i32x4_cmp.wast","passed":450,"failed":25},{"name":"simd_i32x4_dot_i16x8.wast","passed":14,"failed":16},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":5,"failed":16},{"name":"simd_i32x4_extmul_i16x8.wast","passed":13,"failed":104},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":17,"failed":90},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":5,"failed":102},{"name":"simd_i64x2_arith.wast","passed":200,"failed":0},{"name":"simd_i64x2_arith2.wast","passed":25,"failed":0},{"name":"simd_i64x2_cmp.wast","passed":113,"failed":0},{"name":"simd_i64x2_extmul_i32x4.wast","passed":13,"failed":104},{"name":"simd_i8x16_arith.wast","passed":131,"failed":0},{"name":"simd_i8x16_arith2.wast","passed":179,"failed":32},{"name":"simd_i8x16_cmp.wast","passed":409,"failed":36},{"name":"simd_i8x16_sat_arith.wast","passed":214,"failed":0},{"name":"simd_int_to_int_extend.wast","passed":25,"failed":228},{"name":"simd_lane.wast","passed":331,"failed":144},{"name":"simd_linking.wast","passed":3,"failed":0},{"name":"simd_load.wast","passed":37,"failed":2},{"name":"simd_load16_lane.wast","passed":36,"failed":0},{"name":"simd_load32_lane.wast","passed":24,"failed":0},{"name":"simd_load64_lane.wast","passed":16,"failed":0},{"name":"simd_load8_lane.wast","passed":52,"failed":0},{"name":"simd_load_extend.wast","passed":20,"failed":84},{"name":"simd_load_splat.wast","passed":14,"failed":112},{"name":"simd_load_zero.wast","passed":39,"failed":0},{"name":"simd_memory-multi.wast","passed":1,"failed":0},{"name":"simd_select.wast","passed":7,"failed":0},{"name":"simd_splat.wast","passed":183,"failed":2},{"name":"simd_store.wast","passed":28,"failed":0},{"name":"simd_store16_lane.wast","passed":36,"failed":0},{"name":"simd_store32_lane.wast","passed":24,"failed":0},{"name":"simd_store64_lane.wast","passed":16,"failed":0},{"name":"simd_store8_lane.wast","passed":52,"failed":0}] +0.9.0-alpha.0,1741,24238,[{"name":"simd_address.wast","passed":7,"failed":42},{"name":"simd_align.wast","passed":93,"failed":7},{"name":"simd_bit_shift.wast","passed":41,"failed":211},{"name":"simd_bitwise.wast","passed":30,"failed":139},{"name":"simd_boolean.wast","passed":18,"failed":259},{"name":"simd_const.wast","passed":551,"failed":206},{"name":"simd_conversions.wast","passed":50,"failed":232},{"name":"simd_f32x4.wast","passed":18,"failed":772},{"name":"simd_f32x4_arith.wast","passed":19,"failed":1803},{"name":"simd_f32x4_cmp.wast","passed":26,"failed":2581},{"name":"simd_f32x4_pmin_pmax.wast","passed":15,"failed":3872},{"name":"simd_f32x4_rounding.wast","passed":25,"failed":176},{"name":"simd_f64x2.wast","passed":10,"failed":793},{"name":"simd_f64x2_arith.wast","passed":19,"failed":1806},{"name":"simd_f64x2_cmp.wast","passed":26,"failed":2659},{"name":"simd_f64x2_pmin_pmax.wast","passed":15,"failed":3872},{"name":"simd_f64x2_rounding.wast","passed":25,"failed":176},{"name":"simd_i16x8_arith.wast","passed":13,"failed":181},{"name":"simd_i16x8_arith2.wast","passed":21,"failed":151},{"name":"simd_i16x8_cmp.wast","passed":32,"failed":433},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":5,"failed":16},{"name":"simd_i16x8_extmul_i8x16.wast","passed":13,"failed":104},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":4,"failed":26},{"name":"simd_i16x8_sat_arith.wast","passed":18,"failed":204},{"name":"simd_i32x4_arith.wast","passed":13,"failed":181},{"name":"simd_i32x4_arith2.wast","passed":28,"failed":121},{"name":"simd_i32x4_cmp.wast","passed":42,"failed":433},{"name":"simd_i32x4_dot_i16x8.wast","passed":4,"failed":26},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":5,"failed":16},{"name":"simd_i32x4_extmul_i16x8.wast","passed":13,"failed":104},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":5,"failed":102},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":5,"failed":102},{"name":"simd_i64x2_arith.wast","passed":13,"failed":187},{"name":"simd_i64x2_arith2.wast","passed":4,"failed":21},{"name":"simd_i64x2_cmp.wast","passed":11,"failed":102},{"name":"simd_i64x2_extmul_i32x4.wast","passed":13,"failed":104},{"name":"simd_i8x16_arith.wast","passed":10,"failed":121},{"name":"simd_i8x16_arith2.wast","passed":27,"failed":184},{"name":"simd_i8x16_cmp.wast","passed":32,"failed":413},{"name":"simd_i8x16_sat_arith.wast","passed":26,"failed":188},{"name":"simd_int_to_int_extend.wast","passed":25,"failed":228},{"name":"simd_lane.wast","passed":209,"failed":266},{"name":"simd_linking.wast","passed":0,"failed":3},{"name":"simd_load.wast","passed":24,"failed":15},{"name":"simd_load16_lane.wast","passed":4,"failed":32},{"name":"simd_load32_lane.wast","passed":4,"failed":20},{"name":"simd_load64_lane.wast","passed":4,"failed":12},{"name":"simd_load8_lane.wast","passed":4,"failed":48},{"name":"simd_load_extend.wast","passed":24,"failed":80},{"name":"simd_load_splat.wast","passed":17,"failed":109},{"name":"simd_load_zero.wast","passed":12,"failed":27},{"name":"simd_splat.wast","passed":37,"failed":148},{"name":"simd_store.wast","passed":20,"failed":8},{"name":"simd_store16_lane.wast","passed":3,"failed":33},{"name":"simd_store32_lane.wast","passed":3,"failed":21},{"name":"simd_store64_lane.wast","passed":3,"failed":13},{"name":"simd_store8_lane.wast","passed":3,"failed":49}] diff --git a/crates/tinywasm/tests/generated/wasm-tail-call.csv b/crates/tinywasm/tests/generated/wasm-tail-call.csv deleted file mode 100644 index c9f0910..0000000 --- a/crates/tinywasm/tests/generated/wasm-tail-call.csv +++ /dev/null @@ -1 +0,0 @@ -0.9.0-alpha.0,119,0,[{"name":"return_call.wast","passed":44,"failed":0},{"name":"return_call_indirect.wast","passed":75,"failed":0}] diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index 59013ad..c60b9ce 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -1,6 +1,8 @@ +use std::hash::Hasher; use std::panic::{self, AssertUnwindSafe}; use eyre::{Result, bail, eyre}; +use tinywasm::{CoroState, SuspendConditions, SuspendReason}; use tinywasm_types::{ExternRef, FuncRef, ModuleInstanceAddr, TinyWasmModule, ValType, WasmValue}; use wasm_testsuite::wast; use wasm_testsuite::wast::{QuoteWat, core::AbstractHeapType}; @@ -12,6 +14,24 @@ pub fn try_downcast_panic(panic: Box) -> String { info.unwrap_or(info_str.unwrap_or(&info_string.unwrap_or("unknown panic".to_owned())).to_string()) } +// due to imprecision it's not exact +fn make_sometimes_breaking_cb(probability: f64) -> impl FnMut(&tinywasm::Store) -> std::ops::ControlFlow<(), ()> { + let mut counter = 0 as u64; + let mut hasher = std::hash::DefaultHasher::new(); + let threshhold = (probability * (u64::MAX as f64)) as u64; // 2 lossy conversions + + move |_| { + hasher.write_u64(counter); + counter += 1; + if hasher.finish() < threshhold { + std::ops::ControlFlow::Break(()) + } else { + std::ops::ControlFlow::Continue(()) + } + } +} + +#[cfg(not(feature = "test_async"))] pub fn exec_fn_instance( instance: Option<&ModuleInstanceAddr>, store: &mut tinywasm::Store, @@ -30,6 +50,50 @@ pub fn exec_fn_instance( func.call(store, args) } +#[cfg(feature = "test_async")] +pub fn exec_fn_instance( + instance: Option<&ModuleInstanceAddr>, + store: &mut tinywasm::Store, + name: &str, + args: &[tinywasm_types::WasmValue], +) -> Result, tinywasm::Error> { + let Some(instance) = instance else { + return Err(tinywasm::Error::Other("no instance found".to_string())); + }; + + let mut prev_reason = None; + store.update_suspend_conditions(|old_cond| { + prev_reason = Some(old_cond); + SuspendConditions::new().with_suspend_callback(Box::new(make_sometimes_breaking_cb(2.0 / 3.0))) + }); + let res = || -> Result, tinywasm::Error> { + let Some(instance) = store.get_module_instance(*instance) else { + return Err(tinywasm::Error::Other("no instance found".to_string())); + }; + + let func = instance.exported_func_untyped(store, name)?; + let mut state = match func.call_coro(store, args)? { + tinywasm::PotentialCoroCallResult::Return(val) => return Ok(val), + tinywasm::PotentialCoroCallResult::Suspended(suspend_reason, state) => { + assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback)); + state + } + }; + loop { + match state.resume(store, None)? { + tinywasm::CoroStateResumeResult::Return(val) => return Ok(val), + tinywasm::CoroStateResumeResult::Suspended(suspend_reason) => { + assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback)) + } + } + } + }(); + // restore store suspend conditions before returning error or success + store.set_suspend_conditions(prev_reason.unwrap()); + res +} + +#[cfg(not(feature = "test_async"))] pub fn exec_fn( module: Option<&TinyWasmModule>, name: &str, @@ -39,13 +103,60 @@ pub fn exec_fn( let Some(module) = module else { return Err(tinywasm::Error::Other("no module found".to_string())); }; - let mut store = tinywasm::Store::new(); let module = tinywasm::Module::from(module); let instance = module.instantiate(&mut store, imports)?; instance.exported_func_untyped(&store, name)?.call(&mut store, args) } +#[cfg(feature = "test_async")] +pub fn exec_fn( + module: Option<&TinyWasmModule>, + name: &str, + args: &[tinywasm_types::WasmValue], + imports: Option, +) -> Result, tinywasm::Error> { + let Some(module) = module else { + return Err(tinywasm::Error::Other("no module found".to_string())); + }; + + let mut store = tinywasm::Store::new(); + + store.set_suspend_conditions( + SuspendConditions::new().with_suspend_callback(Box::new(make_sometimes_breaking_cb(2.0 / 3.0))), + ); + + let module = tinywasm::Module::from(module); + let instance = match module.instantiate_coro(&mut store, imports)? { + tinywasm::PotentialCoroCallResult::Return(res) => res, + tinywasm::PotentialCoroCallResult::Suspended(suspend_reason, mut state) => loop { + assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback)); + match state.resume(&mut store, None)? { + tinywasm::CoroStateResumeResult::Return(res) => break res, + tinywasm::CoroStateResumeResult::Suspended(suspend_reason) => { + assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback)); + } + } + }, + }; + + let mut state = match instance.exported_func_untyped(&store, name)?.call_coro(&mut store, args)? { + tinywasm::PotentialCoroCallResult::Return(r) => return Ok(r), + tinywasm::PotentialCoroCallResult::Suspended(suspend_reason, state) => { + assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback)); + state + } + }; + loop { + match state.resume(&mut store, None)? { + tinywasm::CoroStateResumeResult::Return(res) => return Ok(res), + tinywasm::CoroStateResumeResult::Suspended(suspend_reason) => { + assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback)) + } + } + } +} + pub fn catch_unwind_silent(f: impl FnOnce() -> R) -> std::thread::Result { let prev_hook = panic::take_hook(); panic::set_hook(Box::new(|_| {})); diff --git a/crates/tinywasm/tests/wasm_resume.rs b/crates/tinywasm/tests/wasm_resume.rs new file mode 100644 index 0000000..873bf61 --- /dev/null +++ b/crates/tinywasm/tests/wasm_resume.rs @@ -0,0 +1,372 @@ +use core::panic; +use eyre; +use std::sync; +use std::{ops::ControlFlow, time::Duration}; +use tinywasm::{ + CoroState, CoroStateResumeResult, Extern, Imports, Module, ModuleInstance, PotentialCoroCallResult, Store, + SuspendConditions, SuspendReason, +}; +use wat; + +#[test] +fn main() -> std::result::Result<(), eyre::Report> { + println!("\n# testing with callback"); + let mut cb_cond = |store: &mut Store| { + let callback = make_suspend_in_time_cb(30); + store.set_suspend_conditions(SuspendConditions::new().with_suspend_callback(Box::new(callback))); + }; + suspend_with_pure_loop(&mut cb_cond, SuspendReason::SuspendedCallback)?; + suspend_with_wasm_fn(&mut cb_cond, SuspendReason::SuspendedCallback)?; + suspend_with_host_fn(&mut cb_cond, SuspendReason::SuspendedCallback)?; + + println!("\n# testing with epoch"); + let mut time_cond = |store: &mut Store| { + store.set_suspend_conditions(SuspendConditions::new().with_timeout_in(Duration::from_millis(10))) + }; + suspend_with_pure_loop(&mut time_cond, SuspendReason::SuspendedEpoch)?; + suspend_with_wasm_fn(&mut time_cond, SuspendReason::SuspendedEpoch)?; + suspend_with_host_fn(&mut time_cond, SuspendReason::SuspendedEpoch)?; + + println!("\n# testing atomic bool"); + let mut cb_thead = |store: &mut Store| { + let arc = sync::Arc::::new(sync::atomic::AtomicBool::new(false)); + store.set_suspend_conditions(SuspendConditions::new().with_suspend_flag(arc.clone())); + let handle = std::thread::spawn(move || { + std::thread::sleep(Duration::from_millis(10)); + arc.store(true, sync::atomic::Ordering::Release); + }); + drop(handle); + }; + suspend_with_pure_loop(&mut cb_thead, SuspendReason::SuspendedFlag)?; + suspend_with_wasm_fn(&mut cb_thead, SuspendReason::SuspendedFlag)?; + suspend_with_host_fn(&mut cb_thead, SuspendReason::SuspendedFlag)?; + + Ok(()) +} + +fn make_suspend_in_time_cb(milis: u64) -> impl FnMut(&Store) -> ControlFlow<(), ()> { + let mut counter = 0 as u64; + move |_| -> ControlFlow<(), ()> { + counter += 1; + if counter > milis { + counter = 0; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } +} + +fn try_compare(lhs: &SuspendReason, rhs: &SuspendReason) -> eyre::Result { + Ok(match lhs { + SuspendReason::Yield(_) => eyre::bail!("Can't compare yields"), + SuspendReason::SuspendedEpoch => matches!(rhs, SuspendReason::SuspendedEpoch), + SuspendReason::SuspendedCallback => matches!(rhs, SuspendReason::SuspendedCallback), + SuspendReason::SuspendedFlag => matches!(rhs, SuspendReason::SuspendedFlag), + _ => eyre::bail!("unimplemented new variant"), + }) +} + +// check if you can suspend while looping +fn suspend_with_pure_loop( + set_cond: &mut impl FnMut(&mut Store) -> (), + expected_reason: SuspendReason, +) -> eyre::Result<()> { + println!("## test suspend in loop"); + + let wasm: String = { + let detect_overflow = overflow_detect_snippet("$res"); + format!( + r#"(module + (memory $mem 1) + (export "memory" (memory $mem)) ;; first 8 bytes - counter, next 4 - overflow flag + + (func (export "start_counter") + (local $res i64) + (loop $lp + (i32.const 0) ;;where to store + (i64.load $mem (i32.const 0)) + (i64.const 1) + (i64.add) + (local.set $res) + (local.get $res) + (i64.store $mem) + {detect_overflow} + (br $lp) + ) + ) + )"# + ) + .into() + }; + + let mut tested = { + let wasm = wat::parse_str(wasm)?; + let module = Module::parse_bytes(&wasm)?; + let mut store = Store::default(); + let instance = module.instantiate(&mut store, None)?; + TestedModule { store, instance: instance, resumable: None } + }; + + let increases = run_loops_look_at_numbers(&mut tested, set_cond, expected_reason, 16)?; + assert!(increases > 2, "code doesn't enough: either suspend condition is too tight or something is broken"); + Ok(()) +} + +// check if you can suspend when calling wasm function +fn suspend_with_wasm_fn( + set_cond: &mut impl FnMut(&mut Store) -> (), + expected_reason: SuspendReason, +) -> eyre::Result<()> { + println!("## test suspend wasm fn"); + + let wasm: String = { + let detect_overflow = overflow_detect_snippet("$res"); + format!( + r#"(module + (memory $mem 1) + (export "memory" (memory $mem)) ;; first 8 bytes - counter, next 8 - overflow counter + + (func $wasm_nop + nop + ) + + (func $wasm_adder (param i64 i64) (result i64) + (local.get 0) + (local.get 1) + (i64.add) + ) + + (func $overflow_detect (param $res i64) + {detect_overflow} + ) + + (func (export "start_counter") + (local $res i64) + (loop $lp + (call $wasm_nop) + (i32.const 0) ;;where to store + (i64.load $mem (i32.const 0)) + (i64.const 1) + (call $wasm_adder) + (local.set $res) + (call $wasm_nop) + (local.get $res) + (i64.store $mem) + (local.get $res) + (call $overflow_detect) + (call $wasm_nop) + (br $lp) + ) + ) + )"# + ) + .into() + }; + + let mut tested = { + let wasm = wat::parse_str(wasm)?; + let module = Module::parse_bytes(&wasm)?; + let mut store = Store::default(); + let instance = module.instantiate(&mut store, None)?; + TestedModule { store, instance: instance, resumable: None } + }; + + let increases = run_loops_look_at_numbers(&mut tested, set_cond, expected_reason, 16)?; + assert!(increases > 2, "code doesn't enough: either suspend condition is too tight or something is broken"); + + Ok(()) +} + +// check if you can suspend when calling host function +fn suspend_with_host_fn( + set_cond: &mut impl FnMut(&mut Store) -> (), + expected_reason: SuspendReason, +) -> eyre::Result<()> { + println!("## test suspend host fn"); + + let wasm: String = { + format!( + r#"(module + (import "host" "adder" (func $host_adder (param i64 i64)(result i64))) + (import "host" "nop" (func $host_nop)) + (import "host" "detect" (func $overflow_detect (param $res i64))) + (memory $mem 1) + (export "memory" (memory $mem)) ;; first 8 bytes - counter, next 8 - overflow counter + + (func (export "start_counter") + (local $res i64) + (loop $lp + (call $host_nop) + (i32.const 0) ;;where to store + (i64.load $mem (i32.const 0)) + (i64.const 1) + (call $host_adder) + (local.set $res) + (call $host_nop) + (local.get $res) + (i64.store $mem) + (local.get $res) + (call $overflow_detect) + (call $host_nop) + (br $lp) + ) + ) + )"# + ) + .into() + }; + + let mut tested = { + let wasm = wat::parse_str(wasm)?; + let module = Module::parse_bytes(&wasm)?; + let mut store = Store::default(); + let mut imports = Imports::new(); + imports.define( + "host", + "adder", + Extern::typed_func(|_, args: (i64, i64)| -> tinywasm::Result { Ok(args.0 + args.1) }), + )?; + imports.define( + "host", + "nop", + Extern::typed_func(|_, ()| -> tinywasm::Result<()> { + std::thread::sleep(Duration::from_micros(1)); + Ok(()) + }), + )?; + imports.define( + "host", + "detect", + Extern::typed_func(|mut ctx, arg: i64| -> tinywasm::Result<()> { + if arg != 0 { + return Ok(()); + } + let mut mem = ctx.module().exported_memory_mut(ctx.store_mut(), "memory").expect("where's memory"); + let mut buf = [0 as u8; 8]; + buf.copy_from_slice(mem.load(8, 8)?); + let counter = i64::from_be_bytes(buf); + mem.store(8, 8, &i64::to_be_bytes(counter + 1))?; + Ok(()) + }), + )?; + + let instance = module.instantiate(&mut store, Some(imports))?; + TestedModule { store, instance: instance, resumable: None } + }; + + let increases = run_loops_look_at_numbers(&mut tested, set_cond, expected_reason, 16)?; + assert!(increases > 2, "code doesn't enough: either suspend condition is too tight or something is broken"); + Ok(()) +} + +fn run_loops_look_at_numbers( + tested: &mut TestedModule, + set_cond: &mut impl FnMut(&mut Store) -> (), + expected_reason: SuspendReason, + times: u32, +) -> eyre::Result { + set_cond(&mut tested.store); + let suspend = tested.start_counter_incrementing_loop("start_counter")?; + assert!(try_compare(&suspend, &expected_reason).expect("unexpected yield")); + + let mut prev_counter = tested.get_counter(); + let mut times_increased = 0 as u32; + + { + let (big, small) = prev_counter; + println!("after start {big} {small}"); + } + + assert!(prev_counter >= (0, 0)); + + for _ in 0..times - 1 { + set_cond(&mut tested.store); + assert!(try_compare(&tested.continue_counter_incrementing_loop()?, &expected_reason)?); + let new_counter = tested.get_counter(); + // save for scheduling weirdness, loop should run for a bunch of times in 3ms + assert!(new_counter >= prev_counter); + { + let (big, small) = new_counter; + println!("after continue {big} {small}"); + } + if new_counter > prev_counter { + times_increased += 1; + } + prev_counter = new_counter; + } + Ok(times_increased) +} + +fn overflow_detect_snippet(var: &str) -> String { + format!( + r#"(i64.eq (i64.const 0) (local.get {var})) + (if + (then + ;; we wrapped around back to 0 - set flag + (i32.const 8) ;;where to store + (i32.const 8) ;;where to load + (i64.load) + (i64.const 1) + (i64.add) + (i64.store $mem) + ) + (else + nop + ) + ) + "# + ) + .into() +} + +// should have exported memory "memory" and +struct TestedModule { + store: Store, + instance: ModuleInstance, + resumable: Option, +} + +impl TestedModule { + fn start_counter_incrementing_loop(&mut self, fn_name: &str) -> tinywasm::Result { + let starter = self.instance.exported_func_untyped(&self.store, fn_name)?; + if let PotentialCoroCallResult::Suspended(res, coro) = starter.call_coro(&mut self.store, &[])? { + self.resumable = Some(coro); + return Ok(res); + } else { + panic!("that should never return"); + } + } + + fn continue_counter_incrementing_loop(&mut self) -> tinywasm::Result { + let paused = if let Some(val) = self.resumable.as_mut() { + val + } else { + panic!("nothing to continue"); + }; + let resume_res = (*paused).resume(&mut self.store, None)?; + match resume_res { + CoroStateResumeResult::Suspended(res) => Ok(res), + CoroStateResumeResult::Return(_) => panic!("should never return"), + } + } + + // (counter, overflow flag) + fn get_counter(&self) -> (u64, u64) { + let counter_now = { + let mem = self.instance.exported_memory(&self.store, "memory").expect("where's memory"); + let mut buff: [u8; 8] = [0; 8]; + let in_mem = mem.load(0, 8).expect("where's memory"); + buff.clone_from_slice(in_mem); + u64::from_le_bytes(buff) + }; + let overflow_times = { + let mem = self.instance.exported_memory(&self.store, "memory").expect("where's memory"); + let mut buff: [u8; 8] = [0; 8]; + let in_mem = mem.load(8, 8).expect("where's memory"); + buff.clone_from_slice(in_mem); + u64::from_le_bytes(buff) + }; + (overflow_times, counter_now) + } +} diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index c34b705..94f6f1b 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -10,7 +10,7 @@ extern crate alloc; use alloc::boxed::Box; -use core::{fmt::Debug, ops::Range}; +use core::{any::Any, fmt::Debug, ops::Range}; // Memory defaults const MEM_PAGE_SIZE: u64 = 65536; @@ -420,3 +420,6 @@ pub enum ElementItem { Func(FuncAddr), Expr(ConstInstruction), } + +pub type YieldedValue = Option>; +pub type ResumeArgument = Option>; diff --git a/examples/host_coro.rs b/examples/host_coro.rs new file mode 100644 index 0000000..394506e --- /dev/null +++ b/examples/host_coro.rs @@ -0,0 +1,156 @@ +use eyre::{self, bail}; +use tinywasm::{ + types::{FuncType, ValType, WasmValue}, + CoroState, CoroStateResumeResult, Extern, FuncContext, HostCoroState, Imports, Module, PotentialCoroCallResult, + Store, SuspendReason, +}; +use wat; + +fn main() -> eyre::Result<()> { + untyped()?; + typed()?; + Ok(()) +} + +const WASM: &str = r#"(module + (import "host" "hello" (func $host_hello (param i32))) + (import "host" "wait" (func $host_suspend (param i32)(result i32))) + + (func (export "call_hello") (result f32) + (call $host_hello (i32.const -3)) + (call $host_suspend (i32.const 10)) + (call $host_hello) + (f32.const 6.28) + ) +) +"#; + +#[derive(Debug)] +struct MyUserData { + magic: u16, +} + +#[derive(Debug)] +struct MySuspendedState { + base: i32, +} +impl<'a> CoroState, FuncContext<'a>> for MySuspendedState { + fn resume( + &mut self, + _: FuncContext<'a>, + arg: tinywasm::types::ResumeArgument, + ) -> tinywasm::Result>> { + let val = arg.expect("you din't send").downcast::().expect("you sent wrong"); + return Ok(CoroStateResumeResult::Return(vec![WasmValue::I32(*val + self.base)])); + } +} + +fn untyped() -> eyre::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", + "hello", + Extern::func(&FuncType { params: Box::new([ValType::I32]), results: Box::new([]) }, |_: FuncContext<'_>, x| { + x.first().map(|x| println!("{:?}", x)); + Ok(vec![]) + }), + )?; + let my_coro_starter = |_ctx: FuncContext<'_>, + vals: &[WasmValue]| + -> tinywasm::Result, Box>> { + let base = if let WasmValue::I32(v) = vals.first().expect("wrong args") { v } else { panic!("wrong arg") }; + let coro = Box::new(MySuspendedState { base: *base }); + return Ok(PotentialCoroCallResult::Suspended( + SuspendReason::make_yield::(MyUserData { magic: 42 }), + coro, + )); + }; + imports.define( + "host", + "wait", + Extern::func_coro( + &FuncType { params: Box::new([ValType::I32]), results: Box::new([ValType::I32]) }, + my_coro_starter, + ), + )?; + + let instance = module.instantiate(&mut store, Some(imports))?; + + let greeter = instance.exported_func_untyped(&store, "call_hello")?; + let call_res = greeter.call_coro(&mut store, &[])?; + let mut resumable = match call_res { + tinywasm::PotentialCoroCallResult::Return(..) => bail!("it's not supposed to return yet"), + tinywasm::PotentialCoroCallResult::Suspended(SuspendReason::Yield(Some(val)), resumable) => { + match val.downcast::() { + Ok(val) => assert_eq!(val.magic, 42), + Err(_) => bail!("invalid yielded val"), + } + resumable + } + tinywasm::PotentialCoroCallResult::Suspended(..) => bail!("wrong suspend"), + }; + + let final_res = resumable.resume(&mut store, Some(Box::::new(7)))?; + if let CoroStateResumeResult::Return(vals) = final_res { + println!("{:?}", vals.first().unwrap()); + } else { + panic!("should have finished"); + } + + Ok(()) +} + +fn typed() -> eyre::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", + "hello", + Extern::typed_func(|_: FuncContext<'_>, x: i32| { + println!("{x}"); + Ok(()) + }), + )?; + let my_coro_starter = + |_ctx: FuncContext<'_>, base: i32| -> tinywasm::Result>> { + let coro = Box::new(MySuspendedState { base: base }); + return Ok(PotentialCoroCallResult::Suspended( + SuspendReason::make_yield::(MyUserData { magic: 42 }), + coro, + )); + }; + imports.define("host", "wait", Extern::typed_func_coro(my_coro_starter))?; + + let instance = module.instantiate(&mut store, Some(imports))?; + + let greeter = instance.exported_func::<(), f32>(&store, "call_hello")?; + let call_res = greeter.call_coro(&mut store, ())?; + let mut resumable = match call_res { + tinywasm::PotentialCoroCallResult::Return(..) => bail!("it's not supposed to return yet"), + tinywasm::PotentialCoroCallResult::Suspended(SuspendReason::Yield(Some(val)), resumable) => { + match val.downcast::() { + Ok(val) => assert_eq!(val.magic, 42), + Err(_) => bail!("invalid yielded val"), + } + resumable + } + tinywasm::PotentialCoroCallResult::Suspended(..) => bail!("wrong suspend"), + }; + + let final_res = resumable.resume(&mut store, Some(Box::::new(7)))?; + + if let CoroStateResumeResult::Return(res) = final_res { + println!("{res}"); + } else { + panic!("should have returned"); + } + + Ok(()) +}