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