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}