Skip to main content

wasmtime/runtime/externals/
tag.rs

1use crate::Result;
2use crate::runtime::types::TagType;
3use crate::trampoline::generate_tag_export;
4use crate::{
5    AsContext, AsContextMut,
6    store::{StoreInstanceId, StoreOpaque},
7};
8use wasmtime_environ::DefinedTagIndex;
9
10#[cfg(feature = "gc")]
11use crate::store::InstanceId;
12#[cfg(feature = "gc")]
13use wasmtime_environ::EntityRef;
14
15/// A WebAssembly `tag`.
16#[derive(Copy, Clone, Debug)]
17#[repr(C)] // here for the C API in the future
18pub struct Tag {
19    instance: StoreInstanceId,
20    index: DefinedTagIndex,
21}
22
23impl Tag {
24    pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTagIndex) -> Tag {
25        Tag { instance, index }
26    }
27
28    /// Create a new tag instance from a given TagType.
29    ///
30    /// # Errors
31    ///
32    /// Returns an error if `ty` was not created with the same
33    /// [`Engine`](crate::Engine) as `store`.
34    ///
35    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
36    /// memory allocation fails. See the `OutOfMemory` type's documentation for
37    /// details on Wasmtime's out-of-memory handling.
38    pub fn new(mut store: impl AsContextMut, ty: &TagType) -> Result<Tag> {
39        generate_tag_export(store.as_context_mut().0, ty)
40    }
41
42    /// Returns the underlying type of this `tag`.
43    ///
44    /// # Panics
45    ///
46    /// Panics if `store` does not own this tag.
47    pub fn ty(&self, store: impl AsContext) -> TagType {
48        self._ty(store.as_context().0)
49    }
50
51    pub(crate) fn _ty(&self, store: &StoreOpaque) -> TagType {
52        TagType::from_wasmtime_tag(store.engine(), self.wasmtime_ty(store))
53    }
54
55    pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Tag {
56        let module = store[self.instance].env_module();
57        let index = module.tag_index(self.index);
58        &module.tags[index]
59    }
60
61    pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMTagImport {
62        let instance = &store[self.instance];
63        crate::runtime::vm::VMTagImport {
64            from: instance.tag_ptr(self.index).into(),
65            vmctx: instance.vmctx().into(),
66            index: self.index,
67        }
68    }
69
70    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
71        store.id() == self.instance.store_id()
72    }
73
74    /// Returns a stable identifier for this tag within its store.
75    ///
76    /// This allows distinguishing tags when introspecting them
77    /// e.g. via debug APIs.
78    #[cfg(feature = "debug")]
79    pub fn debug_index_in_store(&self) -> u64 {
80        u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
81    }
82
83    /// Determines whether this tag is reference equal to the other
84    /// given tag in the given store.
85    ///
86    /// # Panics
87    ///
88    /// Panics if either tag do not belong to the given `store`.
89    pub fn eq(a: &Tag, b: &Tag, store: impl AsContext) -> bool {
90        // make sure both tags belong to the store
91        let store = store.as_context();
92        let _ = &store[a.instance];
93        let _ = &store[b.instance];
94
95        // then compare to see if they have the same definition
96        a.instance == b.instance && a.index == b.index
97    }
98
99    /// Get the "index coordinates" for this `Tag`: the raw instance
100    /// ID and defined-tag index within that instance. This can be
101    /// used to "serialize" the tag as a pair of plain integers, e.g.
102    /// within the GC store for an exception object. Nothing keeps those
103    /// integers from being tampered with, so `from_raw_indices`
104    /// re-validates them.
105    #[cfg(feature = "gc")]
106    pub(crate) fn to_raw_indices(&self) -> (InstanceId, DefinedTagIndex) {
107        (self.instance.instance(), self.index)
108    }
109
110    /// Create a new `Tag` from raw indices as produced by `to_raw_indices()`,
111    /// if those indices are in bounds for the given store.
112    ///
113    /// Returns `None` if either index is out-of-bounds, which can happen when
114    /// they were round-tripped through somewhere untrusted, such as the GC
115    /// heap.
116    #[cfg(feature = "gc")]
117    pub(crate) fn from_raw_indices(
118        store: &StoreOpaque,
119        instance: InstanceId,
120        index: DefinedTagIndex,
121    ) -> Option<Tag> {
122        let num_defined_tags = store
123            .try_instance(instance)?
124            .env_module()
125            .num_defined_tags();
126        if index.index() >= num_defined_tags {
127            return None;
128        }
129
130        let instance = StoreInstanceId::new(store.id(), instance);
131        Some(Tag { instance, index })
132    }
133}