use super::Func;
use crate::{
engine::Stack,
func::CallResultsTuple,
AsContextMut,
Engine,
Error,
Value,
WasmResults,
};
use core::{fmt, marker::PhantomData, mem::replace, ops::Deref};
use wasmi_core::Trap;
#[derive(Debug)]
pub(crate) enum ResumableCallBase<T> {
Finished(T),
Resumable(ResumableInvocation),
}
#[derive(Debug)]
pub enum ResumableCall {
Finished,
Resumable(ResumableInvocation),
}
impl ResumableCall {
pub(crate) fn new(call: ResumableCallBase<()>) -> Self {
match call {
ResumableCallBase::Finished(()) => Self::Finished,
ResumableCallBase::Resumable(invocation) => Self::Resumable(invocation),
}
}
}
#[derive(Debug)]
pub struct ResumableInvocation {
engine: Engine,
func: Func,
host_func: Func,
host_error: Trap,
pub(super) stack: Stack,
}
impl ResumableInvocation {
pub(super) fn new(
engine: Engine,
func: Func,
host_func: Func,
host_error: Trap,
stack: Stack,
) -> Self {
Self {
engine,
func,
host_func,
host_error,
stack,
}
}
pub(super) fn take_stack(&mut self) -> Stack {
replace(&mut self.stack, Stack::empty())
}
pub(super) fn update(&mut self, host_func: Func, host_error: Trap) {
self.host_func = host_func;
self.host_error = host_error;
}
}
impl Drop for ResumableInvocation {
fn drop(&mut self) {
let stack = self.take_stack();
self.engine.recycle_stack(stack);
}
}
impl ResumableInvocation {
pub fn host_func(&self) -> Func {
self.host_func
}
pub fn host_error(&self) -> &Trap {
&self.host_error
}
pub fn resume<T>(
self,
mut ctx: impl AsContextMut<UserState = T>,
inputs: &[Value],
outputs: &mut [Value],
) -> Result<ResumableCall, Error> {
self.engine
.resolve_func_type(self.host_func().ty_dedup(ctx.as_context()), |func_type| {
func_type.match_results(inputs, true)
})?;
self.engine
.resolve_func_type(self.func.ty_dedup(ctx.as_context()), |func_type| {
func_type.match_results(outputs, false)?;
func_type.prepare_outputs(outputs);
<Result<(), Error>>::Ok(()) })?;
self.engine
.clone()
.resume_func(ctx.as_context_mut(), self, inputs, outputs)
.map_err(Into::into)
.map(ResumableCall::new)
}
}
#[derive(Debug)]
pub enum TypedResumableCall<T> {
Finished(T),
Resumable(TypedResumableInvocation<T>),
}
impl<Results> TypedResumableCall<Results> {
pub(crate) fn new(call: ResumableCallBase<Results>) -> Self {
match call {
ResumableCallBase::Finished(results) => Self::Finished(results),
ResumableCallBase::Resumable(invocation) => {
Self::Resumable(TypedResumableInvocation::new(invocation))
}
}
}
}
pub struct TypedResumableInvocation<Results> {
invocation: ResumableInvocation,
results: PhantomData<fn() -> Results>,
}
impl<Results> TypedResumableInvocation<Results> {
pub(crate) fn new(invocation: ResumableInvocation) -> Self {
Self {
invocation,
results: PhantomData,
}
}
pub fn resume<T>(
self,
mut ctx: impl AsContextMut<UserState = T>,
inputs: &[Value],
) -> Result<TypedResumableCall<Results>, Error>
where
Results: WasmResults,
{
self.engine
.resolve_func_type(self.host_func().ty_dedup(ctx.as_context()), |func_type| {
func_type.match_results(inputs, true)
})?;
self.engine
.clone()
.resume_func(
ctx.as_context_mut(),
self.invocation,
inputs,
<CallResultsTuple<Results>>::default(),
)
.map_err(Into::into)
.map(TypedResumableCall::new)
}
}
impl<Results> Deref for TypedResumableInvocation<Results> {
type Target = ResumableInvocation;
fn deref(&self) -> &Self::Target {
&self.invocation
}
}
impl<Results> fmt::Debug for TypedResumableInvocation<Results> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TypedResumableInvocation")
.field("invocation", &self.invocation)
.field("results", &self.results)
.finish()
}
}