twasmi/
func.rs

1use crate::host::Externals;
2use crate::isa;
3use crate::module::ModuleInstance;
4use crate::runner::{check_function_args, Interpreter, InterpreterState, StackRecycler};
5use crate::types::ValueType;
6use crate::value::RuntimeValue;
7use crate::{Signature, Trap};
8use alloc::{
9    borrow::Cow,
10    rc::{Rc, Weak},
11    vec::Vec,
12};
13use core::fmt;
14use tetsy_wasm::elements::Local;
15
16/// Reference to a function (See [`FuncInstance`] for details).
17///
18/// This reference has a reference-counting semantics.
19///
20/// [`FuncInstance`]: struct.FuncInstance.html
21#[derive(Clone, Debug)]
22pub struct FuncRef(Rc<FuncInstance>);
23
24impl ::core::ops::Deref for FuncRef {
25    type Target = FuncInstance;
26    fn deref(&self) -> &FuncInstance {
27        &self.0
28    }
29}
30
31/// Runtime representation of a function.
32///
33/// Functions are the unit of organization of code in WebAssembly. Each function takes a sequence of values
34/// as parameters and either optionally return a value or trap.
35/// Functions can call other function including itself (i.e recursive calls are allowed) and imported functions
36/// (i.e functions defined in another module or by the host environment).
37///
38/// Functions can be defined either:
39///
40/// - by a wasm module,
41/// - by the host environment and passed to a wasm module as an import.
42///   See more in [`Externals`].
43///
44/// [`Externals`]: trait.Externals.html
45pub struct FuncInstance(FuncInstanceInternal);
46
47#[derive(Clone)]
48pub(crate) enum FuncInstanceInternal {
49    Internal {
50        signature: Rc<Signature>,
51        module: Weak<ModuleInstance>,
52        body: Rc<FuncBody>,
53    },
54    Host {
55        signature: Signature,
56        host_func_index: usize,
57    },
58}
59
60impl fmt::Debug for FuncInstance {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        match self.as_internal() {
63            &FuncInstanceInternal::Internal { ref signature, .. } => {
64                // We can't write description of self.module here, because it generate
65                // debug string for function instances and this will lead to infinite loop.
66                write!(f, "Internal {{ signature={:?} }}", signature,)
67            }
68            &FuncInstanceInternal::Host { ref signature, .. } => {
69                write!(f, "Host {{ signature={:?} }}", signature)
70            }
71        }
72    }
73}
74
75impl FuncInstance {
76    /// Allocate a function instance for a host function.
77    ///
78    /// When this function instance will be called by the wasm code,
79    /// the instance of [`Externals`] will be invoked by calling `invoke_index`
80    /// with specified `host_func_index` here.
81    /// This call will be made with the `signature` provided here.
82    ///
83    /// [`Externals`]: trait.Externals.html
84    pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef {
85        let func = FuncInstanceInternal::Host {
86            signature,
87            host_func_index,
88        };
89        FuncRef(Rc::new(FuncInstance(func)))
90    }
91
92    /// Returns [signature] of this function instance.
93    ///
94    /// This function instance can only be called with matching signatures.
95    ///
96    /// [signature]: struct.Signature.html
97    pub fn signature(&self) -> &Signature {
98        match *self.as_internal() {
99            FuncInstanceInternal::Internal { ref signature, .. } => signature,
100            FuncInstanceInternal::Host { ref signature, .. } => signature,
101        }
102    }
103
104    pub(crate) fn as_internal(&self) -> &FuncInstanceInternal {
105        &self.0
106    }
107
108    pub(crate) fn alloc_internal(
109        module: Weak<ModuleInstance>,
110        signature: Rc<Signature>,
111        body: FuncBody,
112    ) -> FuncRef {
113        let func = FuncInstanceInternal::Internal {
114            signature,
115            module: module,
116            body: Rc::new(body),
117        };
118        FuncRef(Rc::new(FuncInstance(func)))
119    }
120
121    pub(crate) fn body(&self) -> Option<Rc<FuncBody>> {
122        match *self.as_internal() {
123            FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)),
124            FuncInstanceInternal::Host { .. } => None,
125        }
126    }
127
128    /// Invoke this function.
129    ///
130    /// # Errors
131    ///
132    /// Returns `Err` if `args` types is not match function [`signature`] or
133    /// if [`Trap`] at execution time occured.
134    ///
135    /// [`signature`]: #method.signature
136    /// [`Trap`]: #enum.Trap.html
137    pub fn invoke<E: Externals>(
138        func: &FuncRef,
139        args: &[RuntimeValue],
140        externals: &mut E,
141    ) -> Result<Option<RuntimeValue>, Trap> {
142        check_function_args(func.signature(), &args)?;
143        match *func.as_internal() {
144            FuncInstanceInternal::Internal { .. } => {
145                let mut interpreter = Interpreter::new(func, args, None)?;
146                interpreter.start_execution(externals)
147            }
148            FuncInstanceInternal::Host {
149                ref host_func_index,
150                ..
151            } => externals.invoke_index(*host_func_index, args.into()),
152        }
153    }
154
155    /// Invoke this function using recycled stacks.
156    ///
157    /// # Errors
158    ///
159    /// Same as [`invoke`].
160    ///
161    /// [`invoke`]: #method.invoke
162    pub fn invoke_with_stack<E: Externals>(
163        func: &FuncRef,
164        args: &[RuntimeValue],
165        externals: &mut E,
166        stack_recycler: &mut StackRecycler,
167    ) -> Result<Option<RuntimeValue>, Trap> {
168        check_function_args(func.signature(), &args)?;
169        match *func.as_internal() {
170            FuncInstanceInternal::Internal { .. } => {
171                let mut interpreter = Interpreter::new(func, args, Some(stack_recycler))?;
172                let return_value = interpreter.start_execution(externals);
173                stack_recycler.recycle(interpreter);
174                return_value
175            }
176            FuncInstanceInternal::Host {
177                ref host_func_index,
178                ..
179            } => externals.invoke_index(*host_func_index, args.into()),
180        }
181    }
182
183    /// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a
184    /// Host trap happens, caller can use [`resume_execution`] to feed the expected return value back in, and then
185    /// continue the execution.
186    ///
187    /// This is an experimental API, and this functionality may not be available in other WebAssembly engines.
188    ///
189    /// # Errors
190    ///
191    /// Returns `Err` if `args` types is not match function [`signature`].
192    ///
193    /// [`signature`]: #method.signature
194    /// [`Trap`]: #enum.Trap.html
195    /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
196    /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
197    pub fn invoke_resumable<'args>(
198        func: &FuncRef,
199        args: impl Into<Cow<'args, [RuntimeValue]>>,
200    ) -> Result<FuncInvocation<'args>, Trap> {
201        let args = args.into();
202        check_function_args(func.signature(), &args)?;
203        match *func.as_internal() {
204            FuncInstanceInternal::Internal { .. } => {
205                let interpreter = Interpreter::new(func, &*args, None)?;
206                Ok(FuncInvocation {
207                    kind: FuncInvocationKind::Internal(interpreter),
208                })
209            }
210            FuncInstanceInternal::Host {
211                ref host_func_index,
212                ..
213            } => Ok(FuncInvocation {
214                kind: FuncInvocationKind::Host {
215                    args,
216                    host_func_index: *host_func_index,
217                    finished: false,
218                },
219            }),
220        }
221    }
222}
223
224/// A resumable invocation error.
225#[derive(Debug)]
226pub enum ResumableError {
227    /// Trap happened.
228    Trap(Trap),
229    /// The invocation is not resumable.
230    ///
231    /// Invocations are only resumable if a host function is called, and the host function returns a trap of `Host` kind. For other cases, this error will be returned. This includes:
232    /// - The invocation is directly a host function.
233    /// - The invocation has not been started.
234    /// - The invocation returns normally or returns any trap other than `Host` kind.
235    ///
236    /// This error is returned by [`resume_execution`].
237    ///
238    /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
239    NotResumable,
240    /// The invocation has already been started.
241    ///
242    /// This error is returned by [`start_execution`].
243    ///
244    /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
245    AlreadyStarted,
246}
247
248impl From<Trap> for ResumableError {
249    fn from(trap: Trap) -> Self {
250        ResumableError::Trap(trap)
251    }
252}
253
254/// A resumable invocation handle. This struct is returned by `FuncInstance::invoke_resumable`.
255pub struct FuncInvocation<'args> {
256    kind: FuncInvocationKind<'args>,
257}
258
259enum FuncInvocationKind<'args> {
260    Internal(Interpreter),
261    Host {
262        args: Cow<'args, [RuntimeValue]>,
263        host_func_index: usize,
264        finished: bool,
265    },
266}
267
268impl<'args> FuncInvocation<'args> {
269    /// Whether this invocation is currently resumable.
270    pub fn is_resumable(&self) -> bool {
271        match &self.kind {
272            &FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(),
273            &FuncInvocationKind::Host { .. } => false,
274        }
275    }
276
277    /// If the invocation is resumable, the expected return value type to be feed back in.
278    pub fn resumable_value_type(&self) -> Option<ValueType> {
279        match &self.kind {
280            &FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
281                &InterpreterState::Resumable(ref value_type) => value_type.clone(),
282                _ => None,
283            },
284            &FuncInvocationKind::Host { .. } => None,
285        }
286    }
287
288    /// Start the invocation execution.
289    pub fn start_execution<'externals, E: Externals + 'externals>(
290        &mut self,
291        externals: &'externals mut E,
292    ) -> Result<Option<RuntimeValue>, ResumableError> {
293        match self.kind {
294            FuncInvocationKind::Internal(ref mut interpreter) => {
295                if interpreter.state() != &InterpreterState::Initialized {
296                    return Err(ResumableError::AlreadyStarted);
297                }
298                Ok(interpreter.start_execution(externals)?)
299            }
300            FuncInvocationKind::Host {
301                ref args,
302                ref mut finished,
303                ref host_func_index,
304            } => {
305                if *finished {
306                    return Err(ResumableError::AlreadyStarted);
307                }
308                *finished = true;
309                Ok(externals.invoke_index(*host_func_index, args.as_ref().into())?)
310            }
311        }
312    }
313
314    /// Resume an execution if a previous trap of Host kind happened.
315    ///
316    /// `return_val` must be of the value type [`resumable_value_type`], defined by the host function import. Otherwise,
317    /// `UnexpectedSignature` trap will be returned. The current invocation must also be resumable
318    /// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned.
319    ///
320    /// [`resumable_value_type`]: #method.resumable_value_type
321    /// [`is_resumable`]: #method.is_resumable
322    pub fn resume_execution<'externals, E: Externals + 'externals>(
323        &mut self,
324        return_val: Option<RuntimeValue>,
325        externals: &'externals mut E,
326    ) -> Result<Option<RuntimeValue>, ResumableError> {
327        use crate::TrapKind;
328
329        if return_val.map(|v| v.value_type()) != self.resumable_value_type() {
330            return Err(ResumableError::Trap(Trap::new(
331                TrapKind::UnexpectedSignature,
332            )));
333        }
334
335        match &mut self.kind {
336            FuncInvocationKind::Internal(interpreter) => {
337                if interpreter.state().is_resumable() {
338                    Ok(interpreter.resume_execution(return_val, externals)?)
339                } else {
340                    Err(ResumableError::AlreadyStarted)
341                }
342            }
343            FuncInvocationKind::Host { .. } => Err(ResumableError::NotResumable),
344        }
345    }
346}
347
348#[derive(Clone, Debug)]
349pub struct FuncBody {
350    pub locals: Vec<Local>,
351    pub code: isa::Instructions,
352}