casper_wasmi/
func.rs

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