radix_wasmi/
global.rs

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