wasmtime/runtime/gc/enabled/
eqref.rs1#![cfg(feature = "gc")]
3use crate::{
4 AnyRef, ArrayRef, ArrayType, AsContext, AsContextMut, GcRefImpl, GcRootIndex, HeapType, I31,
5 OwnedRooted, RefType, Rooted, StructRef, StructType, ValRaw, ValType, WasmTy, bail_bug,
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 .unwrap()
172 .kind()
173 .matches(VMGcKind::EqRef)
174 );
175 Rooted::new(store, gc_ref)
176 }
177
178 #[inline]
179 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
180 self.inner.comes_from_same_store(store)
181 }
182
183 pub fn ty(&self, store: impl AsContext) -> Result<HeapType> {
193 self._ty(store.as_context().0)
194 }
195
196 pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<HeapType> {
197 let gc_ref = self.inner.try_gc_ref(store)?;
198 if gc_ref.is_i31() {
199 return Ok(HeapType::I31);
200 }
201
202 let header = store.require_gc_store()?.header(gc_ref)?;
203 let ty = match header.ty() {
204 Some(ty) => ty,
205 None => bail_bug!("ty should be present"),
206 };
207
208 if header.kind().matches(VMGcKind::StructRef) {
209 return Ok(HeapType::ConcreteStruct(
210 StructType::from_shared_type_index(store.engine(), ty),
211 ));
212 }
213
214 if header.kind().matches(VMGcKind::ArrayRef) {
215 return Ok(HeapType::ConcreteArray(ArrayType::from_shared_type_index(
216 store.engine(),
217 ty,
218 )));
219 }
220
221 bail_bug!("no other kinds of `eqref`s")
222 }
223
224 pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> {
236 self._matches_ty(store.as_context().0, ty)
237 }
238
239 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool> {
240 assert!(self.comes_from_same_store(store));
241 Ok(self._ty(store)?.matches(ty))
242 }
243
244 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()> {
245 if !self.comes_from_same_store(store) {
246 bail!("function used with wrong store");
247 }
248 if self._matches_ty(store, ty)? {
249 Ok(())
250 } else {
251 let actual_ty = self._ty(store)?;
252 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
253 }
254 }
255
256 pub fn from_i31(mut store: impl AsContextMut, value: I31) -> Rooted<Self> {
274 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
275 Self::_from_i31(&mut store, value)
276 }
277
278 pub(crate) fn _from_i31(store: &mut AutoAssertNoGc<'_>, value: I31) -> Rooted<Self> {
279 let gc_ref = VMGcRef::from_i31(value.runtime_i31());
280 Rooted::new(store, gc_ref)
281 }
282
283 pub fn is_i31(&self, store: impl AsContext) -> Result<bool> {
293 self._is_i31(store.as_context().0)
294 }
295
296 pub(crate) fn _is_i31(&self, store: &StoreOpaque) -> Result<bool> {
297 assert!(self.comes_from_same_store(store));
298 let gc_ref = self.inner.try_gc_ref(store)?;
299 Ok(gc_ref.is_i31())
300 }
301
302 pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
316 self._as_i31(store.as_context().0)
317 }
318
319 pub(crate) fn _as_i31(&self, store: &StoreOpaque) -> Result<Option<I31>> {
320 assert!(self.comes_from_same_store(store));
321 let gc_ref = self.inner.try_gc_ref(store)?;
322 Ok(gc_ref.as_i31().map(Into::into))
323 }
324
325 pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
337 Ok(self.as_i31(store)?.expect("EqRef::unwrap_i31 on non-i31"))
338 }
339
340 pub fn is_struct(&self, store: impl AsContext) -> Result<bool> {
350 self._is_struct(store.as_context().0)
351 }
352
353 pub(crate) fn _is_struct(&self, store: &StoreOpaque) -> Result<bool> {
354 let gc_ref = self.inner.try_gc_ref(store)?;
355 Ok(!gc_ref.is_i31()
356 && store
357 .require_gc_store()?
358 .kind(gc_ref)?
359 .matches(VMGcKind::StructRef))
360 }
361
362 pub fn as_struct(&self, store: impl AsContext) -> Result<Option<Rooted<StructRef>>> {
376 self._as_struct(store.as_context().0)
377 }
378
379 pub(crate) fn _as_struct(&self, store: &StoreOpaque) -> Result<Option<Rooted<StructRef>>> {
380 if self._is_struct(store)? {
381 Ok(Some(Rooted::from_gc_root_index(self.inner)))
382 } else {
383 Ok(None)
384 }
385 }
386
387 pub fn unwrap_struct(&self, store: impl AsContext) -> Result<Rooted<StructRef>> {
399 self._unwrap_struct(store.as_context().0)
400 }
401
402 pub(crate) fn _unwrap_struct(&self, store: &StoreOpaque) -> Result<Rooted<StructRef>> {
403 Ok(self
404 ._as_struct(store)?
405 .expect("EqRef::unwrap_struct on non-structref"))
406 }
407
408 pub fn is_array(&self, store: impl AsContext) -> Result<bool> {
418 self._is_array(store.as_context().0)
419 }
420
421 pub(crate) fn _is_array(&self, store: &StoreOpaque) -> Result<bool> {
422 let gc_ref = self.inner.try_gc_ref(store)?;
423 Ok(!gc_ref.is_i31()
424 && store
425 .require_gc_store()?
426 .kind(gc_ref)?
427 .matches(VMGcKind::ArrayRef))
428 }
429
430 pub fn as_array(&self, store: impl AsContext) -> Result<Option<Rooted<ArrayRef>>> {
444 self._as_array(store.as_context().0)
445 }
446
447 pub(crate) fn _as_array(&self, store: &StoreOpaque) -> Result<Option<Rooted<ArrayRef>>> {
448 if self._is_array(store)? {
449 Ok(Some(Rooted::from_gc_root_index(self.inner)))
450 } else {
451 Ok(None)
452 }
453 }
454
455 pub fn unwrap_array(&self, store: impl AsContext) -> Result<Rooted<ArrayRef>> {
467 self._unwrap_array(store.as_context().0)
468 }
469
470 pub(crate) fn _unwrap_array(&self, store: &StoreOpaque) -> Result<Rooted<ArrayRef>> {
471 Ok(self
472 ._as_array(store)?
473 .expect("EqRef::unwrap_array on non-arrayref"))
474 }
475}
476
477unsafe impl WasmTy for Rooted<EqRef> {
478 #[inline]
479 fn valtype() -> ValType {
480 ValType::Ref(RefType::new(false, HeapType::Eq))
481 }
482
483 #[inline]
484 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
485 self.comes_from_same_store(store)
486 }
487
488 #[inline]
489 fn dynamic_concrete_type_check(
490 &self,
491 store: &StoreOpaque,
492 _nullable: bool,
493 ty: &HeapType,
494 ) -> Result<()> {
495 self.ensure_matches_ty(store, ty)
496 }
497
498 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
499 self.wasm_ty_store(store, ptr, ValRaw::anyref)
500 }
501
502 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
503 Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
504 }
505}
506
507unsafe impl WasmTy for Option<Rooted<EqRef>> {
508 #[inline]
509 fn valtype() -> ValType {
510 ValType::EQREF
511 }
512
513 #[inline]
514 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
515 self.map_or(true, |x| x.comes_from_same_store(store))
516 }
517
518 #[inline]
519 fn dynamic_concrete_type_check(
520 &self,
521 store: &StoreOpaque,
522 nullable: bool,
523 ty: &HeapType,
524 ) -> Result<()> {
525 match self {
526 Some(s) => Rooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
527 None => {
528 ensure!(
529 nullable,
530 "expected a non-null reference, but found a null reference"
531 );
532 Ok(())
533 }
534 }
535 }
536
537 #[inline]
538 fn is_vmgcref_and_points_to_object(&self) -> bool {
539 self.is_some()
540 }
541
542 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
543 <Rooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
544 }
545
546 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
547 <Rooted<EqRef>>::wasm_ty_option_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
548 }
549}
550
551unsafe impl WasmTy for OwnedRooted<EqRef> {
552 #[inline]
553 fn valtype() -> ValType {
554 ValType::Ref(RefType::new(false, HeapType::Eq))
555 }
556
557 #[inline]
558 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
559 self.comes_from_same_store(store)
560 }
561
562 #[inline]
563 fn dynamic_concrete_type_check(
564 &self,
565 store: &StoreOpaque,
566 _: bool,
567 ty: &HeapType,
568 ) -> Result<()> {
569 self.ensure_matches_ty(store, ty)
570 }
571
572 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
573 self.wasm_ty_store(store, ptr, ValRaw::anyref)
574 }
575
576 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
577 Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
578 }
579}
580
581unsafe impl WasmTy for Option<OwnedRooted<EqRef>> {
582 #[inline]
583 fn valtype() -> ValType {
584 ValType::EQREF
585 }
586
587 #[inline]
588 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
589 self.as_ref()
590 .map_or(true, |x| x.comes_from_same_store(store))
591 }
592
593 #[inline]
594 fn dynamic_concrete_type_check(
595 &self,
596 store: &StoreOpaque,
597 nullable: bool,
598 ty: &HeapType,
599 ) -> Result<()> {
600 match self {
601 Some(s) => OwnedRooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
602 None => {
603 ensure!(
604 nullable,
605 "expected a non-null reference, but found a null reference"
606 );
607 Ok(())
608 }
609 }
610 }
611
612 #[inline]
613 fn is_vmgcref_and_points_to_object(&self) -> bool {
614 self.is_some()
615 }
616
617 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
618 <OwnedRooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
619 }
620
621 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
622 <OwnedRooted<EqRef>>::wasm_ty_option_load(
623 store,
624 ptr.get_anyref(),
625 EqRef::from_cloned_gc_ref,
626 )
627 }
628}