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