1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Abstractions over different Wasm runtime implementations.

use std::ops::{Deref, DerefMut};

use frunk::HList;

use super::{memory::Memory, RuntimeError};
use crate::memory_layout::FlatLayout;

/// A Wasm runtime.
///
/// Shared types between different guest instances that use the same runtime.
pub trait Runtime: Sized {
    /// A handle to something exported from a guest Wasm module.
    type Export;

    /// A handle to the guest Wasm module's memory.
    type Memory;
}

/// An active guest Wasm module.
pub trait Instance: Sized {
    /// The runtime this instance is running in.
    type Runtime: Runtime;

    /// Custom user data stored in the instance.
    type UserData;

    /// A reference to the custom user data stored in the instance.
    type UserDataReference<'a>: Deref<Target = Self::UserData>
    where
        Self::UserData: 'a,
        Self: 'a;

    /// A mutable reference to the custom user data stored in the instance.
    type UserDataMutReference<'a>: DerefMut<Target = Self::UserData>
    where
        Self::UserData: 'a,
        Self: 'a;

    /// Loads an export from the guest module.
    fn load_export(&mut self, name: &str) -> Option<<Self::Runtime as Runtime>::Export>;

    /// Returns a reference to the custom user data stored in this instance.
    fn user_data(&self) -> Self::UserDataReference<'_>;

    /// Returns a mutable reference to the custom user data stored in this instance.
    fn user_data_mut(&mut self) -> Self::UserDataMutReference<'_>;
}

/// How a runtime supports a function signature.
pub trait InstanceWithFunction<Parameters, Results>: Instance
where
    Parameters: FlatLayout,
    Results: FlatLayout,
{
    /// The runtime-specific type to represent the function.
    type Function;

    /// Converts an export into a function, if it is one.
    fn function_from_export(
        &mut self,
        export: <Self::Runtime as Runtime>::Export,
    ) -> Result<Option<Self::Function>, RuntimeError>;

    /// Calls the `function` from this instance using the specified `parameters`.
    fn call(
        &mut self,
        function: &Self::Function,
        parameters: Parameters,
    ) -> Result<Results, RuntimeError>;

    /// Loads a function from the guest Wasm instance.
    fn load_function(&mut self, name: &str) -> Result<Self::Function, RuntimeError> {
        let export = self
            .load_export(name)
            .ok_or_else(|| RuntimeError::FunctionNotFound(name.to_string()))?;

        self.function_from_export(export)?
            .ok_or_else(|| RuntimeError::NotAFunction(name.to_string()))
    }
}

/// Trait alias for a Wasm module instance with the WIT Canonical ABI `cabi_realloc` function.
pub trait CabiReallocAlias: InstanceWithFunction<HList![i32, i32, i32, i32], HList![i32]> {}

impl<AnyInstance> CabiReallocAlias for AnyInstance where
    AnyInstance: InstanceWithFunction<HList![i32, i32, i32, i32], HList![i32]>
{
}

/// Trait alias for a Wasm module instance with the WIT Canonical ABI `cabi_free` function.
pub trait CabiFreeAlias: InstanceWithFunction<HList![i32], HList![]> {}

impl<AnyInstance> CabiFreeAlias for AnyInstance where
    AnyInstance: InstanceWithFunction<HList![i32], HList![]>
{
}

/// Trait alias for a Wasm module instance with the WIT Canonical ABI functions.
pub trait InstanceWithMemory: CabiReallocAlias + CabiFreeAlias {
    /// Converts an `export` into the runtime's specific memory type.
    fn memory_from_export(
        &self,
        export: <Self::Runtime as Runtime>::Export,
    ) -> Result<Option<<Self::Runtime as Runtime>::Memory>, RuntimeError>;

    /// Returns the memory export from the current Wasm module instance.
    fn memory(&mut self) -> Result<Memory<'_, Self>, RuntimeError> {
        let export = self
            .load_export("memory")
            .ok_or(RuntimeError::MissingMemory)?;

        let memory = self
            .memory_from_export(export)?
            .ok_or(RuntimeError::NotMemory)?;

        Ok(Memory::new(self, memory))
    }
}