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 ComponentTypes, ResourceIndex, RuntimeComponentInstanceIndex, TypeResourceTable,
72 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, ResourceIndex>,
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
132impl ResourcesBuilder {
133 /// Converts the `id` provided into a `TypeResourceTableIndex`.
134 ///
135 /// If `id` has previously been seen or converted, the prior value is
136 /// returned. Otherwise the `resource_id_to_resource_index` table must have
137 /// been previously populated and additionally `current_instance` must have
138 /// been previously set. Using these a new `TypeResourceTable` value is
139 /// allocated which produces a fresh `TypeResourceTableIndex` within the
140 /// `types` provided.
141 ///
142 /// Due to `wasmparser`'s uniqueness of resource IDs combined with the
143 /// snapshotting and restoration behavior of `ResourcesBuilder` itself this
144 /// should have the net effect of the first time a resource is seen within
145 /// any component it's assigned a new table, which is exactly what we want.
146 pub fn convert(
147 &mut self,
148 id: ResourceId,
149 types: &mut ComponentTypes,
150 ) -> TypeResourceTableIndex {
151 *self
152 .resource_id_to_table_index
153 .entry(id)
154 .or_insert_with(|| {
155 let ty = self.resource_id_to_resource_index[&id];
156 let instance = self.current_instance.expect("current instance not set");
157 types.push_resource_table(TypeResourceTable { ty, instance })
158 })
159 }
160
161 /// Walks over the `ty` provided, as defined within `types`, and registers
162 /// all the defined resources found with the `register` function provided.
163 ///
164 /// This is used to register `ResourceIndex` entries within the
165 /// `resource_id_to_resource_index` table of this type for situations such
166 /// as when a resource is imported into a component. During the inlining
167 /// phase of compilation the actual underlying type of the resource is
168 /// known due to tracking dataflow and this registers that relationship.
169 ///
170 /// The `path` provided is temporary storage to pass to the `register`
171 /// function eventually.
172 ///
173 /// The `register` callback is invoked with `path` with a list of names
174 /// which correspond to exports of instances to reach the "leaf" where a
175 /// resource type is expected.
176 pub fn register_component_entity_type<'a>(
177 &mut self,
178 types: &'a TypesRef<'_>,
179 ty: ComponentEntityType,
180 path: &mut Vec<&'a str>,
181 register: &mut dyn FnMut(&[&'a str]) -> ResourceIndex,
182 ) {
183 match ty {
184 // If `ty` is itself a type, and that's a resource type, then this
185 // is where registration happens. The `register` callback is invoked
186 // with the current path and that's inserted in to
187 // `resource_id_to_resource_index` if the resource hasn't been seen
188 // yet.
189 ComponentEntityType::Type {
190 created: ComponentAnyTypeId::Resource(id),
191 ..
192 } => {
193 self.resource_id_to_resource_index
194 .entry(id.resource())
195 .or_insert_with(|| register(path));
196 }
197
198 // Resources can be imported/defined through exports of instances so
199 // all instance exports are walked here. Note the management of
200 // `path` which is used for the recursive invocation of this method.
201 ComponentEntityType::Instance(id) => {
202 let ty = &types[id];
203 for (name, ty) in ty.exports.iter() {
204 path.push(name);
205 self.register_component_entity_type(types, *ty, path, register);
206 path.pop();
207 }
208 }
209
210 // None of these items can introduce a new component type, so
211 // there's no need to recurse over these.
212 ComponentEntityType::Func(_)
213 | ComponentEntityType::Type { .. }
214 | ComponentEntityType::Module(_)
215 | ComponentEntityType::Component(_)
216 | ComponentEntityType::Value(_) => {}
217 }
218 }
219
220 /// Declares that the wasmparser `id`, which must point to a resource, is
221 /// defined by the `ty` provided.
222 ///
223 /// This is used when a local resource is defined within a component for example.
224 pub fn register_resource(&mut self, id: ResourceId, ty: ResourceIndex) {
225 let prev = self.resource_id_to_resource_index.insert(id, ty);
226 assert!(prev.is_none());
227 }
228
229 /// Updates the `current_instance` field to assign instance fields of future
230 /// `TypeResourceTableIndex` values produced via `convert`.
231 pub fn set_current_instance(&mut self, instance: RuntimeComponentInstanceIndex) {
232 self.current_instance = Some(instance);
233 }
234
235 /// Retrieves the `current_instance` field.
236 pub fn get_current_instance(&self) -> Option<RuntimeComponentInstanceIndex> {
237 self.current_instance
238 }
239}