wasmtime_environ/component/types_builder/
resources.rs

1//! Implementation of resource type information within Wasmtime.
2//!
3//! Resource types are one of the trickier parts of the component model. Types
4//! such as `list`, `record`, and `string` are considered "structural" where two
5//! types are considered equal if their components are equal. For example `(list
6//! $a)` and `(list $b)` are the same if `$a` and `$b` are the same. Resources,
7//! however, are not as simple.
8//!
9//! The type of a resource can "change" sort of depending on who you are and how
10//! you view it. Some examples of resources are:
11//!
12//! * When a resource is imported into a component the internal component
13//!   doesn't know the underlying resource type, but the outer component which
14//!   performed an instantiation knows that. This means that if a component
15//!   imports two unique resources but is instantiated with two copies of the
16//!   same resource the internal component can't know they're the same but the
17//!   outer component knows they're the same.
18//!
19//! * Each instantiation of a component produces new resource types. This means
20//!   that if a component instantiates a subcomponent twice then the resources
21//!   defined in that subcomponent are considered different between the two
22//!   instances.
23//!
24//! All this is basically to say that resources require special care. The
25//! purpose of resources are to provide isolation across component boundaries
26//! and strict guarantees around ownership and borrowing. Getting the type
27//! information wrong can compromise on all of these guarantees which is
28//! something Wasmtime would ideally avoid.
29//!
30//! ## Interaction with `wasmparser`
31//!
32//! The trickiness of resource types is not unique of Wasmtime and the first
33//! line of translating a component, `wasmparser`, already has quite a lot of
34//! support for handling all the various special cases of resources. Namely
35//! `wasmparser` has a `ResourceId` type which can be used to test whether two
36//! resources are the same or unique. For example in the above scenario where a
37//! component imports two resources then within that component they'll have
38//! unique ids. Externally though the outer component will be able to see that
39//! the ids are the same.
40//!
41//! Given the subtlety here the goal is to lean on `wasmparser` as much as
42//! possible for this information. The thinking is "well it got things right so
43//! let's not duplicate". This is one of the motivations for plumbing
44//! `wasmparser`'s type information throughout `LocalInitializer` structures
45//! during translation of a component. During conversion to a
46//! `GlobalInitializer` is where everything is boiled away.
47//!
48//! ## Converting to Wasmtime
49//!
50//! The purpose of this module then is to convert `wasmparser`'s view of
51//! resources into Wasmtime's view of resources. Wasmtime's goal is to
52//! determine how many tables are required for each resource within a component
53//! and then from then on purely talk about table indices. Each component
54//! instance will require a table per-resource and this figures that all out.
55//!
56//! The conversion process, however, is relatively tightly intertwined with type
57//! conversion in general. The "leaves" of a type may be resources but there are
58//! other structures in a type such as lists, records, variants, etc. This means
59//! that the `ResourcesBuilder` below is embedded within a
60//! `ComponentTypesBuilder`. This also means that it's unfortunately not easy to
61//! disentangle pieces and have one nice standalone file that handles everything
62//! related to type information about resources. Instead this one file was
63//! chosen as the place for this doc comment but the file itself is deceptively
64//! small as much of the other handling happens elsewhere in component
65//! translation.
66//!
67//! For more details on fiddly bits see the documentation on various fields and
68//! methods below.
69
70use crate::component::{
71    AbstractResourceIndex, ComponentTypes, ResourceIndex, RuntimeComponentInstanceIndex,
72    TypeResourceTable, TypeResourceTableIndex,
73};
74use crate::prelude::*;
75use std::collections::HashMap;
76use wasmparser::component_types::{ComponentAnyTypeId, ComponentEntityType, ResourceId};
77use wasmparser::types::TypesRef;
78
79/// Builder state used to translate wasmparser's `ResourceId` types to
80/// Wasmtime's `TypeResourceTableIndex` type.
81///
82/// This is contained in a `ComponentTypesBuilder` but is modified quite a bit
83/// manually via the `inline` phase of component instantiation.
84///
85/// This type crucially implements the `Clone` trait which is used to "snapshot"
86/// the current state of resource translation. The purpose of `Clone` here is to
87/// record translation information just before a subcomponent is instantiated to
88/// restore it after the subcomponent's instantiation has completed. This is
89/// done to handle instantiations of the same component multiple times
90/// correctly.
91///
92/// Wasmparser produces one set of type information for a component, and not a
93/// unique set of type information about its internals for each instantiation.
94/// Each instance which results from instantiation gets a new type, but when
95/// we're translating the instantiation of a component Wasmtime will re-run all
96/// initializers. This means that if naively implemented the `ResourceId`
97/// mapping from the first instantiation will be reused by the second
98/// instantiation. The snapshotting behavior and restoration guarantees that
99/// whenever a subcomponent is visited and instantiated it's guaranteed that
100/// there's no registered information for its `ResourceId` definitions within
101/// this builder.
102///
103/// Note that `ResourceId` references are guaranteed to be "local" in the sense
104/// that if a resource is defined within a component then the ID it's assigned
105/// internally within a component is different than the ID when it's
106/// instantiated (since all instantiations produce new types). This means that
107/// when throwing away state built-up from within a component that's also
108/// reasonable because the information should never be used after a component is
109/// left anyway.
110#[derive(Default, Clone)]
111pub struct ResourcesBuilder {
112    /// A cache of previously visited `ResourceId` items and which table they
113    /// correspond to. This is lazily populated as resources are visited and is
114    /// exclusively used by the `convert` function below.
115    resource_id_to_table_index: HashMap<ResourceId, TypeResourceTableIndex>,
116
117    /// A cache of the origin resource type behind a `ResourceId`.
118    ///
119    /// Unlike `resource_id_to_table_index` this is required to be eagerly
120    /// populated before translation of a type occurs. This is populated by
121    /// `register_*` methods below and is manually done during the `inline`
122    /// phase. This is used to record the actual underlying type of a resource
123    /// and where it originally comes from. When a resource is later referred to
124    /// then a table is injected to be referred to.
125    resource_id_to_resource_index: HashMap<ResourceId, ResourceIndexKind>,
126
127    /// The current instance index that's being visited. This is updated as
128    /// inliner frames are processed and components are instantiated.
129    current_instance: Option<RuntimeComponentInstanceIndex>,
130}
131
132/// Resources are considered either "concrete" or "abstract" depending on where
133/// the resource type is defined.
134///
135/// Resources defined in a component, or imported into a component, are
136/// considered "concrete" and may actually be instantiated/have a value at
137/// runtime. Resources defined in instance types or component types are
138/// considered "abstract" meaning that they won't ever actually exist at runtime
139/// so only an integer identifier is tracked for them.
140#[derive(Clone, Copy, Debug)]
141enum ResourceIndexKind {
142    Concrete(ResourceIndex),
143    Abstract(AbstractResourceIndex),
144}
145
146impl ResourcesBuilder {
147    /// Converts the `id` provided into a `TypeResourceTableIndex`.
148    ///
149    /// If `id` has previously been seen or converted, the prior value is
150    /// returned. Otherwise the `resource_id_to_resource_index` table must have
151    /// been previously populated and additionally `current_instance` must have
152    /// been previously set. Using these a new `TypeResourceTable` value is
153    /// allocated which produces a fresh `TypeResourceTableIndex` within the
154    /// `types` provided.
155    ///
156    /// Due to `wasmparser`'s uniqueness of resource IDs combined with the
157    /// snapshotting and restoration behavior of `ResourcesBuilder` itself this
158    /// should have the net effect of the first time a resource is seen within
159    /// any component it's assigned a new table, which is exactly what we want.
160    pub fn convert(
161        &mut self,
162        id: ResourceId,
163        types: &mut ComponentTypes,
164    ) -> TypeResourceTableIndex {
165        *self
166            .resource_id_to_table_index
167            .entry(id)
168            .or_insert_with(|| {
169                let table_ty = match self.resource_id_to_resource_index[&id] {
170                    ResourceIndexKind::Concrete(ty) => {
171                        let instance = self.current_instance.expect("current instance not set");
172                        TypeResourceTable::Concrete { ty, instance }
173                    }
174                    ResourceIndexKind::Abstract(i) => TypeResourceTable::Abstract(i),
175                };
176                types.push_resource_table(table_ty)
177            })
178    }
179
180    /// Walks over the `ty` provided, as defined within `types`, and registers
181    /// all the defined resources found with the `register` function provided.
182    ///
183    /// This is used to register `ResourceIndex` entries within the
184    /// `resource_id_to_resource_index` table of this type for situations such
185    /// as when a resource is imported into a component. During the inlining
186    /// phase of compilation the actual underlying type of the resource is
187    /// known due to tracking dataflow and this registers that relationship.
188    ///
189    /// The `path` provided is temporary storage to pass to the `register`
190    /// function eventually.
191    ///
192    /// The `register` callback is invoked with `path` with a list of names
193    /// which correspond to exports of instances to reach the "leaf" where a
194    /// resource type is expected.
195    pub fn register_component_entity_type<'a>(
196        &mut self,
197        types: &'a TypesRef<'_>,
198        ty: ComponentEntityType,
199        path: &mut Vec<&'a str>,
200        register: &mut dyn FnMut(&[&'a str]) -> ResourceIndex,
201    ) {
202        self.register_component_entity_type_(types, ty, path, &mut |path| {
203            ResourceIndexKind::Concrete(register(path))
204        })
205    }
206
207    /// Same as [`Self::register_component_entity_type`], but for when an
208    /// [`AbstractResourceIndex`] is created for all resources.
209    pub fn register_abstract_component_entity_type<'a>(
210        &mut self,
211        types: &'a TypesRef<'_>,
212        ty: ComponentEntityType,
213        path: &mut Vec<&'a str>,
214        register: &mut dyn FnMut(&[&'a str]) -> AbstractResourceIndex,
215    ) {
216        self.register_component_entity_type_(types, ty, path, &mut |path| {
217            ResourceIndexKind::Abstract(register(path))
218        })
219    }
220
221    fn register_component_entity_type_<'a>(
222        &mut self,
223        types: &'a TypesRef<'_>,
224        ty: ComponentEntityType,
225        path: &mut Vec<&'a str>,
226        register: &mut dyn FnMut(&[&'a str]) -> ResourceIndexKind,
227    ) {
228        match ty {
229            // If `ty` is itself a type, and that's a resource type, then this
230            // is where registration happens. The `register` callback is invoked
231            // with the current path and that's inserted in to
232            // `resource_id_to_resource_index` if the resource hasn't been seen
233            // yet.
234            ComponentEntityType::Type {
235                created: ComponentAnyTypeId::Resource(id),
236                ..
237            } => {
238                self.resource_id_to_resource_index
239                    .entry(id.resource())
240                    .or_insert_with(|| register(path));
241            }
242
243            // Resources can be imported/defined through exports of instances so
244            // all instance exports are walked here. Note the management of
245            // `path` which is used for the recursive invocation of this method.
246            ComponentEntityType::Instance(id) => {
247                let ty = &types[id];
248                for (name, ty) in ty.exports.iter() {
249                    path.push(name);
250                    self.register_component_entity_type_(types, *ty, path, register);
251                    path.pop();
252                }
253            }
254
255            // None of these items can introduce a new component type, so
256            // there's no need to recurse over these.
257            ComponentEntityType::Func(_)
258            | ComponentEntityType::Type { .. }
259            | ComponentEntityType::Module(_)
260            | ComponentEntityType::Component(_)
261            | ComponentEntityType::Value(_) => {}
262        }
263    }
264    /// Declares that the wasmparser `id`, which must point to a resource, is
265    /// defined by the `ty` provided.
266    ///
267    /// This is used when a local resource is defined within a component for example.
268    pub fn register_resource(&mut self, id: ResourceId, ty: ResourceIndex) {
269        let prev = self
270            .resource_id_to_resource_index
271            .insert(id, ResourceIndexKind::Concrete(ty));
272        assert!(prev.is_none());
273    }
274
275    /// Updates the `current_instance` field to assign instance fields of future
276    /// `TypeResourceTableIndex` values produced via `convert`.
277    pub fn set_current_instance(&mut self, instance: RuntimeComponentInstanceIndex) {
278        self.current_instance = Some(instance);
279    }
280
281    /// Retrieves the `current_instance` field.
282    pub fn get_current_instance(&self) -> Option<RuntimeComponentInstanceIndex> {
283        self.current_instance
284    }
285}