Coverage Report

Created: 2026-08-02 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wit-component/src/encoding/world.rs
Line
Count
Source
1
use super::{Adapter, ComponentEncoder, LibraryInfo, RequiredOptions};
2
use crate::validation::{
3
    Import, ImportMap, PayloadType, ValidatedModule, validate_adapter_module, validate_module,
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
    Function, InterfaceId, LiveTypes, Resolve, TypeDefKind, TypeId, TypeOwner, WorldId, WorldItem,
11
    WorldKey,
12
    abi::{AbiVariant, WasmSignature},
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
    pub implements: Option<String>,
53
    pub external_id: Option<String>,
54
}
55
56
#[derive(Debug)]
57
pub enum Lowering {
58
    Direct,
59
    Indirect {
60
        sig: WasmSignature,
61
        options: RequiredOptions,
62
    },
63
    ResourceDrop(TypeId),
64
}
65
66
impl<'a> ComponentWorld<'a> {
67
2.95k
    pub fn new(encoder: &'a ComponentEncoder) -> Result<Self> {
68
2.95k
        let info = validate_module(encoder, &encoder.module, encoder.module_import_map.as_ref())
69
2.95k
            .context("module was not valid")?;
70
71
2.95k
        let mut ret = ComponentWorld {
72
2.95k
            encoder,
73
2.95k
            info,
74
2.95k
            adapters: IndexMap::new(),
75
2.95k
            import_map: IndexMap::new(),
76
2.95k
            live_type_imports: Default::default(),
77
2.95k
            exports_used: HashMap::new(),
78
2.95k
        };
79
80
2.95k
        ret.process_adapters()?;
81
2.95k
        ret.process_imports()?;
82
2.95k
        ret.process_exports_used();
83
2.95k
        ret.process_live_type_imports();
84
85
2.95k
        Ok(ret)
86
2.95k
    }
87
88
    /// Process adapters which are required here. Iterate over all
89
    /// adapters and figure out what functions are required from the
90
    /// adapter itself, either because the functions are imported by the
91
    /// main module or they're part of the adapter's exports.
92
2.95k
    fn process_adapters(&mut self) -> Result<()> {
93
2.95k
        let resolve = &self.encoder.metadata.resolve;
94
2.95k
        let world = self.encoder.metadata.world;
95
        for (
96
0
            name,
97
            Adapter {
98
0
                wasm,
99
                metadata: _,
100
0
                required_exports,
101
0
                library_info,
102
            },
103
2.95k
        ) in self.encoder.adapters.iter()
104
        {
105
0
            let required_by_import = self.info.imports.required_from_adapter(name.as_str());
106
0
            let no_required_by_import = || required_by_import.is_empty();
107
0
            let no_required_exports = || {
108
0
                required_exports
109
0
                    .iter()
110
0
                    .all(|name| match &resolve.worlds[world].exports[name] {
111
0
                        WorldItem::Function(_) => false,
112
0
                        WorldItem::Interface { id, .. } => {
113
0
                            resolve.interfaces[*id].functions.is_empty()
114
                        }
115
0
                        WorldItem::Type { .. } => true,
116
0
                    })
117
0
            };
118
0
            if no_required_by_import() && no_required_exports() && library_info.is_none() {
119
0
                continue;
120
0
            }
121
0
            let wasm = if library_info.is_some() {
122
0
                Cow::Borrowed(wasm as &[u8])
123
            } else {
124
                // Without `library_info` this means that this is an adapter.
125
                // The goal of the adapter is to provide a suite of symbols that
126
                // can be imported, but not all symbols may be imported. Here
127
                // the module is trimmed down to only what's needed by the
128
                // original main module.
129
                //
130
                // The main module requires `required_by_import` above, but
131
                // adapters may themselves also export WIT items. To handle this
132
                // the sequence of operations here are:
133
                //
134
                // 1. First the adapter is validated as-is. This ensures that
135
                //    everything looks good before GC.
136
                // 2. The metadata from step (1) is used to determine the set of
137
                //    WIT-level exports that are needed. This includes things
138
                //    like realloc functions and such.
139
                // 3. The set of WIT-level functions from (2) is unioned with
140
                //    `required_by_import` to create the set of required exports
141
                //    of the adapter.
142
                // 4. This set of exports is used to delete some exports of the
143
                //    adapter and then perform a GC pass.
144
                //
145
                // Finally at the end of all of this the
146
                // `validate_adapter_module` method is called for a second time
147
                // on the minimized adapter. This is done because deleting
148
                // imports may have deleted some imports which means that the
149
                // final component may not need to import as many interfaces.
150
0
                let info = validate_adapter_module(
151
0
                    self.encoder,
152
0
                    &wasm,
153
0
                    &required_by_import,
154
0
                    required_exports,
155
0
                    library_info.as_ref(),
156
                )
157
0
                .with_context(|| {
158
0
                    format!("failed to validate the imports of the adapter module `{name}`")
159
0
                })?;
160
0
                let mut required = IndexSet::new();
161
0
                for (name, _ty) in required_by_import.iter() {
162
0
                    required.insert(name.to_string());
163
0
                }
164
0
                for (name, _export) in info.exports.iter() {
165
0
                    required.insert(name.to_string());
166
0
                }
167
168
                Cow::Owned(
169
0
                    crate::gc::run(
170
0
                        wasm,
171
0
                        &required,
172
0
                        if self.encoder.realloc_via_memory_grow {
173
0
                            None
174
                        } else {
175
0
                            self.info.exports.realloc_to_import_into_adapter()
176
                        },
177
                    )
178
0
                    .context("failed to reduce input adapter module to its minimal size")?,
179
                )
180
            };
181
0
            let info = validate_adapter_module(
182
0
                self.encoder,
183
0
                &wasm,
184
0
                &required_by_import,
185
0
                required_exports,
186
0
                library_info.as_ref(),
187
            )
188
0
            .with_context(|| {
189
0
                format!("failed to validate the imports of the minimized adapter module `{name}`")
190
0
            })?;
191
0
            self.adapters.insert(
192
0
                name,
193
0
                WorldAdapter {
194
0
                    info,
195
0
                    wasm,
196
0
                    library_info: library_info.as_ref(),
197
0
                },
198
            );
199
        }
200
2.95k
        Ok(())
201
2.95k
    }
202
203
    /// Fills out the `import_map` field of `self` by determining the live
204
    /// functions from all imports. This additionally classifies imported
205
    /// functions into direct or indirect lowerings for managing shims.
206
2.95k
    fn process_imports(&mut self) -> Result<()> {
207
2.95k
        let resolve = &self.encoder.metadata.resolve;
208
2.95k
        let world = self.encoder.metadata.world;
209
210
        // Inspect all imports of the main module and adapters to find all
211
        // WIT-looking things and register those as required. This is used to
212
        // prune out unneeded things in the `add_item` function below.
213
2.95k
        let mut required = Required::default();
214
10.4k
        for (_, _, import) in self
215
2.95k
            .adapters
216
2.95k
            .values()
217
2.95k
            .flat_map(|a| a.info.imports.imports())
218
2.95k
            .chain(self.info.imports.imports())
219
        {
220
10.4k
            match import {
221
672
                Import::WorldFunc(_, name, abi) => {
222
672
                    required
223
672
                        .interface_funcs
224
672
                        .entry(None)
225
672
                        .or_default()
226
672
                        .insert((name, *abi));
227
672
                }
228
937
                Import::InterfaceFunc(_, id, name, abi) => {
229
937
                    required
230
937
                        .interface_funcs
231
937
                        .entry(Some(*id))
232
937
                        .or_default()
233
937
                        .insert((name, *abi));
234
937
                }
235
196
                Import::ImportedResourceDrop(_, _, id) => {
236
196
                    required.resource_drops.insert(*id);
237
196
                }
238
8.63k
                _ => {}
239
            }
240
        }
241
2.95k
        for (name, item) in resolve.worlds[world].imports.iter() {
242
2.71k
            add_item(&mut self.import_map, resolve, name, item, &required)?;
243
        }
244
2.95k
        return Ok(());
245
246
2.71k
        fn add_item(
247
2.71k
            import_map: &mut IndexMap<Option<String>, ImportedInterface>,
248
2.71k
            resolve: &Resolve,
249
2.71k
            key: &WorldKey,
250
2.71k
            item: &WorldItem,
251
2.71k
            required: &Required<'_>,
252
2.71k
        ) -> Result<()> {
253
2.71k
            let name = resolve.name_world_key(key);
254
2.71k
            log::trace!("register import `{name}`");
255
2.71k
            let import_map_key = match item {
256
2.20k
                WorldItem::Function(_) | WorldItem::Type { .. } => None,
257
511
                WorldItem::Interface { .. } => Some(name),
258
            };
259
2.71k
            let interface_id = match item {
260
2.20k
                WorldItem::Function(_) | WorldItem::Type { .. } => None,
261
511
                WorldItem::Interface { id, .. } => Some(*id),
262
            };
263
2.71k
            let implements = resolve.implements_value(key, item);
264
            // Note that `external_id` is only tracked for interface imports
265
            // here. World-level functions and types all share the `None` entry
266
            // in `import_map` but each item can have its own `external-id`
267
            // which is emitted on a per-item basis instead.
268
2.71k
            let external_id = match item {
269
2.20k
                WorldItem::Function(_) | WorldItem::Type { .. } => None,
270
511
                WorldItem::Interface { .. } => resolve.external_id_value(key, item),
271
            };
272
2.71k
            let interface = import_map
273
2.71k
                .entry(import_map_key)
274
2.71k
                .or_insert_with(|| ImportedInterface {
275
1.40k
                    interface: interface_id,
276
1.40k
                    lowerings: Default::default(),
277
1.40k
                    implements: implements.clone(),
278
1.40k
                    external_id: external_id.clone(),
279
1.40k
                });
280
2.71k
            assert_eq!(interface.interface, interface_id);
281
2.71k
            assert_eq!(interface.implements, implements);
282
2.71k
            assert_eq!(interface.external_id, external_id);
283
2.71k
            match item {
284
672
                WorldItem::Function(func) => {
285
672
                    interface.add_func(required, resolve, func);
286
672
                }
287
1.52k
                WorldItem::Type { id, .. } => {
288
1.52k
                    interface.add_type(required, resolve, *id);
289
1.52k
                }
290
511
                WorldItem::Interface { id, .. } => {
291
1.43k
                    for (_name, ty) in resolve.interfaces[*id].types.iter() {
292
1.43k
                        interface.add_type(required, resolve, *ty);
293
1.43k
                    }
294
937
                    for (_name, func) in resolve.interfaces[*id].functions.iter() {
295
937
                        interface.add_func(required, resolve, func);
296
937
                    }
297
                }
298
            }
299
2.71k
            Ok(())
300
2.71k
        }
301
2.95k
    }
302
303
    /// Determines the set of live imported types which are required to satisfy
304
    /// the imports and exports of the lifted core module.
305
2.95k
    fn process_live_type_imports(&mut self) {
306
2.95k
        let mut live = LiveTypes::default();
307
2.95k
        let resolve = &self.encoder.metadata.resolve;
308
2.95k
        let world = self.encoder.metadata.world;
309
310
        // First use the previously calculated metadata about live imports to
311
        // determine the set of live types in those imports.
312
2.95k
        self.add_live_imports(world, &self.info.imports, &mut live);
313
2.95k
        for (adapter_name, adapter) in self.adapters.iter() {
314
0
            log::trace!("processing adapter `{adapter_name}`");
315
0
            self.add_live_imports(world, &adapter.info.imports, &mut live);
316
        }
317
318
        // Next any imported types used by an export must also be considered
319
        // live. This is a little tricky though because interfaces can be both
320
        // imported and exported, so it's not as simple as registering the
321
        // entire export's set of types and their transitive references
322
        // (otherwise if you only export an interface it would consider those
323
        // types imports live too).
324
        //
325
        // Here if the export is an interface the set of live types for that
326
        // interface is calculated separately. The `exports_used` field
327
        // previously calculated is then consulted to add any types owned by
328
        // interfaces not in the `exports_used` set to the live imported types
329
        // set. This means that only types not defined by referenced exports
330
        // will get added here.
331
2.95k
        for (name, item) in resolve.worlds[world].exports.iter() {
332
1.92k
            log::trace!("add live world export `{}`", resolve.name_world_key(name));
333
1.92k
            let id = match item {
334
1.68k
                WorldItem::Interface { id, .. } => id,
335
                WorldItem::Function(_) | WorldItem::Type { .. } => {
336
231
                    live.add_world_item(resolve, item);
337
231
                    continue;
338
                }
339
            };
340
341
1.68k
            let exports_used = &self.exports_used[id];
342
1.68k
            let mut live_from_export = LiveTypes::default();
343
1.68k
            live_from_export.add_world_item(resolve, item);
344
11.7k
            for ty in live_from_export.iter() {
345
11.7k
                let owner = match resolve.types[ty].owner {
346
7.98k
                    TypeOwner::Interface(id) => id,
347
3.78k
                    _ => continue,
348
                };
349
7.98k
                if owner != *id && !exports_used.contains(&owner) {
350
101
                    live.add_type_id(resolve, ty);
351
7.88k
                }
352
            }
353
        }
354
355
7.06k
        for live in live.iter() {
356
7.06k
            let owner = match resolve.types[live].owner {
357
450
                TypeOwner::Interface(id) => id,
358
6.61k
                _ => continue,
359
            };
360
450
            self.live_type_imports
361
450
                .entry(owner)
362
450
                .or_insert(Default::default())
363
450
                .insert(live);
364
        }
365
2.95k
    }
366
367
2.95k
    fn add_live_imports(&self, world: WorldId, imports: &ImportMap, live: &mut LiveTypes) {
368
2.95k
        let resolve = &self.encoder.metadata.resolve;
369
2.95k
        let world = &resolve.worlds[world];
370
371
        // FIXME: ideally liveness information here would be plumbed through to
372
        // encoding but that's not done at this time. Only liveness for each
373
        // interface is plumbed so top-level world types are unconditionally
374
        // encoded and therefore unconditionally live here. Once encoding is
375
        // based on conditionally-live things then this should be removed.
376
2.95k
        for (_, item) in world.imports.iter() {
377
2.71k
            if let WorldItem::Type { id, .. } = item {
378
1.52k
                live.add_type_id(resolve, *id);
379
1.52k
            }
380
        }
381
382
10.4k
        for (_, _, import) in imports.imports() {
383
10.4k
            match import {
384
                // WIT-level function imports need the associated WIT definition.
385
672
                Import::WorldFunc(key, _, _) => {
386
672
                    live.add_world_item(resolve, &world.imports[key]);
387
672
                }
388
937
                Import::InterfaceFunc(_, id, name, _) => {
389
937
                    live.add_func(resolve, &resolve.interfaces[*id].functions[name]);
390
937
                }
391
392
                // Resource-related intrinsics will need the resource.
393
196
                Import::ImportedResourceDrop(.., ty)
394
211
                | Import::ExportedResourceDrop(_, ty)
395
211
                | Import::ExportedResourceNew(_, ty)
396
829
                | Import::ExportedResourceRep(_, ty) => live.add_type_id(resolve, *ty),
397
398
                // Future/Stream related intrinsics need to refer to the type
399
                // that the intrinsic is operating on.
400
20
                Import::StreamNew(info)
401
20
                | Import::StreamRead { info, async_: _ }
402
20
                | Import::StreamWrite { info, async_: _ }
403
20
                | Import::StreamCancelRead { info, async_: _ }
404
20
                | Import::StreamCancelWrite { info, async_: _ }
405
20
                | Import::StreamDropReadable(info)
406
20
                | Import::StreamDropWritable(info)
407
93
                | Import::FutureNew(info)
408
93
                | Import::FutureRead { info, async_: _ }
409
93
                | Import::FutureWrite { info, async_: _ }
410
93
                | Import::FutureCancelRead { info, async_: _ }
411
93
                | Import::FutureCancelWrite { info, async_: _ }
412
93
                | Import::FutureDropReadable(info)
413
93
                | Import::FutureDropWritable(info) => {
414
791
                    if let PayloadType::Type { id, .. } = info.ty {
415
791
                        live.add_type_id(resolve, id);
416
791
                    }
417
                }
418
419
                // The `task.return` intrinsic needs to be able to refer to the
420
                // type that is being returned.
421
771
                Import::ExportedTaskReturn(.., func) => {
422
771
                    if let Some(ty) = func.result {
423
742
                        live.add_type(resolve, &ty);
424
742
                    }
425
                }
426
427
                // Intrinsics that don't need to refer to WIT types can be
428
                // skipped here.
429
                Import::AdapterExport { .. }
430
                | Import::MainModuleMemory
431
                | Import::MainModuleExport { .. }
432
                | Import::Item(_)
433
                | Import::ContextGet { .. }
434
                | Import::ContextSet { .. }
435
                | Import::BackpressureInc
436
                | Import::BackpressureDec
437
                | Import::WaitableSetNew
438
                | Import::WaitableSetWait { .. }
439
                | Import::WaitableSetPoll { .. }
440
                | Import::WaitableSetDrop
441
                | Import::WaitableJoin
442
                | Import::SubtaskDrop
443
                | Import::SubtaskCancel { .. }
444
                | Import::ErrorContextNew { .. }
445
                | Import::ErrorContextDebugMessage { .. }
446
                | Import::ErrorContextDrop
447
                | Import::ExportedTaskCancel
448
                | Import::ThreadIndex
449
                | Import::ThreadNewIndirect { .. }
450
                | Import::ThreadResumeLater
451
                | Import::ThreadSuspend { .. }
452
                | Import::ThreadYield { .. }
453
                | Import::ThreadSuspendThenResume { .. }
454
                | Import::ThreadYieldThenResume { .. }
455
                | Import::ThreadSuspendThenPromote { .. }
456
6.43k
                | Import::ThreadYieldThenPromote { .. } => {}
457
            }
458
        }
459
2.95k
    }
460
461
2.95k
    fn process_exports_used(&mut self) {
462
2.95k
        let resolve = &self.encoder.metadata.resolve;
463
2.95k
        let world = self.encoder.metadata.world;
464
465
2.95k
        let exports = &resolve.worlds[world].exports;
466
2.95k
        for (_key, item) in exports.iter() {
467
1.92k
            let id = match item {
468
231
                WorldItem::Function(_) => continue,
469
1.68k
                WorldItem::Interface { id, .. } => *id,
470
0
                WorldItem::Type { .. } => unreachable!(),
471
            };
472
1.68k
            let mut set = HashSet::new();
473
474
1.68k
            for other in resolve.interface_direct_deps(id) {
475
107
                let key = WorldKey::Interface(other);
476
                // If this dependency is not exported, then it'll show up
477
                // through an import, so we're not interested in it.
478
107
                if !exports.contains_key(&key) {
479
104
                    continue;
480
3
                }
481
482
                // Otherwise this is a new exported dependency of ours, and
483
                // additionally this interface inherits all the transitive
484
                // dependencies too.
485
3
                if set.insert(other) {
486
2
                    set.extend(self.exports_used[&other].iter().copied());
487
2
                }
488
            }
489
1.68k
            let prev = self.exports_used.insert(id, set);
490
1.68k
            assert!(prev.is_none());
491
        }
492
2.95k
    }
493
}
494
495
#[derive(Default)]
496
struct Required<'a> {
497
    interface_funcs: IndexMap<Option<InterfaceId>, IndexSet<(&'a str, AbiVariant)>>,
498
    resource_drops: IndexSet<TypeId>,
499
}
500
501
impl ImportedInterface {
502
1.60k
    fn add_func(&mut self, required: &Required<'_>, resolve: &Resolve, func: &Function) {
503
1.60k
        let mut abis = Vec::with_capacity(2);
504
1.60k
        if let Some(set) = required.interface_funcs.get(&self.interface) {
505
1.60k
            if set.contains(&(func.name.as_str(), AbiVariant::GuestImport)) {
506
1.52k
                abis.push(AbiVariant::GuestImport);
507
1.52k
            }
508
1.60k
            if set.contains(&(func.name.as_str(), AbiVariant::GuestImportAsync)) {
509
86
                abis.push(AbiVariant::GuestImportAsync);
510
1.52k
            }
511
0
        }
512
1.60k
        for abi in abis {
513
1.60k
            log::trace!("add func {} {abi:?}", func.name);
514
1.60k
            let options = RequiredOptions::for_import(resolve, func, abi);
515
1.60k
            let lowering = if options.is_empty() {
516
1.33k
                Lowering::Direct
517
            } else {
518
271
                let sig = resolve.wasm_signature(abi, func);
519
271
                Lowering::Indirect { sig, options }
520
            };
521
522
1.60k
            let prev = self.lowerings.insert((func.name.clone(), abi), lowering);
523
1.60k
            assert!(prev.is_none());
524
        }
525
1.60k
    }
526
527
2.96k
    fn add_type(&mut self, required: &Required<'_>, resolve: &Resolve, id: TypeId) {
528
2.96k
        let ty = &resolve.types[id];
529
2.96k
        match &ty.kind {
530
196
            TypeDefKind::Resource => {}
531
2.76k
            _ => return,
532
        }
533
196
        let name = ty.name.as_deref().expect("resources must be named");
534
535
196
        if required.resource_drops.contains(&id) {
536
196
            let name = format!("{name}_drop");
537
196
            let prev = self
538
196
                .lowerings
539
196
                .insert((name, AbiVariant::GuestImport), Lowering::ResourceDrop(id));
540
196
            assert!(prev.is_none());
541
0
        }
542
2.96k
    }
543
}