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}