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