wasmtime/runtime/gc/enabled/
eqref.rs1use crate::{
4 AnyRef, ArrayRef, ArrayType, AsContext, AsContextMut, GcRefImpl, GcRootIndex, HeapType, I31,
5 OwnedRooted, RefType, Rooted, StructRef, StructType, ValRaw, ValType, WasmTy,
6 prelude::*,
7 runtime::vm::VMGcRef,
8 store::{AutoAssertNoGc, StoreOpaque},
9};
10use core::mem::{self, MaybeUninit};
11use wasmtime_environ::VMGcKind;
12
13#[derive(Debug)]
90#[repr(transparent)]
91pub struct EqRef {
92 pub(super) inner: GcRootIndex,
93}
94
95impl From<Rooted<StructRef>> for Rooted<EqRef> {
96 #[inline]
97 fn from(s: Rooted<StructRef>) -> Self {
98 s.to_eqref()
99 }
100}
101
102impl From<OwnedRooted<StructRef>> for OwnedRooted<EqRef> {
103 #[inline]
104 fn from(s: OwnedRooted<StructRef>) -> Self {
105 s.to_eqref()
106 }
107}
108
109impl From<Rooted<ArrayRef>> for Rooted<EqRef> {
110 #[inline]
111 fn from(s: Rooted<ArrayRef>) -> Self {
112 s.to_eqref()
113 }
114}
115
116impl From<OwnedRooted<ArrayRef>> for OwnedRooted<EqRef> {
117 #[inline]
118 fn from(s: OwnedRooted<ArrayRef>) -> Self {
119 s.to_eqref()
120 }
121}
122
123unsafe impl GcRefImpl for EqRef {
124 fn transmute_ref(index: &GcRootIndex) -> &Self {
125 let me: &Self = unsafe { mem::transmute(index) };
127
128 assert!(matches!(
130 me,
131 Self {
132 inner: GcRootIndex { .. },
133 }
134 ));
135
136 me
137 }
138}
139
140impl Rooted<EqRef> {
141 #[inline]
143 pub fn to_anyref(self) -> Rooted<AnyRef> {
144 self.unchecked_cast()
145 }
146}
147
148impl OwnedRooted<EqRef> {
149 #[inline]
151 pub fn to_anyref(self) -> OwnedRooted<AnyRef> {
152 self.unchecked_cast()
153 }
154}
155
156impl EqRef {
157 pub(crate) fn from_cloned_gc_ref(
163 store: &mut AutoAssertNoGc<'_>,
164 gc_ref: VMGcRef,
165 ) -> Rooted<Self> {
166 debug_assert!(
167 gc_ref.is_i31()
168 || store
169 .unwrap_gc_store()
170 .header(&gc_ref)
171 .kind()
172 .matches(VMGcKind::EqRef)
173 );
174 Rooted::new(store, gc_ref)
175 }
176
177 #[inline]
178 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
179 self.inner.comes_from_same_store(store)
180 }
181
182 pub fn ty(&self, store: impl AsContext) -> Result<HeapType> {
192 self._ty(store.as_context().0)
193 }
194
195 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<HeapType> {
196 let gc_ref = self.inner.try_gc_ref(store)?;
197 if gc_ref.is_i31() {
198 return Ok(HeapType::I31);
199 }
200
201 let header = store.require_gc_store()?.header(gc_ref);
202
203 if header.kind().matches(VMGcKind::StructRef) {
204 return Ok(HeapType::ConcreteStruct(
205 StructType::from_shared_type_index(store.engine(), header.ty().unwrap()),
206 ));
207 }
208
209 if header.kind().matches(VMGcKind::ArrayRef) {
210 return Ok(HeapType::ConcreteArray(ArrayType::from_shared_type_index(
211 store.engine(),
212 header.ty().unwrap(),
213 )));
214 }
215
216 unreachable!("no other kinds of `eqref`s")
217 }
218
219 pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> {
231 self._matches_ty(store.as_context().0, ty)
232 }
233
234 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool> {
235 assert!(self.comes_from_same_store(store));
236 Ok(self._ty(store)?.matches(ty))
237 }
238
239 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()> {
240 if !self.comes_from_same_store(store) {
241 bail!("function used with wrong store");
242 }
243 if self._matches_ty(store, ty)? {
244 Ok(())
245 } else {
246 let actual_ty = self._ty(store)?;
247 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
248 }
249 }
250
251 pub fn from_i31(mut store: impl AsContextMut, value: I31) -> Rooted<Self> {
269 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
270 Self::_from_i31(&mut store, value)
271 }
272
273 pub(crate) fn _from_i31(store: &mut AutoAssertNoGc<'_>, value: I31) -> Rooted<Self> {
274 let gc_ref = VMGcRef::from_i31(value.runtime_i31());
275 Rooted::new(store, gc_ref)
276 }
277
278 pub fn is_i31(&self, store: impl AsContext) -> Result<bool> {
288 self._is_i31(store.as_context().0)
289 }
290
291 pub(crate) fn _is_i31(&self, store: &StoreOpaque) -> Result<bool> {
292 assert!(self.comes_from_same_store(store));
293 let gc_ref = self.inner.try_gc_ref(store)?;
294 Ok(gc_ref.is_i31())
295 }
296
297 pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
311 self._as_i31(store.as_context().0)
312 }
313
314 pub(crate) fn _as_i31(&self, store: &StoreOpaque) -> Result<Option<I31>> {
315 assert!(self.comes_from_same_store(store));
316 let gc_ref = self.inner.try_gc_ref(store)?;
317 Ok(gc_ref.as_i31().map(Into::into))
318 }
319
320 pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
332 Ok(self.as_i31(store)?.expect("EqRef::unwrap_i31 on non-i31"))
333 }
334
335 pub fn is_struct(&self, store: impl AsContext) -> Result<bool> {
345 self._is_struct(store.as_context().0)
346 }
347
348 pub(crate) fn _is_struct(&self, store: &StoreOpaque) -> Result<bool> {
349 let gc_ref = self.inner.try_gc_ref(store)?;
350 Ok(!gc_ref.is_i31()
351 && store
352 .require_gc_store()?
353 .kind(gc_ref)
354 .matches(VMGcKind::StructRef))
355 }
356
357 pub fn as_struct(&self, store: impl AsContext) -> Result<Option<Rooted<StructRef>>> {
371 self._as_struct(store.as_context().0)
372 }
373
374 pub(crate) fn _as_struct(&self, store: &StoreOpaque) -> Result<Option<Rooted<StructRef>>> {
375 if self._is_struct(store)? {
376 Ok(Some(Rooted::from_gc_root_index(self.inner)))
377 } else {
378 Ok(None)
379 }
380 }
381
382 pub fn unwrap_struct(&self, store: impl AsContext) -> Result<Rooted<StructRef>> {
394 self._unwrap_struct(store.as_context().0)
395 }
396
397 pub(crate) fn _unwrap_struct(&self, store: &StoreOpaque) -> Result<Rooted<StructRef>> {
398 Ok(self
399 ._as_struct(store)?
400 .expect("EqRef::unwrap_struct on non-structref"))
401 }
402
403 pub fn is_array(&self, store: impl AsContext) -> Result<bool> {
413 self._is_array(store.as_context().0)
414 }
415
416 pub(crate) fn _is_array(&self, store: &StoreOpaque) -> Result<bool> {
417 let gc_ref = self.inner.try_gc_ref(store)?;
418 Ok(!gc_ref.is_i31()
419 && store
420 .require_gc_store()?
421 .kind(gc_ref)
422 .matches(VMGcKind::ArrayRef))
423 }
424
425 pub fn as_array(&self, store: impl AsContext) -> Result<Option<Rooted<ArrayRef>>> {
439 self._as_array(store.as_context().0)
440 }
441
442 pub(crate) fn _as_array(&self, store: &StoreOpaque) -> Result<Option<Rooted<ArrayRef>>> {
443 if self._is_array(store)? {
444 Ok(Some(Rooted::from_gc_root_index(self.inner)))
445 } else {
446 Ok(None)
447 }
448 }
449
450 pub fn unwrap_array(&self, store: impl AsContext) -> Result<Rooted<ArrayRef>> {
462 self._unwrap_array(store.as_context().0)
463 }
464
465 pub(crate) fn _unwrap_array(&self, store: &StoreOpaque) -> Result<Rooted<ArrayRef>> {
466 Ok(self
467 ._as_array(store)?
468 .expect("EqRef::unwrap_array on non-arrayref"))
469 }
470}
471
472unsafe impl WasmTy for Rooted<EqRef> {
473 #[inline]
474 fn valtype() -> ValType {
475 ValType::Ref(RefType::new(false, HeapType::Eq))
476 }
477
478 #[inline]
479 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
480 self.comes_from_same_store(store)
481 }
482
483 #[inline]
484 fn dynamic_concrete_type_check(
485 &self,
486 store: &StoreOpaque,
487 _nullable: bool,
488 ty: &HeapType,
489 ) -> Result<()> {
490 self.ensure_matches_ty(store, ty)
491 }
492
493 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
494 self.wasm_ty_store(store, ptr, ValRaw::anyref)
495 }
496
497 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
498 Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
499 }
500}
501
502unsafe impl WasmTy for Option<Rooted<EqRef>> {
503 #[inline]
504 fn valtype() -> ValType {
505 ValType::EQREF
506 }
507
508 #[inline]
509 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
510 self.map_or(true, |x| x.comes_from_same_store(store))
511 }
512
513 #[inline]
514 fn dynamic_concrete_type_check(
515 &self,
516 store: &StoreOpaque,
517 nullable: bool,
518 ty: &HeapType,
519 ) -> Result<()> {
520 match self {
521 Some(s) => Rooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
522 None => {
523 ensure!(
524 nullable,
525 "expected a non-null reference, but found a null reference"
526 );
527 Ok(())
528 }
529 }
530 }
531
532 #[inline]
533 fn is_vmgcref_and_points_to_object(&self) -> bool {
534 self.is_some()
535 }
536
537 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
538 <Rooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
539 }
540
541 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
542 <Rooted<EqRef>>::wasm_ty_option_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
543 }
544}
545
546unsafe impl WasmTy for OwnedRooted<EqRef> {
547 #[inline]
548 fn valtype() -> ValType {
549 ValType::Ref(RefType::new(false, HeapType::Eq))
550 }
551
552 #[inline]
553 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
554 self.comes_from_same_store(store)
555 }
556
557 #[inline]
558 fn dynamic_concrete_type_check(
559 &self,
560 store: &StoreOpaque,
561 _: bool,
562 ty: &HeapType,
563 ) -> Result<()> {
564 self.ensure_matches_ty(store, ty)
565 }
566
567 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
568 self.wasm_ty_store(store, ptr, ValRaw::anyref)
569 }
570
571 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
572 Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
573 }
574}
575
576unsafe impl WasmTy for Option<OwnedRooted<EqRef>> {
577 #[inline]
578 fn valtype() -> ValType {
579 ValType::EQREF
580 }
581
582 #[inline]
583 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
584 self.as_ref()
585 .map_or(true, |x| x.comes_from_same_store(store))
586 }
587
588 #[inline]
589 fn dynamic_concrete_type_check(
590 &self,
591 store: &StoreOpaque,
592 nullable: bool,
593 ty: &HeapType,
594 ) -> Result<()> {
595 match self {
596 Some(s) => OwnedRooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
597 None => {
598 ensure!(
599 nullable,
600 "expected a non-null reference, but found a null reference"
601 );
602 Ok(())
603 }
604 }
605 }
606
607 #[inline]
608 fn is_vmgcref_and_points_to_object(&self) -> bool {
609 self.is_some()
610 }
611
612 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
613 <OwnedRooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
614 }
615
616 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
617 <OwnedRooted<EqRef>>::wasm_ty_option_load(
618 store,
619 ptr.get_anyref(),
620 EqRef::from_cloned_gc_ref,
621 )
622 }
623}