smoldot/executor/
vm.rs

1// Smoldot
2// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18//! General-purpose WebAssembly virtual machine.
19//!
20//! Contains code related to running a WebAssembly virtual machine. Contrary to
21//! (`HostVm`)[`super::host::HostVm`], this module isn't aware of any of the host
22//! functions available to Substrate runtimes. It only contains the code required to run a virtual
23//! machine, with some adjustments explained below.
24//!
25//! # Usage
26//!
27//! Call [`VirtualMachinePrototype::new`] in order to parse and/or compile some WebAssembly code.
28//! One of the parameters of this function is a function that is passed the name of functions
29//! imported by the Wasm code, and must return an opaque `usize`. This `usize` doesn't have any
30//! meaning, but will later be passed back to the user through [`ExecOutcome::Interrupted::id`]
31//! when the corresponding function is called.
32//!
33//! Use [`VirtualMachinePrototype::prepare`] then [`Prepare::start`] in order to start executing
34//! a function exported through an `(export)` statement.
35//!
36//! Call [`VirtualMachine::run`] on the [`VirtualMachine`] returned by `start` in order to run the
37//! WebAssembly code. The `run` method returns either if the function being called returns, or if
38//! the WebAssembly code calls a host function. In the latter case, [`ExecOutcome::Interrupted`]
39//! is returned and the virtual machine is now paused. Once the logic of the host function has
40//! been executed, call `run` again, passing the return value of that host function.
41//!
42//! # About imported vs exported memory
43//!
44//! WebAssembly supports, in theory, addressing multiple different memory objects. The WebAssembly
45//! module can declare memory in two ways:
46//!
47//! - Either by exporting a memory object in the `(export)` section under the name `memory`.
48//! - Or by importing a memory object in its `(import)` section.
49//!
50//! The virtual machine in this module supports both variants. However, no more than one memory
51//! object can be exported or imported, and it is illegal to not use any memory.
52//!
53//! The first variant used to be the default model when compiling to WebAssembly, but the second
54//! variant (importing memory objects) is preferred nowadays.
55//!
56//! # Wasm features
57//!
58//! The WebAssembly specification is a moving one. The specification as it was when it launched
59//! in 2017 is commonly referred to as "the MVP" (minimum viable product). Since then, various
60//! extensions have been added to the WebAssembly format.
61//!
62//! The code in this module, however, doesn't allow any of the feature that were added post-MVP.
63//! Trying to use WebAssembly code that uses one of these features will result in an error.
64//!
65
66mod interpreter;
67
68// This list of targets matches the one in the `Cargo.toml` file.
69#[cfg(all(
70    any(
71        all(
72            target_arch = "x86_64",
73            any(
74                target_os = "windows",
75                all(target_os = "linux", target_env = "gnu"),
76                target_os = "macos"
77            )
78        ),
79        all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
80        all(target_arch = "s390x", target_os = "linux", target_env = "gnu")
81    ),
82    feature = "wasmtime"
83))]
84mod jit;
85
86mod tests;
87
88use alloc::{string::String, vec::Vec};
89use core::{fmt, iter};
90use smallvec::SmallVec;
91
92/// Configuration to pass to [`VirtualMachinePrototype::new`].
93pub struct Config<'a> {
94    /// Encoded wasm bytecode.
95    pub module_bytes: &'a [u8],
96
97    /// Hint about how to execute the WebAssembly code.
98    pub exec_hint: ExecHint,
99
100    /// Called for each import that the module has. It must assign a number to each import, or
101    /// return an error if the import can't be resolved. When the VM calls one of these functions,
102    /// this number will be returned back in order for the user to know how to handle the call.
103    pub symbols: &'a mut dyn FnMut(&str, &str, &Signature) -> Result<usize, ()>,
104}
105
106/// Virtual machine ready to start executing a function.
107///
108/// > **Note**: This struct implements `Clone`. Cloning a [`VirtualMachinePrototype`] allocates
109/// >           memory necessary for the clone to run.
110#[derive(Clone)]
111pub struct VirtualMachinePrototype {
112    inner: VirtualMachinePrototypeInner,
113}
114
115#[derive(Clone)]
116enum VirtualMachinePrototypeInner {
117    #[cfg(all(
118        any(
119            all(
120                target_arch = "x86_64",
121                any(
122                    target_os = "windows",
123                    all(target_os = "linux", target_env = "gnu"),
124                    target_os = "macos"
125                )
126            ),
127            all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
128            all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
129        ),
130        feature = "wasmtime"
131    ))]
132    Jit(jit::JitPrototype),
133    Interpreter(interpreter::InterpreterPrototype),
134}
135
136impl VirtualMachinePrototype {
137    /// Creates a new process state machine from the given module. This method notably allocates
138    /// the memory necessary for the virtual machine to run.
139    ///
140    ///
141    /// See [the module-level documentation](..) for an explanation of the parameters.
142    pub fn new(config: Config) -> Result<Self, NewErr> {
143        Ok(VirtualMachinePrototype {
144            inner: match config.exec_hint {
145                #[cfg(all(
146                    any(
147                        all(
148                            target_arch = "x86_64",
149                            any(
150                                target_os = "windows",
151                                all(target_os = "linux", target_env = "gnu"),
152                                target_os = "macos"
153                            )
154                        ),
155                        all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
156                        all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
157                    ),
158                    feature = "wasmtime"
159                ))]
160                ExecHint::ValidateAndCompile => VirtualMachinePrototypeInner::Jit(
161                    jit::JitPrototype::new(config.module_bytes, config.symbols)?,
162                ),
163                #[cfg(not(all(
164                    any(
165                        all(
166                            target_arch = "x86_64",
167                            any(
168                                target_os = "windows",
169                                all(target_os = "linux", target_env = "gnu"),
170                                target_os = "macos"
171                            )
172                        ),
173                        all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
174                        all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
175                    ),
176                    feature = "wasmtime"
177                )))]
178                ExecHint::ValidateAndCompile => VirtualMachinePrototypeInner::Interpreter(
179                    interpreter::InterpreterPrototype::new(
180                        config.module_bytes,
181                        interpreter::CompilationMode::Eager,
182                        config.symbols,
183                    )?,
184                ),
185                ExecHint::ValidateAndExecuteOnce | ExecHint::Untrusted => {
186                    VirtualMachinePrototypeInner::Interpreter(
187                        interpreter::InterpreterPrototype::new(
188                            config.module_bytes,
189                            interpreter::CompilationMode::Eager,
190                            config.symbols,
191                        )?,
192                    )
193                }
194                ExecHint::CompileWithNonDeterministicValidation
195                | ExecHint::ExecuteOnceWithNonDeterministicValidation => {
196                    VirtualMachinePrototypeInner::Interpreter(
197                        interpreter::InterpreterPrototype::new(
198                            config.module_bytes,
199                            interpreter::CompilationMode::Lazy,
200                            config.symbols,
201                        )?,
202                    )
203                }
204                ExecHint::ForceWasmi { lazy_validation } => {
205                    VirtualMachinePrototypeInner::Interpreter(
206                        interpreter::InterpreterPrototype::new(
207                            config.module_bytes,
208                            if lazy_validation {
209                                interpreter::CompilationMode::Lazy
210                            } else {
211                                interpreter::CompilationMode::Eager
212                            },
213                            config.symbols,
214                        )?,
215                    )
216                }
217
218                #[cfg(all(
219                    any(
220                        all(
221                            target_arch = "x86_64",
222                            any(
223                                target_os = "windows",
224                                all(target_os = "linux", target_env = "gnu"),
225                                target_os = "macos"
226                            )
227                        ),
228                        all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
229                        all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
230                    ),
231                    feature = "wasmtime"
232                ))]
233                ExecHint::ForceWasmtime => VirtualMachinePrototypeInner::Jit(
234                    jit::JitPrototype::new(config.module_bytes, config.symbols)?,
235                ),
236            },
237        })
238    }
239
240    /// Returns the value of a global that the module exports.
241    ///
242    /// The global variable must be a `i32`, otherwise an error is returned. Negative values are
243    /// silently reinterpreted as an unsigned integer.
244    pub fn global_value(&mut self, name: &str) -> Result<u32, GlobalValueErr> {
245        match &mut self.inner {
246            #[cfg(all(
247                any(
248                    all(
249                        target_arch = "x86_64",
250                        any(
251                            target_os = "windows",
252                            all(target_os = "linux", target_env = "gnu"),
253                            target_os = "macos"
254                        )
255                    ),
256                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
257                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
258                ),
259                feature = "wasmtime"
260            ))]
261            VirtualMachinePrototypeInner::Jit(inner) => inner.global_value(name),
262            VirtualMachinePrototypeInner::Interpreter(inner) => inner.global_value(name),
263        }
264    }
265
266    /// Returns the maximum number of pages that the memory can have.
267    ///
268    /// `None` if there is no limit.
269    pub fn memory_max_pages(&self) -> Option<HeapPages> {
270        match &self.inner {
271            #[cfg(all(
272                any(
273                    all(
274                        target_arch = "x86_64",
275                        any(
276                            target_os = "windows",
277                            all(target_os = "linux", target_env = "gnu"),
278                            target_os = "macos"
279                        )
280                    ),
281                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
282                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
283                ),
284                feature = "wasmtime"
285            ))]
286            VirtualMachinePrototypeInner::Jit(inner) => inner.memory_max_pages(),
287            VirtualMachinePrototypeInner::Interpreter(inner) => inner.memory_max_pages(),
288        }
289    }
290
291    /// Prepares the prototype for running a function.
292    ///
293    /// This preliminary step is necessary as it allows reading and writing memory before starting
294    /// the actual execution..
295    pub fn prepare(self) -> Prepare {
296        match self.inner {
297            #[cfg(all(
298                any(
299                    all(
300                        target_arch = "x86_64",
301                        any(
302                            target_os = "windows",
303                            all(target_os = "linux", target_env = "gnu"),
304                            target_os = "macos"
305                        )
306                    ),
307                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
308                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
309                ),
310                feature = "wasmtime"
311            ))]
312            VirtualMachinePrototypeInner::Jit(inner) => Prepare {
313                inner: PrepareInner::Jit(inner.prepare()),
314            },
315            VirtualMachinePrototypeInner::Interpreter(inner) => Prepare {
316                inner: PrepareInner::Interpreter(inner.prepare()),
317            },
318        }
319    }
320}
321
322impl fmt::Debug for VirtualMachinePrototype {
323    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
324        match &self.inner {
325            #[cfg(all(
326                any(
327                    all(
328                        target_arch = "x86_64",
329                        any(
330                            target_os = "windows",
331                            all(target_os = "linux", target_env = "gnu"),
332                            target_os = "macos"
333                        )
334                    ),
335                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
336                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
337                ),
338                feature = "wasmtime"
339            ))]
340            VirtualMachinePrototypeInner::Jit(inner) => fmt::Debug::fmt(inner, f),
341            VirtualMachinePrototypeInner::Interpreter(inner) => fmt::Debug::fmt(inner, f),
342        }
343    }
344}
345
346pub struct Prepare {
347    inner: PrepareInner,
348}
349
350enum PrepareInner {
351    #[cfg(all(
352        any(
353            all(
354                target_arch = "x86_64",
355                any(
356                    target_os = "windows",
357                    all(target_os = "linux", target_env = "gnu"),
358                    target_os = "macos"
359                )
360            ),
361            all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
362            all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
363        ),
364        feature = "wasmtime"
365    ))]
366    Jit(jit::Prepare),
367    Interpreter(interpreter::Prepare),
368}
369
370impl Prepare {
371    /// Turns back this virtual machine into a prototype.
372    pub fn into_prototype(self) -> VirtualMachinePrototype {
373        VirtualMachinePrototype {
374            inner: match self.inner {
375                #[cfg(all(
376                    any(
377                        all(
378                            target_arch = "x86_64",
379                            any(
380                                target_os = "windows",
381                                all(target_os = "linux", target_env = "gnu"),
382                                target_os = "macos"
383                            )
384                        ),
385                        all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
386                        all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
387                    ),
388                    feature = "wasmtime"
389                ))]
390                PrepareInner::Jit(inner) => {
391                    VirtualMachinePrototypeInner::Jit(inner.into_prototype())
392                }
393                PrepareInner::Interpreter(inner) => {
394                    VirtualMachinePrototypeInner::Interpreter(inner.into_prototype())
395                }
396            },
397        }
398    }
399
400    /// Returns the size of the memory, in bytes.
401    pub fn memory_size(&self) -> HeapPages {
402        match &self.inner {
403            #[cfg(all(
404                any(
405                    all(
406                        target_arch = "x86_64",
407                        any(
408                            target_os = "windows",
409                            all(target_os = "linux", target_env = "gnu"),
410                            target_os = "macos"
411                        )
412                    ),
413                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
414                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
415                ),
416                feature = "wasmtime"
417            ))]
418            PrepareInner::Jit(inner) => inner.memory_size(),
419            PrepareInner::Interpreter(inner) => inner.memory_size(),
420        }
421    }
422
423    /// Copies the given memory range into a `Vec<u8>`.
424    ///
425    /// Returns an error if the range is invalid or out of range.
426    pub fn read_memory(
427        &self,
428        offset: u32,
429        size: u32,
430    ) -> Result<impl AsRef<[u8]>, OutOfBoundsError> {
431        Ok(match &self.inner {
432            #[cfg(all(
433                any(
434                    all(
435                        target_arch = "x86_64",
436                        any(
437                            target_os = "windows",
438                            all(target_os = "linux", target_env = "gnu"),
439                            target_os = "macos"
440                        )
441                    ),
442                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
443                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
444                ),
445                feature = "wasmtime"
446            ))]
447            PrepareInner::Jit(inner) => either::Left(inner.read_memory(offset, size)?),
448            #[cfg(all(
449                any(
450                    all(
451                        target_arch = "x86_64",
452                        any(
453                            target_os = "windows",
454                            all(target_os = "linux", target_env = "gnu"),
455                            target_os = "macos"
456                        )
457                    ),
458                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
459                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
460                ),
461                feature = "wasmtime"
462            ))]
463            PrepareInner::Interpreter(inner) => either::Right(inner.read_memory(offset, size)?),
464            #[cfg(not(all(
465                any(
466                    all(
467                        target_arch = "x86_64",
468                        any(
469                            target_os = "windows",
470                            all(target_os = "linux", target_env = "gnu"),
471                            target_os = "macos"
472                        )
473                    ),
474                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
475                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
476                ),
477                feature = "wasmtime"
478            )))]
479            PrepareInner::Interpreter(inner) => inner.read_memory(offset, size)?,
480        })
481    }
482
483    /// Write the data at the given memory location.
484    ///
485    /// Returns an error if the range is invalid or out of range.
486    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
487        match &mut self.inner {
488            #[cfg(all(
489                any(
490                    all(
491                        target_arch = "x86_64",
492                        any(
493                            target_os = "windows",
494                            all(target_os = "linux", target_env = "gnu"),
495                            target_os = "macos"
496                        )
497                    ),
498                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
499                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
500                ),
501                feature = "wasmtime"
502            ))]
503            PrepareInner::Jit(inner) => inner.write_memory(offset, value),
504            PrepareInner::Interpreter(inner) => inner.write_memory(offset, value),
505        }
506    }
507
508    /// Increases the size of the memory by the given number of pages.
509    ///
510    /// Returns an error if the size of the memory can't be expanded more. This can be known ahead
511    /// of time by using [`VirtualMachinePrototype::memory_max_pages`].
512    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
513        match &mut self.inner {
514            #[cfg(all(
515                any(
516                    all(
517                        target_arch = "x86_64",
518                        any(
519                            target_os = "windows",
520                            all(target_os = "linux", target_env = "gnu"),
521                            target_os = "macos"
522                        )
523                    ),
524                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
525                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
526                ),
527                feature = "wasmtime"
528            ))]
529            PrepareInner::Jit(inner) => inner.grow_memory(additional),
530            PrepareInner::Interpreter(inner) => inner.grow_memory(additional),
531        }
532    }
533
534    /// Turns this prototype into an actual virtual machine. This requires choosing which function
535    /// to execute.
536    pub fn start(
537        self,
538        function_name: &str,
539        params: &[WasmValue],
540    ) -> Result<VirtualMachine, (StartErr, VirtualMachinePrototype)> {
541        Ok(VirtualMachine {
542            inner: match self.inner {
543                #[cfg(all(
544                    any(
545                        all(
546                            target_arch = "x86_64",
547                            any(
548                                target_os = "windows",
549                                all(target_os = "linux", target_env = "gnu"),
550                                target_os = "macos"
551                            )
552                        ),
553                        all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
554                        all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
555                    ),
556                    feature = "wasmtime"
557                ))]
558                PrepareInner::Jit(inner) => match inner.start(function_name, params) {
559                    Ok(vm) => VirtualMachineInner::Jit(vm),
560                    Err((err, proto)) => {
561                        return Err((
562                            err,
563                            VirtualMachinePrototype {
564                                inner: VirtualMachinePrototypeInner::Jit(proto),
565                            },
566                        ));
567                    }
568                },
569                PrepareInner::Interpreter(inner) => match inner.start(function_name, params) {
570                    Ok(vm) => VirtualMachineInner::Interpreter(vm),
571                    Err((err, proto)) => {
572                        return Err((
573                            err,
574                            VirtualMachinePrototype {
575                                inner: VirtualMachinePrototypeInner::Interpreter(proto),
576                            },
577                        ));
578                    }
579                },
580            },
581        })
582    }
583}
584
585impl fmt::Debug for Prepare {
586    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
587        match &self.inner {
588            #[cfg(all(
589                any(
590                    all(
591                        target_arch = "x86_64",
592                        any(
593                            target_os = "windows",
594                            all(target_os = "linux", target_env = "gnu"),
595                            target_os = "macos"
596                        )
597                    ),
598                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
599                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
600                ),
601                feature = "wasmtime"
602            ))]
603            PrepareInner::Jit(inner) => fmt::Debug::fmt(inner, f),
604            PrepareInner::Interpreter(inner) => fmt::Debug::fmt(inner, f),
605        }
606    }
607}
608
609pub struct VirtualMachine {
610    inner: VirtualMachineInner,
611}
612
613enum VirtualMachineInner {
614    #[cfg(all(
615        any(
616            all(
617                target_arch = "x86_64",
618                any(
619                    target_os = "windows",
620                    all(target_os = "linux", target_env = "gnu"),
621                    target_os = "macos"
622                )
623            ),
624            all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
625            all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
626        ),
627        feature = "wasmtime"
628    ))]
629    Jit(jit::Jit),
630    Interpreter(interpreter::Interpreter),
631}
632
633impl VirtualMachine {
634    /// Starts or continues execution of this thread.
635    ///
636    /// If this is the first call you call [`run`](VirtualMachine::run) for this thread, then you
637    /// must pass a value of `None`.
638    /// If, however, you call this function after a previous call to [`run`](VirtualMachine::run)
639    /// that was interrupted by a host function call, then you must pass back the outcome of
640    /// that call.
641    pub fn run(&mut self, value: Option<WasmValue>) -> Result<ExecOutcome, RunErr> {
642        match &mut self.inner {
643            #[cfg(all(
644                any(
645                    all(
646                        target_arch = "x86_64",
647                        any(
648                            target_os = "windows",
649                            all(target_os = "linux", target_env = "gnu"),
650                            target_os = "macos"
651                        )
652                    ),
653                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
654                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
655                ),
656                feature = "wasmtime"
657            ))]
658            VirtualMachineInner::Jit(inner) => inner.run(value),
659            VirtualMachineInner::Interpreter(inner) => inner.run(value),
660        }
661    }
662
663    /// Returns the size of the memory, in bytes.
664    ///
665    /// > **Note**: This can change over time if the Wasm code uses the `grow` opcode.
666    pub fn memory_size(&self) -> HeapPages {
667        match &self.inner {
668            #[cfg(all(
669                any(
670                    all(
671                        target_arch = "x86_64",
672                        any(
673                            target_os = "windows",
674                            all(target_os = "linux", target_env = "gnu"),
675                            target_os = "macos"
676                        )
677                    ),
678                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
679                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
680                ),
681                feature = "wasmtime"
682            ))]
683            VirtualMachineInner::Jit(inner) => inner.memory_size(),
684            VirtualMachineInner::Interpreter(inner) => inner.memory_size(),
685        }
686    }
687
688    /// Copies the given memory range into a `Vec<u8>`.
689    ///
690    /// Returns an error if the range is invalid or out of range.
691    pub fn read_memory(
692        &self,
693        offset: u32,
694        size: u32,
695    ) -> Result<impl AsRef<[u8]>, OutOfBoundsError> {
696        Ok(match &self.inner {
697            #[cfg(all(
698                any(
699                    all(
700                        target_arch = "x86_64",
701                        any(
702                            target_os = "windows",
703                            all(target_os = "linux", target_env = "gnu"),
704                            target_os = "macos"
705                        )
706                    ),
707                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
708                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
709                ),
710                feature = "wasmtime"
711            ))]
712            VirtualMachineInner::Jit(inner) => either::Left(inner.read_memory(offset, size)?),
713            #[cfg(all(
714                any(
715                    all(
716                        target_arch = "x86_64",
717                        any(
718                            target_os = "windows",
719                            all(target_os = "linux", target_env = "gnu"),
720                            target_os = "macos"
721                        )
722                    ),
723                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
724                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
725                ),
726                feature = "wasmtime"
727            ))]
728            VirtualMachineInner::Interpreter(inner) => {
729                either::Right(inner.read_memory(offset, size)?)
730            }
731            #[cfg(not(all(
732                any(
733                    all(
734                        target_arch = "x86_64",
735                        any(
736                            target_os = "windows",
737                            all(target_os = "linux", target_env = "gnu"),
738                            target_os = "macos"
739                        )
740                    ),
741                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
742                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
743                ),
744                feature = "wasmtime"
745            )))]
746            VirtualMachineInner::Interpreter(inner) => inner.read_memory(offset, size)?,
747        })
748    }
749
750    /// Write the data at the given memory location.
751    ///
752    /// Returns an error if the range is invalid or out of range.
753    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
754        match &mut self.inner {
755            #[cfg(all(
756                any(
757                    all(
758                        target_arch = "x86_64",
759                        any(
760                            target_os = "windows",
761                            all(target_os = "linux", target_env = "gnu"),
762                            target_os = "macos"
763                        )
764                    ),
765                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
766                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
767                ),
768                feature = "wasmtime"
769            ))]
770            VirtualMachineInner::Jit(inner) => inner.write_memory(offset, value),
771            VirtualMachineInner::Interpreter(inner) => inner.write_memory(offset, value),
772        }
773    }
774
775    /// Increases the size of the memory by the given number of pages.
776    ///
777    /// Returns an error if the size of the memory can't be expanded more. This can be known ahead
778    /// of time by using [`VirtualMachinePrototype::memory_max_pages`].
779    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
780        match &mut self.inner {
781            #[cfg(all(
782                any(
783                    all(
784                        target_arch = "x86_64",
785                        any(
786                            target_os = "windows",
787                            all(target_os = "linux", target_env = "gnu"),
788                            target_os = "macos"
789                        )
790                    ),
791                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
792                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
793                ),
794                feature = "wasmtime"
795            ))]
796            VirtualMachineInner::Jit(inner) => inner.grow_memory(additional),
797            VirtualMachineInner::Interpreter(inner) => inner.grow_memory(additional),
798        }
799    }
800
801    /// Turns back this virtual machine into a prototype.
802    pub fn into_prototype(self) -> VirtualMachinePrototype {
803        VirtualMachinePrototype {
804            inner: match self.inner {
805                #[cfg(all(
806                    any(
807                        all(
808                            target_arch = "x86_64",
809                            any(
810                                target_os = "windows",
811                                all(target_os = "linux", target_env = "gnu"),
812                                target_os = "macos"
813                            )
814                        ),
815                        all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
816                        all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
817                    ),
818                    feature = "wasmtime"
819                ))]
820                VirtualMachineInner::Jit(inner) => {
821                    VirtualMachinePrototypeInner::Jit(inner.into_prototype())
822                }
823                VirtualMachineInner::Interpreter(inner) => {
824                    VirtualMachinePrototypeInner::Interpreter(inner.into_prototype())
825                }
826            },
827        }
828    }
829}
830
831impl fmt::Debug for VirtualMachine {
832    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
833        match &self.inner {
834            #[cfg(all(
835                any(
836                    all(
837                        target_arch = "x86_64",
838                        any(
839                            target_os = "windows",
840                            all(target_os = "linux", target_env = "gnu"),
841                            target_os = "macos"
842                        )
843                    ),
844                    all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
845                    all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
846                ),
847                feature = "wasmtime"
848            ))]
849            VirtualMachineInner::Jit(inner) => fmt::Debug::fmt(inner, f),
850            VirtualMachineInner::Interpreter(inner) => fmt::Debug::fmt(inner, f),
851        }
852    }
853}
854
855/// Hint used by the implementation to decide which kind of virtual machine to use.
856#[derive(Debug, Copy, Clone, PartialEq, Eq)]
857pub enum ExecHint {
858    /// The WebAssembly code will be instantiated once and run many times.
859    /// If possible, compile this WebAssembly code ahead of time.
860    ValidateAndCompile,
861
862    /// The WebAssembly code will be instantiated once and run many times.
863    /// Contrary to [`ExecHint::ValidateAndCompile`], the WebAssembly code isn't fully validated
864    /// ahead of time, meaning that invalid WebAssembly modules might successfully be compiled,
865    /// which is an indesirable property in some contexts.
866    CompileWithNonDeterministicValidation,
867
868    /// The WebAssembly code is expected to be only run once.
869    ///
870    /// > **Note**: This isn't a hard requirement but a hint.
871    ValidateAndExecuteOnce,
872
873    /// The WebAssembly code will be instantiated once and run many times.
874    /// Contrary to [`ExecHint::ValidateAndExecuteOnce`], the WebAssembly code isn't fully
875    /// validated ahead of time, meaning that invalid WebAssembly modules might successfully be
876    /// compiled, which is an indesirable property in some contexts.
877    ExecuteOnceWithNonDeterministicValidation,
878
879    /// The WebAssembly code running through this VM is untrusted.
880    Untrusted,
881
882    /// Forces using the `wasmi` backend.
883    ///
884    /// This variant is useful for testing purposes.
885    ForceWasmi {
886        /// If `true`, lazy validation is enabled. This leads to a faster initialization time,
887        /// but can also successfully validate invalid modules, which is an indesirable property
888        /// in some contexts.
889        lazy_validation: bool,
890    },
891    /// Forces using the `wasmtime` backend.
892    ///
893    /// This variant is useful for testing purposes.
894    #[cfg(all(
895        any(
896            all(
897                target_arch = "x86_64",
898                any(
899                    target_os = "windows",
900                    all(target_os = "linux", target_env = "gnu"),
901                    target_os = "macos"
902                )
903            ),
904            all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
905            all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
906        ),
907        feature = "wasmtime"
908    ))]
909    ForceWasmtime,
910}
911
912impl ExecHint {
913    /// Returns an iterator of all the [`ExecHint`] values corresponding to execution engines.
914    ///
915    /// > **Note**: This function is most useful for testing purposes.
916    pub fn available_engines() -> impl Iterator<Item = ExecHint> {
917        iter::once(ExecHint::ForceWasmi {
918            lazy_validation: false,
919        })
920        .chain(Self::force_wasmtime_if_available())
921    }
922
923    /// Returns `ForceWasmtime` if it is available on the current platform, and `None` otherwise.
924    pub fn force_wasmtime_if_available() -> Option<ExecHint> {
925        #[cfg(all(
926            any(
927                all(
928                    target_arch = "x86_64",
929                    any(
930                        target_os = "windows",
931                        all(target_os = "linux", target_env = "gnu"),
932                        target_os = "macos"
933                    )
934                ),
935                all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
936                all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
937            ),
938            feature = "wasmtime"
939        ))]
940        fn value() -> Option<ExecHint> {
941            Some(ExecHint::ForceWasmtime)
942        }
943        #[cfg(not(all(
944            any(
945                all(
946                    target_arch = "x86_64",
947                    any(
948                        target_os = "windows",
949                        all(target_os = "linux", target_env = "gnu"),
950                        target_os = "macos"
951                    )
952                ),
953                all(target_arch = "aarch64", all(target_os = "linux", target_env = "gnu")),
954                all(target_arch = "s390x", all(target_os = "linux", target_env = "gnu"))
955            ),
956            feature = "wasmtime"
957        )))]
958        fn value() -> Option<ExecHint> {
959            None
960        }
961        value()
962    }
963}
964
965/// Number of heap pages available to the Wasm code.
966///
967/// Each page is `64kiB`.
968#[derive(
969    Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Add, derive_more::Sub,
970)]
971pub struct HeapPages(u32);
972
973impl HeapPages {
974    pub const fn new(v: u32) -> Self {
975        HeapPages(v)
976    }
977}
978
979impl From<u32> for HeapPages {
980    fn from(v: u32) -> Self {
981        HeapPages(v)
982    }
983}
984
985impl From<HeapPages> for u32 {
986    fn from(v: HeapPages) -> Self {
987        v.0
988    }
989}
990
991/// Low-level Wasm function signature.
992#[derive(Debug, Clone, PartialEq, Eq, Hash)]
993pub struct Signature {
994    params: SmallVec<[ValueType; 8]>,
995    ret_ty: Option<ValueType>,
996}
997
998// TODO: figure out how to optimize so that we can use this macro in a const context
999#[macro_export]
1000macro_rules! signature {
1001    (($($param:expr),* $(,)?) => ()) => {
1002        $crate::executor::vm::Signature::from_components(smallvec::smallvec!($($param),*), None)
1003    };
1004    (($($param:expr),* $(,)?) => $ret:expr) => {
1005        $crate::executor::vm::Signature::from_components(smallvec::smallvec!($($param),*), Some($ret))
1006    };
1007}
1008
1009impl Signature {
1010    /// Creates a [`Signature`] from the given parameter types and return type.
1011    pub fn new(
1012        params: impl Iterator<Item = ValueType>,
1013        ret_ty: impl Into<Option<ValueType>>,
1014    ) -> Signature {
1015        Signature {
1016            params: params.collect(),
1017            ret_ty: ret_ty.into(),
1018        }
1019    }
1020
1021    // TODO: find a way to remove? it is used only by the signature! macro
1022    #[doc(hidden)]
1023    pub(crate) fn from_components(
1024        params: SmallVec<[ValueType; 8]>,
1025        ret_ty: Option<ValueType>,
1026    ) -> Self {
1027        Signature { params, ret_ty }
1028    }
1029
1030    /// Returns a list of all the types of the parameters.
1031    pub fn parameters(&self) -> impl ExactSizeIterator<Item = &ValueType> {
1032        self.params.iter()
1033    }
1034
1035    /// Returns the type of the return type of the function. `None` means "void".
1036    pub fn return_type(&self) -> Option<&ValueType> {
1037        self.ret_ty.as_ref()
1038    }
1039}
1040
1041impl<'a> From<&'a Signature> for wasmi::FuncType {
1042    fn from(sig: &'a Signature) -> Self {
1043        wasmi::FuncType::new(
1044            sig.params
1045                .iter()
1046                .copied()
1047                .map(wasmi::core::ValType::from)
1048                .collect::<Vec<_>>(),
1049            sig.ret_ty.map(wasmi::core::ValType::from),
1050        )
1051    }
1052}
1053
1054impl From<Signature> for wasmi::FuncType {
1055    fn from(sig: Signature) -> wasmi::FuncType {
1056        wasmi::FuncType::from(&sig)
1057    }
1058}
1059
1060impl<'a> TryFrom<&'a wasmi::FuncType> for Signature {
1061    type Error = UnsupportedTypeError;
1062
1063    fn try_from(sig: &'a wasmi::FuncType) -> Result<Self, Self::Error> {
1064        if sig.results().len() > 1 {
1065            return Err(UnsupportedTypeError);
1066        }
1067
1068        Ok(Signature {
1069            params: sig
1070                .params()
1071                .iter()
1072                .copied()
1073                .map(ValueType::try_from)
1074                .collect::<Result<_, _>>()?,
1075            ret_ty: sig
1076                .results()
1077                .first()
1078                .copied()
1079                .map(ValueType::try_from)
1080                .transpose()?,
1081        })
1082    }
1083}
1084
1085#[cfg(all(
1086    any(
1087        all(
1088            target_arch = "x86_64",
1089            any(
1090                target_os = "windows",
1091                all(target_os = "linux", target_env = "gnu"),
1092                target_os = "macos"
1093            )
1094        ),
1095        all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
1096        all(target_arch = "s390x", target_os = "linux", target_env = "gnu")
1097    ),
1098    feature = "wasmtime"
1099))]
1100impl<'a> TryFrom<&'a wasmtime::FuncType> for Signature {
1101    type Error = UnsupportedTypeError;
1102
1103    fn try_from(sig: &'a wasmtime::FuncType) -> Result<Self, Self::Error> {
1104        if sig.results().len() > 1 {
1105            return Err(UnsupportedTypeError);
1106        }
1107
1108        Ok(Signature {
1109            params: sig
1110                .params()
1111                .map(ValueType::try_from)
1112                .collect::<Result<_, _>>()?,
1113            ret_ty: sig.results().next().map(ValueType::try_from).transpose()?,
1114        })
1115    }
1116}
1117
1118impl TryFrom<wasmi::FuncType> for Signature {
1119    type Error = UnsupportedTypeError;
1120
1121    fn try_from(sig: wasmi::FuncType) -> Result<Self, Self::Error> {
1122        Signature::try_from(&sig)
1123    }
1124}
1125
1126/// Value that a Wasm function can accept or produce.
1127#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1128pub enum WasmValue {
1129    /// A 32-bits integer. There is no fundamental difference between signed and unsigned
1130    /// integer, and the signed-ness should be determined depending on the context.
1131    I32(i32),
1132    /// A 32-bits integer. There is no fundamental difference between signed and unsigned
1133    /// integer, and the signed-ness should be determined depending on the context.
1134    I64(i64),
1135}
1136
1137/// Type of a value passed as parameter or returned by a function.
1138#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1139pub enum ValueType {
1140    /// A 32-bits integer. Used for both signed and unsigned integers.
1141    I32,
1142    /// A 64-bits integer. Used for both signed and unsigned integers.
1143    I64,
1144}
1145
1146impl WasmValue {
1147    /// Returns the type corresponding to this value.
1148    pub fn ty(&self) -> ValueType {
1149        match self {
1150            WasmValue::I32(_) => ValueType::I32,
1151            WasmValue::I64(_) => ValueType::I64,
1152        }
1153    }
1154
1155    /// Unwraps [`WasmValue::I32`] into its value.
1156    pub fn into_i32(self) -> Option<i32> {
1157        if let WasmValue::I32(v) = self {
1158            Some(v)
1159        } else {
1160            None
1161        }
1162    }
1163
1164    /// Unwraps [`WasmValue::I64`] into its value.
1165    pub fn into_i64(self) -> Option<i64> {
1166        if let WasmValue::I64(v) = self {
1167            Some(v)
1168        } else {
1169            None
1170        }
1171    }
1172}
1173
1174impl TryFrom<wasmi::Val> for WasmValue {
1175    type Error = UnsupportedTypeError;
1176
1177    fn try_from(val: wasmi::Val) -> Result<Self, Self::Error> {
1178        match val {
1179            wasmi::Val::I32(v) => Ok(WasmValue::I32(v)),
1180            wasmi::Val::I64(v) => Ok(WasmValue::I64(v)),
1181            _ => Err(UnsupportedTypeError),
1182        }
1183    }
1184}
1185
1186impl<'a> TryFrom<&'a wasmi::Val> for WasmValue {
1187    type Error = UnsupportedTypeError;
1188
1189    fn try_from(val: &'a wasmi::Val) -> Result<Self, Self::Error> {
1190        match val {
1191            wasmi::Val::I32(v) => Ok(WasmValue::I32(*v)),
1192            wasmi::Val::I64(v) => Ok(WasmValue::I64(*v)),
1193            _ => Err(UnsupportedTypeError),
1194        }
1195    }
1196}
1197
1198impl From<WasmValue> for wasmi::Val {
1199    fn from(val: WasmValue) -> Self {
1200        match val {
1201            WasmValue::I32(v) => wasmi::Val::I32(v),
1202            WasmValue::I64(v) => wasmi::Val::I64(v),
1203        }
1204    }
1205}
1206
1207#[cfg(all(
1208    any(
1209        all(
1210            target_arch = "x86_64",
1211            any(
1212                target_os = "windows",
1213                all(target_os = "linux", target_env = "gnu"),
1214                target_os = "macos"
1215            )
1216        ),
1217        all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
1218        all(target_arch = "s390x", target_os = "linux", target_env = "gnu")
1219    ),
1220    feature = "wasmtime"
1221))]
1222impl From<WasmValue> for wasmtime::Val {
1223    fn from(val: WasmValue) -> Self {
1224        match val {
1225            WasmValue::I32(v) => wasmtime::Val::I32(v),
1226            WasmValue::I64(v) => wasmtime::Val::I64(v),
1227        }
1228    }
1229}
1230
1231#[cfg(all(
1232    any(
1233        all(
1234            target_arch = "x86_64",
1235            any(
1236                target_os = "windows",
1237                all(target_os = "linux", target_env = "gnu"),
1238                target_os = "macos"
1239            )
1240        ),
1241        all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
1242        all(target_arch = "s390x", target_os = "linux", target_env = "gnu")
1243    ),
1244    feature = "wasmtime"
1245))]
1246impl<'a> TryFrom<&'a wasmtime::Val> for WasmValue {
1247    type Error = UnsupportedTypeError;
1248
1249    fn try_from(val: &'a wasmtime::Val) -> Result<Self, Self::Error> {
1250        match val {
1251            wasmtime::Val::I32(v) => Ok(WasmValue::I32(*v)),
1252            wasmtime::Val::I64(v) => Ok(WasmValue::I64(*v)),
1253            _ => Err(UnsupportedTypeError),
1254        }
1255    }
1256}
1257
1258impl From<ValueType> for wasmi::core::ValType {
1259    fn from(ty: ValueType) -> wasmi::core::ValType {
1260        match ty {
1261            ValueType::I32 => wasmi::core::ValType::I32,
1262            ValueType::I64 => wasmi::core::ValType::I64,
1263        }
1264    }
1265}
1266
1267impl TryFrom<wasmi::core::ValType> for ValueType {
1268    type Error = UnsupportedTypeError;
1269
1270    fn try_from(val: wasmi::core::ValType) -> Result<Self, Self::Error> {
1271        match val {
1272            wasmi::core::ValType::I32 => Ok(ValueType::I32),
1273            wasmi::core::ValType::I64 => Ok(ValueType::I64),
1274            _ => Err(UnsupportedTypeError),
1275        }
1276    }
1277}
1278
1279#[cfg(all(
1280    any(
1281        all(
1282            target_arch = "x86_64",
1283            any(
1284                target_os = "windows",
1285                all(target_os = "linux", target_env = "gnu"),
1286                target_os = "macos"
1287            )
1288        ),
1289        all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
1290        all(target_arch = "s390x", target_os = "linux", target_env = "gnu")
1291    ),
1292    feature = "wasmtime"
1293))]
1294impl TryFrom<wasmtime::ValType> for ValueType {
1295    type Error = UnsupportedTypeError;
1296
1297    fn try_from(val: wasmtime::ValType) -> Result<Self, Self::Error> {
1298        match val {
1299            wasmtime::ValType::I32 => Ok(ValueType::I32),
1300            wasmtime::ValType::I64 => Ok(ValueType::I64),
1301            _ => Err(UnsupportedTypeError),
1302        }
1303    }
1304}
1305
1306/// Error used in the conversions between VM implementation and the public API.
1307#[derive(Debug, derive_more::Display, derive_more::Error)]
1308pub struct UnsupportedTypeError;
1309
1310/// Outcome of the [`run`](VirtualMachine::run) function.
1311#[derive(Debug)]
1312pub enum ExecOutcome {
1313    /// The execution has finished.
1314    ///
1315    /// The state machine is now in a poisoned state, and calling [`run`](VirtualMachine::run)
1316    /// will return [`RunErr::Poisoned`].
1317    Finished {
1318        /// Return value of the function.
1319        return_value: Result<Option<WasmValue>, Trap>,
1320    },
1321
1322    /// The virtual machine has been paused due to a call to a host function.
1323    ///
1324    /// This variant contains the identifier of the host function that is expected to be
1325    /// called, and its parameters. When you call [`run`](VirtualMachine::run) again, you must
1326    /// pass back the outcome of calling that function.
1327    ///
1328    /// > **Note**: The type of the return value of the function is called is not specified, as the
1329    /// >           user is supposed to know it based on the identifier. It is an error to call
1330    /// >           [`run`](VirtualMachine::run) with a value of the wrong type.
1331    Interrupted {
1332        /// Identifier of the function to call. Corresponds to the value provided at
1333        /// initialization when resolving imports.
1334        id: usize,
1335
1336        /// Parameters of the function call.
1337        params: Vec<WasmValue>,
1338    },
1339}
1340
1341/// Opaque error that happened during execution, such as an `unreachable` instruction.
1342#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
1343#[display("{_0}")]
1344pub struct Trap(#[error(not(source))] String);
1345
1346/// Error that can happen when initializing a [`VirtualMachinePrototype`].
1347#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
1348pub enum NewErr {
1349    /// Error while compiling the WebAssembly code.
1350    ///
1351    /// Contains an opaque error message.
1352    #[display("{_0}")]
1353    InvalidWasm(#[error(not(source))] String),
1354    /// Error while instantiating the WebAssembly module.
1355    ///
1356    /// Contains an opaque error message.
1357    #[display("{_0}")]
1358    Instantiation(#[error(not(source))] String),
1359    /// Failed to resolve a function imported by the module.
1360    #[display("Unresolved function `{module_name}`:`{function}`")]
1361    UnresolvedFunctionImport {
1362        /// Name of the function that was unresolved.
1363        function: String,
1364        /// Name of module associated with the unresolved function.
1365        module_name: String,
1366    },
1367    /// Smoldot doesn't support wasm runtime that have a start function. It is unclear whether
1368    /// this is allowed in the Substrate/Polkadot specification.
1369    #[display("Start function not supported")]
1370    // TODO: figure this out
1371    StartFunctionNotSupported,
1372    /// If a "memory" symbol is provided, it must be a memory.
1373    #[display("If a \"memory\" symbol is provided, it must be a memory.")]
1374    MemoryIsntMemory,
1375    /// Wasm module imports a memory that isn't named "memory".
1376    MemoryNotNamedMemory,
1377    /// Wasm module doesn't contain any memory.
1378    NoMemory,
1379    /// Wasm module both imports and exports a memory.
1380    TwoMemories,
1381    /// Failed to allocate memory for the virtual machine.
1382    CouldntAllocateMemory,
1383    /// The Wasm module requires importing a global or a table, which isn't supported.
1384    ImportTypeNotSupported,
1385}
1386
1387/// Error that can happen when calling [`Prepare::start`].
1388#[derive(Debug, Clone, derive_more::Display, derive_more::Error)]
1389pub enum StartErr {
1390    /// Couldn't find the requested function.
1391    #[display("Function to start was not found.")]
1392    FunctionNotFound,
1393    /// The requested function has been found in the list of exports, but it is not a function.
1394    #[display("Symbol to start is not a function.")]
1395    NotAFunction,
1396    /// The requested function has a signature that isn't supported.
1397    #[display("Function to start uses unsupported signature.")]
1398    SignatureNotSupported,
1399    /// The types of the provided parameters don't match the signature.
1400    #[display("The types of the provided parameters don't match the signature.")]
1401    InvalidParameters,
1402}
1403
1404/// Error while reading memory.
1405#[derive(Debug, derive_more::Display, derive_more::Error)]
1406#[display("Out of bounds when accessing virtual machine memory")]
1407pub struct OutOfBoundsError;
1408
1409/// Error that can happen when resuming the execution of a function.
1410#[derive(Debug, derive_more::Display, derive_more::Error)]
1411pub enum RunErr {
1412    /// The state machine is poisoned.
1413    #[display("State machine is poisoned")]
1414    Poisoned,
1415    /// Passed a wrong value back.
1416    #[display("Expected value of type {expected:?} but got {obtained:?} instead")]
1417    BadValueTy {
1418        /// Type of the value that was expected.
1419        expected: Option<ValueType>,
1420        /// Type of the value that was actually passed.
1421        obtained: Option<ValueType>,
1422    },
1423}
1424
1425/// Error that can happen when calling [`VirtualMachinePrototype::global_value`].
1426#[derive(Debug, derive_more::Display, derive_more::Error)]
1427pub enum GlobalValueErr {
1428    /// Couldn't find requested symbol.
1429    NotFound,
1430    /// Requested symbol isn't a `u32`.
1431    Invalid,
1432}