1 use crate::r#ref::ExternRef;
2 use crate::{Func, Store, ValType};
3 use anyhow::{bail, Result};
4 use std::ptr;
5 use wasmtime_runtime::VMExternRef;
6 
7 /// Possible runtime values that a WebAssembly module can either consume or
8 /// produce.
9 #[derive(Debug, Clone)]
10 pub enum Val {
11     /// A 32-bit integer
12     I32(i32),
13 
14     /// A 64-bit integer
15     I64(i64),
16 
17     /// A 32-bit float.
18     ///
19     /// Note that the raw bits of the float are stored here, and you can use
20     /// `f32::from_bits` to create an `f32` value.
21     F32(u32),
22 
23     /// A 64-bit float.
24     ///
25     /// Note that the raw bits of the float are stored here, and you can use
26     /// `f64::from_bits` to create an `f64` value.
27     F64(u64),
28 
29     /// An `externref` value which can hold opaque data to the wasm instance itself.
30     ExternRef(Option<ExternRef>),
31 
32     /// A first-class reference to a WebAssembly function.
33     FuncRef(Func),
34 
35     /// A 128-bit number
36     V128(u128),
37 }
38 
39 macro_rules! accessors {
40     ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
41         /// Attempt to access the underlying value of this `Val`, returning
42         /// `None` if it is not the correct type.
43         pub fn $get(&self) -> Option<$ty> {
44             if let Val::$variant($bind) = self {
45                 Some($cvt)
46             } else {
47                 None
48             }
49         }
50 
51         /// Returns the underlying value of this `Val`, panicking if it's the
52         /// wrong type.
53         ///
54         /// # Panics
55         ///
56         /// Panics if `self` is not of the right type.
57         pub fn $unwrap(&self) -> $ty {
58             self.$get().expect(concat!("expected ", stringify!($ty)))
59         }
60     )*)
61 }
62 
63 impl Val {
64     /// Returns a null `externref` value.
null() -> Val65     pub fn null() -> Val {
66         Val::ExternRef(None)
67     }
68 
69     /// Returns the corresponding [`ValType`] for this `Val`.
ty(&self) -> ValType70     pub fn ty(&self) -> ValType {
71         match self {
72             Val::I32(_) => ValType::I32,
73             Val::I64(_) => ValType::I64,
74             Val::F32(_) => ValType::F32,
75             Val::F64(_) => ValType::F64,
76             Val::ExternRef(_) => ValType::ExternRef,
77             Val::FuncRef(_) => ValType::FuncRef,
78             Val::V128(_) => ValType::V128,
79         }
80     }
81 
write_value_to(&self, p: *mut u128)82     pub(crate) unsafe fn write_value_to(&self, p: *mut u128) {
83         match self {
84             Val::I32(i) => ptr::write(p as *mut i32, *i),
85             Val::I64(i) => ptr::write(p as *mut i64, *i),
86             Val::F32(u) => ptr::write(p as *mut u32, *u),
87             Val::F64(u) => ptr::write(p as *mut u64, *u),
88             Val::V128(b) => ptr::write(p as *mut u128, *b),
89             Val::ExternRef(None) => ptr::write(p, 0),
90             Val::ExternRef(Some(x)) => ptr::write(p as *mut *mut u8, x.inner.clone().into_raw()),
91             _ => unimplemented!("Val::write_value_to"),
92         }
93     }
94 
read_value_from(store: &Store, p: *const u128, ty: &ValType) -> Val95     pub(crate) unsafe fn read_value_from(store: &Store, p: *const u128, ty: &ValType) -> Val {
96         match ty {
97             ValType::I32 => Val::I32(ptr::read(p as *const i32)),
98             ValType::I64 => Val::I64(ptr::read(p as *const i64)),
99             ValType::F32 => Val::F32(ptr::read(p as *const u32)),
100             ValType::F64 => Val::F64(ptr::read(p as *const u64)),
101             ValType::V128 => Val::V128(ptr::read(p as *const u128)),
102             ValType::ExternRef => {
103                 let raw = ptr::read(p as *const *mut u8);
104                 if raw.is_null() {
105                     Val::ExternRef(None)
106                 } else {
107                     Val::ExternRef(Some(ExternRef {
108                         inner: VMExternRef::from_raw(raw),
109                         store: store.weak(),
110                     }))
111                 }
112             }
113             _ => unimplemented!("Val::read_value_from: {:?}", ty),
114         }
115     }
116 
117     accessors! {
118         e
119         (I32(i32) i32 unwrap_i32 *e)
120         (I64(i64) i64 unwrap_i64 *e)
121         (F32(f32) f32 unwrap_f32 f32::from_bits(*e))
122         (F64(f64) f64 unwrap_f64 f64::from_bits(*e))
123         (FuncRef(&Func) funcref unwrap_funcref e)
124         (V128(u128) v128 unwrap_v128 *e)
125     }
126 
127     /// Attempt to access the underlying `externref` value of this `Val`.
128     ///
129     /// If this is not an `externref`, then `None` is returned.
130     ///
131     /// If this is a null `externref`, then `Some(None)` is returned.
132     ///
133     /// If this is a non-null `externref`, then `Some(Some(..))` is returned.
externref(&self) -> Option<Option<ExternRef>>134     pub fn externref(&self) -> Option<Option<ExternRef>> {
135         match self {
136             Val::ExternRef(e) => Some(e.clone()),
137             _ => None,
138         }
139     }
140 
141     /// Returns the underlying `externref` value of this `Val`, panicking if it's the
142     /// wrong type.
143     ///
144     /// If this is a null `externref`, then `None` is returned.
145     ///
146     /// If this is a non-null `externref`, then `Some(..)` is returned.
147     ///
148     /// # Panics
149     ///
150     /// Panics if `self` is not a (nullable) `externref`.
unwrap_externref(&self) -> Option<ExternRef>151     pub fn unwrap_externref(&self) -> Option<ExternRef> {
152         self.externref().expect("expected externref")
153     }
154 
comes_from_same_store(&self, store: &Store) -> bool155     pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
156         match self {
157             Val::FuncRef(f) => Store::same(store, f.store()),
158 
159             // TODO: need to implement this once we actually finalize what
160             // `externref` will look like and it's actually implemented to pass it
161             // to compiled wasm as well.
162             Val::ExternRef(Some(e)) => e.store.ptr_eq(&store.weak()),
163             Val::ExternRef(None) => true,
164 
165             // Integers have no association with any particular store, so
166             // they're always considered as "yes I came from that store",
167             Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true,
168         }
169     }
170 }
171 
172 impl From<i32> for Val {
from(val: i32) -> Val173     fn from(val: i32) -> Val {
174         Val::I32(val)
175     }
176 }
177 
178 impl From<i64> for Val {
from(val: i64) -> Val179     fn from(val: i64) -> Val {
180         Val::I64(val)
181     }
182 }
183 
184 impl From<f32> for Val {
from(val: f32) -> Val185     fn from(val: f32) -> Val {
186         Val::F32(val.to_bits())
187     }
188 }
189 
190 impl From<f64> for Val {
from(val: f64) -> Val191     fn from(val: f64) -> Val {
192         Val::F64(val.to_bits())
193     }
194 }
195 
196 impl From<ExternRef> for Val {
from(val: ExternRef) -> Val197     fn from(val: ExternRef) -> Val {
198         Val::ExternRef(Some(val))
199     }
200 }
201 
202 impl From<Option<ExternRef>> for Val {
from(val: Option<ExternRef>) -> Val203     fn from(val: Option<ExternRef>) -> Val {
204         Val::ExternRef(val)
205     }
206 }
207 
208 impl From<Func> for Val {
from(val: Func) -> Val209     fn from(val: Func) -> Val {
210         Val::FuncRef(val)
211     }
212 }
213 
into_checked_anyfunc( val: Val, store: &Store, ) -> Result<wasmtime_runtime::VMCallerCheckedAnyfunc>214 pub(crate) fn into_checked_anyfunc(
215     val: Val,
216     store: &Store,
217 ) -> Result<wasmtime_runtime::VMCallerCheckedAnyfunc> {
218     if !val.comes_from_same_store(store) {
219         bail!("cross-`Store` values are not supported");
220     }
221     Ok(match val {
222         Val::ExternRef(None) => wasmtime_runtime::VMCallerCheckedAnyfunc {
223             func_ptr: ptr::null(),
224             type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
225             vmctx: ptr::null_mut(),
226         },
227         Val::FuncRef(f) => {
228             let f = f.wasmtime_function();
229             wasmtime_runtime::VMCallerCheckedAnyfunc {
230                 func_ptr: f.address,
231                 type_index: f.signature,
232                 vmctx: f.vmctx,
233             }
234         }
235         _ => bail!("val is not funcref"),
236     })
237 }
238 
from_checked_anyfunc( item: wasmtime_runtime::VMCallerCheckedAnyfunc, store: &Store, ) -> Val239 pub(crate) fn from_checked_anyfunc(
240     item: wasmtime_runtime::VMCallerCheckedAnyfunc,
241     store: &Store,
242 ) -> Val {
243     if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
244         return Val::ExternRef(None);
245     }
246     let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
247     let export = wasmtime_runtime::ExportFunction {
248         address: item.func_ptr,
249         signature: item.type_index,
250         vmctx: item.vmctx,
251     };
252     let instance = store.existing_instance_handle(instance_handle);
253     let f = Func::from_wasmtime_function(export, instance);
254     Val::FuncRef(f)
255 }
256