Coverage Report

Created: 2025-10-12 07:32

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