1#![cfg(feature = "gc")]
3
4use crate::runtime::vm::VMGcRef;
5use crate::store::{Asyncness, StoreId};
6#[cfg(feature = "async")]
7use crate::vm::VMStore;
8use crate::vm::{self, VMGcHeader, VMStructRef};
9use crate::{AnyRef, FieldType};
10use crate::{
11 AsContext, AsContextMut, EqRef, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType,
12 OwnedRooted, RefType, Rooted, StructType, Val, ValRaw, ValType, WasmTy,
13 prelude::*,
14 store::{AutoAssertNoGc, StoreContextMut, StoreOpaque, StoreResourceLimiter},
15};
16use alloc::sync::Arc;
17use core::mem::{self, MaybeUninit};
18use wasmtime_environ::{GcLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex};
19
20pub struct StructRefPre {
66 store_id: StoreId,
67 ty: StructType,
68}
69
70impl StructRefPre {
71 pub fn new(mut store: impl AsContextMut, ty: StructType) -> Self {
79 Self::_new(store.as_context_mut().0, ty)
80 }
81
82 pub(crate) fn _new(store: &mut StoreOpaque, ty: StructType) -> Self {
83 store.insert_gc_host_alloc_type(ty.registered_type().clone());
84 let store_id = store.id();
85 StructRefPre { store_id, ty }
86 }
87
88 pub(crate) fn layout(&self) -> &GcStructLayout {
89 self.ty
90 .registered_type()
91 .layout()
92 .expect("struct types have a layout")
93 .unwrap_struct()
94 }
95
96 pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
97 self.ty.registered_type().index()
98 }
99}
100
101#[derive(Debug)]
160#[repr(transparent)]
161pub struct StructRef {
162 pub(super) inner: GcRootIndex,
163}
164
165unsafe impl GcRefImpl for StructRef {
166 fn transmute_ref(index: &GcRootIndex) -> &Self {
167 let me: &Self = unsafe { mem::transmute(index) };
169
170 assert!(matches!(
172 me,
173 Self {
174 inner: GcRootIndex { .. },
175 }
176 ));
177
178 me
179 }
180}
181
182impl Rooted<StructRef> {
183 #[inline]
185 pub fn to_anyref(self) -> Rooted<AnyRef> {
186 self.unchecked_cast()
187 }
188
189 #[inline]
191 pub fn to_eqref(self) -> Rooted<EqRef> {
192 self.unchecked_cast()
193 }
194}
195
196impl OwnedRooted<StructRef> {
197 #[inline]
199 pub fn to_anyref(self) -> OwnedRooted<AnyRef> {
200 self.unchecked_cast()
201 }
202
203 #[inline]
205 pub fn to_eqref(self) -> OwnedRooted<EqRef> {
206 self.unchecked_cast()
207 }
208}
209
210impl StructRef {
211 pub fn new(
239 mut store: impl AsContextMut,
240 allocator: &StructRefPre,
241 fields: &[Val],
242 ) -> Result<Rooted<StructRef>> {
243 let (mut limiter, store) = store
244 .as_context_mut()
245 .0
246 .validate_sync_resource_limiter_and_store_opaque()?;
247 vm::assert_ready(Self::_new_async(
248 store,
249 limiter.as_mut(),
250 allocator,
251 fields,
252 Asyncness::No,
253 ))
254 }
255
256 #[cfg(feature = "async")]
279 pub async fn new_async(
280 mut store: impl AsContextMut,
281 allocator: &StructRefPre,
282 fields: &[Val],
283 ) -> Result<Rooted<StructRef>> {
284 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
285 Self::_new_async(store, limiter.as_mut(), allocator, fields, Asyncness::Yes).await
286 }
287
288 pub(crate) async fn _new_async(
289 store: &mut StoreOpaque,
290 limiter: Option<&mut StoreResourceLimiter<'_>>,
291 allocator: &StructRefPre,
292 fields: &[Val],
293 asyncness: Asyncness,
294 ) -> Result<Rooted<StructRef>> {
295 Self::type_check_fields(store, allocator, fields)?;
296 store
297 .retry_after_gc_async(limiter, (), asyncness, |store, ()| {
298 Self::new_unchecked(store, allocator, fields)
299 })
300 .await
301 }
302
303 fn type_check_fields(
305 store: &mut StoreOpaque,
306 allocator: &StructRefPre,
307 fields: &[Val],
308 ) -> Result<(), Error> {
309 let expected_len = allocator.ty.fields().len();
310 let actual_len = fields.len();
311 ensure!(
312 actual_len == expected_len,
313 "expected {expected_len} fields, got {actual_len}"
314 );
315 for (ty, val) in allocator.ty.fields().zip(fields) {
316 assert!(
317 val.comes_from_same_store(store),
318 "field value comes from the wrong store",
319 );
320 let ty = ty.element_type().unpack();
321 val.ensure_matches_ty(store, ty)
322 .context("field type mismatch")?;
323 }
324 Ok(())
325 }
326
327 fn new_unchecked(
332 store: &mut StoreOpaque,
333 allocator: &StructRefPre,
334 fields: &[Val],
335 ) -> Result<Rooted<StructRef>> {
336 assert_eq!(
337 store.id(),
338 allocator.store_id,
339 "attempted to use a `StructRefPre` with the wrong store"
340 );
341
342 let structref = store
345 .require_gc_store_mut()?
346 .alloc_uninit_struct(allocator.type_index(), &allocator.layout())
347 .context("unrecoverable error when allocating new `structref`")?
348 .map_err(|n| GcHeapOutOfMemory::new((), n))?;
349
350 let mut store = AutoAssertNoGc::new(store);
355 match (|| {
356 for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() {
357 structref.initialize_field(
358 &mut store,
359 allocator.layout(),
360 ty.element_type(),
361 index,
362 *val,
363 )?;
364 }
365 Ok(())
366 })() {
367 Ok(()) => Ok(Rooted::new(&mut store, structref.into())),
368 Err(e) => {
369 store
370 .require_gc_store_mut()?
371 .dealloc_uninit_struct(structref)?;
372 Err(e)
373 }
374 }
375 }
376
377 #[inline]
378 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
379 self.inner.comes_from_same_store(store)
380 }
381
382 pub fn ty(&self, store: impl AsContext) -> Result<StructType> {
392 self._ty(store.as_context().0)
393 }
394
395 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<StructType> {
396 assert!(self.comes_from_same_store(store));
397 let index = self.type_index(store)?;
398 Ok(StructType::from_shared_type_index(store.engine(), index))
399 }
400
401 pub fn matches_ty(&self, store: impl AsContext, ty: &StructType) -> Result<bool> {
414 self._matches_ty(store.as_context().0, ty)
415 }
416
417 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<bool> {
418 assert!(self.comes_from_same_store(store));
419 Ok(self._ty(store)?.matches(ty))
420 }
421
422 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<()> {
423 if !self.comes_from_same_store(store) {
424 bail!("function used with wrong store");
425 }
426 if self._matches_ty(store, ty)? {
427 Ok(())
428 } else {
429 let actual_ty = self._ty(store)?;
430 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
431 }
432 }
433
434 pub fn fields<'a, T: 'static>(
447 &'a self,
448 store: impl Into<StoreContextMut<'a, T>>,
449 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
450 self._fields(store.into().0)
451 }
452
453 pub(crate) fn _fields<'a>(
454 &'a self,
455 store: &'a mut StoreOpaque,
456 ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
457 assert!(self.comes_from_same_store(store));
458 let store = AutoAssertNoGc::new(store);
459
460 let gc_ref = self.inner.try_gc_ref(&store)?;
461 let header = store.require_gc_store()?.header(gc_ref)?;
462 debug_assert!(header.kind().matches(VMGcKind::StructRef));
463
464 let index = header.ty().expect("structrefs should have concrete types");
465 let ty = StructType::from_shared_type_index(store.engine(), index);
466 let len = ty.fields().len();
467
468 return Ok(Fields {
469 structref: self,
470 store,
471 index: 0,
472 len,
473 });
474
475 struct Fields<'a, 'b> {
476 structref: &'a StructRef,
477 store: AutoAssertNoGc<'b>,
478 index: usize,
479 len: usize,
480 }
481
482 impl Iterator for Fields<'_, '_> {
483 type Item = Val;
484
485 #[inline]
486 fn next(&mut self) -> Option<Self::Item> {
487 let i = self.index;
488 debug_assert!(i <= self.len);
489 if i >= self.len {
490 return None;
491 }
492 self.index += 1;
493 self.structref._field(&mut self.store, i).ok()
494 }
495
496 #[inline]
497 fn size_hint(&self) -> (usize, Option<usize>) {
498 let len = self.len - self.index;
499 (len, Some(len))
500 }
501 }
502
503 impl ExactSizeIterator for Fields<'_, '_> {
504 #[inline]
505 fn len(&self) -> usize {
506 self.len - self.index
507 }
508 }
509 }
510
511 fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
512 assert!(self.comes_from_same_store(&store));
513 let gc_ref = self.inner.try_gc_ref(store)?;
514 Ok(store.require_gc_store()?.header(gc_ref)?)
515 }
516
517 fn structref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMStructRef> {
518 assert!(self.comes_from_same_store(&store));
519 let gc_ref = self.inner.try_gc_ref(store)?;
520 debug_assert!(self.header(store)?.kind().matches(VMGcKind::StructRef));
521 Ok(gc_ref.as_structref_unchecked())
522 }
523
524 fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<Arc<GcStructLayout>> {
525 assert!(self.comes_from_same_store(&store));
526 let type_index = self.type_index(store)?;
527 let layout = store
528 .engine()
529 .signatures()
530 .layout(type_index)
531 .expect("struct types should have GC layouts");
532 match layout {
533 GcLayout::Struct(s) => Ok(s),
534 GcLayout::Array(_) => unreachable!(),
535 }
536 }
537
538 fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> {
539 let ty = self._ty(store)?;
540 match ty.field(field) {
541 Some(f) => Ok(f),
542 None => {
543 let len = ty.fields().len();
544 bail!("cannot access field {field}: struct only has {len} fields")
545 }
546 }
547 }
548
549 pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> {
563 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
564 self._field(&mut store, index)
565 }
566
567 pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> {
568 assert!(self.comes_from_same_store(store));
569 let structref = self.structref(store)?.unchecked_copy();
570 let field_ty = self.field_ty(store, index)?;
571 let layout = self.layout(store)?;
572 structref.read_field(store, &layout, field_ty.element_type(), index)
573 }
574
575 pub fn set_field(&self, mut store: impl AsContextMut, index: usize, value: Val) -> Result<()> {
595 self._set_field(store.as_context_mut().0, index, value)
596 }
597
598 pub(crate) fn _set_field(
599 &self,
600 store: &mut StoreOpaque,
601 index: usize,
602 value: Val,
603 ) -> Result<()> {
604 assert!(self.comes_from_same_store(store));
605 let mut store = AutoAssertNoGc::new(store);
606
607 let field_ty = self.field_ty(&store, index)?;
608 ensure!(
609 field_ty.mutability().is_var(),
610 "cannot set field {index}: field is not mutable"
611 );
612
613 value
614 .ensure_matches_ty(&store, &field_ty.element_type().unpack())
615 .with_context(|| format!("cannot set field {index}: type mismatch"))?;
616
617 let layout = self.layout(&store)?;
618 let structref = self.structref(&store)?.unchecked_copy();
619
620 structref.write_field(&mut store, &layout, field_ty.element_type(), index, value)
621 }
622
623 pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
624 let gc_ref = self.inner.try_gc_ref(store)?;
625 let header = store.require_gc_store()?.header(gc_ref)?;
626 debug_assert!(header.kind().matches(VMGcKind::StructRef));
627 Ok(header.ty().expect("structrefs should have concrete types"))
628 }
629
630 pub(crate) fn from_cloned_gc_ref(
636 store: &mut AutoAssertNoGc<'_>,
637 gc_ref: VMGcRef,
638 ) -> Rooted<Self> {
639 debug_assert!(gc_ref.is_structref(&*store.unwrap_gc_store().gc_heap));
640 Rooted::new(store, gc_ref)
641 }
642}
643
644unsafe impl WasmTy for Rooted<StructRef> {
645 #[inline]
646 fn valtype() -> ValType {
647 ValType::Ref(RefType::new(false, HeapType::Struct))
648 }
649
650 #[inline]
651 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
652 self.comes_from_same_store(store)
653 }
654
655 #[inline]
656 fn dynamic_concrete_type_check(
657 &self,
658 store: &StoreOpaque,
659 _nullable: bool,
660 ty: &HeapType,
661 ) -> Result<()> {
662 match ty {
663 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
664 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
665
666 HeapType::Extern
667 | HeapType::NoExtern
668 | HeapType::Func
669 | HeapType::ConcreteFunc(_)
670 | HeapType::NoFunc
671 | HeapType::I31
672 | HeapType::Array
673 | HeapType::ConcreteArray(_)
674 | HeapType::None
675 | HeapType::NoCont
676 | HeapType::Cont
677 | HeapType::ConcreteCont(_)
678 | HeapType::NoExn
679 | HeapType::Exn
680 | HeapType::ConcreteExn(_) => bail!(
681 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
682 self._ty(store)?,
683 ),
684 }
685 }
686
687 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
688 self.wasm_ty_store(store, ptr, ValRaw::anyref)
689 }
690
691 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
692 Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
693 }
694}
695
696unsafe impl WasmTy for Option<Rooted<StructRef>> {
697 #[inline]
698 fn valtype() -> ValType {
699 ValType::STRUCTREF
700 }
701
702 #[inline]
703 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
704 self.map_or(true, |x| x.comes_from_same_store(store))
705 }
706
707 #[inline]
708 fn dynamic_concrete_type_check(
709 &self,
710 store: &StoreOpaque,
711 nullable: bool,
712 ty: &HeapType,
713 ) -> Result<()> {
714 match self {
715 Some(s) => Rooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty),
716 None => {
717 ensure!(
718 nullable,
719 "expected a non-null reference, but found a null reference"
720 );
721 Ok(())
722 }
723 }
724 }
725
726 #[inline]
727 fn is_vmgcref_and_points_to_object(&self) -> bool {
728 self.is_some()
729 }
730
731 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
732 <Rooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
733 }
734
735 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
736 <Rooted<StructRef>>::wasm_ty_option_load(
737 store,
738 ptr.get_anyref(),
739 StructRef::from_cloned_gc_ref,
740 )
741 }
742}
743
744unsafe impl WasmTy for OwnedRooted<StructRef> {
745 #[inline]
746 fn valtype() -> ValType {
747 ValType::Ref(RefType::new(false, HeapType::Struct))
748 }
749
750 #[inline]
751 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
752 self.comes_from_same_store(store)
753 }
754
755 #[inline]
756 fn dynamic_concrete_type_check(
757 &self,
758 store: &StoreOpaque,
759 _: bool,
760 ty: &HeapType,
761 ) -> Result<()> {
762 match ty {
763 HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
764 HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
765
766 HeapType::Extern
767 | HeapType::NoExtern
768 | HeapType::Func
769 | HeapType::ConcreteFunc(_)
770 | HeapType::NoFunc
771 | HeapType::I31
772 | HeapType::Array
773 | HeapType::ConcreteArray(_)
774 | HeapType::None
775 | HeapType::NoCont
776 | HeapType::Cont
777 | HeapType::ConcreteCont(_)
778 | HeapType::NoExn
779 | HeapType::Exn
780 | HeapType::ConcreteExn(_) => bail!(
781 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
782 self._ty(store)?,
783 ),
784 }
785 }
786
787 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
788 self.wasm_ty_store(store, ptr, ValRaw::anyref)
789 }
790
791 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
792 Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
793 }
794}
795
796unsafe impl WasmTy for Option<OwnedRooted<StructRef>> {
797 #[inline]
798 fn valtype() -> ValType {
799 ValType::STRUCTREF
800 }
801
802 #[inline]
803 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
804 self.as_ref()
805 .map_or(true, |x| x.comes_from_same_store(store))
806 }
807
808 #[inline]
809 fn dynamic_concrete_type_check(
810 &self,
811 store: &StoreOpaque,
812 nullable: bool,
813 ty: &HeapType,
814 ) -> Result<()> {
815 match self {
816 Some(s) => {
817 OwnedRooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty)
818 }
819 None => {
820 ensure!(
821 nullable,
822 "expected a non-null reference, but found a null reference"
823 );
824 Ok(())
825 }
826 }
827 }
828
829 #[inline]
830 fn is_vmgcref_and_points_to_object(&self) -> bool {
831 self.is_some()
832 }
833
834 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
835 <OwnedRooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
836 }
837
838 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
839 <OwnedRooted<StructRef>>::wasm_ty_option_load(
840 store,
841 ptr.get_anyref(),
842 StructRef::from_cloned_gc_ref,
843 )
844 }
845}