unc_vm_types/
values.rs

1use crate::extern_ref::ExternRef;
2use crate::lib::std::convert::TryFrom;
3use crate::lib::std::fmt;
4use crate::lib::std::ptr;
5use crate::lib::std::string::{String, ToString};
6use crate::types::Type;
7
8/// Possible runtime values that a WebAssembly module can either consume or
9/// produce.
10#[derive(Clone, PartialEq)]
11pub enum Value<T> {
12    /// A 32-bit integer.
13    ///
14    /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
15    I32(i32),
16
17    /// A 64-bit integer.
18    ///
19    /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
20    I64(i64),
21
22    /// A 32-bit float.
23    F32(f32),
24
25    /// A 64-bit float.
26    F64(f64),
27
28    /// An `externref` value which can hold opaque data to the wasm instance itself.
29    ///
30    /// Note that this is a nullable value as well.
31    ExternRef(ExternRef),
32
33    /// A first-class reference to a WebAssembly function.
34    FuncRef(Option<T>),
35
36    /// A 128-bit number
37    V128(u128),
38}
39
40macro_rules! accessors {
41    ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
42        /// Attempt to access the underlying value of this `Value`, returning
43        /// `None` if it is not the correct type.
44        pub fn $get(&self) -> Option<$ty> {
45            if let Self::$variant($bind) = self {
46                Some($cvt)
47            } else {
48                None
49            }
50        }
51
52        /// Returns the underlying value of this `Value`, panicking if it's the
53        /// wrong type.
54        ///
55        /// # Panics
56        ///
57        /// Panics if `self` is not of the right type.
58        pub fn $unwrap(&self) -> $ty {
59            self.$get().expect(concat!("expected ", stringify!($ty)))
60        }
61    )*)
62}
63
64/// Trait for reading and writing Wasm values into binary for use on the layer
65/// between the API and the VM internals, specifically with `unc_vm_types::Value`.
66pub trait WasmValueType: std::fmt::Debug + 'static {
67    /// Write the value
68    unsafe fn write_value_to(&self, p: *mut i128);
69
70    /// read the value
71    // TODO(reftypes): passing the store as `dyn Any` is a hack to work around the
72    // structure of our crates. We need to talk about the store in the rest of the
73    // VM (for example where this method is used) but cannot do so. Fixing this
74    // may be non-trivial.
75    unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128) -> Self;
76}
77
78impl WasmValueType for () {
79    unsafe fn write_value_to(&self, _p: *mut i128) {}
80
81    unsafe fn read_value_from(_store: &dyn std::any::Any, _p: *const i128) -> Self {
82        ()
83    }
84}
85
86impl<T> Value<T>
87where
88    T: WasmValueType,
89{
90    /// Returns a null `externref` value.
91    pub fn null() -> Self {
92        Self::ExternRef(ExternRef::null())
93    }
94
95    /// Returns the corresponding [`Type`] for this `Value`.
96    pub fn ty(&self) -> Type {
97        match self {
98            Self::I32(_) => Type::I32,
99            Self::I64(_) => Type::I64,
100            Self::F32(_) => Type::F32,
101            Self::F64(_) => Type::F64,
102            Self::ExternRef(_) => Type::ExternRef,
103            Self::FuncRef(_) => Type::FuncRef,
104            Self::V128(_) => Type::V128,
105        }
106    }
107
108    /// Writes it's value to a given pointer
109    ///
110    /// # Safety
111    /// `p` must be:
112    /// - Sufficiently aligned for the Rust equivalent of the type in `self`
113    /// - Non-null and pointing to valid, mutable memory
114    pub unsafe fn write_value_to(&self, p: *mut i128) {
115        match self {
116            Self::I32(i) => ptr::write(p as *mut i32, *i),
117            Self::I64(i) => ptr::write(p as *mut i64, *i),
118            Self::F32(u) => ptr::write(p as *mut f32, *u),
119            Self::F64(u) => ptr::write(p as *mut f64, *u),
120            Self::V128(b) => ptr::write(p as *mut u128, *b),
121            Self::FuncRef(Some(b)) => T::write_value_to(b, p),
122            Self::FuncRef(None) => ptr::write(p as *mut usize, 0),
123            // TODO(reftypes): review clone here
124            Self::ExternRef(extern_ref) => ptr::write(p as *mut ExternRef, extern_ref.clone()),
125        }
126    }
127
128    /// Gets a `Value` given a pointer and a `Type`
129    ///
130    /// # Safety
131    /// `p` must be:
132    /// - Properly aligned to the specified `ty`'s Rust equivalent
133    /// - Non-null and pointing to valid memory
134    pub unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128, ty: Type) -> Self {
135        match ty {
136            Type::I32 => Self::I32(ptr::read(p as *const i32)),
137            Type::I64 => Self::I64(ptr::read(p as *const i64)),
138            Type::F32 => Self::F32(ptr::read(p as *const f32)),
139            Type::F64 => Self::F64(ptr::read(p as *const f64)),
140            Type::V128 => Self::V128(ptr::read(p as *const u128)),
141            Type::FuncRef => {
142                // We do the null check ourselves
143                if (*(p as *const usize)) == 0 {
144                    Self::FuncRef(None)
145                } else {
146                    Self::FuncRef(Some(T::read_value_from(store, p)))
147                }
148            }
149            Type::ExternRef => {
150                let extern_ref = (&*(p as *const ExternRef)).clone();
151                Self::ExternRef(extern_ref)
152            }
153        }
154    }
155
156    accessors! {
157        e
158        (I32(i32) i32 unwrap_i32 *e)
159        (I64(i64) i64 unwrap_i64 *e)
160        (F32(f32) f32 unwrap_f32 *e)
161        (F64(f64) f64 unwrap_f64 *e)
162        (ExternRef(ExternRef) externref unwrap_externref e.clone())
163        (FuncRef(&Option<T>) funcref unwrap_funcref e)
164        (V128(u128) v128 unwrap_v128 *e)
165    }
166}
167
168impl<T> fmt::Debug for Value<T>
169where
170    T: WasmValueType,
171{
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        match self {
174            Self::I32(v) => write!(f, "I32({:?})", v),
175            Self::I64(v) => write!(f, "I64({:?})", v),
176            Self::F32(v) => write!(f, "F32({:?})", v),
177            Self::F64(v) => write!(f, "F64({:?})", v),
178            Self::ExternRef(v) => write!(f, "ExternRef({:?})", v),
179            Self::FuncRef(None) => write!(f, "Null FuncRef"),
180            Self::FuncRef(Some(v)) => write!(f, "FuncRef({:?})", v),
181            Self::V128(v) => write!(f, "V128({:?})", v),
182        }
183    }
184}
185
186impl<T> ToString for Value<T>
187where
188    T: WasmValueType,
189{
190    fn to_string(&self) -> String {
191        match self {
192            Self::I32(v) => v.to_string(),
193            Self::I64(v) => v.to_string(),
194            Self::F32(v) => v.to_string(),
195            Self::F64(v) => v.to_string(),
196            Self::ExternRef(_) => "externref".to_string(),
197            Self::FuncRef(_) => "funcref".to_string(),
198            Self::V128(v) => v.to_string(),
199        }
200    }
201}
202
203impl<T> From<i32> for Value<T>
204where
205    T: WasmValueType,
206{
207    fn from(val: i32) -> Self {
208        Self::I32(val)
209    }
210}
211
212impl<T> From<u32> for Value<T>
213where
214    T: WasmValueType,
215{
216    fn from(val: u32) -> Self {
217        // In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers.
218        Self::I32(val as i32)
219    }
220}
221
222impl<T> From<i64> for Value<T>
223where
224    T: WasmValueType,
225{
226    fn from(val: i64) -> Self {
227        Self::I64(val)
228    }
229}
230
231impl<T> From<u64> for Value<T>
232where
233    T: WasmValueType,
234{
235    fn from(val: u64) -> Self {
236        // In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers.
237        Self::I64(val as i64)
238    }
239}
240
241impl<T> From<f32> for Value<T>
242where
243    T: WasmValueType,
244{
245    fn from(val: f32) -> Self {
246        Self::F32(val)
247    }
248}
249
250impl<T> From<f64> for Value<T>
251where
252    T: WasmValueType,
253{
254    fn from(val: f64) -> Self {
255        Self::F64(val)
256    }
257}
258
259impl<T> From<ExternRef> for Value<T>
260where
261    T: WasmValueType,
262{
263    fn from(val: ExternRef) -> Self {
264        Self::ExternRef(val)
265    }
266}
267
268// impl<T> From<T> for Value<T> {
269//     fn from(val: T) -> Self {
270//         Self::FuncRef(val)
271//     }
272// }
273
274const NOT_I32: &str = "Value is not of Wasm type i32";
275const NOT_I64: &str = "Value is not of Wasm type i64";
276const NOT_F32: &str = "Value is not of Wasm type f32";
277const NOT_F64: &str = "Value is not of Wasm type f64";
278
279impl<T> TryFrom<Value<T>> for i32
280where
281    T: WasmValueType,
282{
283    type Error = &'static str;
284
285    fn try_from(value: Value<T>) -> Result<Self, Self::Error> {
286        value.i32().ok_or(NOT_I32)
287    }
288}
289
290impl<T> TryFrom<Value<T>> for u32
291where
292    T: WasmValueType,
293{
294    type Error = &'static str;
295
296    fn try_from(value: Value<T>) -> Result<Self, Self::Error> {
297        value.i32().ok_or(NOT_I32).map(|int| int as Self)
298    }
299}
300
301impl<T> TryFrom<Value<T>> for i64
302where
303    T: WasmValueType,
304{
305    type Error = &'static str;
306
307    fn try_from(value: Value<T>) -> Result<Self, Self::Error> {
308        value.i64().ok_or(NOT_I64)
309    }
310}
311
312impl<T> TryFrom<Value<T>> for u64
313where
314    T: WasmValueType,
315{
316    type Error = &'static str;
317
318    fn try_from(value: Value<T>) -> Result<Self, Self::Error> {
319        value.i64().ok_or(NOT_I64).map(|int| int as Self)
320    }
321}
322
323impl<T> TryFrom<Value<T>> for f32
324where
325    T: WasmValueType,
326{
327    type Error = &'static str;
328
329    fn try_from(value: Value<T>) -> Result<Self, Self::Error> {
330        value.f32().ok_or(NOT_F32)
331    }
332}
333
334impl<T> TryFrom<Value<T>> for f64
335where
336    T: WasmValueType,
337{
338    type Error = &'static str;
339
340    fn try_from(value: Value<T>) -> Result<Self, Self::Error> {
341        value.f64().ok_or(NOT_F64)
342    }
343}
344
345#[cfg(test)]
346mod tests {
347    use super::*;
348
349    #[test]
350    fn test_value_i32_from_u32() {
351        let bytes = [0x00, 0x00, 0x00, 0x00];
352        let v = Value::<()>::from(u32::from_be_bytes(bytes));
353        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
354
355        let bytes = [0x00, 0x00, 0x00, 0x01];
356        let v = Value::<()>::from(u32::from_be_bytes(bytes));
357        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
358
359        let bytes = [0xAA, 0xBB, 0xCC, 0xDD];
360        let v = Value::<()>::from(u32::from_be_bytes(bytes));
361        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
362
363        let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
364        let v = Value::<()>::from(u32::from_be_bytes(bytes));
365        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
366    }
367
368    #[test]
369    fn test_value_i64_from_u64() {
370        let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
371        let v = Value::<()>::from(u64::from_be_bytes(bytes));
372        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
373
374        let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
375        let v = Value::<()>::from(u64::from_be_bytes(bytes));
376        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
377
378        let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11];
379        let v = Value::<()>::from(u64::from_be_bytes(bytes));
380        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
381
382        let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
383        let v = Value::<()>::from(u64::from_be_bytes(bytes));
384        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
385    }
386
387    #[test]
388    fn convert_value_to_i32() {
389        let value = Value::<()>::I32(5678);
390        let result = i32::try_from(value);
391        assert_eq!(result.unwrap(), 5678);
392
393        let value = Value::<()>::from(u32::MAX);
394        let result = i32::try_from(value);
395        assert_eq!(result.unwrap(), -1);
396
397        let value = Value::<()>::V128(42);
398        let result = i32::try_from(value);
399        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32");
400    }
401
402    #[test]
403    fn convert_value_to_u32() {
404        let value = Value::<()>::from(u32::MAX);
405        let result = u32::try_from(value);
406        assert_eq!(result.unwrap(), u32::MAX);
407
408        let value = Value::<()>::I32(-1);
409        let result = u32::try_from(value);
410        assert_eq!(result.unwrap(), u32::MAX);
411
412        let value = Value::<()>::V128(42);
413        let result = u32::try_from(value);
414        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32");
415    }
416
417    #[test]
418    fn convert_value_to_i64() {
419        let value = Value::<()>::I64(5678);
420        let result = i64::try_from(value);
421        assert_eq!(result.unwrap(), 5678);
422
423        let value = Value::<()>::from(u64::MAX);
424        let result = i64::try_from(value);
425        assert_eq!(result.unwrap(), -1);
426
427        let value = Value::<()>::V128(42);
428        let result = i64::try_from(value);
429        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64");
430    }
431
432    #[test]
433    fn convert_value_to_u64() {
434        let value = Value::<()>::from(u64::MAX);
435        let result = u64::try_from(value);
436        assert_eq!(result.unwrap(), u64::MAX);
437
438        let value = Value::<()>::I64(-1);
439        let result = u64::try_from(value);
440        assert_eq!(result.unwrap(), u64::MAX);
441
442        let value = Value::<()>::V128(42);
443        let result = u64::try_from(value);
444        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64");
445    }
446
447    #[test]
448    fn convert_value_to_f32() {
449        let value = Value::<()>::F32(1.234);
450        let result = f32::try_from(value);
451        assert_eq!(result.unwrap(), 1.234);
452
453        let value = Value::<()>::V128(42);
454        let result = f32::try_from(value);
455        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32");
456
457        let value = Value::<()>::F64(1.234);
458        let result = f32::try_from(value);
459        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32");
460    }
461
462    #[test]
463    fn convert_value_to_f64() {
464        let value = Value::<()>::F64(1.234);
465        let result = f64::try_from(value);
466        assert_eq!(result.unwrap(), 1.234);
467
468        let value = Value::<()>::V128(42);
469        let result = f64::try_from(value);
470        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64");
471
472        let value = Value::<()>::F32(1.234);
473        let result = f64::try_from(value);
474        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64");
475    }
476}