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 .kind(&gc_ref)
171 .unwrap()
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 let kind = match header.kind() {
203 Some(kind) => kind,
204 None => bail_bug!("invalid `VMGcKind`"),
205 };
206 let ty = match header.ty() {
207 Some(ty) => ty,
208 None => bail_bug!("ty should be present"),
209 };
210
211 if kind.matches(VMGcKind::StructRef) {
212 return match StructType::from_shared_type_index(store.engine(), ty) {
213 Some(ty) => Ok(HeapType::ConcreteStruct(ty)),
214 None => bail_bug!("invalid struct type index"),
215 };
216 }
217
218 if kind.matches(VMGcKind::ArrayRef) {
219 return match ArrayType::from_shared_type_index(store.engine(), ty) {
220 Some(ty) => Ok(HeapType::ConcreteArray(ty)),
221 None => bail_bug!("invalid array type index"),
222 };
223 }
224
225 bail_bug!("no other kinds of `eqref`s")
226 }
227
228 pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> {
240 self._matches_ty(store.as_context().0, ty)
241 }
242
243 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool> {
244 assert!(self.comes_from_same_store(store));
245 Ok(self._ty(store)?.matches(ty))
246 }
247
248 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()> {
249 if !self.comes_from_same_store(store) {
250 bail!("function used with wrong store");
251 }
252 if self._matches_ty(store, ty)? {
253 Ok(())
254 } else {
255 let actual_ty = self._ty(store)?;
256 bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
257 }
258 }
259
260 pub fn from_i31(mut store: impl AsContextMut, value: I31) -> Rooted<Self> {
278 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
279 Self::_from_i31(&mut store, value)
280 }
281
282 pub(crate) fn _from_i31(store: &mut AutoAssertNoGc<'_>, value: I31) -> Rooted<Self> {
283 let gc_ref = VMGcRef::from_i31(value.runtime_i31());
284 Rooted::new(store, gc_ref)
285 }
286
287 pub fn is_i31(&self, store: impl AsContext) -> Result<bool> {
297 self._is_i31(store.as_context().0)
298 }
299
300 pub(crate) fn _is_i31(&self, store: &StoreOpaque) -> Result<bool> {
301 assert!(self.comes_from_same_store(store));
302 let gc_ref = self.inner.try_gc_ref(store)?;
303 Ok(gc_ref.is_i31())
304 }
305
306 pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
320 self._as_i31(store.as_context().0)
321 }
322
323 pub(crate) fn _as_i31(&self, store: &StoreOpaque) -> Result<Option<I31>> {
324 assert!(self.comes_from_same_store(store));
325 let gc_ref = self.inner.try_gc_ref(store)?;
326 Ok(gc_ref.as_i31().map(Into::into))
327 }
328
329 pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
341 Ok(self.as_i31(store)?.expect("EqRef::unwrap_i31 on non-i31"))
342 }
343
344 pub fn is_struct(&self, store: impl AsContext) -> Result<bool> {
354 self._is_struct(store.as_context().0)
355 }
356
357 pub(crate) fn _is_struct(&self, store: &StoreOpaque) -> Result<bool> {
358 let gc_ref = self.inner.try_gc_ref(store)?;
359 Ok(!gc_ref.is_i31()
360 && store
361 .require_gc_store()?
362 .kind(gc_ref)?
363 .matches(VMGcKind::StructRef))
364 }
365
366 pub fn as_struct(&self, store: impl AsContext) -> Result<Option<Rooted<StructRef>>> {
380 self._as_struct(store.as_context().0)
381 }
382
383 pub(crate) fn _as_struct(&self, store: &StoreOpaque) -> Result<Option<Rooted<StructRef>>> {
384 if self._is_struct(store)? {
385 Ok(Some(Rooted::from_gc_root_index(self.inner)))
386 } else {
387 Ok(None)
388 }
389 }
390
391 pub fn unwrap_struct(&self, store: impl AsContext) -> Result<Rooted<StructRef>> {
403 self._unwrap_struct(store.as_context().0)
404 }
405
406 pub(crate) fn _unwrap_struct(&self, store: &StoreOpaque) -> Result<Rooted<StructRef>> {
407 Ok(self
408 ._as_struct(store)?
409 .expect("EqRef::unwrap_struct on non-structref"))
410 }
411
412 pub fn is_array(&self, store: impl AsContext) -> Result<bool> {
422 self._is_array(store.as_context().0)
423 }
424
425 pub(crate) fn _is_array(&self, store: &StoreOpaque) -> Result<bool> {
426 let gc_ref = self.inner.try_gc_ref(store)?;
427 Ok(!gc_ref.is_i31()
428 && store
429 .require_gc_store()?
430 .kind(gc_ref)?
431 .matches(VMGcKind::ArrayRef))
432 }
433
434 pub fn as_array(&self, store: impl AsContext) -> Result<Option<Rooted<ArrayRef>>> {
448 self._as_array(store.as_context().0)
449 }
450
451 pub(crate) fn _as_array(&self, store: &StoreOpaque) -> Result<Option<Rooted<ArrayRef>>> {
452 if self._is_array(store)? {
453 Ok(Some(Rooted::from_gc_root_index(self.inner)))
454 } else {
455 Ok(None)
456 }
457 }
458
459 pub fn unwrap_array(&self, store: impl AsContext) -> Result<Rooted<ArrayRef>> {
471 self._unwrap_array(store.as_context().0)
472 }
473
474 pub(crate) fn _unwrap_array(&self, store: &StoreOpaque) -> Result<Rooted<ArrayRef>> {
475 Ok(self
476 ._as_array(store)?
477 .expect("EqRef::unwrap_array on non-arrayref"))
478 }
479}
480
481unsafe impl WasmTy for Rooted<EqRef> {
482 #[inline]
483 fn valtype() -> ValType {
484 ValType::Ref(RefType::new(false, HeapType::Eq))
485 }
486
487 #[inline]
488 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
489 self.comes_from_same_store(store)
490 }
491
492 #[inline]
493 fn dynamic_concrete_type_check(
494 &self,
495 store: &StoreOpaque,
496 _nullable: bool,
497 ty: &HeapType,
498 ) -> Result<()> {
499 self.ensure_matches_ty(store, ty)
500 }
501
502 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
503 self.wasm_ty_store(store, ptr, ValRaw::anyref)
504 }
505
506 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
507 Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
508 }
509}
510
511unsafe impl WasmTy for Option<Rooted<EqRef>> {
512 #[inline]
513 fn valtype() -> ValType {
514 ValType::EQREF
515 }
516
517 #[inline]
518 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
519 self.map_or(true, |x| x.comes_from_same_store(store))
520 }
521
522 #[inline]
523 fn dynamic_concrete_type_check(
524 &self,
525 store: &StoreOpaque,
526 nullable: bool,
527 ty: &HeapType,
528 ) -> Result<()> {
529 match self {
530 Some(s) => Rooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
531 None => {
532 ensure!(
533 nullable,
534 "expected a non-null reference, but found a null reference"
535 );
536 Ok(())
537 }
538 }
539 }
540
541 #[inline]
542 fn is_vmgcref_and_points_to_object(&self) -> bool {
543 self.is_some()
544 }
545
546 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
547 <Rooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
548 }
549
550 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
551 <Rooted<EqRef>>::wasm_ty_option_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
552 }
553}
554
555unsafe impl WasmTy for OwnedRooted<EqRef> {
556 #[inline]
557 fn valtype() -> ValType {
558 ValType::Ref(RefType::new(false, HeapType::Eq))
559 }
560
561 #[inline]
562 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
563 self.comes_from_same_store(store)
564 }
565
566 #[inline]
567 fn dynamic_concrete_type_check(
568 &self,
569 store: &StoreOpaque,
570 _: bool,
571 ty: &HeapType,
572 ) -> Result<()> {
573 self.ensure_matches_ty(store, ty)
574 }
575
576 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
577 self.wasm_ty_store(store, ptr, ValRaw::anyref)
578 }
579
580 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
581 Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
582 }
583}
584
585unsafe impl WasmTy for Option<OwnedRooted<EqRef>> {
586 #[inline]
587 fn valtype() -> ValType {
588 ValType::EQREF
589 }
590
591 #[inline]
592 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
593 self.as_ref()
594 .map_or(true, |x| x.comes_from_same_store(store))
595 }
596
597 #[inline]
598 fn dynamic_concrete_type_check(
599 &self,
600 store: &StoreOpaque,
601 nullable: bool,
602 ty: &HeapType,
603 ) -> Result<()> {
604 match self {
605 Some(s) => OwnedRooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
606 None => {
607 ensure!(
608 nullable,
609 "expected a non-null reference, but found a null reference"
610 );
611 Ok(())
612 }
613 }
614 }
615
616 #[inline]
617 fn is_vmgcref_and_points_to_object(&self) -> bool {
618 self.is_some()
619 }
620
621 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
622 <OwnedRooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
623 }
624
625 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
626 <OwnedRooted<EqRef>>::wasm_ty_option_load(
627 store,
628 ptr.get_anyref(),
629 EqRef::from_cloned_gc_ref,
630 )
631 }
632}