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}