Coverage Report

Created: 2026-06-07 07:42

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