1use super::{Adapter, ComponentEncoder, LibraryInfo, RequiredOptions};
2use crate::validation::{
3 validate_adapter_module, validate_module, Import, ImportMap, ValidatedModule,
4};
5use anyhow::{Context, Result};
6use indexmap::{IndexMap, IndexSet};
7use std::borrow::Cow;
8use std::collections::{HashMap, HashSet};
9use wit_parser::{
10 abi::{AbiVariant, WasmSignature},
11 Function, InterfaceId, LiveTypes, Resolve, TypeDefKind, TypeId, TypeOwner, WorldId, WorldItem,
12 WorldKey,
13};
14
15pub 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.
26pub 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)]
49pub struct ImportedInterface {
50 pub lowerings: IndexMap<(String, AbiVariant), Lowering>,
51 pub interface: Option<InterfaceId>,
52}
53
54#[derive(Debug)]
55pub enum Lowering {
56 Direct,
57 Indirect {
58 sig: WasmSignature,
59 options: RequiredOptions,
60 },
61 ResourceDrop(TypeId),
62}
63
64impl<'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)]
416struct Required<'a> {
417 interface_funcs: IndexMap<Option<InterfaceId>, IndexSet<(&'a str, AbiVariant)>>,
418 resource_drops: IndexSet<TypeId>,
419}
420
421impl 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