wasmer/entities/
value.rs

1use wasmer_types::{RawValue, Type};
2
3use crate::{
4    AsStoreRef, Tag,
5    entities::{Exception, ExternRef, Function},
6    vm::{VMExceptionRef, VMExternRef, VMFuncRef},
7};
8
9/// WebAssembly computations manipulate values of basic value types:
10/// * Integers (32 or 64 bit width)
11/// * Floating-point (32 or 64 bit width)
12/// * Vectors (128 bits, with 32 or 64 bit lanes)
13///
14/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
15#[derive(Clone)]
16pub enum Value {
17    /// A 32-bit integer.
18    ///
19    /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
20    I32(i32),
21
22    /// A 64-bit integer.
23    ///
24    /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
25    I64(i64),
26
27    /// A 32-bit float.
28    F32(f32),
29
30    /// A 64-bit float.
31    F64(f64),
32
33    /// A 128-bit number
34    V128(u128),
35
36    // -- references --
37    /// A nullable `externref` value which can hold opaque data to the wasm instance itself.
38    ExternRef(Option<ExternRef>),
39
40    /// A nullable first-class reference to a WebAssembly function.
41    FuncRef(Option<Function>),
42
43    /// A nullable first-class reference to a WebAssembly exception.
44    ExceptionRef(Option<Exception>),
45}
46
47macro_rules! accessors {
48    ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
49        /// Attempt to access the underlying value of this `Value`, returning
50        /// `None` if it is not the correct type.
51        pub fn $get(&self) -> Option<$ty> {
52            if let Self::$variant($bind) = self {
53                Some($cvt)
54            } else {
55                None
56            }
57        }
58
59        /// Returns the underlying value of this `Value`, panicking if it's the
60        /// wrong type.
61        ///
62        /// # Panics
63        ///
64        /// Panics if `self` is not of the right type.
65        pub fn $unwrap(&self) -> $ty {
66            self.$get().expect(concat!("expected ", stringify!($ty)))
67        }
68    )*)
69}
70
71impl Value {
72    /// Returns a null `externref` value.
73    pub fn null() -> Self {
74        Self::ExternRef(None)
75    }
76
77    /// Returns the corresponding [`Type`] for this [`Value`].
78    pub fn ty(&self) -> Type {
79        match self {
80            Self::I32(_) => Type::I32,
81            Self::I64(_) => Type::I64,
82            Self::F32(_) => Type::F32,
83            Self::F64(_) => Type::F64,
84            Self::V128(_) => Type::V128,
85            Self::ExternRef(_) => Type::ExternRef,
86            Self::FuncRef(_) => Type::FuncRef,
87            Self::ExceptionRef(_) => Type::ExceptionRef,
88        }
89    }
90
91    /// Converts the `Value` into a `RawValue`.
92    pub fn as_raw(&self, store: &impl AsStoreRef) -> RawValue {
93        match *self {
94            Self::I32(i32) => RawValue { i32 },
95            Self::I64(i64) => RawValue { i64 },
96            Self::F32(f32) => RawValue { f32 },
97            Self::F64(f64) => RawValue { f64 },
98            Self::V128(u128) => RawValue { u128 },
99            Self::ExceptionRef(Some(ref f)) => f.vm_exceptionref().into_raw(),
100            Self::ExceptionRef(None) => RawValue { exnref: 0 },
101            Self::FuncRef(Some(ref f)) => f.vm_funcref(store).into_raw(),
102            Self::FuncRef(None) => RawValue { funcref: 0 },
103            Self::ExternRef(Some(ref e)) => e.vm_externref().into_raw(),
104            Self::ExternRef(None) => RawValue { externref: 0 },
105        }
106    }
107
108    /// Converts a `RawValue` to a `Value`.
109    ///
110    /// # Safety
111    ///
112    pub unsafe fn from_raw(
113        store: &mut impl crate::entities::store::AsStoreMut,
114        ty: Type,
115        raw: RawValue,
116    ) -> Self {
117        match ty {
118            Type::I32 => Self::I32(unsafe { raw.i32 }),
119            Type::I64 => Self::I64(unsafe { raw.i64 }),
120            Type::F32 => Self::F32(unsafe { raw.f32 }),
121            Type::F64 => Self::F64(unsafe { raw.f64 }),
122            Type::V128 => Self::V128(unsafe { raw.u128 }),
123            Type::FuncRef => match store.as_store_ref().inner.store {
124                #[cfg(feature = "sys")]
125                crate::BackendStore::Sys(_) => Self::FuncRef({
126                    unsafe { crate::backend::sys::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Sys) }
127                        .map(|f| unsafe { Function::from_vm_funcref(store, f) })
128                }),
129                #[cfg(feature = "wamr")]
130                crate::BackendStore::Wamr(_) => Self::FuncRef({
131                    unsafe {
132                        crate::backend::wamr::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Wamr)
133                    }
134                    .map(|f| unsafe { Function::from_vm_funcref(store, f) })
135                }),
136                #[cfg(feature = "wasmi")]
137                crate::BackendStore::Wasmi(_) => Self::FuncRef({
138                    unsafe {
139                        crate::backend::wasmi::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Wasmi)
140                    }
141                    .map(|f| unsafe { Function::from_vm_funcref(store, f) })
142                }),
143
144                #[cfg(feature = "v8")]
145                crate::BackendStore::V8(_) => Self::FuncRef({
146                    unsafe { crate::backend::v8::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::V8) }
147                        .map(|f| unsafe { Function::from_vm_funcref(store, f) })
148                }),
149                #[cfg(feature = "js")]
150                crate::BackendStore::Js(_) => Self::FuncRef({
151                    unsafe { crate::backend::js::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Js) }
152                        .map(|f| unsafe { Function::from_vm_funcref(store, f) })
153                }),
154                #[cfg(feature = "jsc")]
155                crate::BackendStore::Jsc(_) => Self::FuncRef({
156                    unsafe { crate::backend::jsc::vm::VMFuncRef::from_raw(raw).map(VMFuncRef::Jsc) }
157                        .map(|f| unsafe { Function::from_vm_funcref(store, f) })
158                }),
159            },
160            Type::ExternRef => match store.as_store_ref().inner.store {
161                #[cfg(feature = "sys")]
162                crate::BackendStore::Sys(_) => Self::ExternRef({
163                    unsafe {
164                        crate::backend::sys::vm::VMExternRef::from_raw(raw).map(VMExternRef::Sys)
165                    }
166                    .map(|f| unsafe { ExternRef::from_vm_externref(store, f) })
167                }),
168                #[cfg(feature = "wamr")]
169                crate::BackendStore::Wamr(_) => Self::ExternRef({
170                    unsafe {
171                        crate::backend::wamr::vm::VMExternRef::from_raw(raw).map(VMExternRef::Wamr)
172                    }
173                    .map(|f| unsafe { ExternRef::from_vm_externref(store, f) })
174                }),
175                #[cfg(feature = "wasmi")]
176                crate::BackendStore::Wasmi(_) => Self::ExternRef({
177                    unsafe {
178                        crate::backend::wasmi::vm::VMExternRef::from_raw(raw)
179                            .map(VMExternRef::Wasmi)
180                    }
181                    .map(|f| unsafe { ExternRef::from_vm_externref(store, f) })
182                }),
183
184                #[cfg(feature = "v8")]
185                crate::BackendStore::V8(_) => Self::ExternRef({
186                    unsafe {
187                        crate::backend::v8::vm::VMExternRef::from_raw(raw).map(VMExternRef::V8)
188                    }
189                    .map(|f| unsafe { ExternRef::from_vm_externref(store, f) })
190                }),
191                #[cfg(feature = "js")]
192                crate::BackendStore::Js(_) => Self::ExternRef({
193                    unsafe {
194                        crate::backend::js::vm::VMExternRef::from_raw(raw).map(VMExternRef::Js)
195                    }
196                    .map(|f| unsafe { ExternRef::from_vm_externref(store, f) })
197                }),
198                #[cfg(feature = "jsc")]
199                crate::BackendStore::Jsc(_) => Self::ExternRef({
200                    unsafe {
201                        crate::backend::jsc::vm::VMExternRef::from_raw(raw).map(VMExternRef::Jsc)
202                    }
203                    .map(|f| unsafe { ExternRef::from_vm_externref(store, f) })
204                }),
205            },
206            Type::ExceptionRef => match store.as_store_ref().inner.store {
207                #[cfg(feature = "sys")]
208                crate::BackendStore::Sys(_) => Self::ExceptionRef(
209                    unsafe {
210                        crate::backend::sys::vm::VMExceptionRef::from_raw(
211                            store.as_store_ref().objects().id(),
212                            raw,
213                        )
214                    }
215                    .map(VMExceptionRef::Sys)
216                    .map(Exception::from_vm_exceptionref),
217                ),
218                #[cfg(feature = "wamr")]
219                crate::BackendStore::Wamr(_) => Self::ExceptionRef(
220                    unsafe { crate::backend::wamr::vm::VMExceptionRef::from_raw(raw) }
221                        .map(VMExceptionRef::Wamr)
222                        .map(Exception::from_vm_exceptionref),
223                ),
224                #[cfg(feature = "wasmi")]
225                crate::BackendStore::Wasmi(_) => Self::ExceptionRef(
226                    unsafe { crate::backend::wasmi::vm::VMExceptionRef::from_raw(raw) }
227                        .map(VMExceptionRef::Wasmi)
228                        .map(Exception::from_vm_exceptionref),
229                ),
230
231                #[cfg(feature = "v8")]
232                crate::BackendStore::V8(_) => Self::ExceptionRef(
233                    unsafe { crate::backend::v8::vm::VMExceptionRef::from_raw(raw) }
234                        .map(VMExceptionRef::V8)
235                        .map(Exception::from_vm_exceptionref),
236                ),
237                #[cfg(feature = "js")]
238                crate::BackendStore::Js(_) => Self::ExceptionRef(
239                    unsafe { crate::backend::js::vm::VMExceptionRef::from_raw(raw) }
240                        .map(VMExceptionRef::Js)
241                        .map(Exception::from_vm_exceptionref),
242                ),
243                #[cfg(feature = "jsc")]
244                crate::BackendStore::Jsc(_) => Self::ExceptionRef(
245                    unsafe { crate::backend::jsc::vm::VMExceptionRef::from_raw(raw) }
246                        .map(VMExceptionRef::Jsc)
247                        .map(Exception::from_vm_exceptionref),
248                ),
249            },
250        }
251    }
252
253    /// Checks whether a value can be used with the given context.
254    ///
255    /// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not
256    /// tied to a context and can be freely shared between contexts.
257    ///
258    /// Externref and funcref values are tied to a context and can only be used
259    /// with that context.
260    pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
261        match self {
262            Self::I32(_)
263            | Self::I64(_)
264            | Self::F32(_)
265            | Self::F64(_)
266            | Self::V128(_)
267            | Self::ExternRef(None)
268            | Self::ExceptionRef(None)
269            | Self::FuncRef(None) => true,
270            Self::ExternRef(Some(e)) => e.is_from_store(store),
271            Self::ExceptionRef(Some(e)) => e.is_from_store(store),
272            Self::FuncRef(Some(f)) => f.is_from_store(store),
273        }
274    }
275
276    accessors! {
277        e
278        (I32(i32) i32 unwrap_i32 *e)
279        (I64(i64) i64 unwrap_i64 *e)
280        (F32(f32) f32 unwrap_f32 *e)
281        (F64(f64) f64 unwrap_f64 *e)
282        (ExternRef(&Option<ExternRef>) externref unwrap_externref e)
283        (FuncRef(&Option<Function>) funcref unwrap_funcref e)
284        (V128(u128) v128 unwrap_v128 *e)
285    }
286}
287
288impl std::fmt::Debug for Value {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        match self {
291            Self::I32(v) => write!(f, "I32({v:?})"),
292            Self::I64(v) => write!(f, "I64({v:?})"),
293            Self::F32(v) => write!(f, "F32({v:?})"),
294            Self::F64(v) => write!(f, "F64({v:?})"),
295            Self::ExceptionRef(None) => write!(f, "Null ExceptionRef"),
296            Self::ExceptionRef(Some(v)) => write!(f, "ExceptionRef({v:?})"),
297            Self::ExternRef(None) => write!(f, "Null ExternRef"),
298            Self::ExternRef(Some(v)) => write!(f, "ExternRef({v:?})"),
299            Self::FuncRef(None) => write!(f, "Null FuncRef"),
300            Self::FuncRef(Some(v)) => write!(f, "FuncRef({v:?})"),
301            Self::V128(v) => write!(f, "V128({v:?})"),
302        }
303    }
304}
305
306impl std::fmt::Display for Value {
307    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308        write!(
309            f,
310            "{}",
311            match self {
312                Self::I32(v) => v.to_string(),
313                Self::I64(v) => v.to_string(),
314                Self::F32(v) => v.to_string(),
315                Self::F64(v) => v.to_string(),
316                Self::ExceptionRef(_) => "exnref".to_string(),
317                Self::ExternRef(_) => "externref".to_string(),
318                Self::FuncRef(_) => "funcref".to_string(),
319                Self::V128(v) => v.to_string(),
320            }
321        )
322    }
323}
324
325impl PartialEq for Value {
326    fn eq(&self, o: &Self) -> bool {
327        match (self, o) {
328            (Self::I32(a), Self::I32(b)) => a == b,
329            (Self::I64(a), Self::I64(b)) => a == b,
330            (Self::F32(a), Self::F32(b)) => a == b,
331            (Self::F64(a), Self::F64(b)) => a == b,
332            (Self::V128(a), Self::V128(b)) => a == b,
333            _ => false,
334        }
335    }
336}
337
338impl From<i32> for Value {
339    fn from(val: i32) -> Self {
340        Self::I32(val)
341    }
342}
343
344impl From<u32> for Value {
345    fn from(val: u32) -> Self {
346        // In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers.
347        Self::I32(val as i32)
348    }
349}
350
351impl From<i64> for Value {
352    fn from(val: i64) -> Self {
353        Self::I64(val)
354    }
355}
356
357impl From<u64> for Value {
358    fn from(val: u64) -> Self {
359        // In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers.
360        Self::I64(val as i64)
361    }
362}
363
364impl From<f32> for Value {
365    fn from(val: f32) -> Self {
366        Self::F32(val)
367    }
368}
369
370impl From<f64> for Value {
371    fn from(val: f64) -> Self {
372        Self::F64(val)
373    }
374}
375
376impl From<Function> for Value {
377    fn from(val: Function) -> Self {
378        Self::FuncRef(Some(val))
379    }
380}
381
382impl From<Option<Function>> for Value {
383    fn from(val: Option<Function>) -> Self {
384        Self::FuncRef(val)
385    }
386}
387
388impl From<ExternRef> for Value {
389    fn from(val: ExternRef) -> Self {
390        Self::ExternRef(Some(val))
391    }
392}
393
394impl From<Option<ExternRef>> for Value {
395    fn from(val: Option<ExternRef>) -> Self {
396        Self::ExternRef(val)
397    }
398}
399
400impl From<Exception> for Value {
401    fn from(val: Exception) -> Self {
402        Self::ExceptionRef(Some(val))
403    }
404}
405
406impl From<Option<Exception>> for Value {
407    fn from(val: Option<Exception>) -> Self {
408        Self::ExceptionRef(val)
409    }
410}
411
412const NOT_I32: &str = "Value is not of Wasm type i32";
413const NOT_I64: &str = "Value is not of Wasm type i64";
414const NOT_F32: &str = "Value is not of Wasm type f32";
415const NOT_F64: &str = "Value is not of Wasm type f64";
416const NOT_FUNCREF: &str = "Value is not of Wasm type funcref";
417const NOT_EXTERNREF: &str = "Value is not of Wasm type externref";
418const NOT_EXCEPTIONREF: &str = "Value is not of Wasm type exceptionref";
419
420impl TryFrom<Value> for i32 {
421    type Error = &'static str;
422
423    fn try_from(value: Value) -> Result<Self, Self::Error> {
424        value.i32().ok_or(NOT_I32)
425    }
426}
427
428impl TryFrom<Value> for u32 {
429    type Error = &'static str;
430
431    fn try_from(value: Value) -> Result<Self, Self::Error> {
432        value.i32().ok_or(NOT_I32).map(|int| int as Self)
433    }
434}
435
436impl TryFrom<Value> for i64 {
437    type Error = &'static str;
438
439    fn try_from(value: Value) -> Result<Self, Self::Error> {
440        value.i64().ok_or(NOT_I64)
441    }
442}
443
444impl TryFrom<Value> for u64 {
445    type Error = &'static str;
446
447    fn try_from(value: Value) -> Result<Self, Self::Error> {
448        value.i64().ok_or(NOT_I64).map(|int| int as Self)
449    }
450}
451
452impl TryFrom<Value> for f32 {
453    type Error = &'static str;
454
455    fn try_from(value: Value) -> Result<Self, Self::Error> {
456        value.f32().ok_or(NOT_F32)
457    }
458}
459
460impl TryFrom<Value> for f64 {
461    type Error = &'static str;
462
463    fn try_from(value: Value) -> Result<Self, Self::Error> {
464        value.f64().ok_or(NOT_F64)
465    }
466}
467
468impl TryFrom<Value> for Option<Function> {
469    type Error = &'static str;
470
471    fn try_from(value: Value) -> Result<Self, Self::Error> {
472        match value {
473            Value::FuncRef(f) => Ok(f),
474            _ => Err(NOT_FUNCREF),
475        }
476    }
477}
478
479impl TryFrom<Value> for Option<ExternRef> {
480    type Error = &'static str;
481
482    fn try_from(value: Value) -> Result<Self, Self::Error> {
483        match value {
484            Value::ExternRef(e) => Ok(e),
485            _ => Err(NOT_EXTERNREF),
486        }
487    }
488}
489
490impl TryFrom<Value> for Option<Exception> {
491    type Error = &'static str;
492
493    fn try_from(value: Value) -> Result<Self, Self::Error> {
494        match value {
495            Value::ExceptionRef(e) => Ok(e),
496            _ => Err(NOT_EXCEPTIONREF),
497        }
498    }
499}
500
501#[cfg(test)]
502mod tests {
503    use super::*;
504
505    #[test]
506    fn test_value_i32_from_u32() {
507        let bytes = [0x00, 0x00, 0x00, 0x00];
508        let v = Value::from(u32::from_be_bytes(bytes));
509        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
510
511        let bytes = [0x00, 0x00, 0x00, 0x01];
512        let v = Value::from(u32::from_be_bytes(bytes));
513        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
514
515        let bytes = [0xAA, 0xBB, 0xCC, 0xDD];
516        let v = Value::from(u32::from_be_bytes(bytes));
517        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
518
519        let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
520        let v = Value::from(u32::from_be_bytes(bytes));
521        assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
522    }
523
524    #[test]
525    fn test_value_i64_from_u64() {
526        let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
527        let v = Value::from(u64::from_be_bytes(bytes));
528        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
529
530        let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
531        let v = Value::from(u64::from_be_bytes(bytes));
532        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
533
534        let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11];
535        let v = Value::from(u64::from_be_bytes(bytes));
536        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
537
538        let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
539        let v = Value::from(u64::from_be_bytes(bytes));
540        assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
541    }
542
543    #[test]
544    fn convert_value_to_i32() {
545        let value = Value::I32(5678);
546        let result = i32::try_from(value);
547        assert_eq!(result.unwrap(), 5678);
548
549        let value = Value::from(u32::MAX);
550        let result = i32::try_from(value);
551        assert_eq!(result.unwrap(), -1);
552
553        let value = Value::V128(42);
554        let result = i32::try_from(value);
555        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32");
556    }
557
558    #[test]
559    fn convert_value_to_u32() {
560        let value = Value::from(u32::MAX);
561        let result = u32::try_from(value);
562        assert_eq!(result.unwrap(), u32::MAX);
563
564        let value = Value::I32(-1);
565        let result = u32::try_from(value);
566        assert_eq!(result.unwrap(), u32::MAX);
567
568        let value = Value::V128(42);
569        let result = u32::try_from(value);
570        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32");
571    }
572
573    #[test]
574    fn convert_value_to_i64() {
575        let value = Value::I64(5678);
576        let result = i64::try_from(value);
577        assert_eq!(result.unwrap(), 5678);
578
579        let value = Value::from(u64::MAX);
580        let result = i64::try_from(value);
581        assert_eq!(result.unwrap(), -1);
582
583        let value = Value::V128(42);
584        let result = i64::try_from(value);
585        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64");
586    }
587
588    #[test]
589    fn convert_value_to_u64() {
590        let value = Value::from(u64::MAX);
591        let result = u64::try_from(value);
592        assert_eq!(result.unwrap(), u64::MAX);
593
594        let value = Value::I64(-1);
595        let result = u64::try_from(value);
596        assert_eq!(result.unwrap(), u64::MAX);
597
598        let value = Value::V128(42);
599        let result = u64::try_from(value);
600        assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64");
601    }
602
603    #[test]
604    fn convert_value_to_f32() {
605        let value = Value::F32(1.234);
606        let result = f32::try_from(value);
607        assert_eq!(result.unwrap(), 1.234);
608
609        let value = Value::V128(42);
610        let result = f32::try_from(value);
611        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32");
612
613        let value = Value::F64(1.234);
614        let result = f32::try_from(value);
615        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32");
616    }
617
618    #[test]
619    fn convert_value_to_f64() {
620        let value = Value::F64(1.234);
621        let result = f64::try_from(value);
622        assert_eq!(result.unwrap(), 1.234);
623
624        let value = Value::V128(42);
625        let result = f64::try_from(value);
626        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64");
627
628        let value = Value::F32(1.234);
629        let result = f64::try_from(value);
630        assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64");
631    }
632}