wasmtime/runtime/gc/enabled/
i31.rs

1//! Unboxed 31-bit integers.
2//!
3//! Note that ideally, we would just re-export `wasmtime_runtime::I31` here, but
4//! in order to get doc comments with example code that shows the use of `I31`
5//! with typed functions and such, we need to wrap it in a newtype.
6
7use crate::runtime::vm::{VMGcRef, ValRaw};
8use crate::{
9    store::{AutoAssertNoGc, StoreOpaque},
10    HeapType, RefType, Result, ValType, WasmTy,
11};
12use core::fmt;
13use core::mem::MaybeUninit;
14
15/// A 31-bit integer.
16///
17/// Represents WebAssembly's `(ref i31)` and `(ref null i31)` (aka `i31ref`)
18/// references.
19///
20/// You can convert this into any of the `(ref i31)` supertypes such as `(ref
21/// eq)` or `(ref any)`, and their nullable equivalents. After conversion, the
22/// resulting reference does not actually point at a GC object in the heap,
23/// instead it is a 31-bit integer that is stored unboxed/inline in the
24/// reference itself.
25///
26/// # Example
27///
28/// ```
29/// # use wasmtime::*;
30/// # fn _foo() -> Result<()> {
31/// // Enable the Wasm GC proposal for Wasm to use i31 references.
32/// let mut config = Config::new();
33/// config.wasm_gc(true);
34///
35/// let engine = Engine::new(&config)?;
36/// let mut store = Store::new(&engine, ());
37///
38/// // A Wasm module that exports a function that increments an i31.
39/// let module = Module::new(&engine, r#"
40///     (module
41///         (func (export "inc_i31") (param (ref i31)) (result (ref i31))
42///             local.get 0
43///             i31.get_u
44///             i32.const 1
45///             i32.add
46///             ref.i31
47///         )
48/// "#)?;
49///
50/// // Instantiate the module.
51/// let instance = Instance::new(&mut store, &module, &[])?;
52///
53/// // Get the exported `inc_i31` function.
54/// let inc_i31 = instance.get_func(&mut store, "inc_i31").unwrap();
55///
56/// // Call the function using the untyped functions API, meaning we need to
57/// // pack our `I31` argument into an `AnyRef` that is packed into a `Val`, and
58/// // then we need to do the opposite unpacking to extract the result.
59/// let i31 = I31::wrapping_u32(0x1234);
60/// let anyref = AnyRef::from_i31(&mut store, i31);
61/// let val = Val::AnyRef(Some(anyref));
62/// let mut results = [Val::null_any_ref()];
63/// inc_i31.call(&mut store, &[val], &mut results)?;
64/// let nullable_anyref = results[0].unwrap_anyref();
65/// let anyref = nullable_anyref.unwrap();
66/// let i31 = anyref.unwrap_i31(&store)?;
67/// assert_eq!(i31.get_u32(), 0x1235);
68///
69/// // Alternatively, we can use the typed function API to make this all a lot
70/// // more ergonomic.
71/// let inc_i31 = inc_i31.typed::<I31, I31>(&mut store)?;
72/// let i31 = I31::wrapping_u32(0x5678);
73/// let result = inc_i31.call(&mut store, i31)?;
74/// assert_eq!(result.get_u32(), 0x5679);
75/// # Ok(())
76/// # }
77/// ```
78#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
79pub struct I31(crate::runtime::vm::I31);
80
81impl fmt::Debug for I31 {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        f.debug_struct("I31")
84            .field("as_u32", &self.get_u32())
85            .field("as_i32", &self.get_i32())
86            .finish()
87    }
88}
89
90impl From<crate::runtime::vm::I31> for I31 {
91    #[inline]
92    fn from(value: crate::runtime::vm::I31) -> Self {
93        Self(value)
94    }
95}
96
97impl From<I31> for crate::runtime::vm::I31 {
98    #[inline]
99    fn from(value: I31) -> Self {
100        value.0
101    }
102}
103
104impl I31 {
105    /// Get this `wasmtime::I31`'s internal `crate::runtime::vm::I31`.
106    pub(crate) fn runtime_i31(self) -> crate::runtime::vm::I31 {
107        self.0
108    }
109
110    /// Construct a new `I31` from the given unsigned value.
111    ///
112    /// Returns `None` if the value does not fit in the bottom 31 bits.
113    ///
114    /// # Example
115    ///
116    /// ```
117    /// # use wasmtime::*;
118    /// // This value does not fit into 31 bits.
119    /// assert!(I31::new_u32(0x8000_0000).is_none());
120    ///
121    /// // This value does fit into 31 bits.
122    /// let x = I31::new_u32(5).unwrap();
123    /// assert_eq!(x.get_u32(), 5);
124    /// ```
125    #[inline]
126    pub fn new_u32(value: u32) -> Option<Self> {
127        crate::runtime::vm::I31::new_u32(value).map(Self)
128    }
129
130    /// Construct a new `I31` from the given signed value.
131    ///
132    /// Returns `None` if the value does not fit in the bottom 31 bits.
133    ///
134    /// # Example
135    ///
136    /// ```
137    /// # use wasmtime::*;
138    /// // This value does not fit into 31 bits.
139    /// assert!(I31::new_i32(-2147483648).is_none());
140    ///
141    /// // This value does fit into 31 bits.
142    /// let x = I31::new_i32(-5).unwrap();
143    /// assert_eq!(x.get_i32(), -5);
144    /// ```
145    #[inline]
146    pub fn new_i32(value: i32) -> Option<Self> {
147        crate::runtime::vm::I31::new_i32(value).map(Self)
148    }
149
150    /// Construct a new `I31` from the given unsigned value.
151    ///
152    /// If the value doesn't fit in the bottom 31 bits, it is wrapped such that
153    /// the wrapped value does.
154    ///
155    /// # Example
156    ///
157    /// ```
158    /// # use wasmtime::*;
159    /// // Values that fit in 31 bits are preserved.
160    /// let x = I31::wrapping_u32(5);
161    /// assert_eq!(x.get_u32(), 5);
162    ///
163    /// // Values that do not fit in 31 bits are wrapped to 31 bits.
164    /// let y = I31::wrapping_u32(0xffff_ffff);
165    /// assert_eq!(y.get_u32(), 0x7fff_ffff);
166    /// ```
167    #[inline]
168    pub fn wrapping_u32(value: u32) -> Self {
169        Self(crate::runtime::vm::I31::wrapping_u32(value))
170    }
171
172    /// Construct a new `I31` from the given signed value.
173    ///
174    /// If the value doesn't fit in the bottom 31 bits, it is wrapped such that
175    /// the wrapped value does.
176    ///
177    /// # Example
178    ///
179    /// ```
180    /// # use wasmtime::*;
181    /// // Values that fit in 31 bits are preserved.
182    /// let x = I31::wrapping_i32(-5);
183    /// assert_eq!(x.get_i32(), -5);
184    ///
185    /// // Values that do not fit in 31 bits are wrapped to 31 bits.
186    /// let y = I31::wrapping_i32(-1073741825); // 0xbfffffff
187    /// assert_eq!(y.get_i32(), 1073741823);    // 0x3fffffff
188    /// ```
189    #[inline]
190    pub fn wrapping_i32(value: i32) -> Self {
191        Self(crate::runtime::vm::I31::wrapping_i32(value))
192    }
193
194    /// Get this `I31`'s value as an unsigned integer.
195    ///
196    /// # Example
197    ///
198    /// ```
199    /// # use wasmtime::*;
200    /// let x = I31::new_i32(-1).unwrap();
201    /// assert_eq!(x.get_u32(), 0x7fff_ffff);
202    /// ```
203    #[inline]
204    pub fn get_u32(&self) -> u32 {
205        self.0.get_u32()
206    }
207
208    /// Get this `I31`'s value as a signed integer.
209    ///
210    /// # Example
211    ///
212    /// ```
213    /// # use wasmtime::*;
214    /// let x = I31::new_u32(0x7fff_ffff).unwrap();
215    /// assert_eq!(x.get_i32(), -1);
216    /// ```
217    #[inline]
218    pub fn get_i32(&self) -> i32 {
219        self.0.get_i32()
220    }
221}
222
223unsafe impl WasmTy for I31 {
224    #[inline]
225    fn valtype() -> ValType {
226        ValType::Ref(RefType::new(false, HeapType::I31))
227    }
228
229    #[inline]
230    fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
231        true
232    }
233
234    fn dynamic_concrete_type_check(
235        &self,
236        _store: &StoreOpaque,
237        _nullable: bool,
238        _actual: &HeapType,
239    ) -> Result<()> {
240        unreachable!()
241    }
242
243    fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
244        let gc_ref = VMGcRef::from_i31(self.into()).as_raw_u32();
245        ptr.write(ValRaw::anyref(gc_ref));
246        Ok(())
247    }
248
249    unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
250        let raw = ptr.get_anyref();
251        let gc_ref = VMGcRef::from_raw_u32(raw).expect("non-null");
252        gc_ref.unwrap_i31().into()
253    }
254}
255
256unsafe impl WasmTy for Option<I31> {
257    #[inline]
258    fn valtype() -> ValType {
259        ValType::Ref(RefType::new(true, HeapType::I31))
260    }
261
262    #[inline]
263    fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
264        true
265    }
266
267    fn dynamic_concrete_type_check(
268        &self,
269        _store: &StoreOpaque,
270        _nullable: bool,
271        _actual: &HeapType,
272    ) -> Result<()> {
273        unreachable!()
274    }
275
276    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
277        match self {
278            Some(i) => i.store(store, ptr),
279            None => {
280                ptr.write(ValRaw::anyref(0));
281                Ok(())
282            }
283        }
284    }
285
286    unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
287        let raw = ptr.get_anyref();
288        let gc_ref = VMGcRef::from_raw_u32(raw)?;
289        Some(I31(gc_ref.unwrap_i31()))
290    }
291}