1use crate::runtime::vm::VMGcRef;
4use crate::store::StoreId;
5use crate::vm::{VMGcHeader, VMStructRef};
6use crate::{AnyRef, FieldType};
7use crate::{
8 AsContext, AsContextMut, EqRef, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType,
9 ManuallyRooted, RefType, Rooted, StructType, Val, ValRaw, ValType, WasmTy,
10 prelude::*,
11 store::{AutoAssertNoGc, StoreContextMut, StoreOpaque},
12};
13use core::mem::{self, MaybeUninit};
14use wasmtime_environ::{GcLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex};
15
16pub struct StructRefPre {
62 store_id: StoreId,
63 ty: StructType,
64}
65
66impl StructRefPre {
67 pub fn new(mut store: impl AsContextMut, ty: StructType) -> Self {
75 Self::_new(store.as_context_mut().0, ty)
76 }
77
78 pub(crate) fn _new(store: &mut StoreOpaque, ty: StructType) -> Self {
79 store.insert_gc_host_alloc_type(ty.registered_type().clone());
80 let store_id = store.id();
81
82 StructRefPre { store_id, ty }
83 }
84
85 pub(crate) fn layout(&self) -> &GcStructLayout {
86 self.ty
87 .registered_type()
88 .layout()
89 .expect("struct types have a layout")
90 .unwrap_struct()
91 }
92
93 pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
94 self.ty.registered_type().index()
95 }
96}
97
98#[derive(Debug)]
157#[repr(transparent)]
158pub struct StructRef {
159 pub(super) inner: GcRootIndex,
160}
161
162unsafe impl GcRefImpl for StructRef {
163 fn transmute_ref(index: &GcRootIndex) -> &Self {
164 let me: &Self = unsafe { mem::transmute(index) };
166
167 assert!(matches!(
169 me,
170 Self {
171 inner: GcRootIndex { .. },
172 }
173 ));
174
175 me
176 }
177}
178
179impl Rooted<StructRef> {
180 #[inline]
182 pub fn to_anyref(self) -> Rooted<AnyRef> {
183 self.unchecked_cast()
184 }
185
186 #[inline]
188 pub fn to_eqref(self) -> Rooted<EqRef> {
189 self.unchecked_cast()
190 }
191}
192
193impl ManuallyRooted<StructRef> {
194 #[inline]
196 pub fn to_anyref(self) -> ManuallyRooted<AnyRef> {
197 self.unchecked_cast()
198 }
199
200 #[inline]
202 pub fn to_eqref(self) -> ManuallyRooted<EqRef> {
203 self.unchecked_cast()
204 }
205}
206
207impl StructRef {
208 pub fn new(
235 mut store: impl AsContextMut,
236 allocator: &StructRefPre,
237 fields: &[Val],
238 ) -> Result<Rooted<StructRef>> {
239 Self::_new(store.as_context_mut().0, allocator, fields)
240 }
241
242 pub(crate) fn _new(
243 store: &mut StoreOpaque,
244 allocator: &StructRefPre,
245 fields: &[Val],
246 ) -> Result<Rooted<StructRef>> {
247 assert!(
248 !store.async_support(),
249 "use `StructRef::new_async` with asynchronous stores"
250 );
251 Self::type_check_fields(store, allocator, fields)?;
252 store.retry_after_gc((), |store, ()| {
253 Self::new_unchecked(store, allocator, fields)
254 })
255 }
256
257 #[cfg(feature = "async")]
284 pub async fn new_async(
285 mut store: impl AsContextMut,
286 allocator: &StructRefPre,
287 fields: &[Val],
288 ) -> Result<Rooted<StructRef>> {
289 Self::_new_async(store.as_context_mut().0, allocator, fields).await
290 }
291
292 #[cfg(feature = "async")]
293 pub(crate) async fn _new_async(
294 store: &mut StoreOpaque,
295 allocator: &StructRefPre,
296 fields: &[Val],
297 ) -> Result<Rooted<StructRef>> {
298 assert!(
299 store.async_support(),
300 "use `StructRef::new` with synchronous stores"
301 );
302 Self::type_check_fields(store, allocator, fields)?;
303 store
304 .retry_after_gc_async((), |store, ()| {
305 Self::new_unchecked(store, allocator, fields)
306 })
307 .await
308 }
309
310 pub(crate) unsafe fn new_maybe_async(
313 store: &mut StoreOpaque,
314 allocator: &StructRefPre,
315 fields: &[Val],
316 ) -> Result<Rooted<StructRef>> {
317 Self::type_check_fields(store, allocator, fields)?;
318 unsafe {
319 store.retry_after_gc_maybe_async((), |store, ()| {
320 Self::new_unchecked(store, allocator, fields)
321 })
322 }
323 }
324
325 fn type_check_fields(
327 store: &mut StoreOpaque,
328 allocator: &StructRefPre,
329 fields: &[Val],
330 ) -> Result<(), Error> {
331 let expected_len = allocator.ty.fields().len();
332 let actual_len = fields.len();
333 ensure!(
334 actual_len == expected_len,
335 "expected {expected_len} fields, got {actual_len}"
336 );
337 for (ty, val) in allocator.ty.fields().zip(fields) {
338 assert!(
339 val.comes_from_same_store(store),
340 "field value comes from the wrong store",
341 );
342 let ty = ty.element_type().unpack();
343 val.ensure_matches_ty(store, ty)
344 .context("field type mismatch")?;
345 }
346 Ok(())
347 }
348
349 fn new_unchecked(
354 store: &mut StoreOpaque,
355 allocator: &StructRefPre,
356 fields: &[Val],
357 ) -> Result<Rooted<StructRef>> {
358 assert_eq!(
359 store.id(),
360 allocator.store_id,
361 "attempted to use a `StructRefPre` with the wrong store"
362 );
363
364 let structref = store
367 .gc_store_mut()?
368 .alloc_uninit_struct(allocator.type_index(), &allocator.layout())
369 .context("unrecoverable error when allocating new `structref`")?
370 .map_err(|n| GcHeapOutOfMemory::new((), n))?;
371
372 let mut store = AutoAssertNoGc::new(store);
377 match (|| {
378 for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() {
379 structref.initialize_field(
380 &mut store,
381 allocator.layout(),
382 ty.element_type(),
383 index,
384 *val,
385 )?;
386 }
387 Ok(())
388 })() {
389 Ok(()) => Ok(Rooted::new(&mut store, structref.into())),
390 Err(e) => {
391 store.gc_store_mut()?.dealloc_uninit_struct(structref);
392 Err(e)
393 }
394 }
395 }
396
397 #[inline]
398 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
399 self.inner.comes_from_same_store(store)
400 }
401
402 pub fn ty(&self, store: impl AsContext) -> Result<StructType> {
412 self._ty(store.as_context().0)
413 }
414
415 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<StructType> {
416 assert!(self.comes_from_same_store(store));
417 let index = self.type_index(store)?;
418 Ok(StructType::from_shared_type_index(store.engine(), index))
419 }
420
421 pub fn matches_ty(&self, store: impl AsContext, ty: &StructType) -> Result<bool> {
434 self._matches_ty(store.as_context().0, ty)
435 }
436
437 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<bool> {
438 assert!(self.comes_from_same_store(store));
439 Ok(self._ty(store)?.matches(ty))
440 }
441
442 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<()> {
443 if !self.comes_from_same_store(store) {
444 bail!("function used with wrong store");
445 }
446 if self._matches_ty(store, ty)? {
447 Ok(())
448 } else {
449 let actual_ty = self._ty(store)?;
450 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
451 }
452 }
453
454 pub fn fields<'a, T: 'static>(
467 &'a self,
468 store: impl Into<StoreContextMut<'a, T>>,
469 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
470 self._fields(store.into().0)
471 }
472
473 pub(crate) fn _fields<'a>(
474 &'a self,
475 store: &'a mut StoreOpaque,
476 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
477 assert!(self.comes_from_same_store(store));
478 let store = AutoAssertNoGc::new(store);
479
480 let gc_ref = self.inner.try_gc_ref(&store)?;
481 let header = store.gc_store()?.header(gc_ref);
482 debug_assert!(header.kind().matches(VMGcKind::StructRef));
483
484 let index = header.ty().expect("structrefs should have concrete types");
485 let ty = StructType::from_shared_type_index(store.engine(), index);
486 let len = ty.fields().len();
487
488 return Ok(Fields {
489 structref: self,
490 store,
491 index: 0,
492 len,
493 });
494
495 struct Fields<'a, 'b> {
496 structref: &'a StructRef,
497 store: AutoAssertNoGc<'b>,
498 index: usize,
499 len: usize,
500 }
501
502 impl Iterator for Fields<'_, '_> {
503 type Item = Val;
504
505 #[inline]
506 fn next(&mut self) -> Option<Self::Item> {
507 let i = self.index;
508 debug_assert!(i <= self.len);
509 if i >= self.len {
510 return None;
511 }
512 self.index += 1;
513 Some(self.structref._field(&mut self.store, i).unwrap())
514 }
515
516 #[inline]
517 fn size_hint(&self) -> (usize, Option<usize>) {
518 let len = self.len - self.index;
519 (len, Some(len))
520 }
521 }
522
523 impl ExactSizeIterator for Fields<'_, '_> {
524 #[inline]
525 fn len(&self) -> usize {
526 self.len - self.index
527 }
528 }
529 }
530
531 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
532 assert!(self.comes_from_same_store(&store));
533 let gc_ref = self.inner.try_gc_ref(store)?;
534 Ok(store.gc_store()?.header(gc_ref))
535 }
536
537 fn structref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMStructRef> {
538 assert!(self.comes_from_same_store(&store));
539 let gc_ref = self.inner.try_gc_ref(store)?;
540 debug_assert!(self.header(store)?.kind().matches(VMGcKind::StructRef));
541 Ok(gc_ref.as_structref_unchecked())
542 }
543
544 fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcStructLayout> {
545 assert!(self.comes_from_same_store(&store));
546 let type_index = self.type_index(store)?;
547 let layout = store
548 .engine()
549 .signatures()
550 .layout(type_index)
551 .expect("struct types should have GC layouts");
552 match layout {
553 GcLayout::Struct(s) => Ok(s),
554 GcLayout::Array(_) => unreachable!(),
555 GcLayout::Exception(_) => unreachable!(),
556 }
557 }
558
559 fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> {
560 let ty = self._ty(store)?;
561 match ty.field(field) {
562 Some(f) => Ok(f),
563 None => {
564 let len = ty.fields().len();
565 bail!("cannot access field {field}: struct only has {len} fields")
566 }
567 }
568 }
569
570 pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> {
584 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
585 self._field(&mut store, index)
586 }
587
588 pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> {
589 assert!(self.comes_from_same_store(store));
590 let structref = self.structref(store)?.unchecked_copy();
591 let field_ty = self.field_ty(store, index)?;
592 let layout = self.layout(store)?;
593 Ok(structref.read_field(store, &layout, field_ty.element_type(), index))
594 }
595
596 pub fn set_field(&self, mut store: impl AsContextMut, index: usize, value: Val) -> Result<()> {
616 self._set_field(store.as_context_mut().0, index, value)
617 }
618
619 pub(crate) fn _set_field(
620 &self,
621 store: &mut StoreOpaque,
622 index: usize,
623 value: Val,
624 ) -> Result<()> {
625 assert!(self.comes_from_same_store(store));
626 let mut store = AutoAssertNoGc::new(store);
627
628 let field_ty = self.field_ty(&store, index)?;
629 ensure!(
630 field_ty.mutability().is_var(),
631 "cannot set field {index}: field is not mutable"
632 );
633
634 value
635 .ensure_matches_ty(&store, &field_ty.element_type().unpack())
636 .with_context(|| format!("cannot set field {index}: type mismatch"))?;
637
638 let layout = self.layout(&store)?;
639 let structref = self.structref(&store)?.unchecked_copy();
640
641 structref.write_field(&mut store, &layout, field_ty.element_type(), index, value)
642 }
643
644 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
645 let gc_ref = self.inner.try_gc_ref(store)?;
646 let header = store.gc_store()?.header(gc_ref);
647 debug_assert!(header.kind().matches(VMGcKind::StructRef));
648 Ok(header.ty().expect("structrefs should have concrete types"))
649 }
650
651 pub(crate) fn from_cloned_gc_ref(
657 store: &mut AutoAssertNoGc<'_>,
658 gc_ref: VMGcRef,
659 ) -> Rooted<Self> {
660 debug_assert!(gc_ref.is_structref(&*store.unwrap_gc_store().gc_heap));
661 Rooted::new(store, gc_ref)
662 }
663}
664
665unsafe impl WasmTy for Rooted<StructRef> {
666 #[inline]
667 fn valtype() -> ValType {
668 ValType::Ref(RefType::new(false, HeapType::Struct))
669 }
670
671 #[inline]
672 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
673 self.comes_from_same_store(store)
674 }
675
676 #[inline]
677 fn dynamic_concrete_type_check(
678 &self,
679 store: &StoreOpaque,
680 _nullable: bool,
681 ty: &HeapType,
682 ) -> Result<()> {
683 match ty {
684 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
685 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
686
687 HeapType::Extern
688 | HeapType::NoExtern
689 | HeapType::Func
690 | HeapType::ConcreteFunc(_)
691 | HeapType::NoFunc
692 | HeapType::I31
693 | HeapType::Array
694 | HeapType::ConcreteArray(_)
695 | HeapType::None
696 | HeapType::NoCont
697 | HeapType::Cont
698 | HeapType::ConcreteCont(_)
699 | HeapType::NoExn
700 | HeapType::Exn
701 | HeapType::ConcreteExn(_) => bail!(
702 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
703 self._ty(store)?,
704 ),
705 }
706 }
707
708 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
709 self.wasm_ty_store(store, ptr, ValRaw::anyref)
710 }
711
712 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
713 Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
714 }
715}
716
717unsafe impl WasmTy for Option<Rooted<StructRef>> {
718 #[inline]
719 fn valtype() -> ValType {
720 ValType::STRUCTREF
721 }
722
723 #[inline]
724 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
725 self.map_or(true, |x| x.comes_from_same_store(store))
726 }
727
728 #[inline]
729 fn dynamic_concrete_type_check(
730 &self,
731 store: &StoreOpaque,
732 nullable: bool,
733 ty: &HeapType,
734 ) -> Result<()> {
735 match self {
736 Some(s) => Rooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty),
737 None => {
738 ensure!(
739 nullable,
740 "expected a non-null reference, but found a null reference"
741 );
742 Ok(())
743 }
744 }
745 }
746
747 #[inline]
748 fn is_vmgcref_and_points_to_object(&self) -> bool {
749 self.is_some()
750 }
751
752 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
753 <Rooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
754 }
755
756 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
757 <Rooted<StructRef>>::wasm_ty_option_load(
758 store,
759 ptr.get_anyref(),
760 StructRef::from_cloned_gc_ref,
761 )
762 }
763}
764
765unsafe impl WasmTy for ManuallyRooted<StructRef> {
766 #[inline]
767 fn valtype() -> ValType {
768 ValType::Ref(RefType::new(false, HeapType::Struct))
769 }
770
771 #[inline]
772 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
773 self.comes_from_same_store(store)
774 }
775
776 #[inline]
777 fn dynamic_concrete_type_check(
778 &self,
779 store: &StoreOpaque,
780 _: bool,
781 ty: &HeapType,
782 ) -> Result<()> {
783 match ty {
784 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
785 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
786
787 HeapType::Extern
788 | HeapType::NoExtern
789 | HeapType::Func
790 | HeapType::ConcreteFunc(_)
791 | HeapType::NoFunc
792 | HeapType::I31
793 | HeapType::Array
794 | HeapType::ConcreteArray(_)
795 | HeapType::None
796 | HeapType::NoCont
797 | HeapType::Cont
798 | HeapType::ConcreteCont(_)
799 | HeapType::NoExn
800 | HeapType::Exn
801 | HeapType::ConcreteExn(_) => bail!(
802 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
803 self._ty(store)?,
804 ),
805 }
806 }
807
808 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
809 self.wasm_ty_store(store, ptr, ValRaw::anyref)
810 }
811
812 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
813 Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
814 }
815}
816
817unsafe impl WasmTy for Option<ManuallyRooted<StructRef>> {
818 #[inline]
819 fn valtype() -> ValType {
820 ValType::STRUCTREF
821 }
822
823 #[inline]
824 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
825 self.as_ref()
826 .map_or(true, |x| x.comes_from_same_store(store))
827 }
828
829 #[inline]
830 fn dynamic_concrete_type_check(
831 &self,
832 store: &StoreOpaque,
833 nullable: bool,
834 ty: &HeapType,
835 ) -> Result<()> {
836 match self {
837 Some(s) => {
838 ManuallyRooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty)
839 }
840 None => {
841 ensure!(
842 nullable,
843 "expected a non-null reference, but found a null reference"
844 );
845 Ok(())
846 }
847 }
848 }
849
850 #[inline]
851 fn is_vmgcref_and_points_to_object(&self) -> bool {
852 self.is_some()
853 }
854
855 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
856 <ManuallyRooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
857 }
858
859 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
860 <ManuallyRooted<StructRef>>::wasm_ty_option_load(
861 store,
862 ptr.get_anyref(),
863 StructRef::from_cloned_gc_ref,
864 )
865 }
866}