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 7 use crate::runtime::vm::{VMGcRef, ValRaw}; 8 use crate::{ 9 HeapType, RefType, Result, ValType, WasmTy, 10 store::{AutoAssertNoGc, StoreOpaque}, 11 }; 12 use core::fmt; 13 use 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)] 79 pub struct I31(crate::runtime::vm::I31); 80 81 impl fmt::Debug for I31 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result82 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 90 impl From<crate::runtime::vm::I31> for I31 { 91 #[inline] from(value: crate::runtime::vm::I31) -> Self92 fn from(value: crate::runtime::vm::I31) -> Self { 93 Self(value) 94 } 95 } 96 97 impl From<I31> for crate::runtime::vm::I31 { 98 #[inline] from(value: I31) -> Self99 fn from(value: I31) -> Self { 100 value.0 101 } 102 } 103 104 impl I31 { 105 /// Get this `wasmtime::I31`'s internal `crate::runtime::vm::I31`. runtime_i31(self) -> crate::runtime::vm::I31106 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] new_u32(value: u32) -> Option<Self>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] new_i32(value: i32) -> Option<Self>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] wrapping_u32(value: u32) -> Self168 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] wrapping_i32(value: i32) -> Self190 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] get_u32(&self) -> u32204 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] get_i32(&self) -> i32218 pub fn get_i32(&self) -> i32 { 219 self.0.get_i32() 220 } 221 } 222 223 unsafe impl WasmTy for I31 { 224 #[inline] valtype() -> ValType225 fn valtype() -> ValType { 226 ValType::Ref(RefType::new(false, HeapType::I31)) 227 } 228 229 #[inline] compatible_with_store(&self, _store: &StoreOpaque) -> bool230 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool { 231 true 232 } 233 dynamic_concrete_type_check( &self, _store: &StoreOpaque, _nullable: bool, _actual: &HeapType, ) -> Result<()>234 fn dynamic_concrete_type_check( 235 &self, 236 _store: &StoreOpaque, 237 _nullable: bool, 238 _actual: &HeapType, 239 ) -> Result<()> { 240 unreachable!() 241 } 242 store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>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 load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self249 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 256 unsafe impl WasmTy for Option<I31> { 257 #[inline] valtype() -> ValType258 fn valtype() -> ValType { 259 ValType::Ref(RefType::new(true, HeapType::I31)) 260 } 261 262 #[inline] compatible_with_store(&self, _store: &StoreOpaque) -> bool263 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool { 264 true 265 } 266 dynamic_concrete_type_check( &self, _store: &StoreOpaque, _nullable: bool, _actual: &HeapType, ) -> Result<()>267 fn dynamic_concrete_type_check( 268 &self, 269 _store: &StoreOpaque, 270 _nullable: bool, 271 _actual: &HeapType, 272 ) -> Result<()> { 273 unreachable!() 274 } 275 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>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 load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self286 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 } 292