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 
70 use crate::component::{
71     AbstractResourceIndex, ComponentTypes, ResourceIndex, RuntimeComponentInstanceIndex,
72     TypeResourceTable, TypeResourceTableIndex,
73 };
74 use crate::prelude::*;
75 use std::collections::HashMap;
76 use wasmparser::component_types::{ComponentAnyTypeId, ComponentEntityType, ResourceId};
77 use 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)]
111 pub 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)]
141 enum ResourceIndexKind {
142     Concrete(ResourceIndex),
143     Abstract(AbstractResourceIndex),
144 }
145 
146 impl 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.
convert( &mut self, id: ResourceId, types: &mut ComponentTypes, ) -> TypeResourceTableIndex160     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.
register_component_entity_type<'a>( &mut self, types: &'a TypesRef<'_>, ty: ComponentEntityType, path: &mut Vec<&'a str>, register: &mut dyn FnMut(&[&'a str]) -> ResourceIndex, )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.
register_abstract_component_entity_type<'a>( &mut self, types: &'a TypesRef<'_>, ty: ComponentEntityType, path: &mut Vec<&'a str>, register: &mut dyn FnMut(&[&'a str]) -> AbstractResourceIndex, )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 
register_component_entity_type_<'a>( &mut self, types: &'a TypesRef<'_>, ty: ComponentEntityType, path: &mut Vec<&'a str>, register: &mut dyn FnMut(&[&'a str]) -> ResourceIndexKind, )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.
register_resource(&mut self, id: ResourceId, ty: ResourceIndex)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`.
set_current_instance(&mut self, instance: RuntimeComponentInstanceIndex)277     pub fn set_current_instance(&mut self, instance: RuntimeComponentInstanceIndex) {
278         self.current_instance = Some(instance);
279     }
280 
281     /// Retrieves the `current_instance` field.
get_current_instance(&self) -> Option<RuntimeComponentInstanceIndex>282     pub fn get_current_instance(&self) -> Option<RuntimeComponentInstanceIndex> {
283         self.current_instance
284     }
285 }
286