1 | use super::{Adapter, ComponentEncoder, LibraryInfo, RequiredOptions}; |
2 | use crate::validation::{ |
3 | validate_adapter_module, validate_module, Import, ImportMap, ValidatedModule, |
4 | }; |
5 | use anyhow::{Context, Result}; |
6 | use indexmap::{IndexMap, IndexSet}; |
7 | use std::borrow::Cow; |
8 | use std::collections::{HashMap, HashSet}; |
9 | use wit_parser::{ |
10 | abi::{AbiVariant, WasmSignature}, |
11 | Function, InterfaceId, LiveTypes, Resolve, TypeDefKind, TypeId, TypeOwner, WorldId, WorldItem, |
12 | WorldKey, |
13 | }; |
14 | |
15 | pub struct WorldAdapter<'a> { |
16 | pub wasm: Cow<'a, [u8]>, |
17 | pub info: ValidatedModule, |
18 | pub library_info: Option<&'a LibraryInfo>, |
19 | } |
20 | |
21 | /// Metadata discovered from the state configured in a `ComponentEncoder`. |
22 | /// |
23 | /// This is stored separately from `EncodingState` to be stored as a borrow in |
24 | /// `EncodingState` as this information doesn't change throughout the encoding |
25 | /// process. |
26 | pub struct ComponentWorld<'a> { |
27 | /// Encoder configuration with modules, the document ,etc. |
28 | pub encoder: &'a ComponentEncoder, |
29 | /// Validation information of the input module, or `None` in `--types-only` |
30 | /// mode. |
31 | pub info: ValidatedModule, |
32 | /// Validation information about adapters populated only for required |
33 | /// adapters. Additionally stores the gc'd wasm for each adapter. |
34 | pub adapters: IndexMap<&'a str, WorldAdapter<'a>>, |
35 | /// Map of all imports and descriptions of what they're importing. |
36 | pub import_map: IndexMap<Option<String>, ImportedInterface>, |
37 | /// Set of all live types which must be exported either because they're |
38 | /// directly used or because they're transitively used. |
39 | pub live_type_imports: IndexMap<InterfaceId, IndexSet<TypeId>>, |
40 | /// For each exported interface in the desired world this map lists |
41 | /// the set of interfaces that it depends on which are also exported. |
42 | /// |
43 | /// This set is used to determine when types are imported/used whether they |
44 | /// come from imports or exports. |
45 | pub exports_used: HashMap<InterfaceId, HashSet<InterfaceId>>, |
46 | } |
47 | |
48 | #[derive (Debug)] |
49 | pub struct ImportedInterface { |
50 | pub lowerings: IndexMap<(String, AbiVariant), Lowering>, |
51 | pub interface: Option<InterfaceId>, |
52 | } |
53 | |
54 | #[derive (Debug)] |
55 | pub enum Lowering { |
56 | Direct, |
57 | Indirect { |
58 | sig: WasmSignature, |
59 | options: RequiredOptions, |
60 | }, |
61 | ResourceDrop(TypeId), |
62 | } |
63 | |
64 | impl<'a> ComponentWorld<'a> { |
65 | pub fn new(encoder: &'a ComponentEncoder) -> Result<Self> { |
66 | let info = validate_module(encoder, &encoder.module).context("module was not valid" )?; |
67 | |
68 | let mut ret = ComponentWorld { |
69 | encoder, |
70 | info, |
71 | adapters: IndexMap::new(), |
72 | import_map: IndexMap::new(), |
73 | live_type_imports: Default::default(), |
74 | exports_used: HashMap::new(), |
75 | }; |
76 | |
77 | ret.process_adapters()?; |
78 | ret.process_imports()?; |
79 | ret.process_exports_used(); |
80 | ret.process_live_type_imports(); |
81 | |
82 | Ok(ret) |
83 | } |
84 | |
85 | /// Process adapters which are required here. Iterate over all |
86 | /// adapters and figure out what functions are required from the |
87 | /// adapter itself, either because the functions are imported by the |
88 | /// main module or they're part of the adapter's exports. |
89 | fn process_adapters(&mut self) -> Result<()> { |
90 | let resolve = &self.encoder.metadata.resolve; |
91 | let world = self.encoder.metadata.world; |
92 | for ( |
93 | name, |
94 | Adapter { |
95 | wasm, |
96 | metadata: _, |
97 | required_exports, |
98 | library_info, |
99 | }, |
100 | ) in self.encoder.adapters.iter() |
101 | { |
102 | let required_by_import = self.info.imports.required_from_adapter(name.as_str()); |
103 | let no_required_by_import = || required_by_import.is_empty(); |
104 | let no_required_exports = || { |
105 | required_exports |
106 | .iter() |
107 | .all(|name| match &resolve.worlds[world].exports[name] { |
108 | WorldItem::Function(_) => false, |
109 | WorldItem::Interface { id, .. } => { |
110 | resolve.interfaces[*id].functions.is_empty() |
111 | } |
112 | WorldItem::Type(_) => true, |
113 | }) |
114 | }; |
115 | if no_required_by_import() && no_required_exports() && library_info.is_none() { |
116 | continue; |
117 | } |
118 | let wasm = if library_info.is_some() { |
119 | Cow::Borrowed(wasm as &[u8]) |
120 | } else { |
121 | // Without `library_info` this means that this is an adapter. |
122 | // The goal of the adapter is to provide a suite of symbols that |
123 | // can be imported, but not all symbols may be imported. Here |
124 | // the module is trimmed down to only what's needed by the |
125 | // original main module. |
126 | // |
127 | // The main module requires `required_by_import` above, but |
128 | // adapters may themselves also export WIT items. To handle this |
129 | // the sequence of operations here are: |
130 | // |
131 | // 1. First the adapter is validated as-is. This ensures that |
132 | // everything looks good before GC. |
133 | // 2. The metadata from step (1) is used to determine the set of |
134 | // WIT-level exports that are needed. This includes things |
135 | // like realloc functions and such. |
136 | // 3. The set of WIT-level functions from (2) is unioned with |
137 | // `required_by_import` to create the set of required exports |
138 | // of the adapter. |
139 | // 4. This set of exports is used to delete some exports of the |
140 | // adapter and then perform a GC pass. |
141 | // |
142 | // Finally at the end of all of this the |
143 | // `validate_adapter_module` method is called for a second time |
144 | // on the minimized adapter. This is done because deleting |
145 | // imports may have deleted some imports which means that the |
146 | // final component may not need to import as many interfaces. |
147 | let info = validate_adapter_module( |
148 | self.encoder, |
149 | &wasm, |
150 | &required_by_import, |
151 | required_exports, |
152 | library_info.as_ref(), |
153 | ) |
154 | .with_context(|| { |
155 | format!("failed to validate the imports of the adapter module ` {name}`" ) |
156 | })?; |
157 | let mut required = IndexSet::new(); |
158 | for (name, _ty) in required_by_import.iter() { |
159 | required.insert(name.to_string()); |
160 | } |
161 | for (name, _export) in info.exports.iter() { |
162 | required.insert(name.to_string()); |
163 | } |
164 | |
165 | Cow::Owned( |
166 | crate::gc::run( |
167 | wasm, |
168 | &required, |
169 | if self.encoder.realloc_via_memory_grow { |
170 | None |
171 | } else { |
172 | self.info.exports.realloc_to_import_into_adapter() |
173 | }, |
174 | ) |
175 | .context("failed to reduce input adapter module to its minimal size" )?, |
176 | ) |
177 | }; |
178 | let info = validate_adapter_module( |
179 | self.encoder, |
180 | &wasm, |
181 | &required_by_import, |
182 | required_exports, |
183 | library_info.as_ref(), |
184 | ) |
185 | .with_context(|| { |
186 | format!("failed to validate the imports of the minimized adapter module ` {name}`" ) |
187 | })?; |
188 | self.adapters.insert( |
189 | name, |
190 | WorldAdapter { |
191 | info, |
192 | wasm, |
193 | library_info: library_info.as_ref(), |
194 | }, |
195 | ); |
196 | } |
197 | Ok(()) |
198 | } |
199 | |
200 | /// Fills out the `import_map` field of `self` by determining the live |
201 | /// functions from all imports. This additionally classifies imported |
202 | /// functions into direct or indirect lowerings for managing shims. |
203 | fn process_imports(&mut self) -> Result<()> { |
204 | let resolve = &self.encoder.metadata.resolve; |
205 | let world = self.encoder.metadata.world; |
206 | |
207 | // Inspect all imports of the main module and adapters to find all |
208 | // WIT-looking things and register those as required. This is used to |
209 | // prune out unneeded things in the `add_item` function below. |
210 | let mut required = Required::default(); |
211 | for (_, _, import) in self |
212 | .adapters |
213 | .values() |
214 | .flat_map(|a| a.info.imports.imports()) |
215 | .chain(self.info.imports.imports()) |
216 | { |
217 | match import { |
218 | Import::WorldFunc(_, name, abi) => { |
219 | required |
220 | .interface_funcs |
221 | .entry(None) |
222 | .or_default() |
223 | .insert((name, *abi)); |
224 | } |
225 | Import::InterfaceFunc(_, id, name, abi) => { |
226 | required |
227 | .interface_funcs |
228 | .entry(Some(*id)) |
229 | .or_default() |
230 | .insert((name, *abi)); |
231 | } |
232 | Import::ImportedResourceDrop(_, _, id) => { |
233 | required.resource_drops.insert(*id); |
234 | } |
235 | _ => {} |
236 | } |
237 | } |
238 | for (name, item) in resolve.worlds[world].imports.iter() { |
239 | add_item(&mut self.import_map, resolve, name, item, &required)?; |
240 | } |
241 | return Ok(()); |
242 | |
243 | fn add_item( |
244 | import_map: &mut IndexMap<Option<String>, ImportedInterface>, |
245 | resolve: &Resolve, |
246 | name: &WorldKey, |
247 | item: &WorldItem, |
248 | required: &Required<'_>, |
249 | ) -> Result<()> { |
250 | let name = resolve.name_world_key(name); |
251 | log::trace!("register import ` {name}`" ); |
252 | let import_map_key = match item { |
253 | WorldItem::Function(_) | WorldItem::Type(_) => None, |
254 | WorldItem::Interface { .. } => Some(name), |
255 | }; |
256 | let interface_id = match item { |
257 | WorldItem::Function(_) | WorldItem::Type(_) => None, |
258 | WorldItem::Interface { id, .. } => Some(*id), |
259 | }; |
260 | let interface = import_map |
261 | .entry(import_map_key) |
262 | .or_insert_with(|| ImportedInterface { |
263 | interface: interface_id, |
264 | lowerings: Default::default(), |
265 | }); |
266 | assert_eq!(interface.interface, interface_id); |
267 | match item { |
268 | WorldItem::Function(func) => { |
269 | interface.add_func(required, resolve, func); |
270 | } |
271 | WorldItem::Type(ty) => { |
272 | interface.add_type(required, resolve, *ty); |
273 | } |
274 | WorldItem::Interface { id, .. } => { |
275 | for (_name, ty) in resolve.interfaces[*id].types.iter() { |
276 | interface.add_type(required, resolve, *ty); |
277 | } |
278 | for (_name, func) in resolve.interfaces[*id].functions.iter() { |
279 | interface.add_func(required, resolve, func); |
280 | } |
281 | } |
282 | } |
283 | Ok(()) |
284 | } |
285 | } |
286 | |
287 | /// Determines the set of live imported types which are required to satisfy |
288 | /// the imports and exports of the lifted core module. |
289 | fn process_live_type_imports(&mut self) { |
290 | let mut live = LiveTypes::default(); |
291 | let resolve = &self.encoder.metadata.resolve; |
292 | let world = self.encoder.metadata.world; |
293 | |
294 | // First use the previously calculated metadata about live imports to |
295 | // determine the set of live types in those imports. |
296 | self.add_live_imports(world, &self.info.imports, &mut live); |
297 | for (adapter_name, adapter) in self.adapters.iter() { |
298 | log::trace!("processing adapter ` {adapter_name}`" ); |
299 | self.add_live_imports(world, &adapter.info.imports, &mut live); |
300 | } |
301 | |
302 | // Next any imported types used by an export must also be considered |
303 | // live. This is a little tricky though because interfaces can be both |
304 | // imported and exported, so it's not as simple as registering the |
305 | // entire export's set of types and their transitive references |
306 | // (otherwise if you only export an interface it would consider those |
307 | // types imports live too). |
308 | // |
309 | // Here if the export is an interface the set of live types for that |
310 | // interface is calculated separately. The `exports_used` field |
311 | // previously calculated is then consulted to add any types owned by |
312 | // interfaces not in the `exports_used` set to the live imported types |
313 | // set. This means that only types not defined by referenced exports |
314 | // will get added here. |
315 | for (name, item) in resolve.worlds[world].exports.iter() { |
316 | log::trace!("add live world export ` {}`" , resolve.name_world_key(name)); |
317 | let id = match item { |
318 | WorldItem::Interface { id, .. } => id, |
319 | WorldItem::Function(_) | WorldItem::Type(_) => { |
320 | live.add_world_item(resolve, item); |
321 | continue; |
322 | } |
323 | }; |
324 | |
325 | let exports_used = &self.exports_used[id]; |
326 | let mut live_from_export = LiveTypes::default(); |
327 | live_from_export.add_world_item(resolve, item); |
328 | for ty in live_from_export.iter() { |
329 | let owner = match resolve.types[ty].owner { |
330 | TypeOwner::Interface(id) => id, |
331 | _ => continue, |
332 | }; |
333 | if owner != *id && !exports_used.contains(&owner) { |
334 | live.add_type_id(resolve, ty); |
335 | } |
336 | } |
337 | } |
338 | |
339 | for live in live.iter() { |
340 | let owner = match resolve.types[live].owner { |
341 | TypeOwner::Interface(id) => id, |
342 | _ => continue, |
343 | }; |
344 | self.live_type_imports |
345 | .entry(owner) |
346 | .or_insert(Default::default()) |
347 | .insert(live); |
348 | } |
349 | } |
350 | |
351 | fn add_live_imports(&self, world: WorldId, imports: &ImportMap, live: &mut LiveTypes) { |
352 | let resolve = &self.encoder.metadata.resolve; |
353 | for (name, item) in resolve.worlds[world].imports.iter() { |
354 | let name = resolve.name_world_key(name); |
355 | match item { |
356 | WorldItem::Function(func) => { |
357 | if !imports.uses_toplevel_func(name.as_str()) { |
358 | continue; |
359 | } |
360 | log::trace!("add live function import ` {name}`" ); |
361 | live.add_func(resolve, func); |
362 | } |
363 | WorldItem::Interface { id, .. } => { |
364 | log::trace!("add live interface import ` {name}`" ); |
365 | for (name, func) in resolve.interfaces[*id].functions.iter() { |
366 | if imports.uses_interface_func(*id, name.as_str()) { |
367 | log::trace!("add live func ` {name}`" ); |
368 | live.add_func(resolve, func); |
369 | } |
370 | } |
371 | for (_name, ty) in resolve.interfaces[*id].types.iter() { |
372 | if imports.uses_imported_resource_drop(*ty) { |
373 | live.add_type_id(resolve, *ty); |
374 | } |
375 | } |
376 | } |
377 | WorldItem::Type(id) => live.add_type_id(resolve, *id), |
378 | } |
379 | } |
380 | } |
381 | |
382 | fn process_exports_used(&mut self) { |
383 | let resolve = &self.encoder.metadata.resolve; |
384 | let world = self.encoder.metadata.world; |
385 | |
386 | let exports = &resolve.worlds[world].exports; |
387 | for (_name, item) in exports.iter() { |
388 | let id = match item { |
389 | WorldItem::Function(_) => continue, |
390 | WorldItem::Interface { id, .. } => *id, |
391 | WorldItem::Type(_) => unreachable!(), |
392 | }; |
393 | let mut set = HashSet::new(); |
394 | |
395 | for other in resolve.interface_direct_deps(id) { |
396 | // If this dependency is not exported, then it'll show up |
397 | // through an import, so we're not interested in it. |
398 | if !exports.contains_key(&WorldKey::Interface(other)) { |
399 | continue; |
400 | } |
401 | |
402 | // Otherwise this is a new exported dependency of ours, and |
403 | // additionally this interface inherits all the transitive |
404 | // dependencies too. |
405 | if set.insert(other) { |
406 | set.extend(self.exports_used[&other].iter().copied()); |
407 | } |
408 | } |
409 | let prev = self.exports_used.insert(id, set); |
410 | assert!(prev.is_none()); |
411 | } |
412 | } |
413 | } |
414 | |
415 | #[derive (Default)] |
416 | struct Required<'a> { |
417 | interface_funcs: IndexMap<Option<InterfaceId>, IndexSet<(&'a str, AbiVariant)>>, |
418 | resource_drops: IndexSet<TypeId>, |
419 | } |
420 | |
421 | impl ImportedInterface { |
422 | fn add_func(&mut self, required: &Required<'_>, resolve: &Resolve, func: &Function) { |
423 | let mut abis = Vec::with_capacity(2); |
424 | if let Some(set) = required.interface_funcs.get(&self.interface) { |
425 | if set.contains(&(func.name.as_str(), AbiVariant::GuestImport)) { |
426 | abis.push(AbiVariant::GuestImport); |
427 | } |
428 | if set.contains(&(func.name.as_str(), AbiVariant::GuestImportAsync)) { |
429 | abis.push(AbiVariant::GuestImportAsync); |
430 | } |
431 | } |
432 | for abi in abis { |
433 | log::trace!("add func {} {abi:?}" , func.name); |
434 | let options = RequiredOptions::for_import(resolve, func, abi); |
435 | let lowering = if options.is_empty() { |
436 | Lowering::Direct |
437 | } else { |
438 | let sig = resolve.wasm_signature(abi, func); |
439 | Lowering::Indirect { sig, options } |
440 | }; |
441 | |
442 | let prev = self.lowerings.insert((func.name.clone(), abi), lowering); |
443 | assert!(prev.is_none()); |
444 | } |
445 | } |
446 | |
447 | fn add_type(&mut self, required: &Required<'_>, resolve: &Resolve, id: TypeId) { |
448 | let ty = &resolve.types[id]; |
449 | match &ty.kind { |
450 | TypeDefKind::Resource => {} |
451 | _ => return, |
452 | } |
453 | let name = ty.name.as_deref().expect("resources must be named" ); |
454 | |
455 | if required.resource_drops.contains(&id) { |
456 | let name = format!(" {name}_drop" ); |
457 | let prev = self |
458 | .lowerings |
459 | .insert((name, AbiVariant::GuestImport), Lowering::ResourceDrop(id)); |
460 | assert!(prev.is_none()); |
461 | } |
462 | } |
463 | } |
464 | |