1use crate::store::{AutoAssertNoGc, StoreOpaque};
2use crate::{
3 AnyRef, AsContext, AsContextMut, ExternRef, Func, HeapType, RefType, Rooted, RootedGcRefImpl,
4 ValType, V128,
5};
6use anyhow::{bail, Context, Result};
7use std::ptr;
8use wasmtime_runtime::{TableElement, VMGcRef};
9
10pub use wasmtime_runtime::ValRaw;
11
12#[derive(Debug, Clone)]
18pub enum Val {
19 I32(i32),
24
25 I64(i64),
27
28 F32(u32),
33
34 F64(u64),
39
40 V128(V128),
42
43 FuncRef(Option<Func>),
45
46 ExternRef(Option<Rooted<ExternRef>>),
48
49 AnyRef(Option<Rooted<AnyRef>>),
51}
52
53macro_rules! accessors {
54 ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
55 #[inline]
58 pub fn $get(&self) -> Option<$ty> {
59 if let Val::$variant($bind) = self {
60 Some($cvt)
61 } else {
62 None
63 }
64 }
65
66 #[inline]
73 pub fn $unwrap(&self) -> $ty {
74 self.$get().expect(concat!("expected ", stringify!($ty)))
75 }
76 )*)
77}
78
79impl Val {
80 pub fn null_ref(heap_type: HeapType) -> Val {
82 match heap_type {
83 HeapType::Func | HeapType::NoFunc | HeapType::Concrete(_) => Val::FuncRef(None),
84 HeapType::Extern => Val::ExternRef(None),
85 HeapType::Any | HeapType::I31 | HeapType::None => Val::AnyRef(None),
86 }
87 }
88
89 #[inline]
94 pub const fn null_func_ref() -> Val {
95 Val::FuncRef(None)
96 }
97
98 #[inline]
103 pub const fn null_extern_ref() -> Val {
104 Val::ExternRef(None)
105 }
106
107 #[inline]
112 pub const fn null_any_ref() -> Val {
113 Val::AnyRef(None)
114 }
115
116 #[inline]
118 pub fn ty(&self, store: impl AsContext) -> ValType {
119 self.load_ty(&store.as_context().0)
120 }
121
122 #[inline]
123 pub(crate) fn load_ty(&self, store: &StoreOpaque) -> ValType {
124 match self {
125 Val::I32(_) => ValType::I32,
126 Val::I64(_) => ValType::I64,
127 Val::F32(_) => ValType::F32,
128 Val::F64(_) => ValType::F64,
129 Val::V128(_) => ValType::V128,
130 Val::ExternRef(_) => ValType::EXTERNREF,
131 Val::FuncRef(None) => ValType::NULLFUNCREF,
132 Val::FuncRef(Some(f)) => {
133 ValType::Ref(RefType::new(false, HeapType::Concrete(f.load_ty(store))))
134 }
135 Val::AnyRef(None) => ValType::NULLREF,
136 Val::AnyRef(Some(_)) => {
137 assert!(VMGcRef::ONLY_EXTERN_REF_AND_I31);
138 ValType::Ref(RefType::new(false, HeapType::I31))
139 }
140 }
141 }
142
143 pub fn matches_ty(&self, store: impl AsContext, ty: &ValType) -> Result<bool> {
151 self._matches_ty(&store.as_context().0, ty)
152 }
153
154 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ValType) -> Result<bool> {
155 assert!(self.comes_from_same_store(store));
156 assert!(ty.comes_from_same_engine(store.engine()));
157 Ok(match (self, ty) {
158 (Val::I32(_), ValType::I32)
159 | (Val::I64(_), ValType::I64)
160 | (Val::F32(_), ValType::F32)
161 | (Val::F64(_), ValType::F64)
162 | (Val::V128(_), ValType::V128) => true,
163
164 (Val::FuncRef(f), ValType::Ref(ref_ty)) => {
165 Ref::from(f.clone())._matches_ty(store, ref_ty)?
166 }
167 (Val::ExternRef(e), ValType::Ref(ref_ty)) => {
168 Ref::from(*e)._matches_ty(store, ref_ty)?
169 }
170 (Val::AnyRef(a), ValType::Ref(ref_ty)) => Ref::from(*a)._matches_ty(store, ref_ty)?,
171
172 (Val::I32(_), _)
173 | (Val::I64(_), _)
174 | (Val::F32(_), _)
175 | (Val::F64(_), _)
176 | (Val::V128(_), _)
177 | (Val::FuncRef(_), _)
178 | (Val::ExternRef(_), _)
179 | (Val::AnyRef(_), _) => false,
180 })
181 }
182
183 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ValType) -> Result<()> {
184 if !self.comes_from_same_store(store) {
185 bail!("value used with wrong store")
186 }
187 if !ty.comes_from_same_engine(store.engine()) {
188 bail!("type used with wrong engine")
189 }
190 if self._matches_ty(store, ty)? {
191 Ok(())
192 } else {
193 let actual_ty = self.load_ty(store);
194 bail!("type mismatch: expected {ty}, found {actual_ty}")
195 }
196 }
197
198 pub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<ValRaw> {
208 match self {
209 Val::I32(i) => Ok(ValRaw::i32(*i)),
210 Val::I64(i) => Ok(ValRaw::i64(*i)),
211 Val::F32(u) => Ok(ValRaw::f32(*u)),
212 Val::F64(u) => Ok(ValRaw::f64(*u)),
213 Val::V128(b) => Ok(ValRaw::v128(b.as_u128())),
214 Val::ExternRef(e) => Ok(ValRaw::externref(match e {
215 None => 0,
216 Some(e) => e.to_raw(store)?,
217 })),
218 Val::AnyRef(e) => Ok(ValRaw::anyref(match e {
219 None => 0,
220 Some(e) => e.to_raw(store)?,
221 })),
222 Val::FuncRef(f) => Ok(ValRaw::funcref(match f {
223 Some(f) => f.to_raw(store),
224 None => ptr::null_mut(),
225 })),
226 }
227 }
228
229 pub unsafe fn from_raw(store: impl AsContextMut, raw: ValRaw, ty: ValType) -> Val {
237 match ty {
238 ValType::I32 => Val::I32(raw.get_i32()),
239 ValType::I64 => Val::I64(raw.get_i64()),
240 ValType::F32 => Val::F32(raw.get_f32()),
241 ValType::F64 => Val::F64(raw.get_f64()),
242 ValType::V128 => Val::V128(raw.get_v128().into()),
243 ValType::Ref(ref_ty) => {
244 let ref_ = match ref_ty.heap_type() {
245 HeapType::Func | HeapType::Concrete(_) => {
246 Func::from_raw(store, raw.get_funcref()).into()
247 }
248 HeapType::NoFunc => Ref::Func(None),
249 HeapType::Extern => ExternRef::from_raw(store, raw.get_externref()).into(),
250 HeapType::Any | HeapType::I31 => {
251 AnyRef::from_raw(store, raw.get_anyref()).into()
252 }
253 HeapType::None => Ref::Any(None),
254 };
255 assert!(
256 ref_ty.is_nullable() || !ref_.is_null(),
257 "if the type is not nullable, we shouldn't get null; got \
258 type = {ref_ty}, ref = {ref_:?}"
259 );
260 ref_.into()
261 }
262 }
263 }
264
265 accessors! {
266 e
267 (I32(i32) i32 unwrap_i32 *e)
268 (I64(i64) i64 unwrap_i64 *e)
269 (F32(f32) f32 unwrap_f32 f32::from_bits(*e))
270 (F64(f64) f64 unwrap_f64 f64::from_bits(*e))
271 (FuncRef(Option<&Func>) func_ref unwrap_func_ref e.as_ref())
272 (ExternRef(Option<&Rooted<ExternRef>>) extern_ref unwrap_extern_ref e.as_ref())
273 (AnyRef(Option<&Rooted<AnyRef>>) any_ref unwrap_any_ref e.as_ref())
274 (V128(V128) v128 unwrap_v128 *e)
275 }
276
277 #[inline]
279 pub fn ref_(self) -> Option<Ref> {
280 match self {
281 Val::FuncRef(f) => Some(Ref::Func(f)),
282 Val::ExternRef(e) => Some(Ref::Extern(e)),
283 Val::AnyRef(a) => Some(Ref::Any(a)),
284 Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => None,
285 }
286 }
287
288 #[inline]
296 pub fn externref(&self) -> Option<Option<&Rooted<ExternRef>>> {
297 match self {
298 Val::ExternRef(None) => Some(None),
299 Val::ExternRef(Some(e)) => Some(Some(e)),
300 _ => None,
301 }
302 }
303
304 #[inline]
315 pub fn unwrap_externref(&self) -> Option<&Rooted<ExternRef>> {
316 self.externref().expect("expected externref")
317 }
318
319 #[inline]
327 pub fn anyref(&self) -> Option<Option<&Rooted<AnyRef>>> {
328 match self {
329 Val::AnyRef(None) => Some(None),
330 Val::AnyRef(Some(e)) => Some(Some(e)),
331 _ => None,
332 }
333 }
334
335 #[inline]
346 pub fn unwrap_anyref(&self) -> Option<&Rooted<AnyRef>> {
347 self.anyref().expect("expected anyref")
348 }
349
350 #[inline]
358 pub fn funcref(&self) -> Option<Option<&Func>> {
359 match self {
360 Val::FuncRef(None) => Some(None),
361 Val::FuncRef(Some(f)) => Some(Some(f)),
362 _ => None,
363 }
364 }
365
366 #[inline]
377 pub fn unwrap_funcref(&self) -> Option<&Func> {
378 self.funcref().expect("expected funcref")
379 }
380
381 #[inline]
382 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
383 match self {
384 Val::FuncRef(Some(f)) => f.comes_from_same_store(store),
385 Val::FuncRef(None) => true,
386
387 Val::ExternRef(Some(x)) => x.comes_from_same_store(store),
388 Val::ExternRef(None) => true,
389
390 Val::AnyRef(Some(a)) => a.comes_from_same_store(store),
391 Val::AnyRef(None) => true,
392
393 Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true,
397 }
398 }
399}
400
401impl From<i32> for Val {
402 #[inline]
403 fn from(val: i32) -> Val {
404 Val::I32(val)
405 }
406}
407
408impl From<i64> for Val {
409 #[inline]
410 fn from(val: i64) -> Val {
411 Val::I64(val)
412 }
413}
414
415impl From<f32> for Val {
416 #[inline]
417 fn from(val: f32) -> Val {
418 Val::F32(val.to_bits())
419 }
420}
421
422impl From<f64> for Val {
423 #[inline]
424 fn from(val: f64) -> Val {
425 Val::F64(val.to_bits())
426 }
427}
428
429impl From<Ref> for Val {
430 #[inline]
431 fn from(val: Ref) -> Val {
432 match val {
433 Ref::Extern(e) => Val::ExternRef(e),
434 Ref::Func(f) => Val::FuncRef(f),
435 Ref::Any(a) => Val::AnyRef(a),
436 }
437 }
438}
439
440impl From<Rooted<ExternRef>> for Val {
441 #[inline]
442 fn from(val: Rooted<ExternRef>) -> Val {
443 Val::ExternRef(Some(val))
444 }
445}
446
447impl From<Option<Rooted<ExternRef>>> for Val {
448 #[inline]
449 fn from(val: Option<Rooted<ExternRef>>) -> Val {
450 Val::ExternRef(val)
451 }
452}
453
454impl From<Rooted<AnyRef>> for Val {
455 #[inline]
456 fn from(val: Rooted<AnyRef>) -> Val {
457 Val::AnyRef(Some(val))
458 }
459}
460
461impl From<Option<Rooted<AnyRef>>> for Val {
462 #[inline]
463 fn from(val: Option<Rooted<AnyRef>>) -> Val {
464 Val::AnyRef(val)
465 }
466}
467
468impl From<Func> for Val {
469 #[inline]
470 fn from(val: Func) -> Val {
471 Val::FuncRef(Some(val))
472 }
473}
474
475impl From<Option<Func>> for Val {
476 #[inline]
477 fn from(val: Option<Func>) -> Val {
478 Val::FuncRef(val)
479 }
480}
481
482impl From<u128> for Val {
483 #[inline]
484 fn from(val: u128) -> Val {
485 Val::V128(val.into())
486 }
487}
488
489impl From<V128> for Val {
490 #[inline]
491 fn from(val: V128) -> Val {
492 Val::V128(val)
493 }
494}
495
496#[derive(Debug, Clone)]
521pub enum Ref {
522 Func(Option<Func>),
559
560 Extern(Option<Rooted<ExternRef>>),
570
571 Any(Option<Rooted<AnyRef>>),
580}
581
582impl From<Func> for Ref {
583 #[inline]
584 fn from(f: Func) -> Ref {
585 Ref::Func(Some(f))
586 }
587}
588
589impl From<Option<Func>> for Ref {
590 #[inline]
591 fn from(f: Option<Func>) -> Ref {
592 Ref::Func(f)
593 }
594}
595
596impl From<Rooted<ExternRef>> for Ref {
597 #[inline]
598 fn from(e: Rooted<ExternRef>) -> Ref {
599 Ref::Extern(Some(e))
600 }
601}
602
603impl From<Option<Rooted<ExternRef>>> for Ref {
604 #[inline]
605 fn from(e: Option<Rooted<ExternRef>>) -> Ref {
606 Ref::Extern(e)
607 }
608}
609
610impl From<Rooted<AnyRef>> for Ref {
611 #[inline]
612 fn from(e: Rooted<AnyRef>) -> Ref {
613 Ref::Any(Some(e))
614 }
615}
616
617impl From<Option<Rooted<AnyRef>>> for Ref {
618 #[inline]
619 fn from(e: Option<Rooted<AnyRef>>) -> Ref {
620 Ref::Any(e)
621 }
622}
623
624impl Ref {
625 #[inline]
627 pub fn is_null(&self) -> bool {
628 match self {
629 Ref::Any(None) | Ref::Extern(None) | Ref::Func(None) => true,
630 Ref::Any(Some(_)) | Ref::Extern(Some(_)) | Ref::Func(Some(_)) => false,
631 }
632 }
633
634 #[inline]
636 pub fn is_non_null(&self) -> bool {
637 !self.is_null()
638 }
639
640 #[inline]
642 pub fn is_extern(&self) -> bool {
643 matches!(self, Ref::Extern(_))
644 }
645
646 #[inline]
655 pub fn as_extern(&self) -> Option<Option<&Rooted<ExternRef>>> {
656 match self {
657 Ref::Extern(e) => Some(e.as_ref()),
658 _ => None,
659 }
660 }
661
662 #[inline]
669 pub fn unwrap_extern(&self) -> Option<&Rooted<ExternRef>> {
670 self.as_extern()
671 .expect("Ref::unwrap_extern on non-extern reference")
672 }
673
674 #[inline]
676 pub fn is_any(&self) -> bool {
677 matches!(self, Ref::Any(_))
678 }
679
680 #[inline]
689 pub fn as_any(&self) -> Option<Option<&Rooted<AnyRef>>> {
690 match self {
691 Ref::Any(e) => Some(e.as_ref()),
692 _ => None,
693 }
694 }
695
696 #[inline]
703 pub fn unwrap_any(&self) -> Option<&Rooted<AnyRef>> {
704 self.as_any().expect("Ref::unwrap_any on non-any reference")
705 }
706
707 #[inline]
709 pub fn is_func(&self) -> bool {
710 matches!(self, Ref::Func(_))
711 }
712
713 #[inline]
722 pub fn as_func(&self) -> Option<Option<&Func>> {
723 match self {
724 Ref::Func(f) => Some(f.as_ref()),
725 _ => None,
726 }
727 }
728
729 #[inline]
736 pub fn unwrap_func(&self) -> Option<&Func> {
737 self.as_func()
738 .expect("Ref::unwrap_func on non-func reference")
739 }
740
741 pub fn ty(&self, store: impl AsContext) -> RefType {
747 self.load_ty(&store.as_context().0)
748 }
749
750 pub(crate) fn load_ty(&self, store: &StoreOpaque) -> RefType {
751 assert!(self.comes_from_same_store(store));
752 RefType::new(
753 self.is_null(),
754 match self {
755 Ref::Extern(_) => HeapType::Extern,
756
757 Ref::Func(Some(f)) => HeapType::Concrete(f.load_ty(store)),
761 Ref::Func(None) => HeapType::NoFunc,
762
763 Ref::Any(Some(_)) => {
764 assert!(VMGcRef::ONLY_EXTERN_REF_AND_I31);
765 HeapType::I31
766 }
767 Ref::Any(None) => HeapType::None,
768 },
769 )
770 }
771
772 pub fn matches_ty(&self, store: impl AsContext, ty: &RefType) -> Result<bool> {
780 self._matches_ty(&store.as_context().0, ty)
781 }
782
783 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<bool> {
784 assert!(self.comes_from_same_store(store));
785 assert!(ty.comes_from_same_engine(store.engine()));
786 if self.is_null() && !ty.is_nullable() {
787 return Ok(false);
788 }
789 Ok(match (self, ty.heap_type()) {
790 (Ref::Extern(_), HeapType::Extern) => true,
791 (Ref::Extern(_), _) => false,
792
793 (Ref::Func(_), HeapType::Func) => true,
794 (Ref::Func(None), HeapType::NoFunc | HeapType::Concrete(_)) => true,
795 (Ref::Func(Some(f)), HeapType::Concrete(func_ty)) => f._matches_ty(store, func_ty),
796 (Ref::Func(_), _) => false,
797
798 (Ref::Any(_), HeapType::Any) => true,
799 (Ref::Any(Some(a)), HeapType::I31) => a._is_i31(store)?,
800 (Ref::Any(None), HeapType::None | HeapType::I31) => true,
801 (Ref::Any(_), _) => false,
802 })
803 }
804
805 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<()> {
806 if !self.comes_from_same_store(store) {
807 bail!("reference used with wrong store")
808 }
809 if !ty.comes_from_same_engine(store.engine()) {
810 bail!("type used with wrong engine")
811 }
812 if self._matches_ty(store, ty)? {
813 Ok(())
814 } else {
815 let actual_ty = self.load_ty(store);
816 bail!("type mismatch: expected {ty}, found {actual_ty}")
817 }
818 }
819
820 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
821 match self {
822 Ref::Func(Some(f)) => f.comes_from_same_store(store),
823 Ref::Func(None) => true,
824 Ref::Extern(Some(x)) => x.comes_from_same_store(store),
825 Ref::Extern(None) => true,
826 Ref::Any(Some(a)) => a.comes_from_same_store(store),
827 Ref::Any(None) => true,
828 }
829 }
830
831 pub(crate) fn into_table_element(
832 self,
833 store: &mut StoreOpaque,
834 ty: &RefType,
835 ) -> Result<TableElement> {
836 let mut store = AutoAssertNoGc::new(store);
837 self.ensure_matches_ty(&store, &ty)
838 .context("type mismatch: value does not match table element type")?;
839
840 match (self, ty.heap_type().top(store.engine())) {
841 (Ref::Func(None), HeapType::Func) => {
842 assert!(ty.is_nullable());
843 Ok(TableElement::FuncRef(ptr::null_mut()))
844 }
845 (Ref::Func(Some(f)), HeapType::Func) => {
846 debug_assert!(
847 f.comes_from_same_store(&store),
848 "checked in `ensure_matches_ty`"
849 );
850 Ok(TableElement::FuncRef(f.vm_func_ref(&mut store).as_ptr()))
851 }
852
853 (Ref::Extern(e), HeapType::Extern) => match e {
854 None => {
855 assert!(ty.is_nullable());
856 Ok(TableElement::GcRef(None))
857 }
858 Some(e) => {
859 let gc_ref = e.try_clone_gc_ref(&mut store)?;
860 Ok(TableElement::GcRef(Some(gc_ref)))
861 }
862 },
863
864 (Ref::Any(a), HeapType::Any) => match a {
865 None => {
866 assert!(ty.is_nullable());
867 Ok(TableElement::GcRef(None))
868 }
869 Some(a) => {
870 let gc_ref = a.try_clone_gc_ref(&mut store)?;
871 Ok(TableElement::GcRef(Some(gc_ref)))
872 }
873 },
874
875 _ => unreachable!("checked that the value matches the type above"),
876 }
877 }
878}
879
880#[cfg(test)]
881mod tests {
882 use crate::*;
883
884 #[test]
885 fn size_of_val() {
886 assert_eq!(
889 std::mem::size_of::<Val>(),
890 if cfg!(any(
891 target_arch = "x86_64",
892 target_arch = "aarch64",
893 target_arch = "riscv64"
894 )) {
895 32
896 } else if cfg!(target_arch = "s390x") {
897 24
898 } else {
899 panic!("unsupported architecture")
900 }
901 );
902 }
903
904 #[test]
905 fn size_of_ref() {
906 assert_eq!(std::mem::size_of::<Ref>(), 24);
909 }
910
911 #[test]
912 #[should_panic]
913 fn val_matches_ty_wrong_engine() {
914 let e1 = Engine::default();
915 let e2 = Engine::default();
916
917 let t1 = FuncType::new(&e1, None, None);
918 let t2 = FuncType::new(&e2, None, None);
919
920 let mut s1 = Store::new(&e1, ());
921 let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(()));
922
923 let _ = Val::FuncRef(Some(f)).matches_ty(
925 &s1,
926 &ValType::Ref(RefType::new(true, HeapType::Concrete(t2))),
927 );
928 }
929
930 #[test]
931 #[should_panic]
932 fn ref_matches_ty_wrong_engine() {
933 let e1 = Engine::default();
934 let e2 = Engine::default();
935
936 let t1 = FuncType::new(&e1, None, None);
937 let t2 = FuncType::new(&e2, None, None);
938
939 let mut s1 = Store::new(&e1, ());
940 let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(()));
941
942 let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::Concrete(t2)));
944 }
945}