wit_component/encoding/
world.rs

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        let world = &resolve.worlds[world];
354
355        // FIXME: ideally liveness information here would be plumbed through to
356        // encoding but that's not done at this time. Only liveness for each
357        // interface is plumbed so top-level world types are unconditionally
358        // encoded and therefore unconditionally live here. Once encoding is
359        // based on conditionally-live things then this should be removed.
360        for (_, item) in world.imports.iter() {
361            if let WorldItem::Type(id) = item {
362                live.add_type_id(resolve, *id);
363            }
364        }
365
366        for (_, _, import) in imports.imports() {
367            match import {
368                // WIT-level function imports need the associated WIT definition.
369                Import::WorldFunc(key, _, _) => {
370                    live.add_world_item(resolve, &world.imports[key]);
371                }
372                Import::InterfaceFunc(_, id, name, _) => {
373                    live.add_func(resolve, &resolve.interfaces[*id].functions[name]);
374                }
375
376                // Resource-related intrinsics will need the resource.
377                Import::ImportedResourceDrop(.., ty)
378                | Import::ExportedResourceDrop(_, ty)
379                | Import::ExportedResourceNew(_, ty)
380                | Import::ExportedResourceRep(_, ty) => live.add_type_id(resolve, *ty),
381
382                // Future/Stream related intrinsics need to refer to the type
383                // that the intrinsic is operating on.
384                Import::StreamNew(info)
385                | Import::StreamRead { info, async_: _ }
386                | Import::StreamWrite { info, async_: _ }
387                | Import::StreamCancelRead { info, async_: _ }
388                | Import::StreamCancelWrite { info, async_: _ }
389                | Import::StreamCloseReadable(info)
390                | Import::StreamCloseWritable(info)
391                | Import::FutureNew(info)
392                | Import::FutureRead { info, async_: _ }
393                | Import::FutureWrite { info, async_: _ }
394                | Import::FutureCancelRead { info, async_: _ }
395                | Import::FutureCancelWrite { info, async_: _ }
396                | Import::FutureCloseReadable(info)
397                | Import::FutureCloseWritable(info) => {
398                    live.add_type_id(resolve, info.ty);
399                }
400
401                // The `task.return` intrinsic needs to be able to refer to the
402                // type that is being returned.
403                Import::ExportedTaskReturn(.., ty) => {
404                    if let Some(ty) = ty {
405                        live.add_type(resolve, ty);
406                    }
407                }
408
409                // Intrinsics that don't need to refer to WIT types can be
410                // skipped here.
411                Import::AdapterExport(_)
412                | Import::MainModuleMemory
413                | Import::MainModuleExport { .. }
414                | Import::Item(_)
415                | Import::ContextGet(_)
416                | Import::ContextSet(_)
417                | Import::BackpressureSet
418                | Import::WaitableSetNew
419                | Import::WaitableSetWait { .. }
420                | Import::WaitableSetPoll { .. }
421                | Import::WaitableSetDrop
422                | Import::WaitableJoin
423                | Import::Yield { .. }
424                | Import::SubtaskDrop
425                | Import::SubtaskCancel { .. }
426                | Import::ErrorContextNew { .. }
427                | Import::ErrorContextDebugMessage { .. }
428                | Import::ErrorContextDrop
429                | Import::ExportedTaskCancel => {}
430            }
431        }
432    }
433
434    fn process_exports_used(&mut self) {
435        let resolve = &self.encoder.metadata.resolve;
436        let world = self.encoder.metadata.world;
437
438        let exports = &resolve.worlds[world].exports;
439        for (_name, item) in exports.iter() {
440            let id = match item {
441                WorldItem::Function(_) => continue,
442                WorldItem::Interface { id, .. } => *id,
443                WorldItem::Type(_) => unreachable!(),
444            };
445            let mut set = HashSet::new();
446
447            for other in resolve.interface_direct_deps(id) {
448                // If this dependency is not exported, then it'll show up
449                // through an import, so we're not interested in it.
450                if !exports.contains_key(&WorldKey::Interface(other)) {
451                    continue;
452                }
453
454                // Otherwise this is a new exported dependency of ours, and
455                // additionally this interface inherits all the transitive
456                // dependencies too.
457                if set.insert(other) {
458                    set.extend(self.exports_used[&other].iter().copied());
459                }
460            }
461            let prev = self.exports_used.insert(id, set);
462            assert!(prev.is_none());
463        }
464    }
465}
466
467#[derive(Default)]
468struct Required<'a> {
469    interface_funcs: IndexMap<Option<InterfaceId>, IndexSet<(&'a str, AbiVariant)>>,
470    resource_drops: IndexSet<TypeId>,
471}
472
473impl ImportedInterface {
474    fn add_func(&mut self, required: &Required<'_>, resolve: &Resolve, func: &Function) {
475        let mut abis = Vec::with_capacity(2);
476        if let Some(set) = required.interface_funcs.get(&self.interface) {
477            if set.contains(&(func.name.as_str(), AbiVariant::GuestImport)) {
478                abis.push(AbiVariant::GuestImport);
479            }
480            if set.contains(&(func.name.as_str(), AbiVariant::GuestImportAsync)) {
481                abis.push(AbiVariant::GuestImportAsync);
482            }
483        }
484        for abi in abis {
485            log::trace!("add func {} {abi:?}", func.name);
486            let options = RequiredOptions::for_import(resolve, func, abi);
487            let lowering = if options.is_empty() {
488                Lowering::Direct
489            } else {
490                let sig = resolve.wasm_signature(abi, func);
491                Lowering::Indirect { sig, options }
492            };
493
494            let prev = self.lowerings.insert((func.name.clone(), abi), lowering);
495            assert!(prev.is_none());
496        }
497    }
498
499    fn add_type(&mut self, required: &Required<'_>, resolve: &Resolve, id: TypeId) {
500        let ty = &resolve.types[id];
501        match &ty.kind {
502            TypeDefKind::Resource => {}
503            _ => return,
504        }
505        let name = ty.name.as_deref().expect("resources must be named");
506
507        if required.resource_drops.contains(&id) {
508            let name = format!("{name}_drop");
509            let prev = self
510                .lowerings
511                .insert((name, AbiVariant::GuestImport), Lowering::ResourceDrop(id));
512            assert!(prev.is_none());
513        }
514    }
515}