soroban_wasmi/
global.rs

1use super::{AsContext, AsContextMut, Stored};
2use crate::{
3    collections::arena::ArenaIndex,
4    core::{UntypedVal, ValType},
5    value::WithType,
6    Val,
7};
8use core::{fmt, fmt::Display, ptr::NonNull};
9
10/// A raw index to a global variable entity.
11#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub struct GlobalIdx(u32);
13
14impl ArenaIndex for GlobalIdx {
15    fn into_usize(self) -> usize {
16        self.0 as usize
17    }
18
19    fn from_usize(value: usize) -> Self {
20        let value = value.try_into().unwrap_or_else(|error| {
21            panic!("index {value} is out of bounds as global index: {error}")
22        });
23        Self(value)
24    }
25}
26
27/// An error that may occur upon operating on global variables.
28#[derive(Debug)]
29#[non_exhaustive]
30pub enum GlobalError {
31    /// Occurs when trying to write to an immutable global variable.
32    ImmutableWrite,
33    /// Occurs when trying writing a value with mismatching type to a global variable.
34    TypeMismatch {
35        /// The type of the global variable.
36        expected: ValType,
37        /// The type of the new value that mismatches the type of the global variable.
38        encountered: ValType,
39    },
40    /// Occurs when a global type does not satisfy the constraints of another.
41    UnsatisfyingGlobalType {
42        /// The unsatisfying [`GlobalType`].
43        unsatisfying: GlobalType,
44        /// The required [`GlobalType`].
45        required: GlobalType,
46    },
47}
48
49#[cfg(feature = "std")]
50impl std::error::Error for GlobalError {}
51
52impl Display for GlobalError {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        match self {
55            Self::ImmutableWrite => write!(f, "tried to write to immutable global variable"),
56            Self::TypeMismatch {
57                expected,
58                encountered,
59            } => {
60                write!(
61                    f,
62                    "type mismatch upon writing global variable. \
63                    expected {expected:?} but encountered {encountered:?}.",
64                )
65            }
66            Self::UnsatisfyingGlobalType {
67                unsatisfying,
68                required,
69            } => {
70                write!(
71                    f,
72                    "global type {unsatisfying:?} does not \
73                    satisfy requirements of {required:?}",
74                )
75            }
76        }
77    }
78}
79
80/// The mutability of a global variable.
81#[derive(Debug, Copy, Clone, PartialEq, Eq)]
82pub enum Mutability {
83    /// The value of the global variable is a constant.
84    Const,
85    /// The value of the global variable is mutable.
86    Var,
87}
88
89impl Mutability {
90    /// Returns `true` if this mutability is [`Mutability::Const`].
91    pub fn is_const(&self) -> bool {
92        matches!(self, Self::Const)
93    }
94
95    /// Returns `true` if this mutability is [`Mutability::Var`].
96    pub fn is_mut(&self) -> bool {
97        matches!(self, Self::Var)
98    }
99}
100
101/// The type of a global variable.
102#[derive(Debug, Copy, Clone, PartialEq, Eq)]
103pub struct GlobalType {
104    /// The value type of the global variable.
105    content: ValType,
106    /// The mutability of the global variable.
107    mutability: Mutability,
108}
109
110impl GlobalType {
111    /// Creates a new [`GlobalType`] from the given [`ValType`] and [`Mutability`].
112    pub fn new(content: ValType, mutability: Mutability) -> Self {
113        Self {
114            content,
115            mutability,
116        }
117    }
118
119    /// Returns the [`ValType`] of the global variable.
120    pub fn content(&self) -> ValType {
121        self.content
122    }
123
124    /// Returns the [`Mutability`] of the global variable.
125    pub fn mutability(&self) -> Mutability {
126        self.mutability
127    }
128
129    /// Checks if `self` satisfies the given `GlobalType`.
130    ///
131    /// # Errors
132    ///
133    /// - If the initial limits of the `required` [`GlobalType`] are greater than `self`.
134    /// - If the maximum limits of the `required` [`GlobalType`] are greater than `self`.
135    pub(crate) fn satisfies(&self, required: &GlobalType) -> Result<(), GlobalError> {
136        if self != required {
137            return Err(GlobalError::UnsatisfyingGlobalType {
138                unsatisfying: *self,
139                required: *required,
140            });
141        }
142        Ok(())
143    }
144}
145
146/// A global variable entity.
147#[derive(Debug)]
148pub struct GlobalEntity {
149    /// The current value of the global variable.
150    value: UntypedVal,
151    /// The type of the global variable.
152    ty: GlobalType,
153}
154
155impl GlobalEntity {
156    /// Creates a new global entity with the given initial value and mutability.
157    pub fn new(initial_value: Val, mutability: Mutability) -> Self {
158        Self {
159            ty: GlobalType::new(initial_value.ty(), mutability),
160            value: initial_value.into(),
161        }
162    }
163
164    /// Returns the [`GlobalType`] of the global variable.
165    pub fn ty(&self) -> GlobalType {
166        self.ty
167    }
168
169    /// Sets a new value to the global variable.
170    ///
171    /// # Errors
172    ///
173    /// - If the global variable is immutable.
174    /// - If there is a type mismatch between the global variable and the new value.
175    pub fn set(&mut self, new_value: Val) -> Result<(), GlobalError> {
176        if !self.ty().mutability().is_mut() {
177            return Err(GlobalError::ImmutableWrite);
178        }
179        if self.ty().content() != new_value.ty() {
180            return Err(GlobalError::TypeMismatch {
181                expected: self.ty().content(),
182                encountered: new_value.ty(),
183            });
184        }
185        self.set_untyped(new_value.into());
186        Ok(())
187    }
188
189    /// Sets a new untyped value for the global variable.
190    ///
191    /// # Note
192    ///
193    /// This is an inherently unsafe API and only exists to allow
194    /// for efficient `global.set` through the interpreter which is
195    /// safe since the interpreter only handles validated Wasm code
196    /// where the checks in [`Global::set`] cannot fail.
197    pub(crate) fn set_untyped(&mut self, new_value: UntypedVal) {
198        self.value = new_value;
199    }
200
201    /// Returns the current value of the global variable.
202    pub fn get(&self) -> Val {
203        self.get_untyped().with_type(self.ty().content())
204    }
205
206    /// Returns the current untyped value of the global variable.
207    pub(crate) fn get_untyped(&self) -> UntypedVal {
208        self.value
209    }
210
211    /// Returns a pointer to the untyped value of the global variable.
212    pub(crate) fn get_untyped_ptr(&mut self) -> NonNull<UntypedVal> {
213        NonNull::from(&mut self.value)
214    }
215}
216
217/// A Wasm global variable reference.
218#[derive(Debug, Copy, Clone)]
219#[repr(transparent)]
220pub struct Global(Stored<GlobalIdx>);
221
222impl Global {
223    /// Creates a new stored global variable reference.
224    ///
225    /// # Note
226    ///
227    /// This API is primarily used by the [`Store`] itself.
228    ///
229    /// [`Store`]: [`crate::Store`]
230    pub(super) fn from_inner(stored: Stored<GlobalIdx>) -> Self {
231        Self(stored)
232    }
233
234    /// Returns the underlying stored representation.
235    pub(super) fn as_inner(&self) -> &Stored<GlobalIdx> {
236        &self.0
237    }
238
239    /// Creates a new global variable to the store.
240    pub fn new(mut ctx: impl AsContextMut, initial_value: Val, mutability: Mutability) -> Self {
241        ctx.as_context_mut()
242            .store
243            .inner
244            .alloc_global(GlobalEntity::new(initial_value, mutability))
245    }
246
247    /// Returns the [`GlobalType`] of the global variable.
248    pub fn ty(&self, ctx: impl AsContext) -> GlobalType {
249        ctx.as_context().store.inner.resolve_global(self).ty()
250    }
251
252    /// Sets a new value to the global variable.
253    ///
254    /// # Errors
255    ///
256    /// - If the global variable is immutable.
257    /// - If there is a type mismatch between the global variable and the new value.
258    ///
259    /// # Panics
260    ///
261    /// Panics if `ctx` does not own this [`Global`].
262    pub fn set(&self, mut ctx: impl AsContextMut, new_value: Val) -> Result<(), GlobalError> {
263        ctx.as_context_mut()
264            .store
265            .inner
266            .resolve_global_mut(self)
267            .set(new_value)
268    }
269
270    /// Returns the current value of the global variable.
271    ///
272    /// # Panics
273    ///
274    /// Panics if `ctx` does not own this [`Global`].
275    pub fn get(&self, ctx: impl AsContext) -> Val {
276        ctx.as_context().store.inner.resolve_global(self).get()
277    }
278}