Skip to main content

wasmi/engine/executor/
mod.rs

1pub(crate) use self::stack::Stack;
2use self::{
3    instr_ptr::InstructionPtr,
4    instrs::{dispatch_host_func, execute_instrs},
5    stack::CallFrame,
6};
7use crate::{
8    engine::{
9        CallParams,
10        CallResults,
11        EngineInner,
12        ResumableCallBase,
13        ResumableCallHostTrap,
14        ResumableCallOutOfFuel,
15    },
16    func::HostFuncEntity,
17    ir::{Slot, SlotSpan},
18    store::CallHooks,
19    CallHook,
20    Error,
21    Func,
22    FuncEntity,
23    Store,
24    StoreContextMut,
25};
26
27use super::{code_map::CodeMap, ResumableError};
28
29mod cache;
30mod instr_ptr;
31mod instrs;
32mod stack;
33
34impl EngineInner {
35    /// Executes the given [`Func`] with the given `params` and returns the `results`.
36    ///
37    /// Uses the [`StoreContextMut`] for context information about the Wasm [`Store`].
38    ///
39    /// # Errors
40    ///
41    /// If the Wasm execution traps or runs out of resources.
42    pub fn execute_func<T, Results>(
43        &self,
44        ctx: StoreContextMut<T>,
45        func: &Func,
46        params: impl CallParams,
47        results: Results,
48    ) -> Result<<Results as CallResults>::Results, Error>
49    where
50        Results: CallResults,
51    {
52        let mut stack = self.stacks.lock().reuse_or_new();
53        let results = EngineExecutor::new(&self.code_map, &mut stack)
54            .execute_root_func(ctx.store, func, params, results)
55            .map_err(|error| match error.into_resumable() {
56                Ok(error) => error.into_error(),
57                Err(error) => error,
58            });
59        self.stacks.lock().recycle(stack);
60        results
61    }
62
63    /// Executes the given [`Func`] resumably with the given `params` and returns the `results`.
64    ///
65    /// Uses the [`StoreContextMut`] for context information about the Wasm [`Store`].
66    ///
67    /// # Errors
68    ///
69    /// If the Wasm execution traps or runs out of resources.
70    pub fn execute_func_resumable<T, Results>(
71        &self,
72        ctx: StoreContextMut<T>,
73        func: &Func,
74        params: impl CallParams,
75        results: Results,
76    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
77    where
78        Results: CallResults,
79    {
80        let store = ctx.store;
81        let mut stack = self.stacks.lock().reuse_or_new();
82        let results = EngineExecutor::new(&self.code_map, &mut stack)
83            .execute_root_func(store, func, params, results);
84        match results {
85            Ok(results) => {
86                self.stacks.lock().recycle(stack);
87                Ok(ResumableCallBase::Finished(results))
88            }
89            Err(error) => match error.into_resumable() {
90                Ok(ResumableError::HostTrap(error)) => {
91                    let host_func = *error.host_func();
92                    let caller_results = *error.caller_results();
93                    let host_error = error.into_error();
94                    Ok(ResumableCallBase::HostTrap(ResumableCallHostTrap::new(
95                        store.engine().clone(),
96                        *func,
97                        host_func,
98                        host_error,
99                        caller_results,
100                        stack,
101                    )))
102                }
103                Ok(ResumableError::OutOfFuel(error)) => {
104                    let required_fuel = error.required_fuel();
105                    Ok(ResumableCallBase::OutOfFuel(ResumableCallOutOfFuel::new(
106                        store.engine().clone(),
107                        *func,
108                        stack,
109                        required_fuel,
110                    )))
111                }
112                Err(error) => {
113                    self.stacks.lock().recycle(stack);
114                    Err(error)
115                }
116            },
117        }
118    }
119
120    /// Resumes the given [`Func`] with the given `params` and returns the `results`.
121    ///
122    /// Uses the [`StoreContextMut`] for context information about the Wasm [`Store`].
123    ///
124    /// # Errors
125    ///
126    /// If the Wasm execution traps or runs out of resources.
127    pub fn resume_func_host_trap<T, Results>(
128        &self,
129        ctx: StoreContextMut<T>,
130        mut invocation: ResumableCallHostTrap,
131        params: impl CallParams,
132        results: Results,
133    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
134    where
135        Results: CallResults,
136    {
137        let caller_results = invocation.caller_results();
138        let mut executor = EngineExecutor::new(&self.code_map, invocation.common.stack_mut());
139        let results = executor.resume_func_host_trap(ctx.store, params, caller_results, results);
140        match results {
141            Ok(results) => {
142                self.stacks.lock().recycle(invocation.common.take_stack());
143                Ok(ResumableCallBase::Finished(results))
144            }
145            Err(error) => match error.into_resumable() {
146                Ok(ResumableError::HostTrap(error)) => {
147                    let host_func = *error.host_func();
148                    let caller_results = *error.caller_results();
149                    invocation.update(host_func, error.into_error(), caller_results);
150                    Ok(ResumableCallBase::HostTrap(invocation))
151                }
152                Ok(ResumableError::OutOfFuel(error)) => {
153                    let required_fuel = error.required_fuel();
154                    let invocation = invocation.update_to_out_of_fuel(required_fuel);
155                    Ok(ResumableCallBase::OutOfFuel(invocation))
156                }
157                Err(error) => {
158                    self.stacks.lock().recycle(invocation.common.take_stack());
159                    Err(error)
160                }
161            },
162        }
163    }
164
165    /// Resumes the given [`Func`] after running out of fuel and returns the `results`.
166    ///
167    /// Uses the [`StoreContextMut`] for context information about the Wasm [`Store`].
168    ///
169    /// # Errors
170    ///
171    /// If the Wasm execution traps or runs out of resources.
172    pub fn resume_func_out_of_fuel<T, Results>(
173        &self,
174        ctx: StoreContextMut<T>,
175        mut invocation: ResumableCallOutOfFuel,
176        results: Results,
177    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
178    where
179        Results: CallResults,
180    {
181        let mut executor = EngineExecutor::new(&self.code_map, invocation.common.stack_mut());
182        let results = executor.resume_func_out_of_fuel(ctx.store, results);
183        match results {
184            Ok(results) => {
185                self.stacks.lock().recycle(invocation.common.take_stack());
186                Ok(ResumableCallBase::Finished(results))
187            }
188            Err(error) => match error.into_resumable() {
189                Ok(ResumableError::HostTrap(error)) => {
190                    let host_func = *error.host_func();
191                    let caller_results = *error.caller_results();
192                    let invocation = invocation.update_to_host_trap(
193                        host_func,
194                        error.into_error(),
195                        caller_results,
196                    );
197                    Ok(ResumableCallBase::HostTrap(invocation))
198                }
199                Ok(ResumableError::OutOfFuel(error)) => {
200                    invocation.update(error.required_fuel());
201                    Ok(ResumableCallBase::OutOfFuel(invocation))
202                }
203                Err(error) => {
204                    self.stacks.lock().recycle(invocation.common.take_stack());
205                    Err(error)
206                }
207            },
208        }
209    }
210}
211
212/// The internal state of the Wasmi engine.
213#[derive(Debug)]
214pub struct EngineExecutor<'engine> {
215    /// Shared and reusable generic engine resources.
216    code_map: &'engine CodeMap,
217    /// The value and call stacks.
218    stack: &'engine mut Stack,
219}
220
221/// Convenience function that does nothing to its `&mut` parameter.
222#[inline]
223fn do_nothing<T>(_: &mut T) {}
224
225impl<'engine> EngineExecutor<'engine> {
226    /// Creates a new [`EngineExecutor`] for the given [`Stack`].
227    fn new(code_map: &'engine CodeMap, stack: &'engine mut Stack) -> Self {
228        Self { code_map, stack }
229    }
230
231    /// Executes the given [`Func`] using the given `params`.
232    ///
233    /// Stores the execution result into `results` upon a successful execution.
234    ///
235    /// # Errors
236    ///
237    /// - If the given `params` do not match the expected parameters of `func`.
238    /// - If the given `results` do not match the length of the expected results of `func`.
239    /// - When encountering a Wasm or host trap during the execution of `func`.
240    fn execute_root_func<T, Results>(
241        &mut self,
242        store: &mut Store<T>,
243        func: &Func,
244        params: impl CallParams,
245        results: Results,
246    ) -> Result<<Results as CallResults>::Results, Error>
247    where
248        Results: CallResults,
249    {
250        self.stack.reset();
251        match store.inner.resolve_func(func) {
252            FuncEntity::Wasm(wasm_func) => {
253                // We reserve space on the stack to write the results of the root function execution.
254                let len_results = results.len_results();
255                self.stack.values.extend_by(len_results, do_nothing)?;
256                let instance = *wasm_func.instance();
257                let engine_func = wasm_func.func_body();
258                let compiled_func = self
259                    .code_map
260                    .get(Some(store.inner.fuel_mut()), engine_func)?;
261                let (mut uninit_params, offsets) = self
262                    .stack
263                    .values
264                    .alloc_call_frame(compiled_func, do_nothing)?;
265                for value in params.call_params() {
266                    unsafe { uninit_params.init_next(value) };
267                }
268                uninit_params.init_zeroes();
269                self.stack.calls.push(
270                    CallFrame::new(
271                        InstructionPtr::new(compiled_func.instrs().as_ptr()),
272                        offsets,
273                        SlotSpan::new(Slot::from(0)),
274                    ),
275                    Some(instance),
276                )?;
277                store.invoke_call_hook(CallHook::CallingWasm)?;
278                self.execute_func(store)?;
279                store.invoke_call_hook(CallHook::ReturningFromWasm)?;
280            }
281            FuncEntity::Host(host_func) => {
282                // The host function signature is required for properly
283                // adjusting, inspecting and manipulating the value stack.
284                // In case the host function returns more values than it takes
285                // we are required to extend the value stack.
286                let len_params = host_func.len_params();
287                let len_results = host_func.len_results();
288                let max_inout = len_params.max(len_results);
289                let uninit = self
290                    .stack
291                    .values
292                    .extend_by(usize::from(max_inout), do_nothing)?;
293                for (uninit, param) in uninit.iter_mut().zip(params.call_params()) {
294                    uninit.write(param);
295                }
296                let host_func = *host_func;
297                self.dispatch_host_func(store, host_func)?;
298            }
299        };
300        let results = self.write_results_back(results);
301        Ok(results)
302    }
303
304    /// Resumes the execution of the given [`Func`] using `params` after a host function trapped.
305    ///
306    /// Stores the execution result into `results` upon a successful execution.
307    ///
308    /// # Errors
309    ///
310    /// - If the given `params` do not match the expected parameters of `func`.
311    /// - If the given `results` do not match the length of the expected results of `func`.
312    /// - When encountering a Wasm or host trap during the execution of `func`.
313    fn resume_func_host_trap<T, Results>(
314        &mut self,
315        store: &mut Store<T>,
316        params: impl CallParams,
317        caller_results: SlotSpan,
318        results: Results,
319    ) -> Result<<Results as CallResults>::Results, Error>
320    where
321        Results: CallResults,
322    {
323        let caller = self
324            .stack
325            .calls
326            .peek()
327            .expect("must have caller call frame on stack upon function resumption");
328        let mut caller_sp = unsafe { self.stack.values.stack_ptr_at(caller.base_offset()) };
329        let call_params = params.call_params();
330        let len_params = call_params.len();
331        for (result, param) in caller_results.iter_sized(len_params).zip(call_params) {
332            unsafe { caller_sp.set(result, param) };
333        }
334        self.execute_func(store)?;
335        let results = self.write_results_back(results);
336        Ok(results)
337    }
338
339    /// Resumes the execution of the given [`Func`] using `params` after running out of fuel.
340    ///
341    /// Stores the execution result into `results` upon a successful execution.
342    ///
343    /// # Errors
344    ///
345    /// - If the given `results` do not match the length of the expected results of `func`.
346    /// - When encountering a Wasm or host trap during the execution of `func`.
347    fn resume_func_out_of_fuel<T, Results>(
348        &mut self,
349        store: &mut Store<T>,
350        results: Results,
351    ) -> Result<<Results as CallResults>::Results, Error>
352    where
353        Results: CallResults,
354    {
355        self.execute_func(store)?;
356        let results = self.write_results_back(results);
357        Ok(results)
358    }
359
360    /// Executes the top most Wasm function on the [`Stack`] until the [`Stack`] is empty.
361    ///
362    /// # Errors
363    ///
364    /// When encountering a Wasm or host trap during execution.
365    #[inline(always)]
366    fn execute_func<T>(&mut self, store: &mut Store<T>) -> Result<(), Error> {
367        execute_instrs(store.prune(), self.stack, self.code_map)
368    }
369
370    /// Convenience forwarder to [`dispatch_host_func`].
371    #[inline(always)]
372    fn dispatch_host_func<T>(
373        &mut self,
374        store: &mut Store<T>,
375        host_func: HostFuncEntity,
376    ) -> Result<(), Error> {
377        dispatch_host_func(
378            store.prune(),
379            &mut self.stack.values,
380            host_func,
381            None,
382            CallHooks::Ignore,
383        )?;
384        Ok(())
385    }
386
387    /// Writes the results of the function execution back into the `results` buffer.
388    ///
389    /// # Note
390    ///
391    /// The value stack is empty after this operation.
392    ///
393    /// # Panics
394    ///
395    /// - If the `results` buffer length does not match the remaining amount of stack values.
396    #[inline(always)]
397    fn write_results_back<Results>(&mut self, results: Results) -> <Results as CallResults>::Results
398    where
399        Results: CallResults,
400    {
401        let len_results = results.len_results();
402        results.call_results(&self.stack.values.as_slice()[..len_results])
403    }
404}