Coverage Report

Created: 2026-08-28 08:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wit-component/src/validation.rs
Line
Count
Source
1
use crate::encoding::{Instance, Item, LibraryInfo, MainOrAdapter, ModuleImportMap};
2
use crate::{ComponentEncoder, StringEncoding};
3
use anyhow::{Context, Result, anyhow, bail};
4
use indexmap::{IndexMap, IndexSet, map::Entry};
5
use std::fmt;
6
use std::hash::Hash;
7
use std::mem;
8
use wasm_encoder::ExportKind;
9
use wasmparser::names::{ComponentName, ComponentNameKind};
10
use wasmparser::{
11
    Encoding, ExternalKind, FuncType, MemoryType, Parser, Payload, TypeRef, ValType, ValidPayload,
12
    Validator, WasmFeatures, types::TypesRef,
13
};
14
use wit_parser::{
15
    Function, InterfaceId, PackageName, Resolve, Type, TypeDefKind, TypeId, World, WorldId,
16
    WorldItem, WorldKey,
17
    abi::{AbiVariant, WasmSignature, WasmType},
18
};
19
20
4.24k
fn wasm_sig_to_func_type(signature: WasmSignature) -> FuncType {
21
8.73k
    fn from_wasm_type(ty: &WasmType) -> ValType {
22
8.73k
        match ty {
23
7.22k
            WasmType::I32 => ValType::I32,
24
104
            WasmType::I64 => ValType::I64,
25
265
            WasmType::F32 => ValType::F32,
26
183
            WasmType::F64 => ValType::F64,
27
790
            WasmType::Pointer => ValType::I32,
28
3
            WasmType::PointerOrI64 => ValType::I64,
29
165
            WasmType::Length => ValType::I32,
30
        }
31
8.73k
    }
32
33
4.24k
    FuncType::new(
34
4.24k
        signature.params.iter().map(from_wasm_type),
35
4.24k
        signature.results.iter().map(from_wasm_type),
36
    )
37
4.24k
}
38
39
/// Metadata about a validated module and what was found internally.
40
///
41
/// This structure houses information about `imports` and `exports` to the
42
/// module. Each of these specialized types contains "connection" information
43
/// between a module's imports/exports and the WIT or component-level constructs
44
/// they correspond to.
45
46
#[derive(Default)]
47
pub struct ValidatedModule {
48
    /// Information about a module's imports.
49
    pub imports: ImportMap,
50
51
    /// Information about a module's exports.
52
    pub exports: ExportMap,
53
}
54
55
impl ValidatedModule {
56
593
    fn new(
57
593
        encoder: &ComponentEncoder,
58
593
        bytes: &[u8],
59
593
        exports: &IndexSet<WorldKey>,
60
593
        import_map: Option<&ModuleImportMap>,
61
593
        info: Option<&LibraryInfo>,
62
593
    ) -> Result<ValidatedModule> {
63
593
        let mut validator = Validator::new_with_features(WasmFeatures::all());
64
593
        let mut ret = ValidatedModule::default();
65
66
10.5k
        for payload in Parser::new(0).parse_all(bytes) {
67
10.5k
            let payload = payload?;
68
10.5k
            if let ValidPayload::End(_) = validator.payload(&payload)? {
69
593
                break;
70
9.95k
            }
71
72
9.95k
            let types = validator.types(0).unwrap();
73
74
593
            match payload {
75
593
                Payload::Version { encoding, .. } if encoding != Encoding::Module => {
76
0
                    bail!("data is not a WebAssembly module");
77
                }
78
304
                Payload::ImportSection(s) => {
79
4.80k
                    for import in s.into_imports() {
80
4.80k
                        let import = import?;
81
4.80k
                        ret.imports.add(import, encoder, import_map, info, types)?;
82
                    }
83
                }
84
593
                Payload::ExportSection(s) => {
85
6.08k
                    for export in s {
86
6.08k
                        let export = export?;
87
6.08k
                        ret.exports.add(export, encoder, &exports, types)?;
88
                    }
89
                }
90
9.05k
                _ => continue,
91
            }
92
        }
93
94
593
        ret.exports.validate(encoder, exports)?;
95
96
593
        Ok(ret)
97
593
    }
98
}
99
100
/// Metadata information about a module's imports.
101
///
102
/// This structure maintains the connection between component model "things" and
103
/// core wasm "things" by ensuring that all imports to the core wasm module are
104
/// classified by the `Import` enumeration.
105
#[derive(Default)]
106
pub struct ImportMap {
107
    /// The first level of the map here is the module namespace of the import
108
    /// and the second level of the map is the field namespace. The item is then
109
    /// how the import is satisfied.
110
    names: IndexMap<String, ImportInstance>,
111
112
    /// Cache for the last-inserted `MainModuleMemory` into `names`
113
    imported_memory: Option<MemoryType>,
114
}
115
116
pub enum ImportInstance {
117
    /// This import is satisfied by an entire instance of another
118
    /// adapter/module.
119
    Whole(MainOrAdapter),
120
121
    /// This import is satisfied by filling out each name possibly differently.
122
    Names(IndexMap<String, Import>),
123
}
124
125
/// Represents metadata about a `stream<T>` or `future<T>` type for a specific
126
/// payload type `T`.
127
///
128
/// Currently, the name mangling scheme we use to represent `stream` and
129
/// `future` intrinsics as core module function imports refers to a specific
130
/// `stream` or `future` type by naming an imported or exported component
131
/// function which has that type as a parameter or return type (where the
132
/// specific type is referred to using an ordinal numbering scheme).  Not only
133
/// does this approach unambiguously indicate the type of interest, but it
134
/// allows us to reuse the `realloc`, string encoding, memory, etc. used by that
135
/// function when emitting intrinsic declarations.
136
///
137
/// TODO: Rather than reusing the same canon opts as the function in which the
138
/// type appears, consider encoding them in the name mangling stream on an
139
/// individual basis, similar to how we encode `error-context.*` built-in
140
/// imports.
141
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
142
pub struct PayloadInfo {
143
    /// The original, mangled import name used to import this built-in
144
    /// (currently used only for hashing and debugging).
145
    pub name: String,
146
    /// The resolved type id for the `stream` or `future` type of interest.
147
    ///
148
    /// If `Unit{Future,Stream}` this means that it's a "unit" payload or has no associated
149
    /// type being sent.
150
    pub ty: PayloadType,
151
    /// The world key representing the import or export context of `function`.
152
    pub key: WorldKey,
153
    /// The interface that `function` was imported from or exported in, if any.
154
    pub interface: Option<InterfaceId>,
155
    /// Whether `function` is being imported or exported.
156
    ///
157
    /// This may affect how we emit the declaration of the built-in, e.g. if the
158
    /// payload type is an exported resource.
159
    pub imported: bool,
160
}
161
162
/// The type of future/stream referenced by a `PayloadInfo`
163
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
164
pub enum PayloadType {
165
    /// This is a future or stream located in a `Resolve` where `id` points to
166
    /// either of `TypeDefKind::{Future, Stream}`.
167
    Type {
168
        id: TypeId,
169
        /// The component-level function import or export where the type
170
        /// appeared as a parameter or result type.
171
        function: String,
172
    },
173
    /// This is a `future` (no type)
174
    UnitFuture,
175
    /// This is a `stream` (no type)
176
    UnitStream,
177
}
178
179
impl PayloadInfo {
180
    /// Returns the payload type that this future/stream type is using.
181
252
    pub fn payload(&self, resolve: &Resolve) -> Option<Type> {
182
252
        let id = match self.ty {
183
252
            PayloadType::Type { id, .. } => id,
184
0
            PayloadType::UnitFuture | PayloadType::UnitStream => return None,
185
        };
186
252
        match resolve.types[id].kind {
187
252
            TypeDefKind::Future(payload) | TypeDefKind::Stream(payload) => payload,
188
0
            _ => unreachable!(),
189
        }
190
252
    }
191
}
192
193
/// The different kinds of items that a module or an adapter can import.
194
///
195
/// This is intended to be an exhaustive definition of what can be imported into
196
/// core modules within a component that wit-component supports. This doesn't
197
/// get down to the level of storing any idx numbers; at its most specific, it
198
/// gives a name.
199
#[derive(Debug, Clone)]
200
pub enum Import {
201
    /// A top-level world function, with the name provided here, is imported
202
    /// into the module.
203
    WorldFunc(WorldKey, String, AbiVariant),
204
205
    /// An interface's function is imported into the module.
206
    ///
207
    /// The `WorldKey` here is the name of the interface in the world in
208
    /// question. The `InterfaceId` is the interface that was imported from and
209
    /// `String` is the WIT name of the function.
210
    InterfaceFunc(WorldKey, InterfaceId, String, AbiVariant),
211
212
    /// An imported resource's destructor is imported.
213
    ///
214
    /// The key provided indicates whether it's for the top-level types of the
215
    /// world (`None`) or an interface (`Some` with the name of the interface).
216
    /// The `TypeId` is what resource is being dropped.
217
    ImportedResourceDrop(WorldKey, Option<InterfaceId>, TypeId),
218
219
    /// A `canon resource.drop` intrinsic for an exported item is being
220
    /// imported.
221
    ///
222
    /// This lists the key of the interface that's exporting the resource plus
223
    /// the id within that interface.
224
    ExportedResourceDrop(WorldKey, TypeId),
225
226
    /// A `canon resource.new` intrinsic for an exported item is being
227
    /// imported.
228
    ///
229
    /// This lists the key of the interface that's exporting the resource plus
230
    /// the id within that interface.
231
    ExportedResourceNew(WorldKey, TypeId),
232
233
    /// A `canon resource.rep` intrinsic for an exported item is being
234
    /// imported.
235
    ///
236
    /// This lists the key of the interface that's exporting the resource plus
237
    /// the id within that interface.
238
    ExportedResourceRep(WorldKey, TypeId),
239
240
    /// An export of an adapter is being imported with the specified type.
241
    ///
242
    /// This is used for when the main module imports an adapter function. The
243
    /// adapter name and function name match the module's own import, and the
244
    /// type must match that listed here.
245
    AdapterExport {
246
        adapter: String,
247
        func: String,
248
        ty: FuncType,
249
    },
250
251
    /// An adapter is importing the memory of the main module.
252
    ///
253
    /// (should be combined with `MainModuleExport` below one day)
254
    MainModuleMemory(MemoryType),
255
256
    /// An adapter is importing an arbitrary item from the main module.
257
    MainModuleExport { name: String, kind: ExportKind },
258
259
    /// An arbitrary item from either the main module or an adapter is being
260
    /// imported.
261
    ///
262
    /// (should probably subsume `MainModule*` and maybe `AdapterExport` above
263
    /// one day.
264
    Item(Item),
265
266
    /// A `canon task.return` intrinsic for an exported function.
267
    ///
268
    /// This allows an exported function to return a value and then continue
269
    /// running.
270
    ///
271
    /// As of this writing, only async-lifted exports use `task.return`, but the
272
    /// plan is to also support it for sync-lifted exports in the future as
273
    /// well.
274
    ExportedTaskReturn(WorldKey, Option<InterfaceId>, Function),
275
276
    /// A `canon task.cancel` intrinsic for an exported function.
277
    ///
278
    /// This allows an exported function to acknowledge a `CANCELLED` event.
279
    ExportedTaskCancel,
280
281
    /// The `context.get` intrinsic for the nth slot of storage.
282
    ContextGet {
283
        /// The type of the slot (`i32` or `i64`).
284
        ty: ValType,
285
        /// The index of the storage slot.
286
        slot: u32,
287
    },
288
    /// The `context.set` intrinsic for the nth slot of storage.
289
    ContextSet {
290
        /// The type of the slot (`i32` or `i64`).
291
        ty: ValType,
292
        /// The index of the storage slot.
293
        slot: u32,
294
    },
295
296
    /// The `__wasm_get_tls_base` function that LLVM emits to read the base
297
    /// pointer of this module's thread-local storage.
298
    ///
299
    /// Unlike [`Import::ContextGet`] this is not tied to a particular storage
300
    /// mechanism: how it's satisfied depends on whether the program uses
301
    /// cooperative threading. See
302
    /// `EncodingState::materialize_tls_base_import` for the details.
303
    TlsBaseGet {
304
        /// The type of the base pointer (`i32` or `i64`).
305
        ty: ValType,
306
    },
307
308
    /// The `__wasm_set_tls_base` counterpart to [`Import::TlsBaseGet`].
309
    TlsBaseSet {
310
        /// The type of the base pointer (`i32` or `i64`).
311
        ty: ValType,
312
    },
313
314
    /// A `canon backpressure.inc` intrinsic.
315
    BackpressureInc,
316
317
    /// A `canon backpressure.dec` intrinsic.
318
    BackpressureDec,
319
320
    /// A `waitable-set.new` intrinsic.
321
    WaitableSetNew,
322
323
    /// A `canon waitable-set.wait` intrinsic.
324
    ///
325
    /// This allows the guest to wait for any pending calls to async-lowered
326
    /// imports and/or `stream` and `future` operations to complete without
327
    /// unwinding the current Wasm stack.
328
    WaitableSetWait { cancellable: bool },
329
330
    /// A `canon waitable.poll` intrinsic.
331
    ///
332
    /// This allows the guest to check whether any pending calls to
333
    /// async-lowered imports and/or `stream` and `future` operations have
334
    /// completed without unwinding the current Wasm stack and without blocking.
335
    WaitableSetPoll { cancellable: bool },
336
337
    /// A `waitable-set.drop` intrinsic.
338
    WaitableSetDrop,
339
340
    /// A `waitable.join` intrinsic.
341
    WaitableJoin,
342
343
    /// A `canon subtask.drop` intrinsic.
344
    ///
345
    /// This allows the guest to release its handle to a completed subtask.
346
    SubtaskDrop,
347
348
    /// A `canon subtask.cancel` intrinsic.
349
    ///
350
    /// This allows the guest to cancel an in-progress subtask.
351
    SubtaskCancel { async_: bool },
352
353
    /// A `canon stream.new` intrinsic.
354
    ///
355
    /// This allows the guest to create a new `stream` of the specified type.
356
    StreamNew(PayloadInfo),
357
358
    /// A `canon stream.read` intrinsic.
359
    ///
360
    /// This allows the guest to read the next values (if any) from the specified
361
    /// stream.
362
    StreamRead { async_: bool, info: PayloadInfo },
363
364
    /// A `canon stream.write` intrinsic.
365
    ///
366
    /// This allows the guest to write one or more values to the specified
367
    /// stream.
368
    StreamWrite { async_: bool, info: PayloadInfo },
369
370
    /// A `canon stream.cancel-read` intrinsic.
371
    ///
372
    /// This allows the guest to cancel a pending read it initiated earlier (but
373
    /// which may have already partially or entirely completed).
374
    StreamCancelRead { info: PayloadInfo, async_: bool },
375
376
    /// A `canon stream.cancel-write` intrinsic.
377
    ///
378
    /// This allows the guest to cancel a pending write it initiated earlier
379
    /// (but which may have already partially or entirely completed).
380
    StreamCancelWrite { info: PayloadInfo, async_: bool },
381
382
    /// A `canon stream.drop-readable` intrinsic.
383
    ///
384
    /// This allows the guest to drop the readable end of a `stream`.
385
    StreamDropReadable(PayloadInfo),
386
387
    /// A `canon stream.drop-writable` intrinsic.
388
    ///
389
    /// This allows the guest to drop the writable end of a `stream`.
390
    StreamDropWritable(PayloadInfo),
391
392
    /// A `canon future.new` intrinsic.
393
    ///
394
    /// This allows the guest to create a new `future` of the specified type.
395
    FutureNew(PayloadInfo),
396
397
    /// A `canon future.read` intrinsic.
398
    ///
399
    /// This allows the guest to read the value (if any) from the specified
400
    /// future.
401
    FutureRead { async_: bool, info: PayloadInfo },
402
403
    /// A `canon future.write` intrinsic.
404
    ///
405
    /// This allows the guest to write a value to the specified future.
406
    FutureWrite { async_: bool, info: PayloadInfo },
407
408
    /// A `canon future.cancel-read` intrinsic.
409
    ///
410
    /// This allows the guest to cancel a pending read it initiated earlier (but
411
    /// which may have already completed).
412
    FutureCancelRead { info: PayloadInfo, async_: bool },
413
414
    /// A `canon future.cancel-write` intrinsic.
415
    ///
416
    /// This allows the guest to cancel a pending write it initiated earlier
417
    /// (but which may have already completed).
418
    FutureCancelWrite { info: PayloadInfo, async_: bool },
419
420
    /// A `canon future.drop-readable` intrinsic.
421
    ///
422
    /// This allows the guest to drop the readable end of a `future`.
423
    FutureDropReadable(PayloadInfo),
424
425
    /// A `canon future.drop-writable` intrinsic.
426
    ///
427
    /// This allows the guest to drop the writable end of a `future`.
428
    FutureDropWritable(PayloadInfo),
429
430
    /// A `canon error-context.new` intrinsic.
431
    ///
432
    /// This allows the guest to create a new `error-context` instance with a
433
    /// specified debug message.
434
    ErrorContextNew { encoding: StringEncoding },
435
436
    /// A `canon error-context.debug-message` intrinsic.
437
    ///
438
    /// This allows the guest to retrieve the debug message from a
439
    /// `error-context` instance.  Note that the content of this message might
440
    /// not be identical to what was passed in to `error-context.new`.
441
    ErrorContextDebugMessage { encoding: StringEncoding },
442
443
    /// A `canon error-context.drop` intrinsic.
444
    ///
445
    /// This allows the guest to release its handle to the specified
446
    /// `error-context` instance.
447
    ErrorContextDrop,
448
449
    /// A `canon thread.index` intrinsic.
450
    ///
451
    /// This allows the guest to get the index of the current thread.
452
    ThreadIndex,
453
454
    /// A `canon thread.new-indirect` intrinsic.
455
    ///
456
    /// This allows the guest to create a new thread running a specified function.
457
    ThreadNewIndirect,
458
459
    /// A `canon thread.resume-later` intrinsic.
460
    ThreadResumeLater,
461
462
    /// A `canon thread.suspend` intrinsic.
463
    ThreadSuspend { cancellable: bool },
464
465
    /// A `canon thread.yield` intrinsic.
466
    ThreadYield { cancellable: bool },
467
468
    /// A `canon thread.suspend-then-resume` intrinsic.
469
    ThreadSuspendThenResume { cancellable: bool },
470
471
    /// A `canon thread.yield-then-resume` intrinsic.
472
    ThreadYieldThenResume { cancellable: bool },
473
474
    /// A `canon thread.suspend-then-promote` intrinsic.
475
    ThreadSuspendThenPromote { cancellable: bool },
476
477
    /// A `canon thread.yield-then-promote` intrinsic.
478
    ThreadYieldThenPromote { cancellable: bool },
479
}
480
481
impl ImportMap {
482
    /// Returns the list of items that the adapter named `name` must export.
483
0
    pub fn required_from_adapter(&self, name: &str) -> IndexMap<String, FuncType> {
484
0
        let names = match self.names.get(name) {
485
0
            Some(ImportInstance::Names(names)) => names,
486
0
            _ => return IndexMap::new(),
487
        };
488
0
        names
489
0
            .iter()
490
0
            .map(|(_, import)| match import {
491
0
                Import::AdapterExport { ty, func, adapter } => {
492
0
                    assert_eq!(adapter, name);
493
0
                    (func.clone(), ty.clone())
494
                }
495
0
                _ => unreachable!(),
496
0
            })
497
0
            .collect()
498
0
    }
499
500
    /// Returns an iterator over all individual imports registered in this map.
501
    ///
502
    /// Note that this doesn't iterate over the "whole instance" imports.
503
2.96k
    pub fn imports(&self) -> impl Iterator<Item = (&str, &str, &Import)> + '_ {
504
2.96k
        self.names
505
2.96k
            .iter()
506
3.49k
            .filter_map(|(module, m)| match m {
507
3.49k
                ImportInstance::Names(names) => Some((module, names)),
508
0
                ImportInstance::Whole(_) => None,
509
3.49k
            })
510
3.49k
            .flat_map(|(module, m)| {
511
3.49k
                m.iter()
512
24.0k
                    .map(move |(field, import)| (module.as_str(), field.as_str(), import))
513
3.49k
            })
514
2.96k
    }
515
516
    /// Returns the map for how all imports must be satisfied.
517
593
    pub fn modules(&self) -> &IndexMap<String, ImportInstance> {
518
593
        &self.names
519
593
    }
520
521
    /// Returns the type of the `env::memory` import of this module, if present.
522
1.81k
    pub fn imported_memory(&self) -> Option<MemoryType> {
523
1.81k
        self.imported_memory
524
1.81k
    }
525
526
    /// Classify an import and call `insert_import()` on it. Used during
527
    /// validation to build up this `ImportMap`.
528
4.80k
    fn add(
529
4.80k
        &mut self,
530
4.80k
        import: wasmparser::Import<'_>,
531
4.80k
        encoder: &ComponentEncoder,
532
4.80k
        import_map: Option<&ModuleImportMap>,
533
4.80k
        library_info: Option<&LibraryInfo>,
534
4.80k
        types: TypesRef<'_>,
535
4.80k
    ) -> Result<()> {
536
4.80k
        if self.classify_import_with_library(import, library_info)? {
537
0
            return Ok(());
538
4.80k
        }
539
4.80k
        let mut import_to_classify = import;
540
4.80k
        if let Some(map) = import_map {
541
885
            if let Some(original_name) = map.original_name(&import) {
542
798
                import_to_classify.name = original_name;
543
798
            }
544
3.91k
        }
545
4.80k
        let item = self
546
4.80k
            .classify(import_to_classify, encoder, types)
547
4.80k
            .with_context(|| {
548
0
                format!(
549
                    "failed to resolve import `{}::{}`",
550
                    import.module, import.name,
551
                )
552
0
            })?;
553
4.80k
        self.insert_import(import, item)
554
4.80k
    }
555
556
    /// Determines what kind of thing is being imported: maps it from the
557
    /// module/name/type triple in the raw wasm module to an enum.
558
    ///
559
    /// Handles a few special cases, then delegates to
560
    /// `classify_component_model_import()`.
561
4.80k
    fn classify(
562
4.80k
        &self,
563
4.80k
        import: wasmparser::Import<'_>,
564
4.80k
        encoder: &ComponentEncoder,
565
4.80k
        types: TypesRef<'_>,
566
4.80k
    ) -> Result<Import> {
567
        // Special-case the main module's memory imported into adapters which
568
        // currently with `wasm-ld` is not easily configurable.
569
4.80k
        if import.module == "env" && import.name == "memory" {
570
0
            if let TypeRef::Memory(ty) = import.ty {
571
0
                return Ok(Import::MainModuleMemory(ty));
572
0
            }
573
4.80k
        }
574
575
        // Special-case imports from the main module into adapters.
576
4.80k
        if import.module == "__main_module__" {
577
            return Ok(Import::MainModuleExport {
578
0
                name: import.name.to_string(),
579
0
                kind: match import.ty {
580
0
                    TypeRef::Func(_) => ExportKind::Func,
581
0
                    TypeRef::Table(_) => ExportKind::Table,
582
0
                    TypeRef::Memory(_) => ExportKind::Memory,
583
0
                    TypeRef::Global(_) => ExportKind::Global,
584
0
                    TypeRef::Tag(_) => ExportKind::Tag,
585
0
                    TypeRef::FuncExact(_) => bail!("Unexpected func_exact export"),
586
                },
587
            });
588
4.80k
        }
589
590
4.80k
        let ty_index = match import.ty {
591
4.80k
            TypeRef::Func(ty) => ty,
592
0
            _ => bail!("module is only allowed to import functions"),
593
        };
594
4.80k
        let ty = types[types.core_type_at_in_module(ty_index)].unwrap_func();
595
596
        // Handle main module imports that match known adapters and set it up as
597
        // an import of an adapter export.
598
4.80k
        if encoder.adapters.contains_key(import.module) {
599
0
            return Ok(Import::AdapterExport {
600
0
                adapter: import.module.to_string(),
601
0
                func: import.name.to_string(),
602
0
                ty: ty.clone(),
603
0
            });
604
4.80k
        }
605
606
4.80k
        let (module, names) = match import.module.strip_prefix("cm32p2") {
607
53
            Some(suffix) => (suffix, STANDARD),
608
0
            None if encoder.reject_legacy_names => (import.module, STANDARD),
609
4.75k
            None => (import.module, LEGACY),
610
        };
611
4.80k
        self.classify_component_model_import(module, import.name, encoder, ty, names)
612
4.80k
    }
613
614
    /// Attempts to classify the import `{module}::{name}` with the rules
615
    /// specified in WebAssembly/component-model#378
616
4.80k
    fn classify_component_model_import(
617
4.80k
        &self,
618
4.80k
        module: &str,
619
4.80k
        name: &str,
620
4.80k
        encoder: &ComponentEncoder,
621
4.80k
        ty: &FuncType,
622
4.80k
        names: &dyn NameMangling,
623
4.80k
    ) -> Result<Import> {
624
4.80k
        let resolve = &encoder.metadata.resolve;
625
4.80k
        let world_id = encoder.metadata.world;
626
4.80k
        let world = &resolve.worlds[world_id];
627
628
4.80k
        if module == names.import_root() {
629
2.20k
            if names.error_context_drop(name) {
630
0
                let expected = FuncType::new([ValType::I32], []);
631
0
                validate_func_sig(name, &expected, ty)?;
632
0
                return Ok(Import::ErrorContextDrop);
633
2.20k
            }
634
635
2.20k
            if names.backpressure_inc(name) {
636
166
                let expected = FuncType::new([], []);
637
166
                validate_func_sig(name, &expected, ty)?;
638
166
                return Ok(Import::BackpressureInc);
639
2.04k
            }
640
641
2.04k
            if names.backpressure_dec(name) {
642
166
                let expected = FuncType::new([], []);
643
166
                validate_func_sig(name, &expected, ty)?;
644
166
                return Ok(Import::BackpressureDec);
645
1.87k
            }
646
647
1.87k
            if names.waitable_set_new(name) {
648
166
                let expected = FuncType::new([], [ValType::I32]);
649
166
                validate_func_sig(name, &expected, ty)?;
650
166
                return Ok(Import::WaitableSetNew);
651
1.71k
            }
652
653
1.71k
            if let Some((info, result_ty)) = names.waitable_set_wait(name) {
654
166
                let expected = FuncType::new([ValType::I32, result_ty], [ValType::I32]);
655
166
                validate_func_sig(name, &expected, ty)?;
656
166
                return Ok(Import::WaitableSetWait {
657
166
                    cancellable: info.cancellable,
658
166
                });
659
1.54k
            }
660
661
1.54k
            if let Some((info, result_ty)) = names.waitable_set_poll(name) {
662
166
                let expected = FuncType::new([ValType::I32, result_ty], [ValType::I32]);
663
166
                validate_func_sig(name, &expected, ty)?;
664
166
                return Ok(Import::WaitableSetPoll {
665
166
                    cancellable: info.cancellable,
666
166
                });
667
1.37k
            }
668
669
1.37k
            if names.waitable_set_drop(name) {
670
166
                let expected = FuncType::new([ValType::I32], []);
671
166
                validate_func_sig(name, &expected, ty)?;
672
166
                return Ok(Import::WaitableSetDrop);
673
1.21k
            }
674
675
1.21k
            if names.waitable_join(name) {
676
166
                let expected = FuncType::new([ValType::I32; 2], []);
677
166
                validate_func_sig(name, &expected, ty)?;
678
166
                return Ok(Import::WaitableJoin);
679
1.04k
            }
680
681
1.04k
            if names.subtask_drop(name) {
682
166
                let expected = FuncType::new([ValType::I32], []);
683
166
                validate_func_sig(name, &expected, ty)?;
684
166
                return Ok(Import::SubtaskDrop);
685
880
            }
686
687
880
            if let Some(info) = names.subtask_cancel(name) {
688
166
                let expected = FuncType::new([ValType::I32], [ValType::I32]);
689
166
                validate_func_sig(name, &expected, ty)?;
690
166
                return Ok(Import::SubtaskCancel {
691
166
                    async_: info.async_lowered,
692
166
                });
693
714
            }
694
695
714
            if let Some(encoding) = names.error_context_new(name) {
696
0
                let expected = FuncType::new([ValType::I32; 2], [ValType::I32]);
697
0
                validate_func_sig(name, &expected, ty)?;
698
0
                return Ok(Import::ErrorContextNew { encoding });
699
714
            }
700
701
714
            if let Some(encoding) = names.error_context_debug_message(name) {
702
0
                let expected = FuncType::new([ValType::I32; 2], []);
703
0
                validate_func_sig(name, &expected, ty)?;
704
0
                return Ok(Import::ErrorContextDebugMessage { encoding });
705
714
            }
706
707
714
            if let Some((slot_ty, slot)) = names.context_get(name) {
708
166
                let expected = FuncType::new([], [slot_ty]);
709
166
                validate_func_sig(name, &expected, ty)?;
710
166
                return Ok(Import::ContextGet { ty: slot_ty, slot });
711
548
            }
712
548
            if let Some((slot_ty, slot)) = names.context_set(name) {
713
166
                let expected = FuncType::new([slot_ty], []);
714
166
                validate_func_sig(name, &expected, ty)?;
715
166
                return Ok(Import::ContextSet { ty: slot_ty, slot });
716
382
            }
717
382
            if names.thread_index(name) {
718
0
                let expected = FuncType::new([], [ValType::I32]);
719
0
                validate_func_sig(name, &expected, ty)?;
720
0
                return Ok(Import::ThreadIndex);
721
382
            }
722
382
            if names.thread_new_indirect(name) {
723
0
                let expected = FuncType::new([ValType::I32; 2], [ValType::I32]);
724
0
                validate_func_sig(name, &expected, ty)?;
725
0
                return Ok(Import::ThreadNewIndirect);
726
382
            }
727
382
            if names.thread_resume_later(name) {
728
0
                let expected = FuncType::new([ValType::I32], []);
729
0
                validate_func_sig(name, &expected, ty)?;
730
0
                return Ok(Import::ThreadResumeLater);
731
382
            }
732
382
            if let Some(info) = names.thread_suspend(name) {
733
0
                let expected = FuncType::new([], [ValType::I32]);
734
0
                validate_func_sig(name, &expected, ty)?;
735
0
                return Ok(Import::ThreadSuspend {
736
0
                    cancellable: info.cancellable,
737
0
                });
738
382
            }
739
382
            if let Some(info) = names.thread_yield(name) {
740
166
                let expected = FuncType::new([], [ValType::I32]);
741
166
                validate_func_sig(name, &expected, ty)?;
742
166
                return Ok(Import::ThreadYield {
743
166
                    cancellable: info.cancellable,
744
166
                });
745
216
            }
746
216
            if let Some(info) = names.thread_suspend_then_resume(name) {
747
0
                let expected = FuncType::new([ValType::I32], [ValType::I32]);
748
0
                validate_func_sig(name, &expected, ty)?;
749
0
                return Ok(Import::ThreadSuspendThenResume {
750
0
                    cancellable: info.cancellable,
751
0
                });
752
216
            }
753
216
            if let Some(info) = names.thread_yield_then_resume(name) {
754
0
                let expected = FuncType::new([ValType::I32], [ValType::I32]);
755
0
                validate_func_sig(name, &expected, ty)?;
756
0
                return Ok(Import::ThreadYieldThenResume {
757
0
                    cancellable: info.cancellable,
758
0
                });
759
216
            }
760
216
            if let Some(info) = names.thread_suspend_then_promote(name) {
761
0
                let expected = FuncType::new([ValType::I32], [ValType::I32]);
762
0
                validate_func_sig(name, &expected, ty)?;
763
0
                return Ok(Import::ThreadSuspendThenPromote {
764
0
                    cancellable: info.cancellable,
765
0
                });
766
216
            }
767
216
            if let Some(info) = names.thread_yield_then_promote(name) {
768
0
                let expected = FuncType::new([ValType::I32], [ValType::I32]);
769
0
                validate_func_sig(name, &expected, ty)?;
770
0
                return Ok(Import::ThreadYieldThenPromote {
771
0
                    cancellable: info.cancellable,
772
0
                });
773
216
            }
774
775
216
            let (key_name, abi) = names.world_key_name_and_abi(name);
776
216
            let key = WorldKey::Name(key_name.to_string());
777
216
            if let Some(WorldItem::Function(func)) = world.imports.get(&key) {
778
174
                validate_func(resolve, ty, func, abi)?;
779
174
                return Ok(Import::WorldFunc(key, func.name.clone(), abi));
780
42
            }
781
782
42
            if let Some(import) =
783
42
                self.maybe_classify_wit_intrinsic(name, None, encoder, ty, true, names)?
784
            {
785
42
                return Ok(import);
786
0
            }
787
788
0
            match world.imports.get(&key) {
789
0
                Some(_) => bail!("expected world top-level import `{name}` to be a function"),
790
0
                None => bail!("no top-level imported function `{name}` specified"),
791
            }
792
2.59k
        }
793
794
2.59k
        if module == "env" {
795
0
            if let Some(import) = names.env_import(name, ty) {
796
0
                return Ok(import);
797
0
            }
798
2.59k
        }
799
800
        // Check for `[export]$root::[task-return]foo` or similar
801
187
        if matches!(
802
2.59k
            module.strip_prefix(names.import_exported_intrinsic_prefix()),
803
2.02k
            Some(module) if module == names.import_root()
804
        ) {
805
187
            if let Some(import) =
806
187
                self.maybe_classify_wit_intrinsic(name, None, encoder, ty, false, names)?
807
            {
808
187
                return Ok(import);
809
0
            }
810
2.40k
        }
811
812
2.40k
        let interface = match module.strip_prefix(names.import_non_root_prefix()) {
813
2.40k
            Some(name) => name,
814
0
            None => bail!("unknown or invalid component model import syntax"),
815
        };
816
817
2.40k
        if let Some(interface) = interface.strip_prefix(names.import_exported_intrinsic_prefix()) {
818
1.83k
            let (key, id) = names.module_to_interface(interface, resolve, &world.exports)?;
819
820
1.83k
            if let Some(import) =
821
1.83k
                self.maybe_classify_wit_intrinsic(name, Some((key, id)), encoder, ty, false, names)?
822
            {
823
1.83k
                return Ok(import);
824
0
            }
825
0
            bail!("unknown function `{name}`")
826
571
        }
827
828
571
        let (key, id) = names.module_to_interface(interface, resolve, &world.imports)?;
829
571
        let interface = &resolve.interfaces[id];
830
571
        let (function_name, abi) = names.interface_function_name_and_abi(name);
831
571
        if let Some(f) = interface.functions.get(function_name) {
832
525
            validate_func(resolve, ty, f, abi).with_context(|| {
833
0
                let name = resolve.name_world_key(&key);
834
0
                format!("failed to validate import interface `{name}`")
835
0
            })?;
836
525
            return Ok(Import::InterfaceFunc(key, id, f.name.clone(), abi));
837
46
        }
838
839
46
        if let Some(import) =
840
46
            self.maybe_classify_wit_intrinsic(name, Some((key, id)), encoder, ty, true, names)?
841
        {
842
46
            return Ok(import);
843
0
        }
844
0
        bail!(
845
            "import interface `{module}` is missing function \
846
             `{name}` that is required by the module",
847
        )
848
4.80k
    }
849
850
    /// Attempts to detect and classify `name` as a WIT intrinsic.
851
    ///
852
    /// This function is a bit of a sprawling sequence of matches used to
853
    /// detect whether `name` corresponds to a WIT intrinsic, so specifically
854
    /// not a WIT function itself. This is only used for functions imported
855
    /// into a module but the import could be for an imported item in a world
856
    /// or an exported item.
857
    ///
858
    /// ## Parameters
859
    ///
860
    /// * `name` - the core module name which is being pattern-matched. This
861
    ///   should be the "field" of the import. This may include the "[async-lower]"
862
    ///   or "[cancellable]" prefixes.
863
    /// * `key_and_id` - this is the inferred "container" for the function
864
    ///   being described which is inferred from the module portion of the core
865
    ///   wasm import field. This is `None` for root-level function/type
866
    ///   imports, such as when referring to `import x: func();`. This is `Some`
867
    ///   when an interface is used (either `import x: interface { .. }` or a
868
    ///   standalone `interface`) where the world key is specified for the
869
    ///   interface in addition to the interface that was identified.
870
    /// * `encoder` - this is the encoder state that contains
871
    ///   `Resolve`/metadata information.
872
    /// * `ty` - the core wasm type of this import.
873
    /// * `import` - whether or not this core wasm import is operating on a WIT
874
    ///   level import or export. An example of this being an export is when a
875
    ///   core module imports a destructor for an exported resource.
876
    /// * `names` - the name mangling scheme that's configured to be used.
877
2.11k
    fn maybe_classify_wit_intrinsic(
878
2.11k
        &self,
879
2.11k
        name: &str,
880
2.11k
        key_and_id: Option<(WorldKey, InterfaceId)>,
881
2.11k
        encoder: &ComponentEncoder,
882
2.11k
        ty: &FuncType,
883
2.11k
        import: bool,
884
2.11k
        names: &dyn NameMangling,
885
2.11k
    ) -> Result<Option<Import>> {
886
2.11k
        let resolve = &encoder.metadata.resolve;
887
2.11k
        let world_id = encoder.metadata.world;
888
2.11k
        let world = &resolve.worlds[world_id];
889
890
        // Separate out `Option<WorldKey>` and `Option<InterfaceId>`. If an
891
        // interface is NOT specified then the `WorldKey` which is attached to
892
        // imports is going to be calculated based on the name of the item
893
        // extracted, such as the resource or function referenced.
894
2.11k
        let (key, id) = match key_and_id {
895
1.88k
            Some((key, id)) => (Some(key), Some(id)),
896
229
            None => (None, None),
897
        };
898
899
        // Tests whether `name` is a resource within `id` (or `world_id`).
900
2.11k
        let resource_test = |name: &str| match id {
901
361
            Some(id) => resource_test_for_interface(resolve, id)(name),
902
28
            None => resource_test_for_world(resolve, world_id)(name),
903
389
        };
904
905
        // Test whether this is a `resource.drop` intrinsic.
906
2.11k
        if let Some(resource) = names.resource_drop_name(name) {
907
179
            if let Some(resource_id) = resource_test(resource) {
908
179
                let key = key.unwrap_or_else(|| WorldKey::Name(resource.to_string()));
909
179
                let expected = FuncType::new([ValType::I32], []);
910
179
                validate_func_sig(name, &expected, ty)?;
911
179
                return Ok(Some(if import {
912
74
                    Import::ImportedResourceDrop(key, id, resource_id)
913
                } else {
914
105
                    Import::ExportedResourceDrop(key, resource_id)
915
                }));
916
0
            }
917
1.93k
        }
918
919
        // There are some intrinsics which are only applicable to exported
920
        // functions/resources, so check those use cases here.
921
1.93k
        if !import {
922
1.91k
            if let Some(name) = names.resource_new_name(name) {
923
105
                if let Some(id) = resource_test(name) {
924
105
                    let key = key.unwrap_or_else(|| WorldKey::Name(name.to_string()));
925
105
                    let expected = FuncType::new([ValType::I32], [ValType::I32]);
926
105
                    validate_func_sig(name, &expected, ty)?;
927
105
                    return Ok(Some(Import::ExportedResourceNew(key, id)));
928
0
                }
929
1.81k
            }
930
1.81k
            if let Some(name) = names.resource_rep_name(name) {
931
105
                if let Some(id) = resource_test(name) {
932
105
                    let key = key.unwrap_or_else(|| WorldKey::Name(name.to_string()));
933
105
                    let expected = FuncType::new([ValType::I32], [ValType::I32]);
934
105
                    validate_func_sig(name, &expected, ty)?;
935
105
                    return Ok(Some(Import::ExportedResourceRep(key, id)));
936
0
                }
937
1.70k
            }
938
1.70k
            if let Some(name) = names.task_return_name(name) {
939
675
                let func = get_function(resolve, world, name, id, import)?;
940
675
                let key = key.unwrap_or_else(|| WorldKey::Name(name.to_string()));
941
                // TODO: should call `validate_func_sig` but would require
942
                // calculating the expected signature based of `func.result`.
943
675
                return Ok(Some(Import::ExportedTaskReturn(key, id, func.clone())));
944
1.03k
            }
945
1.03k
            if names.task_cancel(name) {
946
166
                let expected = FuncType::new([], []);
947
166
                validate_func_sig(name, &expected, ty)?;
948
166
                return Ok(Some(Import::ExportedTaskCancel));
949
868
            }
950
14
        }
951
952
882
        let lookup_context = PayloadLookupContext {
953
882
            resolve,
954
882
            world,
955
882
            key,
956
882
            id,
957
882
            import,
958
882
        };
959
960
        // Test for a number of async-related intrinsics. All intrinsics are
961
        // prefixed with `[...-N]` where `...` is the name of the intrinsic and
962
        // the `N` is the indexed future/stream that is being referred to.
963
882
        let import = if let Some(info) = names.future_new(&lookup_context, name) {
964
121
            validate_func_sig(name, &FuncType::new([], [ValType::I64]), ty)?;
965
121
            Import::FutureNew(info)
966
761
        } else if let Some(info) = names.future_write(&lookup_context, name) {
967
121
            validate_func_sig(name, &FuncType::new([ValType::I32; 2], [ValType::I32]), ty)?;
968
121
            Import::FutureWrite {
969
121
                async_: info.async_lowered,
970
121
                info: info.inner,
971
121
            }
972
640
        } else if let Some(info) = names.future_read(&lookup_context, name) {
973
121
            validate_func_sig(name, &FuncType::new([ValType::I32; 2], [ValType::I32]), ty)?;
974
121
            Import::FutureRead {
975
121
                async_: info.async_lowered,
976
121
                info: info.inner,
977
121
            }
978
519
        } else if let Some(info) = names.future_cancel_write(&lookup_context, name) {
979
121
            validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?;
980
121
            Import::FutureCancelWrite {
981
121
                async_: info.async_lowered,
982
121
                info: info.inner,
983
121
            }
984
398
        } else if let Some(info) = names.future_cancel_read(&lookup_context, name) {
985
121
            validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?;
986
121
            Import::FutureCancelRead {
987
121
                async_: info.async_lowered,
988
121
                info: info.inner,
989
121
            }
990
277
        } else if let Some(info) = names.future_drop_writable(&lookup_context, name) {
991
121
            validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?;
992
121
            Import::FutureDropWritable(info)
993
156
        } else if let Some(info) = names.future_drop_readable(&lookup_context, name) {
994
121
            validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?;
995
121
            Import::FutureDropReadable(info)
996
35
        } else if let Some(info) = names.stream_new(&lookup_context, name) {
997
5
            validate_func_sig(name, &FuncType::new([], [ValType::I64]), ty)?;
998
5
            Import::StreamNew(info)
999
30
        } else if let Some(info) = names.stream_write(&lookup_context, name) {
1000
5
            validate_func_sig(name, &FuncType::new([ValType::I32; 3], [ValType::I32]), ty)?;
1001
5
            Import::StreamWrite {
1002
5
                async_: info.async_lowered,
1003
5
                info: info.inner,
1004
5
            }
1005
25
        } else if let Some(info) = names.stream_read(&lookup_context, name) {
1006
5
            validate_func_sig(name, &FuncType::new([ValType::I32; 3], [ValType::I32]), ty)?;
1007
5
            Import::StreamRead {
1008
5
                async_: info.async_lowered,
1009
5
                info: info.inner,
1010
5
            }
1011
20
        } else if let Some(info) = names.stream_cancel_write(&lookup_context, name) {
1012
5
            validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?;
1013
5
            Import::StreamCancelWrite {
1014
5
                async_: info.async_lowered,
1015
5
                info: info.inner,
1016
5
            }
1017
15
        } else if let Some(info) = names.stream_cancel_read(&lookup_context, name) {
1018
5
            validate_func_sig(name, &FuncType::new([ValType::I32], [ValType::I32]), ty)?;
1019
5
            Import::StreamCancelRead {
1020
5
                async_: info.async_lowered,
1021
5
                info: info.inner,
1022
5
            }
1023
10
        } else if let Some(info) = names.stream_drop_writable(&lookup_context, name) {
1024
5
            validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?;
1025
5
            Import::StreamDropWritable(info)
1026
5
        } else if let Some(info) = names.stream_drop_readable(&lookup_context, name) {
1027
5
            validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?;
1028
5
            Import::StreamDropReadable(info)
1029
        } else {
1030
0
            return Ok(None);
1031
        };
1032
882
        Ok(Some(import))
1033
2.11k
    }
1034
1035
4.80k
    fn classify_import_with_library(
1036
4.80k
        &mut self,
1037
4.80k
        import: wasmparser::Import<'_>,
1038
4.80k
        library_info: Option<&LibraryInfo>,
1039
4.80k
    ) -> Result<bool> {
1040
4.80k
        let info = match library_info {
1041
0
            Some(info) => info,
1042
4.80k
            None => return Ok(false),
1043
        };
1044
0
        let Some((_, instance)) = info
1045
0
            .arguments
1046
0
            .iter()
1047
0
            .find(|(name, _items)| *name == import.module)
1048
        else {
1049
0
            return Ok(false);
1050
        };
1051
0
        match instance {
1052
0
            Instance::MainOrAdapter(module) => match self.names.get(import.module) {
1053
0
                Some(ImportInstance::Whole(which)) => {
1054
0
                    if which != module {
1055
0
                        bail!("different whole modules imported under the same name");
1056
0
                    }
1057
                }
1058
                Some(ImportInstance::Names(_)) => {
1059
0
                    bail!("cannot mix individual imports and whole module imports")
1060
                }
1061
0
                None => {
1062
0
                    let instance = ImportInstance::Whole(module.clone());
1063
0
                    self.names.insert(import.module.to_string(), instance);
1064
0
                }
1065
            },
1066
0
            Instance::Items(items) => {
1067
0
                let Some(item) = items.iter().find(|i| i.alias == import.name) else {
1068
0
                    return Ok(false);
1069
                };
1070
0
                self.insert_import(import, Import::Item(item.clone()))?;
1071
            }
1072
        }
1073
0
        Ok(true)
1074
4.80k
    }
1075
1076
    /// Map an imported item, by module and field name in `self.names`, to the
1077
    /// kind of `Import` it is: for example, a certain-typed function from an
1078
    /// adapter.
1079
4.80k
    fn insert_import(&mut self, import: wasmparser::Import<'_>, item: Import) -> Result<()> {
1080
4.80k
        if let Import::MainModuleMemory(ty) = item {
1081
0
            if self.imported_memory.is_some() {
1082
0
                bail!("module has multiple imports for memory");
1083
0
            }
1084
0
            self.imported_memory = Some(ty);
1085
4.80k
        }
1086
4.80k
        let entry = self
1087
4.80k
            .names
1088
4.80k
            .entry(import.module.to_string())
1089
4.80k
            .or_insert(ImportInstance::Names(IndexMap::default()));
1090
4.80k
        let names = match entry {
1091
4.80k
            ImportInstance::Names(names) => names,
1092
0
            _ => bail!("cannot mix individual imports with module imports"),
1093
        };
1094
4.80k
        let entry = match names.entry(import.name.to_string()) {
1095
            Entry::Occupied(_) => {
1096
0
                bail!(
1097
                    "module has duplicate import for `{}::{}`",
1098
                    import.module,
1099
                    import.name
1100
                );
1101
            }
1102
4.80k
            Entry::Vacant(v) => v,
1103
        };
1104
4.80k
        log::trace!(
1105
            "classifying import `{}::{} as {item:?}",
1106
            import.module,
1107
            import.name
1108
        );
1109
4.80k
        entry.insert(item);
1110
4.80k
        Ok(())
1111
4.80k
    }
1112
}
1113
1114
/// Dual of `ImportMap` except describes the exports of a module instead of the
1115
/// imports.
1116
#[derive(Default)]
1117
pub struct ExportMap {
1118
    names: IndexMap<String, Export>,
1119
    raw_exports: IndexMap<String, FuncType>,
1120
}
1121
1122
/// All possible (known) exports from a core wasm module that are recognized and
1123
/// handled during the componentization process.
1124
#[derive(Debug)]
1125
pub enum Export {
1126
    /// An export of a top-level function of a world, where the world function
1127
    /// is named here.
1128
    WorldFunc(WorldKey, String, AbiVariant),
1129
1130
    /// A post-return for a top-level function of a world.
1131
    WorldFuncPostReturn(WorldKey),
1132
1133
    /// An export of a function in an interface.
1134
    InterfaceFunc(WorldKey, InterfaceId, String, AbiVariant),
1135
1136
    /// A post-return for the above function.
1137
    InterfaceFuncPostReturn(WorldKey, String),
1138
1139
    /// A destructor for an exported resource.
1140
    ResourceDtor(TypeId),
1141
1142
    /// Memory, typically for an adapter.
1143
    Memory,
1144
1145
    /// `cabi_realloc`
1146
    GeneralPurposeRealloc,
1147
1148
    /// `cabi_export_realloc`
1149
    GeneralPurposeExportRealloc,
1150
1151
    /// `cabi_import_realloc`
1152
    GeneralPurposeImportRealloc,
1153
1154
    /// `_initialize`
1155
    Initialize,
1156
1157
    /// `cabi_realloc_adapter`
1158
    ReallocForAdapter,
1159
1160
    WorldFuncCallback(WorldKey),
1161
1162
    InterfaceFuncCallback(WorldKey, String),
1163
1164
    /// __indirect_function_table, used for `thread.new-indirect`
1165
    IndirectFunctionTable,
1166
1167
    /// Used to hook lifecycle events for tasks.
1168
    WasmTaskHook,
1169
}
1170
1171
impl ExportMap {
1172
6.08k
    fn add(
1173
6.08k
        &mut self,
1174
6.08k
        export: wasmparser::Export<'_>,
1175
6.08k
        encoder: &ComponentEncoder,
1176
6.08k
        exports: &IndexSet<WorldKey>,
1177
6.08k
        types: TypesRef<'_>,
1178
6.08k
    ) -> Result<()> {
1179
6.08k
        if let Some(item) = self.classify(export, encoder, exports, types)? {
1180
6.08k
            log::debug!("classifying export `{}` as {item:?}", export.name);
1181
6.08k
            let prev = self.names.insert(export.name.to_string(), item);
1182
6.08k
            assert!(prev.is_none());
1183
0
        }
1184
6.08k
        Ok(())
1185
6.08k
    }
1186
1187
6.08k
    fn classify(
1188
6.08k
        &mut self,
1189
6.08k
        export: wasmparser::Export<'_>,
1190
6.08k
        encoder: &ComponentEncoder,
1191
6.08k
        exports: &IndexSet<WorldKey>,
1192
6.08k
        types: TypesRef<'_>,
1193
6.08k
    ) -> Result<Option<Export>> {
1194
6.08k
        match export.kind {
1195
5.49k
            ExternalKind::Func => {
1196
5.49k
                let ty = types[types.core_function_at(export.index)].unwrap_func();
1197
5.49k
                self.raw_exports.insert(export.name.to_string(), ty.clone());
1198
5.49k
            }
1199
593
            _ => {}
1200
        }
1201
1202
        // Handle a few special-cased names first.
1203
6.08k
        if export.name == "canonical_abi_realloc" {
1204
0
            return Ok(Some(Export::GeneralPurposeRealloc));
1205
6.08k
        } else if export.name == "cabi_import_realloc" {
1206
0
            return Ok(Some(Export::GeneralPurposeImportRealloc));
1207
6.08k
        } else if export.name == "cabi_export_realloc" {
1208
0
            return Ok(Some(Export::GeneralPurposeExportRealloc));
1209
6.08k
        } else if export.name == "cabi_realloc_adapter" {
1210
0
            return Ok(Some(Export::ReallocForAdapter));
1211
6.08k
        }
1212
1213
6.08k
        let (name, names) = match export.name.strip_prefix("cm32p2") {
1214
301
            Some(name) => (name, STANDARD),
1215
0
            None if encoder.reject_legacy_names => return Ok(None),
1216
5.78k
            None => (export.name, LEGACY),
1217
        };
1218
1219
6.08k
        if let Some(export) = self
1220
6.08k
            .classify_component_export(names, name, &export, encoder, exports, types)
1221
6.08k
            .with_context(|| format!("failed to classify export `{}`", export.name))?
1222
        {
1223
6.08k
            return Ok(Some(export));
1224
0
        }
1225
0
        log::debug!("unknown export `{}`", export.name);
1226
0
        Ok(None)
1227
6.08k
    }
1228
1229
6.08k
    fn classify_component_export(
1230
6.08k
        &mut self,
1231
6.08k
        names: &dyn NameMangling,
1232
6.08k
        name: &str,
1233
6.08k
        export: &wasmparser::Export<'_>,
1234
6.08k
        encoder: &ComponentEncoder,
1235
6.08k
        exports: &IndexSet<WorldKey>,
1236
6.08k
        types: TypesRef<'_>,
1237
6.08k
    ) -> Result<Option<Export>> {
1238
6.08k
        let resolve = &encoder.metadata.resolve;
1239
6.08k
        let world = encoder.metadata.world;
1240
6.08k
        match export.kind {
1241
5.49k
            ExternalKind::Func => {}
1242
            ExternalKind::Memory => {
1243
593
                if name == names.export_memory() {
1244
593
                    return Ok(Some(Export::Memory));
1245
0
                }
1246
0
                return Ok(None);
1247
            }
1248
            ExternalKind::Table => {
1249
0
                if Some(name) == names.export_indirect_function_table() {
1250
0
                    return Ok(Some(Export::IndirectFunctionTable));
1251
0
                }
1252
0
                return Ok(None);
1253
            }
1254
0
            _ => return Ok(None),
1255
        }
1256
5.49k
        let ty = types[types.core_function_at(export.index)].unwrap_func();
1257
1258
        // Handle a few special-cased names first.
1259
5.49k
        if name == names.export_realloc() {
1260
593
            let expected = FuncType::new([ValType::I32; 4], [ValType::I32]);
1261
593
            validate_func_sig(name, &expected, ty)?;
1262
593
            return Ok(Some(Export::GeneralPurposeRealloc));
1263
4.90k
        } else if name == names.export_initialize() {
1264
593
            let expected = FuncType::new([], []);
1265
593
            validate_func_sig(name, &expected, ty)?;
1266
593
            return Ok(Some(Export::Initialize));
1267
4.30k
        } else if Some(name) == names.export_wasm_task_hook() {
1268
0
            let expected = FuncType::new([ValType::I32], []);
1269
0
            validate_func_sig(name, &expected, ty)?;
1270
0
            return Ok(Some(Export::WasmTaskHook));
1271
4.30k
        }
1272
1273
4.30k
        let full_name = name;
1274
4.30k
        let (abi, name) = if let Some(name) = names.async_lift_name(name) {
1275
656
            (AbiVariant::GuestExportAsync, name)
1276
3.65k
        } else if let Some(name) = names.async_lift_stackful_name(name) {
1277
2
            (AbiVariant::GuestExportAsyncStackful, name)
1278
        } else {
1279
3.65k
            (AbiVariant::GuestExport, name)
1280
        };
1281
1282
        // Try to match this to a known WIT export that `exports` allows.
1283
4.30k
        if let Some((key, id, f)) = names.match_wit_export(name, resolve, world, exports) {
1284
2.10k
            validate_func(resolve, ty, f, abi).with_context(|| {
1285
0
                let key = resolve.name_world_key(key);
1286
0
                format!("failed to validate export for `{key}`")
1287
0
            })?;
1288
2.10k
            match id {
1289
2.03k
                Some(id) => {
1290
2.03k
                    return Ok(Some(Export::InterfaceFunc(
1291
2.03k
                        key.clone(),
1292
2.03k
                        id,
1293
2.03k
                        f.name.clone(),
1294
2.03k
                        abi,
1295
2.03k
                    )));
1296
                }
1297
                None => {
1298
70
                    return Ok(Some(Export::WorldFunc(key.clone(), f.name.clone(), abi)));
1299
                }
1300
            }
1301
2.20k
        }
1302
1303
        // See if this is a post-return for any known WIT export.
1304
2.20k
        if let Some(remaining) = names.strip_post_return(name) {
1305
1.44k
            if let Some((key, id, f)) = names.match_wit_export(remaining, resolve, world, exports) {
1306
1.44k
                validate_post_return(resolve, ty, f).with_context(|| {
1307
0
                    let key = resolve.name_world_key(key);
1308
0
                    format!("failed to validate export for `{key}`")
1309
0
                })?;
1310
1.44k
                match id {
1311
1.37k
                    Some(_id) => {
1312
1.37k
                        return Ok(Some(Export::InterfaceFuncPostReturn(
1313
1.37k
                            key.clone(),
1314
1.37k
                            f.name.clone(),
1315
1.37k
                        )));
1316
                    }
1317
                    None => {
1318
68
                        return Ok(Some(Export::WorldFuncPostReturn(key.clone())));
1319
                    }
1320
                }
1321
0
            }
1322
761
        }
1323
1324
761
        if let Some(suffix) = names.async_lift_callback_name(full_name) {
1325
656
            if let Some((key, id, f)) = names.match_wit_export(suffix, resolve, world, exports) {
1326
656
                validate_func_sig(
1327
656
                    full_name,
1328
656
                    &FuncType::new([ValType::I32; 3], [ValType::I32]),
1329
656
                    ty,
1330
0
                )?;
1331
656
                return Ok(Some(if id.is_some() {
1332
656
                    Export::InterfaceFuncCallback(key.clone(), f.name.clone())
1333
                } else {
1334
0
                    Export::WorldFuncCallback(key.clone())
1335
                }));
1336
0
            }
1337
105
        }
1338
1339
        // And, finally, see if it matches a known destructor.
1340
105
        if let Some(dtor) = names.match_wit_resource_dtor(name, resolve, world, exports) {
1341
105
            let expected = FuncType::new([ValType::I32], []);
1342
105
            validate_func_sig(full_name, &expected, ty)?;
1343
105
            return Ok(Some(Export::ResourceDtor(dtor)));
1344
0
        }
1345
1346
0
        Ok(None)
1347
6.08k
    }
1348
1349
    /// Returns the name of the post-return export, if any, for the `key` and
1350
    /// `func` combo.
1351
2.10k
    pub fn post_return(&self, key: &WorldKey, func: &Function) -> Option<&str> {
1352
131k
        self.find(|m| match m {
1353
580
            Export::WorldFuncPostReturn(k) => k == key,
1354
51.3k
            Export::InterfaceFuncPostReturn(k, f) => k == key && func.name == *f,
1355
80.0k
            _ => false,
1356
131k
        })
1357
2.10k
    }
1358
1359
    /// Returns the name of the async callback export, if any, for the `key` and
1360
    /// `func` combo.
1361
2.10k
    pub fn callback(&self, key: &WorldKey, func: &Function) -> Option<&str> {
1362
228k
        self.find(|m| match m {
1363
0
            Export::WorldFuncCallback(k) => k == key,
1364
6.16k
            Export::InterfaceFuncCallback(k, f) => k == key && func.name == *f,
1365
221k
            _ => false,
1366
228k
        })
1367
2.10k
    }
1368
1369
2.10k
    pub fn abi(&self, key: &WorldKey, func: &Function) -> Option<AbiVariant> {
1370
2.10k
        self.names.values().find_map(|m| match m {
1371
583
            Export::WorldFunc(k, f, abi) if k == key && func.name == *f => Some(*abi),
1372
57.4k
            Export::InterfaceFunc(k, _, f, abi) if k == key && func.name == *f => Some(*abi),
1373
114k
            _ => None,
1374
116k
        })
1375
2.10k
    }
1376
1377
    /// Returns the realloc that the exported function `interface` and `func`
1378
    /// are using.
1379
445
    pub fn export_realloc_for(&self, key: &WorldKey, func: &str) -> Option<&str> {
1380
        // TODO: This realloc detection should probably be improved with
1381
        // some sort of scheme to have per-function reallocs like
1382
        // `cabi_realloc_{name}` or something like that.
1383
445
        let _ = (key, func);
1384
1385
26.6k
        if let Some(name) = self.find(|m| matches!(m, Export::GeneralPurposeExportRealloc)) {
1386
0
            return Some(name);
1387
445
        }
1388
445
        self.general_purpose_realloc()
1389
445
    }
1390
1391
    /// Returns the realloc that the imported function `interface` and `func`
1392
    /// are using.
1393
1.41k
    pub fn import_realloc_for(&self, interface: Option<InterfaceId>, func: &str) -> Option<&str> {
1394
        // TODO: This realloc detection should probably be improved with
1395
        // some sort of scheme to have per-function reallocs like
1396
        // `cabi_realloc_{name}` or something like that.
1397
1.41k
        let _ = (interface, func);
1398
1399
1.41k
        self.import_realloc_fallback()
1400
1.41k
    }
1401
1402
    /// Returns the general-purpose realloc function to use for imports.
1403
    ///
1404
    /// Note that `import_realloc_for` should be used instead where possible.
1405
1.41k
    pub fn import_realloc_fallback(&self) -> Option<&str> {
1406
48.0k
        if let Some(name) = self.find(|m| matches!(m, Export::GeneralPurposeImportRealloc)) {
1407
0
            return Some(name);
1408
1.41k
        }
1409
1.41k
        self.general_purpose_realloc()
1410
1.41k
    }
1411
1412
    /// Returns the realloc that the main module is exporting into the adapter.
1413
0
    pub fn realloc_to_import_into_adapter(&self) -> Option<&str> {
1414
0
        if let Some(name) = self.find(|m| matches!(m, Export::ReallocForAdapter)) {
1415
0
            return Some(name);
1416
0
        }
1417
0
        self.general_purpose_realloc()
1418
0
    }
1419
1420
1.86k
    pub fn general_purpose_realloc(&self) -> Option<&str> {
1421
72.8k
        self.find(|m| matches!(m, Export::GeneralPurposeRealloc))
1422
1.86k
    }
1423
1424
    /// Returns an iterator over all `realloc` functions exported by this module
1425
    /// which may be used as a `realloc` canonical option.
1426
    ///
1427
    /// Note that `cabi_realloc_adapter` is intentionally not included here as
1428
    /// that's only ever imported directly into an adapter module and is never
1429
    /// used as a canonical option.
1430
0
    pub fn reallocs(&self) -> impl Iterator<Item = &str> + '_ {
1431
0
        self.names.iter().filter_map(|(name, export)| match export {
1432
            Export::GeneralPurposeRealloc
1433
            | Export::GeneralPurposeExportRealloc
1434
0
            | Export::GeneralPurposeImportRealloc => Some(name.as_str()),
1435
0
            _ => None,
1436
0
        })
1437
0
    }
1438
1439
    /// Returns the memory, if exported, for this module.
1440
593
    pub fn memory(&self) -> Option<&str> {
1441
4.90k
        self.find(|m| matches!(m, Export::Memory))
1442
593
    }
1443
1444
    /// Returns the indirect function table, if exported, for this module.
1445
0
    pub fn indirect_function_table(&self) -> Option<&str> {
1446
0
        self.find(|t| matches!(t, Export::IndirectFunctionTable))
1447
0
    }
1448
1449
    /// Returns the hook for tasks, if exported.
1450
653
    pub fn wasm_task_hook(&self) -> Option<&str> {
1451
10.0k
        self.find(|t| matches!(t, Export::WasmTaskHook))
1452
653
    }
1453
1454
    /// Returns the `_initialize` intrinsic, if exported, for this module.
1455
593
    pub fn initialize(&self) -> Option<&str> {
1456
6.08k
        self.find(|m| matches!(m, Export::Initialize))
1457
593
    }
1458
1459
    /// Returns destructor for the exported resource `ty`, if it was listed.
1460
105
    pub fn resource_dtor(&self, ty: TypeId) -> Option<&str> {
1461
8.22k
        self.find(|m| match m {
1462
361
            Export::ResourceDtor(t) => *t == ty,
1463
7.86k
            _ => false,
1464
8.22k
        })
1465
105
    }
1466
1467
    /// NB: this is a linear search and if that's ever a problem this should
1468
    /// build up an inverse map during construction to accelerate it.
1469
11.9k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
653k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#2}::{closure#0}>::{closure#0}
Line
Count
Source
1470
91
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#1}::{closure#0}>::{closure#0}
Line
Count
Source
1470
116k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::initialize::{closure#0}>::{closure#0}
Line
Count
Source
1470
6.08k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::post_return::{closure#0}>::{closure#0}
Line
Count
Source
1470
131k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::resource_dtor::{closure#0}>::{closure#0}
Line
Count
Source
1470
8.22k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::wasm_task_hook::{closure#0}>::{closure#0}
Line
Count
Source
1470
10.0k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::export_realloc_for::{closure#0}>::{closure#0}
Line
Count
Source
1470
26.6k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::general_purpose_realloc::{closure#0}>::{closure#0}
Line
Count
Source
1470
72.8k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::import_realloc_fallback::{closure#0}>::{closure#0}
Line
Count
Source
1470
48.0k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::indirect_function_table::{closure#0}>::{closure#0}
Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::realloc_to_import_into_adapter::{closure#0}>::{closure#0}
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::memory::{closure#0}>::{closure#0}
Line
Count
Source
1470
4.90k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::callback::{closure#0}>::{closure#0}
Line
Count
Source
1470
228k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
7.35k
        Some(name)
1472
11.9k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#2}::{closure#0}>
Line
Count
Source
1469
70
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
70
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
70
        Some(name)
1472
70
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#1}::{closure#0}>
Line
Count
Source
1469
2.03k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
2.03k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
2.03k
        Some(name)
1472
2.03k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::initialize::{closure#0}>
Line
Count
Source
1469
593
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
593
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
593
        Some(name)
1472
593
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::post_return::{closure#0}>
Line
Count
Source
1469
2.10k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
2.10k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
1.44k
        Some(name)
1472
2.10k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::resource_dtor::{closure#0}>
Line
Count
Source
1469
105
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
105
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
105
        Some(name)
1472
105
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::wasm_task_hook::{closure#0}>
Line
Count
Source
1469
653
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
653
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
0
        Some(name)
1472
653
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::export_realloc_for::{closure#0}>
Line
Count
Source
1469
445
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
445
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
0
        Some(name)
1472
445
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::general_purpose_realloc::{closure#0}>
Line
Count
Source
1469
1.86k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
1.86k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
1.86k
        Some(name)
1472
1.86k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::import_realloc_fallback::{closure#0}>
Line
Count
Source
1469
1.41k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
1.41k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
0
        Some(name)
1472
1.41k
    }
Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::indirect_function_table::{closure#0}>
Unexecuted instantiation: <wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::realloc_to_import_into_adapter::{closure#0}>
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::memory::{closure#0}>
Line
Count
Source
1469
593
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
593
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
593
        Some(name)
1472
593
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::callback::{closure#0}>
Line
Count
Source
1469
2.10k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1470
2.10k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1471
656
        Some(name)
1472
2.10k
    }
1473
1474
    /// Iterates over all exports of this module.
1475
898
    pub fn iter(&self) -> impl Iterator<Item = (&str, &Export)> + '_ {
1476
11.3k
        self.names.iter().map(|(n, e)| (n.as_str(), e))
1477
898
    }
1478
1479
593
    fn validate(&self, encoder: &ComponentEncoder, exports: &IndexSet<WorldKey>) -> Result<()> {
1480
593
        let resolve = &encoder.metadata.resolve;
1481
593
        let world = encoder.metadata.world;
1482
        // Multi-memory isn't supported because otherwise we don't know what
1483
        // memory to put things in.
1484
593
        if self
1485
593
            .names
1486
593
            .values()
1487
6.08k
            .filter(|m| matches!(m, Export::Memory))
1488
593
            .count()
1489
            > 1
1490
        {
1491
0
            bail!("cannot componentize module that exports multiple memories")
1492
593
        }
1493
1494
        // Every async-with-callback-lifted export must have a callback.
1495
6.08k
        for (name, export) in &self.names {
1496
70
            match export {
1497
                Export::WorldFunc(_, _, AbiVariant::GuestExportAsync) => {
1498
0
                    if !matches!(
1499
0
                        self.names.get(&format!("[callback]{name}")),
1500
                        Some(Export::WorldFuncCallback(_))
1501
                    ) {
1502
0
                        bail!("missing callback for `{name}`");
1503
0
                    }
1504
                }
1505
                Export::InterfaceFunc(_, _, _, AbiVariant::GuestExportAsync) => {
1506
0
                    if !matches!(
1507
656
                        self.names.get(&format!("[callback]{name}")),
1508
                        Some(Export::InterfaceFuncCallback(_, _))
1509
                    ) {
1510
0
                        bail!("missing callback for `{name}`");
1511
656
                    }
1512
                }
1513
5.43k
                _ => {}
1514
            }
1515
        }
1516
1517
        // All of `exports` must be exported and found within this module.
1518
1.20k
        for export in exports {
1519
2.03k
            let require_interface_func = |interface: InterfaceId, name: &str| -> Result<()> {
1520
116k
                let result = self.find(|e| match e {
1521
57.4k
                    Export::InterfaceFunc(_, id, s, _) => interface == *id && name == s,
1522
59.2k
                    _ => false,
1523
116k
                });
1524
2.03k
                if result.is_some() {
1525
2.03k
                    Ok(())
1526
                } else {
1527
0
                    let export = resolve.name_world_key(export);
1528
0
                    bail!("failed to find export of interface `{export}` function `{name}`")
1529
                }
1530
2.03k
            };
1531
1.20k
            let require_world_func = |name: &str| -> Result<()> {
1532
91
                let result = self.find(|e| match e {
1533
81
                    Export::WorldFunc(_, s, _) => name == s,
1534
10
                    _ => false,
1535
91
                });
1536
70
                if result.is_some() {
1537
70
                    Ok(())
1538
                } else {
1539
0
                    bail!("failed to find export of function `{name}`")
1540
                }
1541
70
            };
1542
1.20k
            match &resolve.worlds[world].exports[export] {
1543
1.13k
                WorldItem::Interface { id, .. } => {
1544
2.03k
                    for (name, _) in resolve.interfaces[*id].functions.iter() {
1545
2.03k
                        require_interface_func(*id, name)?;
1546
                    }
1547
                }
1548
70
                WorldItem::Function(f) => {
1549
70
                    require_world_func(&f.name)?;
1550
                }
1551
0
                WorldItem::Type { .. } => unreachable!(),
1552
            }
1553
        }
1554
1555
593
        Ok(())
1556
593
    }
1557
}
1558
1559
/// A builtin that may be declared as cancellable.
1560
struct MaybeCancellable<T> {
1561
    #[allow(unused)]
1562
    inner: T,
1563
    cancellable: bool,
1564
}
1565
1566
/// A builtin that may be declared as async-lowered.
1567
struct MaybeAsyncLowered<T> {
1568
    inner: T,
1569
    async_lowered: bool,
1570
}
1571
1572
/// Context passed to `NameMangling` implementations of stream and future functions
1573
/// to help with looking up payload information.
1574
struct PayloadLookupContext<'a> {
1575
    resolve: &'a Resolve,
1576
    world: &'a World,
1577
    id: Option<InterfaceId>,
1578
    import: bool,
1579
    key: Option<WorldKey>,
1580
}
1581
1582
/// Trait dispatch and definition for parsing and interpreting "mangled names"
1583
/// which show up in imports and exports of the component model.
1584
///
1585
/// This trait is used to implement classification of imports and exports in the
1586
/// component model. The methods on `ImportMap` and `ExportMap` will use this to
1587
/// determine what an import is and how it's lifted/lowered in the world being
1588
/// bound.
1589
///
1590
/// This trait has a bit of history behind it as well. Before
1591
/// WebAssembly/component-model#378 there was no standard naming scheme for core
1592
/// wasm imports or exports when componenitizing. This meant that
1593
/// `wit-component` implemented a particular scheme which mostly worked but was
1594
/// mostly along the lines of "this at least works" rather than "someone sat
1595
/// down and designed this". Since then, however, an standard naming scheme has
1596
/// now been specified which was indeed designed.
1597
///
1598
/// This trait serves as the bridge between these two. The historical naming
1599
/// scheme is still supported for now through the `Legacy` implementation below
1600
/// and will be for some time. The transition plan at this time is to support
1601
/// the new scheme, eventually get it supported in bindings generators, and once
1602
/// that's all propagated remove support for the legacy scheme.
1603
trait NameMangling {
1604
    fn import_root(&self) -> &str;
1605
    fn import_non_root_prefix(&self) -> &str;
1606
    fn import_exported_intrinsic_prefix(&self) -> &str;
1607
    fn export_memory(&self) -> &str;
1608
    fn export_initialize(&self) -> &str;
1609
    fn export_realloc(&self) -> &str;
1610
    fn export_indirect_function_table(&self) -> Option<&str>;
1611
    fn export_wasm_task_hook(&self) -> Option<&str>;
1612
    fn resource_drop_name<'a>(&self, name: &'a str) -> Option<&'a str>;
1613
    fn resource_new_name<'a>(&self, name: &'a str) -> Option<&'a str>;
1614
    fn resource_rep_name<'a>(&self, name: &'a str) -> Option<&'a str>;
1615
    fn task_return_name<'a>(&self, name: &'a str) -> Option<&'a str>;
1616
    fn task_cancel(&self, name: &str) -> bool;
1617
    fn backpressure_inc(&self, name: &str) -> bool;
1618
    fn backpressure_dec(&self, name: &str) -> bool;
1619
    fn waitable_set_new(&self, name: &str) -> bool;
1620
    fn waitable_set_wait(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)>;
1621
    fn waitable_set_poll(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)>;
1622
    fn waitable_set_drop(&self, name: &str) -> bool;
1623
    fn waitable_join(&self, name: &str) -> bool;
1624
    fn subtask_drop(&self, name: &str) -> bool;
1625
    fn subtask_cancel(&self, name: &str) -> Option<MaybeAsyncLowered<()>>;
1626
    fn async_lift_callback_name<'a>(&self, name: &'a str) -> Option<&'a str>;
1627
    fn async_lift_name<'a>(&self, name: &'a str) -> Option<&'a str>;
1628
    fn async_lift_stackful_name<'a>(&self, name: &'a str) -> Option<&'a str>;
1629
    fn error_context_new(&self, name: &str) -> Option<StringEncoding>;
1630
    fn error_context_debug_message(&self, name: &str) -> Option<StringEncoding>;
1631
    fn error_context_drop(&self, name: &str) -> bool;
1632
    fn context_get(&self, name: &str) -> Option<(ValType, u32)>;
1633
    fn context_set(&self, name: &str) -> Option<(ValType, u32)>;
1634
    fn future_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo>;
1635
    fn future_write(
1636
        &self,
1637
        lookup_context: &PayloadLookupContext,
1638
        name: &str,
1639
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1640
    fn future_read(
1641
        &self,
1642
        lookup_context: &PayloadLookupContext,
1643
        name: &str,
1644
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1645
    fn future_cancel_write(
1646
        &self,
1647
        lookup_context: &PayloadLookupContext,
1648
        name: &str,
1649
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1650
    fn future_cancel_read(
1651
        &self,
1652
        lookup_context: &PayloadLookupContext,
1653
        name: &str,
1654
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1655
    fn future_drop_writable(
1656
        &self,
1657
        lookup_context: &PayloadLookupContext,
1658
        name: &str,
1659
    ) -> Option<PayloadInfo>;
1660
    fn future_drop_readable(
1661
        &self,
1662
        lookup_context: &PayloadLookupContext,
1663
        name: &str,
1664
    ) -> Option<PayloadInfo>;
1665
    fn stream_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo>;
1666
    fn stream_write(
1667
        &self,
1668
        lookup_context: &PayloadLookupContext,
1669
        name: &str,
1670
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1671
    fn stream_read(
1672
        &self,
1673
        lookup_context: &PayloadLookupContext,
1674
        name: &str,
1675
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1676
    fn stream_cancel_write(
1677
        &self,
1678
        lookup_context: &PayloadLookupContext,
1679
        name: &str,
1680
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1681
    fn stream_cancel_read(
1682
        &self,
1683
        lookup_context: &PayloadLookupContext,
1684
        name: &str,
1685
    ) -> Option<MaybeAsyncLowered<PayloadInfo>>;
1686
    fn stream_drop_writable(
1687
        &self,
1688
        lookup_context: &PayloadLookupContext,
1689
        name: &str,
1690
    ) -> Option<PayloadInfo>;
1691
    fn stream_drop_readable(
1692
        &self,
1693
        lookup_context: &PayloadLookupContext,
1694
        name: &str,
1695
    ) -> Option<PayloadInfo>;
1696
    fn thread_index(&self, name: &str) -> bool;
1697
    fn thread_new_indirect(&self, name: &str) -> bool;
1698
    fn thread_resume_later(&self, name: &str) -> bool;
1699
    fn thread_suspend(&self, name: &str) -> Option<MaybeCancellable<()>>;
1700
    fn thread_yield(&self, name: &str) -> Option<MaybeCancellable<()>>;
1701
    fn thread_suspend_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>>;
1702
    fn thread_yield_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>>;
1703
    fn thread_suspend_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>>;
1704
    fn thread_yield_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>>;
1705
    fn module_to_interface(
1706
        &self,
1707
        module: &str,
1708
        resolve: &Resolve,
1709
        items: &IndexMap<WorldKey, WorldItem>,
1710
    ) -> Result<(WorldKey, InterfaceId)>;
1711
    fn strip_post_return<'a>(&self, name: &'a str) -> Option<&'a str>;
1712
    fn match_wit_export<'a>(
1713
        &self,
1714
        export_name: &str,
1715
        resolve: &'a Resolve,
1716
        world: WorldId,
1717
        exports: &'a IndexSet<WorldKey>,
1718
    ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)>;
1719
    fn match_wit_resource_dtor<'a>(
1720
        &self,
1721
        export_name: &str,
1722
        resolve: &'a Resolve,
1723
        world: WorldId,
1724
        exports: &'a IndexSet<WorldKey>,
1725
    ) -> Option<TypeId>;
1726
    fn world_key_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant);
1727
    fn interface_function_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant);
1728
    fn env_import(&self, name: &str, ty: &FuncType) -> Option<Import>;
1729
}
1730
1731
/// Definition of the "standard" naming scheme which currently starts with
1732
/// "cm32p2". Note that wasm64 is not supported at this time.
1733
struct Standard;
1734
1735
const STANDARD: &'static dyn NameMangling = &Standard;
1736
1737
impl NameMangling for Standard {
1738
53
    fn import_root(&self) -> &str {
1739
53
        ""
1740
53
    }
1741
26
    fn import_non_root_prefix(&self) -> &str {
1742
26
        "|"
1743
26
    }
1744
52
    fn import_exported_intrinsic_prefix(&self) -> &str {
1745
52
        "_ex_"
1746
52
    }
1747
62
    fn export_memory(&self) -> &str {
1748
62
        "_memory"
1749
62
    }
1750
177
    fn export_initialize(&self) -> &str {
1751
177
        "_initialize"
1752
177
    }
1753
239
    fn export_realloc(&self) -> &str {
1754
239
        "_realloc"
1755
239
    }
1756
0
    fn export_indirect_function_table(&self) -> Option<&str> {
1757
0
        None
1758
0
    }
1759
115
    fn export_wasm_task_hook(&self) -> Option<&str> {
1760
115
        None
1761
115
    }
1762
12
    fn resource_drop_name<'a>(&self, name: &'a str) -> Option<&'a str> {
1763
12
        name.strip_suffix("_drop")
1764
12
    }
1765
2
    fn resource_new_name<'a>(&self, name: &'a str) -> Option<&'a str> {
1766
2
        name.strip_suffix("_new")
1767
2
    }
1768
1
    fn resource_rep_name<'a>(&self, name: &'a str) -> Option<&'a str> {
1769
1
        name.strip_suffix("_rep")
1770
1
    }
1771
0
    fn task_return_name<'a>(&self, _name: &'a str) -> Option<&'a str> {
1772
0
        None
1773
0
    }
1774
0
    fn task_cancel(&self, _name: &str) -> bool {
1775
0
        false
1776
0
    }
1777
27
    fn backpressure_inc(&self, _name: &str) -> bool {
1778
27
        false
1779
27
    }
1780
27
    fn backpressure_dec(&self, _name: &str) -> bool {
1781
27
        false
1782
27
    }
1783
27
    fn waitable_set_new(&self, _name: &str) -> bool {
1784
27
        false
1785
27
    }
1786
27
    fn waitable_set_wait(&self, _name: &str) -> Option<(MaybeCancellable<()>, ValType)> {
1787
27
        None
1788
27
    }
1789
27
    fn waitable_set_poll(&self, _name: &str) -> Option<(MaybeCancellable<()>, ValType)> {
1790
27
        None
1791
27
    }
1792
27
    fn waitable_set_drop(&self, _name: &str) -> bool {
1793
27
        false
1794
27
    }
1795
27
    fn waitable_join(&self, _name: &str) -> bool {
1796
27
        false
1797
27
    }
1798
27
    fn subtask_drop(&self, _name: &str) -> bool {
1799
27
        false
1800
27
    }
1801
27
    fn subtask_cancel(&self, _name: &str) -> Option<MaybeAsyncLowered<()>> {
1802
27
        None
1803
27
    }
1804
1
    fn async_lift_callback_name<'a>(&self, _name: &'a str) -> Option<&'a str> {
1805
1
        None
1806
1
    }
1807
115
    fn async_lift_name<'a>(&self, _name: &'a str) -> Option<&'a str> {
1808
115
        None
1809
115
    }
1810
115
    fn async_lift_stackful_name<'a>(&self, _name: &'a str) -> Option<&'a str> {
1811
115
        None
1812
115
    }
1813
27
    fn error_context_new(&self, _name: &str) -> Option<StringEncoding> {
1814
27
        None
1815
27
    }
1816
27
    fn error_context_debug_message(&self, _name: &str) -> Option<StringEncoding> {
1817
27
        None
1818
27
    }
1819
27
    fn error_context_drop(&self, _name: &str) -> bool {
1820
27
        false
1821
27
    }
1822
27
    fn context_get(&self, _name: &str) -> Option<(ValType, u32)> {
1823
27
        None
1824
27
    }
1825
27
    fn context_set(&self, _name: &str) -> Option<(ValType, u32)> {
1826
27
        None
1827
27
    }
1828
27
    fn thread_index(&self, _name: &str) -> bool {
1829
27
        false
1830
27
    }
1831
27
    fn thread_new_indirect(&self, _name: &str) -> bool {
1832
27
        false
1833
27
    }
1834
27
    fn thread_resume_later(&self, _name: &str) -> bool {
1835
27
        false
1836
27
    }
1837
27
    fn thread_suspend(&self, _name: &str) -> Option<MaybeCancellable<()>> {
1838
27
        None
1839
27
    }
1840
27
    fn thread_yield(&self, _name: &str) -> Option<MaybeCancellable<()>> {
1841
27
        None
1842
27
    }
1843
27
    fn thread_suspend_then_resume(&self, _name: &str) -> Option<MaybeCancellable<()>> {
1844
27
        None
1845
27
    }
1846
27
    fn thread_yield_then_resume(&self, _name: &str) -> Option<MaybeCancellable<()>> {
1847
27
        None
1848
27
    }
1849
27
    fn thread_suspend_then_promote(&self, _name: &str) -> Option<MaybeCancellable<()>> {
1850
27
        None
1851
27
    }
1852
27
    fn thread_yield_then_promote(&self, _name: &str) -> Option<MaybeCancellable<()>> {
1853
27
        None
1854
27
    }
1855
0
    fn future_new(
1856
0
        &self,
1857
0
        _lookup_context: &PayloadLookupContext,
1858
0
        _name: &str,
1859
0
    ) -> Option<PayloadInfo> {
1860
0
        None
1861
0
    }
1862
0
    fn future_write(
1863
0
        &self,
1864
0
        _lookup_context: &PayloadLookupContext,
1865
0
        _name: &str,
1866
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1867
0
        None
1868
0
    }
1869
0
    fn future_read(
1870
0
        &self,
1871
0
        _lookup_context: &PayloadLookupContext,
1872
0
        _name: &str,
1873
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1874
0
        None
1875
0
    }
1876
0
    fn future_cancel_write(
1877
0
        &self,
1878
0
        _lookup_context: &PayloadLookupContext,
1879
0
        _name: &str,
1880
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1881
0
        None
1882
0
    }
1883
0
    fn future_cancel_read(
1884
0
        &self,
1885
0
        _lookup_context: &PayloadLookupContext,
1886
0
        _name: &str,
1887
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1888
0
        None
1889
0
    }
1890
0
    fn future_drop_writable(
1891
0
        &self,
1892
0
        _lookup_context: &PayloadLookupContext,
1893
0
        _name: &str,
1894
0
    ) -> Option<PayloadInfo> {
1895
0
        None
1896
0
    }
1897
0
    fn future_drop_readable(
1898
0
        &self,
1899
0
        _lookup_context: &PayloadLookupContext,
1900
0
        _name: &str,
1901
0
    ) -> Option<PayloadInfo> {
1902
0
        None
1903
0
    }
1904
0
    fn stream_new(
1905
0
        &self,
1906
0
        _lookup_context: &PayloadLookupContext,
1907
0
        _name: &str,
1908
0
    ) -> Option<PayloadInfo> {
1909
0
        None
1910
0
    }
1911
0
    fn stream_write(
1912
0
        &self,
1913
0
        _lookup_context: &PayloadLookupContext,
1914
0
        _name: &str,
1915
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1916
0
        None
1917
0
    }
1918
0
    fn stream_read(
1919
0
        &self,
1920
0
        _lookup_context: &PayloadLookupContext,
1921
0
        _name: &str,
1922
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1923
0
        None
1924
0
    }
1925
0
    fn stream_cancel_write(
1926
0
        &self,
1927
0
        _lookup_context: &PayloadLookupContext,
1928
0
        _name: &str,
1929
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1930
0
        None
1931
0
    }
1932
0
    fn stream_cancel_read(
1933
0
        &self,
1934
0
        _lookup_context: &PayloadLookupContext,
1935
0
        _name: &str,
1936
0
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
1937
0
        None
1938
0
    }
1939
0
    fn stream_drop_writable(
1940
0
        &self,
1941
0
        _lookup_context: &PayloadLookupContext,
1942
0
        _name: &str,
1943
0
    ) -> Option<PayloadInfo> {
1944
0
        None
1945
0
    }
1946
0
    fn stream_drop_readable(
1947
0
        &self,
1948
0
        _lookup_context: &PayloadLookupContext,
1949
0
        _name: &str,
1950
0
    ) -> Option<PayloadInfo> {
1951
0
        None
1952
0
    }
1953
26
    fn module_to_interface(
1954
26
        &self,
1955
26
        interface: &str,
1956
26
        resolve: &Resolve,
1957
26
        items: &IndexMap<WorldKey, WorldItem>,
1958
26
    ) -> Result<(WorldKey, InterfaceId)> {
1959
39
        for (key, item) in items.iter() {
1960
39
            let id = match key {
1961
                // Bare keys are matched exactly against `interface`
1962
15
                WorldKey::Name(name) => match item {
1963
12
                    WorldItem::Interface { id, .. } if name == interface => *id,
1964
3
                    _ => continue,
1965
                },
1966
                // ID-identified keys are matched with their "canonical name"
1967
24
                WorldKey::Interface(id) => {
1968
24
                    if resolve.canonicalized_id_of(*id).as_deref() != Some(interface) {
1969
10
                        continue;
1970
14
                    }
1971
14
                    *id
1972
                }
1973
            };
1974
26
            return Ok((key.clone(), id));
1975
        }
1976
0
        bail!("failed to find world item corresponding to interface `{interface}`")
1977
26
    }
1978
58
    fn strip_post_return<'a>(&self, name: &'a str) -> Option<&'a str> {
1979
58
        name.strip_suffix("_post")
1980
58
    }
1981
172
    fn match_wit_export<'a>(
1982
172
        &self,
1983
172
        export_name: &str,
1984
172
        resolve: &'a Resolve,
1985
172
        world: WorldId,
1986
172
        exports: &'a IndexSet<WorldKey>,
1987
172
    ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)> {
1988
172
        if let Some(world_export_name) = export_name.strip_prefix("||") {
1989
27
            let key = exports.get(&WorldKey::Name(world_export_name.to_string()))?;
1990
18
            match &resolve.worlds[world].exports[key] {
1991
18
                WorldItem::Function(f) => return Some((key, None, f)),
1992
0
                _ => return None,
1993
            }
1994
145
        }
1995
1996
145
        let (key, id, func_name) =
1997
145
            self.match_wit_interface(export_name, resolve, world, exports)?;
1998
145
        let func = resolve.interfaces[id].functions.get(func_name)?;
1999
96
        Some((key, Some(id), func))
2000
172
    }
2001
2002
1
    fn match_wit_resource_dtor<'a>(
2003
1
        &self,
2004
1
        export_name: &str,
2005
1
        resolve: &'a Resolve,
2006
1
        world: WorldId,
2007
1
        exports: &'a IndexSet<WorldKey>,
2008
1
    ) -> Option<TypeId> {
2009
1
        let (_key, id, name) =
2010
1
            self.match_wit_interface(export_name.strip_suffix("_dtor")?, resolve, world, exports)?;
2011
1
        let ty = *resolve.interfaces[id].types.get(name)?;
2012
1
        match resolve.types[ty].kind {
2013
1
            TypeDefKind::Resource => Some(ty),
2014
0
            _ => None,
2015
        }
2016
1
    }
2017
2018
27
    fn world_key_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) {
2019
27
        (name, AbiVariant::GuestImport)
2020
27
    }
2021
23
    fn interface_function_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) {
2022
23
        (name, AbiVariant::GuestImport)
2023
23
    }
2024
0
    fn env_import(&self, _name: &str, _ty: &FuncType) -> Option<Import> {
2025
0
        None
2026
0
    }
2027
}
2028
2029
impl Standard {
2030
146
    fn match_wit_interface<'a, 'b>(
2031
146
        &self,
2032
146
        export_name: &'b str,
2033
146
        resolve: &'a Resolve,
2034
146
        world: WorldId,
2035
146
        exports: &'a IndexSet<WorldKey>,
2036
146
    ) -> Option<(&'a WorldKey, InterfaceId, &'b str)> {
2037
146
        let world = &resolve.worlds[world];
2038
146
        let export_name = export_name.strip_prefix("|")?;
2039
2040
486
        for export in exports {
2041
486
            let id = match &world.exports[export] {
2042
466
                WorldItem::Interface { id, .. } => *id,
2043
20
                WorldItem::Function(_) => continue,
2044
0
                WorldItem::Type { .. } => unreachable!(),
2045
            };
2046
466
            let remaining = match export {
2047
459
                WorldKey::Name(name) => export_name.strip_prefix(name),
2048
                WorldKey::Interface(_) => {
2049
7
                    let prefix = resolve.canonicalized_id_of(id).unwrap();
2050
7
                    export_name.strip_prefix(&prefix)
2051
                }
2052
            };
2053
466
            let item_name = match remaining.and_then(|s| s.strip_prefix("|")) {
2054
146
                Some(name) => name,
2055
320
                None => continue,
2056
            };
2057
146
            return Some((export, id, item_name));
2058
        }
2059
2060
0
        None
2061
146
    }
2062
}
2063
2064
/// Definition of wit-component's "legacy" naming scheme which predates
2065
/// WebAssembly/component-model#378.
2066
struct Legacy;
2067
2068
const LEGACY: &'static dyn NameMangling = &Legacy;
2069
2070
impl Legacy {
2071
    // Looks for `[$prefix-N]foo` within `name`. If found then `foo` is
2072
    // used to find a function within `id` and `world` above. Once found
2073
    // then `N` is used to index within that function to extract a
2074
    // future/stream type. If that's all found then a `PayloadInfo` is
2075
    // returned to get attached to an intrinsic.
2076
3.77k
    fn prefixed_payload(
2077
3.77k
        &self,
2078
3.77k
        lookup_context: &PayloadLookupContext,
2079
3.77k
        name: &str,
2080
3.77k
        prefix: &str,
2081
3.77k
    ) -> Option<PayloadInfo> {
2082
        // parse the `prefix` into `func_name` and `type_index`, bailing out
2083
        // with `None` if anything doesn't match.
2084
3.77k
        let (index_or_unit, func_name) = prefixed_intrinsic(name, prefix)?;
2085
882
        let ty = match index_or_unit {
2086
882
            "unit" => {
2087
0
                if name.starts_with("[future") {
2088
0
                    PayloadType::UnitFuture
2089
0
                } else if name.starts_with("[stream") {
2090
0
                    PayloadType::UnitStream
2091
                } else {
2092
0
                    unreachable!()
2093
                }
2094
            }
2095
882
            other => {
2096
                // Note that this is parsed as a `u32` to ensure that the
2097
                // integer parsing is the same across platforms regardless of
2098
                // the the width of `usize`.
2099
882
                let type_index = other.parse::<u32>().ok()? as usize;
2100
2101
                // Double-check that `func_name` is indeed a function name within
2102
                // this interface/world. Then additionally double-check that
2103
                // `type_index` is indeed a valid index for this function's type
2104
                // signature.
2105
882
                let function = get_function(
2106
882
                    lookup_context.resolve,
2107
882
                    lookup_context.world,
2108
882
                    func_name,
2109
882
                    lookup_context.id,
2110
882
                    lookup_context.import,
2111
                )
2112
882
                .ok()?;
2113
                PayloadType::Type {
2114
882
                    id: *function
2115
882
                        .find_futures_and_streams(lookup_context.resolve)
2116
882
                        .get(type_index)?,
2117
882
                    function: function.name.clone(),
2118
                }
2119
            }
2120
        };
2121
2122
        // And if all that passes wrap up everything in a `PayloadInfo`.
2123
        Some(PayloadInfo {
2124
882
            name: name.to_string(),
2125
882
            ty,
2126
882
            key: lookup_context
2127
882
                .key
2128
882
                .clone()
2129
882
                .unwrap_or_else(|| WorldKey::Name(name.to_string())),
2130
882
            interface: lookup_context.id,
2131
882
            imported: lookup_context.import,
2132
        })
2133
3.77k
    }
2134
2135
2.40k
    fn maybe_async_lowered_payload(
2136
2.40k
        &self,
2137
2.40k
        lookup_context: &PayloadLookupContext,
2138
2.40k
        name: &str,
2139
2.40k
        prefix: &str,
2140
2.40k
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2141
2.40k
        let (async_lowered, clean_name) = self.strip_async_lowered_prefix(name);
2142
2.40k
        let payload = self.prefixed_payload(lookup_context, clean_name, prefix)?;
2143
504
        Some(MaybeAsyncLowered {
2144
504
            inner: payload,
2145
504
            async_lowered,
2146
504
        })
2147
2.40k
    }
2148
2149
3.99k
    fn strip_async_lowered_prefix<'a>(&self, name: &'a str) -> (bool, &'a str) {
2150
3.99k
        name.strip_prefix("[async-lower]")
2151
3.99k
            .map_or((false, name), |s| (true, s))
2152
3.99k
    }
2153
853
    fn match_with_async_lowered_prefix(
2154
853
        &self,
2155
853
        name: &str,
2156
853
        expected: &str,
2157
853
    ) -> Option<MaybeAsyncLowered<()>> {
2158
853
        let (async_lowered, clean_name) = self.strip_async_lowered_prefix(name);
2159
853
        if clean_name == expected {
2160
166
            Some(MaybeAsyncLowered {
2161
166
                inner: (),
2162
166
                async_lowered,
2163
166
            })
2164
        } else {
2165
687
            None
2166
        }
2167
853
    }
2168
4.66k
    fn strip_cancellable_prefix<'a>(&self, name: &'a str) -> (bool, &'a str) {
2169
4.66k
        name.strip_prefix("[cancellable]")
2170
4.66k
            .map_or((false, name), |s| (true, s))
2171
4.66k
    }
2172
1.46k
    fn match_with_cancellable_prefix(
2173
1.46k
        &self,
2174
1.46k
        name: &str,
2175
1.46k
        expected: &str,
2176
1.46k
    ) -> Option<MaybeCancellable<()>> {
2177
1.46k
        let (cancellable, clean_name) = self.strip_cancellable_prefix(name);
2178
1.46k
        if clean_name == expected {
2179
166
            Some(MaybeCancellable {
2180
166
                inner: (),
2181
166
                cancellable,
2182
166
            })
2183
        } else {
2184
1.30k
            None
2185
        }
2186
1.46k
    }
2187
2188
    /// Matches a name with the given prefix and either no suffix (for backwards compat) or
2189
    /// "-i32" or "-i64".
2190
    /// Returns a `ValType` based on the suffix and defaults to `I32`.
2191
3.20k
    fn match_with_optional_type_suffix(name: &str, match_prefix: &str) -> Option<ValType> {
2192
3.20k
        let tail = name.strip_prefix(match_prefix)?.strip_suffix(']')?;
2193
332
        if tail.is_empty() {
2194
332
            Some(ValType::I32)
2195
        } else {
2196
0
            match tail.strip_prefix('-')? {
2197
0
                "i32" => Some(ValType::I32),
2198
0
                "i64" => Some(ValType::I64),
2199
                // Other suffixes
2200
0
                _ => None,
2201
            }
2202
        }
2203
3.20k
    }
2204
}
2205
2206
impl NameMangling for Legacy {
2207
6.77k
    fn import_root(&self) -> &str {
2208
6.77k
        "$root"
2209
6.77k
    }
2210
2.38k
    fn import_non_root_prefix(&self) -> &str {
2211
2.38k
        ""
2212
2.38k
    }
2213
4.95k
    fn import_exported_intrinsic_prefix(&self) -> &str {
2214
4.95k
        "[export]"
2215
4.95k
    }
2216
531
    fn export_memory(&self) -> &str {
2217
531
        "memory"
2218
531
    }
2219
4.72k
    fn export_initialize(&self) -> &str {
2220
4.72k
        "_initialize"
2221
4.72k
    }
2222
5.25k
    fn export_realloc(&self) -> &str {
2223
5.25k
        "cabi_realloc"
2224
5.25k
    }
2225
0
    fn export_indirect_function_table(&self) -> Option<&str> {
2226
0
        Some("__indirect_function_table")
2227
0
    }
2228
4.19k
    fn export_wasm_task_hook(&self) -> Option<&str> {
2229
4.19k
        Some(crate::linking::metadata::TASK_HOOK)
2230
4.19k
    }
2231
2.10k
    fn resource_drop_name<'a>(&self, name: &'a str) -> Option<&'a str> {
2232
2.10k
        name.strip_prefix("[resource-drop]")
2233
2.10k
    }
2234
1.91k
    fn resource_new_name<'a>(&self, name: &'a str) -> Option<&'a str> {
2235
1.91k
        name.strip_prefix("[resource-new]")
2236
1.91k
    }
2237
1.81k
    fn resource_rep_name<'a>(&self, name: &'a str) -> Option<&'a str> {
2238
1.81k
        name.strip_prefix("[resource-rep]")
2239
1.81k
    }
2240
1.70k
    fn task_return_name<'a>(&self, name: &'a str) -> Option<&'a str> {
2241
1.70k
        name.strip_prefix("[task-return]")
2242
1.70k
    }
2243
1.03k
    fn task_cancel(&self, name: &str) -> bool {
2244
1.03k
        name == "[task-cancel]"
2245
1.03k
    }
2246
2.18k
    fn backpressure_inc(&self, name: &str) -> bool {
2247
2.18k
        name == "[backpressure-inc]"
2248
2.18k
    }
2249
2.01k
    fn backpressure_dec(&self, name: &str) -> bool {
2250
2.01k
        name == "[backpressure-dec]"
2251
2.01k
    }
2252
1.84k
    fn waitable_set_new(&self, name: &str) -> bool {
2253
1.84k
        name == "[waitable-set-new]"
2254
1.84k
    }
2255
1.68k
    fn waitable_set_wait(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)> {
2256
1.68k
        let (cancellable, clean_name) = self.strip_cancellable_prefix(name);
2257
1.68k
        let mb_cancellable = MaybeCancellable {
2258
1.68k
            inner: (),
2259
1.68k
            cancellable,
2260
1.68k
        };
2261
1.68k
        let result_ty = Legacy::match_with_optional_type_suffix(clean_name, "[waitable-set-wait")?;
2262
166
        Some((mb_cancellable, result_ty))
2263
1.68k
    }
2264
1.51k
    fn waitable_set_poll(&self, name: &str) -> Option<(MaybeCancellable<()>, ValType)> {
2265
1.51k
        let (cancellable, clean_name) = self.strip_cancellable_prefix(name);
2266
1.51k
        let mb_cancellable = MaybeCancellable {
2267
1.51k
            inner: (),
2268
1.51k
            cancellable,
2269
1.51k
        };
2270
1.51k
        let result_ty = Legacy::match_with_optional_type_suffix(clean_name, "[waitable-set-poll")?;
2271
166
        Some((mb_cancellable, result_ty))
2272
1.51k
    }
2273
1.35k
    fn waitable_set_drop(&self, name: &str) -> bool {
2274
1.35k
        name == "[waitable-set-drop]"
2275
1.35k
    }
2276
1.18k
    fn waitable_join(&self, name: &str) -> bool {
2277
1.18k
        name == "[waitable-join]"
2278
1.18k
    }
2279
1.01k
    fn subtask_drop(&self, name: &str) -> bool {
2280
1.01k
        name == "[subtask-drop]"
2281
1.01k
    }
2282
853
    fn subtask_cancel(&self, name: &str) -> Option<MaybeAsyncLowered<()>> {
2283
853
        self.match_with_async_lowered_prefix(name, "[subtask-cancel]")
2284
853
    }
2285
760
    fn async_lift_callback_name<'a>(&self, name: &'a str) -> Option<&'a str> {
2286
760
        name.strip_prefix("[callback][async-lift]")
2287
760
    }
2288
4.19k
    fn async_lift_name<'a>(&self, name: &'a str) -> Option<&'a str> {
2289
4.19k
        name.strip_prefix("[async-lift]")
2290
4.19k
    }
2291
3.53k
    fn async_lift_stackful_name<'a>(&self, name: &'a str) -> Option<&'a str> {
2292
3.53k
        name.strip_prefix("[async-lift-stackful]")
2293
3.53k
    }
2294
687
    fn error_context_new(&self, name: &str) -> Option<StringEncoding> {
2295
687
        match name {
2296
687
            "[error-context-new-utf8]" => Some(StringEncoding::UTF8),
2297
687
            "[error-context-new-utf16]" => Some(StringEncoding::UTF16),
2298
687
            "[error-context-new-latin1+utf16]" => Some(StringEncoding::CompactUTF16),
2299
687
            _ => None,
2300
        }
2301
687
    }
2302
687
    fn error_context_debug_message(&self, name: &str) -> Option<StringEncoding> {
2303
687
        match name {
2304
687
            "[error-context-debug-message-utf8]" => Some(StringEncoding::UTF8),
2305
687
            "[error-context-debug-message-utf16]" => Some(StringEncoding::UTF16),
2306
687
            "[error-context-debug-message-latin1+utf16]" => Some(StringEncoding::CompactUTF16),
2307
687
            _ => None,
2308
        }
2309
687
    }
2310
2.18k
    fn error_context_drop(&self, name: &str) -> bool {
2311
2.18k
        name == "[error-context-drop]"
2312
2.18k
    }
2313
687
    fn context_get(&self, name: &str) -> Option<(ValType, u32)> {
2314
687
        parse_context_name(name, "[context-get-")
2315
687
    }
2316
521
    fn context_set(&self, name: &str) -> Option<(ValType, u32)> {
2317
521
        parse_context_name(name, "[context-set-")
2318
521
    }
2319
355
    fn thread_index(&self, name: &str) -> bool {
2320
355
        name == "[thread-index]"
2321
355
    }
2322
355
    fn thread_new_indirect(&self, name: &str) -> bool {
2323
        // For now, we'll fix the type of the start function and the table to extract it from
2324
355
        name == "[thread-new-indirect-v0]"
2325
355
    }
2326
355
    fn thread_resume_later(&self, name: &str) -> bool {
2327
355
        name == "[thread-resume-later]"
2328
355
    }
2329
355
    fn thread_suspend(&self, name: &str) -> Option<MaybeCancellable<()>> {
2330
355
        self.match_with_cancellable_prefix(name, "[thread-suspend]")
2331
355
    }
2332
355
    fn thread_yield(&self, name: &str) -> Option<MaybeCancellable<()>> {
2333
355
        self.match_with_cancellable_prefix(name, "[thread-yield]")
2334
355
    }
2335
189
    fn thread_suspend_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>> {
2336
189
        self.match_with_cancellable_prefix(name, "[thread-suspend-then-resume]")
2337
189
    }
2338
189
    fn thread_yield_then_resume(&self, name: &str) -> Option<MaybeCancellable<()>> {
2339
189
        self.match_with_cancellable_prefix(name, "[thread-yield-then-resume]")
2340
189
    }
2341
189
    fn thread_suspend_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>> {
2342
189
        self.match_with_cancellable_prefix(name, "[thread-suspend-then-promote]")
2343
189
    }
2344
189
    fn thread_yield_then_promote(&self, name: &str) -> Option<MaybeCancellable<()>> {
2345
189
        self.match_with_cancellable_prefix(name, "[thread-yield-then-promote]")
2346
189
    }
2347
882
    fn future_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo> {
2348
882
        self.prefixed_payload(lookup_context, name, "[future-new-")
2349
882
    }
2350
761
    fn future_write(
2351
761
        &self,
2352
761
        lookup_context: &PayloadLookupContext,
2353
761
        name: &str,
2354
761
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2355
761
        self.maybe_async_lowered_payload(lookup_context, name, "[future-write-")
2356
761
    }
2357
640
    fn future_read(
2358
640
        &self,
2359
640
        lookup_context: &PayloadLookupContext,
2360
640
        name: &str,
2361
640
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2362
640
        self.maybe_async_lowered_payload(lookup_context, name, "[future-read-")
2363
640
    }
2364
519
    fn future_cancel_write(
2365
519
        &self,
2366
519
        lookup_context: &PayloadLookupContext,
2367
519
        name: &str,
2368
519
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2369
519
        self.maybe_async_lowered_payload(lookup_context, name, "[future-cancel-write-")
2370
519
    }
2371
398
    fn future_cancel_read(
2372
398
        &self,
2373
398
        lookup_context: &PayloadLookupContext,
2374
398
        name: &str,
2375
398
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2376
398
        self.maybe_async_lowered_payload(lookup_context, name, "[future-cancel-read-")
2377
398
    }
2378
277
    fn future_drop_writable(
2379
277
        &self,
2380
277
        lookup_context: &PayloadLookupContext,
2381
277
        name: &str,
2382
277
    ) -> Option<PayloadInfo> {
2383
277
        self.prefixed_payload(lookup_context, name, "[future-drop-writable-")
2384
277
    }
2385
156
    fn future_drop_readable(
2386
156
        &self,
2387
156
        lookup_context: &PayloadLookupContext,
2388
156
        name: &str,
2389
156
    ) -> Option<PayloadInfo> {
2390
156
        self.prefixed_payload(lookup_context, name, "[future-drop-readable-")
2391
156
    }
2392
35
    fn stream_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option<PayloadInfo> {
2393
35
        self.prefixed_payload(lookup_context, name, "[stream-new-")
2394
35
    }
2395
30
    fn stream_write(
2396
30
        &self,
2397
30
        lookup_context: &PayloadLookupContext,
2398
30
        name: &str,
2399
30
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2400
30
        self.maybe_async_lowered_payload(lookup_context, name, "[stream-write-")
2401
30
    }
2402
25
    fn stream_read(
2403
25
        &self,
2404
25
        lookup_context: &PayloadLookupContext,
2405
25
        name: &str,
2406
25
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2407
25
        self.maybe_async_lowered_payload(lookup_context, name, "[stream-read-")
2408
25
    }
2409
20
    fn stream_cancel_write(
2410
20
        &self,
2411
20
        lookup_context: &PayloadLookupContext,
2412
20
        name: &str,
2413
20
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2414
20
        self.maybe_async_lowered_payload(lookup_context, name, "[stream-cancel-write-")
2415
20
    }
2416
15
    fn stream_cancel_read(
2417
15
        &self,
2418
15
        lookup_context: &PayloadLookupContext,
2419
15
        name: &str,
2420
15
    ) -> Option<MaybeAsyncLowered<PayloadInfo>> {
2421
15
        self.maybe_async_lowered_payload(lookup_context, name, "[stream-cancel-read-")
2422
15
    }
2423
10
    fn stream_drop_writable(
2424
10
        &self,
2425
10
        lookup_context: &PayloadLookupContext,
2426
10
        name: &str,
2427
10
    ) -> Option<PayloadInfo> {
2428
10
        self.prefixed_payload(lookup_context, name, "[stream-drop-writable-")
2429
10
    }
2430
5
    fn stream_drop_readable(
2431
5
        &self,
2432
5
        lookup_context: &PayloadLookupContext,
2433
5
        name: &str,
2434
5
    ) -> Option<PayloadInfo> {
2435
5
        self.prefixed_payload(lookup_context, name, "[stream-drop-readable-")
2436
5
    }
2437
2.38k
    fn module_to_interface(
2438
2.38k
        &self,
2439
2.38k
        module: &str,
2440
2.38k
        resolve: &Resolve,
2441
2.38k
        items: &IndexMap<WorldKey, WorldItem>,
2442
2.38k
    ) -> Result<(WorldKey, InterfaceId)> {
2443
        // First see if this is a bare name
2444
2.38k
        let bare_name = WorldKey::Name(module.to_string());
2445
2.38k
        if let Some(WorldItem::Interface { id, .. }) = items.get(&bare_name) {
2446
2.01k
            return Ok((bare_name, *id));
2447
372
        }
2448
2449
        // ... and if this isn't a bare name then it's time to do some parsing
2450
        // related to interfaces, versions, and such. First up the `module` name
2451
        // is parsed as a normal component name from `wasmparser` to see if it's
2452
        // of the "interface kind". If it's not then that means the above match
2453
        // should have been a hit but it wasn't, so an error is returned.
2454
372
        let kebab_name = ComponentName::new(module, 0);
2455
372
        let name = match kebab_name.as_ref().map(|k| k.kind()) {
2456
372
            Ok(ComponentNameKind::Interface(name)) => name,
2457
0
            _ => bail!("module requires an import interface named `{module}`"),
2458
        };
2459
2460
        // FIXME: this prevents core wasm from importing from `@1` or
2461
        // `@0.1`, for example. More refactoring will be necessary to enable
2462
        // that.
2463
372
        let version = name.version(None)?;
2464
2465
        // Prioritize an exact match based on versions, so try that first.
2466
372
        let pkgname = PackageName {
2467
372
            namespace: name.namespace().to_string(),
2468
372
            name: name.package().to_string(),
2469
372
            version: version.clone(),
2470
372
        };
2471
372
        if let Some(pkg) = resolve.package_names.get(&pkgname) {
2472
372
            if let Some(id) = resolve.packages[*pkg]
2473
372
                .interfaces
2474
372
                .get(name.interface().as_str())
2475
            {
2476
                // If the interface from the package is directly in `items` then
2477
                // return that.
2478
372
                let key = WorldKey::Interface(*id);
2479
372
                if items.contains_key(&key) {
2480
363
                    return Ok((key, *id));
2481
9
                }
2482
2483
                // .. otherwise see if any interface in `items` is a clone of
2484
                // the package's interface. This means it's created by
2485
                // `generate_nominal_type_ids` and is used to match up exports
2486
                // to their nominal clone since the original is no longer
2487
                // exported.
2488
9
                for k in items.keys() {
2489
9
                    let i = match *k {
2490
9
                        WorldKey::Interface(id) => id,
2491
0
                        WorldKey::Name(_) => continue,
2492
                    };
2493
9
                    if resolve.interfaces[i].clone_of == Some(*id) {
2494
9
                        return Ok((WorldKey::Interface(i), i));
2495
0
                    }
2496
                }
2497
0
            }
2498
0
        }
2499
2500
        // If an exact match wasn't found then instead search for the first
2501
        // match based on versions. This means that a core wasm import for
2502
        // "1.2.3" might end up matching an interface at "1.2.4", for example.
2503
        // (or "1.2.2", depending on what's available).
2504
0
        for (key, _) in items {
2505
0
            let id = match key {
2506
0
                WorldKey::Interface(id) => *id,
2507
0
                WorldKey::Name(_) => continue,
2508
            };
2509
            // Make sure the interface names match
2510
0
            let interface = &resolve.interfaces[id];
2511
0
            if interface.name.as_ref().unwrap() != name.interface().as_str() {
2512
0
                continue;
2513
0
            }
2514
2515
            // Make sure the package name (without version) matches
2516
0
            let pkg = &resolve.packages[interface.package.unwrap()];
2517
0
            if pkg.name.namespace != pkgname.namespace || pkg.name.name != pkgname.name {
2518
0
                continue;
2519
0
            }
2520
2521
0
            let module_version = match &version {
2522
0
                Some(version) => version,
2523
0
                None => continue,
2524
            };
2525
0
            let pkg_version = match &pkg.name.version {
2526
0
                Some(version) => version,
2527
0
                None => continue,
2528
            };
2529
2530
            // Test if the two semver versions are compatible
2531
0
            let module_compat = PackageName::version_compat_track(&module_version);
2532
0
            let pkg_compat = PackageName::version_compat_track(pkg_version);
2533
0
            if module_compat == pkg_compat {
2534
0
                return Ok((key.clone(), id));
2535
0
            }
2536
        }
2537
2538
0
        bail!("module requires an import interface named `{module}`")
2539
2.38k
    }
2540
2.14k
    fn strip_post_return<'a>(&self, name: &'a str) -> Option<&'a str> {
2541
2.14k
        name.strip_prefix("cabi_post_")
2542
2.14k
    }
2543
6.23k
    fn match_wit_export<'a>(
2544
6.23k
        &self,
2545
6.23k
        export_name: &str,
2546
6.23k
        resolve: &'a Resolve,
2547
6.23k
        world: WorldId,
2548
6.23k
        exports: &'a IndexSet<WorldKey>,
2549
6.23k
    ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)> {
2550
6.23k
        let world = &resolve.worlds[world];
2551
23.8k
        for name in exports {
2552
23.8k
            match &world.exports[name] {
2553
1.73k
                WorldItem::Function(f) => {
2554
1.73k
                    if f.legacy_core_export_name(None) == export_name {
2555
120
                        return Some((name, None, f));
2556
1.61k
                    }
2557
                }
2558
22.1k
                WorldItem::Interface { id, .. } => {
2559
22.1k
                    let string = resolve.name_world_key(name);
2560
234k
                    for (_, func) in resolve.interfaces[*id].functions.iter() {
2561
234k
                        if func.legacy_core_export_name(Some(&string)) == export_name {
2562
3.97k
                            return Some((name, Some(*id), func));
2563
230k
                        }
2564
                    }
2565
                }
2566
2567
0
                WorldItem::Type { .. } => unreachable!(),
2568
            }
2569
        }
2570
2571
2.14k
        None
2572
6.23k
    }
2573
2574
104
    fn match_wit_resource_dtor<'a>(
2575
104
        &self,
2576
104
        export_name: &str,
2577
104
        resolve: &'a Resolve,
2578
104
        world: WorldId,
2579
104
        exports: &'a IndexSet<WorldKey>,
2580
104
    ) -> Option<TypeId> {
2581
104
        let world = &resolve.worlds[world];
2582
391
        for name in exports {
2583
391
            let id = match &world.exports[name] {
2584
356
                WorldItem::Interface { id, .. } => *id,
2585
35
                WorldItem::Function(_) => continue,
2586
0
                WorldItem::Type { .. } => unreachable!(),
2587
            };
2588
356
            let name = resolve.name_world_key(name);
2589
356
            let resource = match export_name
2590
356
                .strip_prefix(&name)
2591
356
                .and_then(|s| s.strip_prefix("#[dtor]"))
2592
356
                .and_then(|r| resolve.interfaces[id].types.get(r))
2593
            {
2594
104
                Some(id) => *id,
2595
252
                None => continue,
2596
            };
2597
2598
104
            match resolve.types[resource].kind {
2599
104
                TypeDefKind::Resource => {}
2600
0
                _ => continue,
2601
            }
2602
2603
104
            return Some(resource);
2604
        }
2605
2606
0
        None
2607
104
    }
2608
2609
189
    fn world_key_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) {
2610
189
        let (async_abi, name) = self.strip_async_lowered_prefix(name);
2611
        (
2612
189
            name,
2613
189
            if async_abi {
2614
13
                AbiVariant::GuestImportAsync
2615
            } else {
2616
176
                AbiVariant::GuestImport
2617
            },
2618
        )
2619
189
    }
2620
548
    fn interface_function_name_and_abi<'a>(&self, name: &'a str) -> (&'a str, AbiVariant) {
2621
548
        let (async_abi, name) = self.strip_async_lowered_prefix(name);
2622
        (
2623
548
            name,
2624
548
            if async_abi {
2625
48
                AbiVariant::GuestImportAsync
2626
            } else {
2627
500
                AbiVariant::GuestImport
2628
            },
2629
        )
2630
548
    }
2631
0
    fn env_import(&self, name: &str, ty: &FuncType) -> Option<Import> {
2632
0
        match name {
2633
0
            "__wasm_get_stack_pointer" => {
2634
0
                let ty = *ty.results().get(0)?;
2635
0
                Some(Import::ContextGet { ty, slot: 0 })
2636
            }
2637
0
            "__wasm_set_stack_pointer" => {
2638
0
                let ty = *ty.params().get(0)?;
2639
0
                Some(Import::ContextSet { ty, slot: 0 })
2640
            }
2641
            // TLS handling is slightly different than above to handle
2642
            // coop-threading-vs-not, so the exact resolution of this import is
2643
            // deferred to later.
2644
0
            "__wasm_get_tls_base" => {
2645
0
                let ty = *ty.results().get(0)?;
2646
0
                Some(Import::TlsBaseGet { ty })
2647
            }
2648
0
            "__wasm_set_tls_base" => {
2649
0
                let ty = *ty.params().get(0)?;
2650
0
                Some(Import::TlsBaseSet { ty })
2651
            }
2652
0
            _ => None,
2653
        }
2654
0
    }
2655
}
2656
2657
/// This function validates the following:
2658
///
2659
/// * The `bytes` represent a valid core WebAssembly module.
2660
/// * The module's imports are all satisfied by the given `imports` interfaces
2661
///   or the `adapters` set.
2662
/// * The given default and exported interfaces are satisfied by the module's
2663
///   exports.
2664
///
2665
/// The `ValidatedModule` return value contains the metadata which describes the
2666
/// input module on success. This is then further used to generate a component
2667
/// for this module.
2668
593
pub fn validate_module(
2669
593
    encoder: &ComponentEncoder,
2670
593
    bytes: &[u8],
2671
593
    import_map: Option<&ModuleImportMap>,
2672
593
) -> Result<ValidatedModule> {
2673
593
    ValidatedModule::new(
2674
593
        encoder,
2675
593
        bytes,
2676
593
        &encoder.main_module_exports,
2677
593
        import_map,
2678
593
        None,
2679
    )
2680
593
}
2681
2682
/// This function will validate the `bytes` provided as a wasm adapter module.
2683
/// Notably this will validate the wasm module itself in addition to ensuring
2684
/// that it has the "shape" of an adapter module. Current constraints are:
2685
///
2686
/// * The adapter module can import only one memory
2687
/// * The adapter module can only import from the name of `interface` specified,
2688
///   and all function imports must match the `required` types which correspond
2689
///   to the lowered types of the functions in `interface`.
2690
///
2691
/// The wasm module passed into this function is the output of the GC pass of an
2692
/// adapter module's original source. This means that the adapter module is
2693
/// already minimized and this is a double-check that the minimization pass
2694
/// didn't accidentally break the wasm module.
2695
///
2696
/// If `is_library` is true, we waive some of the constraints described above,
2697
/// allowing the module to import tables and globals, as well as import
2698
/// functions at the world level, not just at the interface level.
2699
0
pub fn validate_adapter_module(
2700
0
    encoder: &ComponentEncoder,
2701
0
    bytes: &[u8],
2702
0
    required_by_import: &IndexMap<String, FuncType>,
2703
0
    exports: &IndexSet<WorldKey>,
2704
0
    library_info: Option<&LibraryInfo>,
2705
0
) -> Result<ValidatedModule> {
2706
0
    let ret = ValidatedModule::new(encoder, bytes, exports, None, library_info)?;
2707
2708
0
    for (name, required_ty) in required_by_import {
2709
0
        let actual = match ret.exports.raw_exports.get(name) {
2710
0
            Some(ty) => ty,
2711
0
            None => return Err(AdapterModuleDidNotExport(name.clone()).into()),
2712
        };
2713
0
        validate_func_sig(name, required_ty, &actual)?;
2714
    }
2715
2716
0
    Ok(ret)
2717
0
}
2718
2719
/// An error that can be returned from adapting a core Wasm module into a
2720
/// component using an adapter module.
2721
///
2722
/// If the core Wasm module contained an import that it requires to be
2723
/// satisfied by the adapter, and the adapter does not contain an export
2724
/// with the same name, an instance of this error is returned.
2725
#[derive(Debug, Clone)]
2726
pub struct AdapterModuleDidNotExport(String);
2727
2728
impl fmt::Display for AdapterModuleDidNotExport {
2729
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2730
0
        write!(f, "adapter module did not export `{}`", self.0)
2731
0
    }
2732
}
2733
2734
impl std::error::Error for AdapterModuleDidNotExport {}
2735
2736
361
fn resource_test_for_interface<'a>(
2737
361
    resolve: &'a Resolve,
2738
361
    id: InterfaceId,
2739
361
) -> impl Fn(&str) -> Option<TypeId> + 'a {
2740
361
    let interface = &resolve.interfaces[id];
2741
361
    move |name: &str| {
2742
361
        let ty = match interface.types.get(name) {
2743
361
            Some(ty) => *ty,
2744
0
            None => return None,
2745
        };
2746
361
        if matches!(resolve.types[ty].kind, TypeDefKind::Resource) {
2747
361
            Some(ty)
2748
        } else {
2749
0
            None
2750
        }
2751
361
    }
2752
361
}
2753
2754
28
fn resource_test_for_world<'a>(
2755
28
    resolve: &'a Resolve,
2756
28
    id: WorldId,
2757
28
) -> impl Fn(&str) -> Option<TypeId> + 'a {
2758
28
    let world = &resolve.worlds[id];
2759
28
    move |name: &str| match world.imports.get(&WorldKey::Name(name.to_string()))? {
2760
28
        WorldItem::Type { id, .. } => {
2761
28
            if matches!(resolve.types[*id].kind, TypeDefKind::Resource) {
2762
28
                Some(*id)
2763
            } else {
2764
0
                None
2765
            }
2766
        }
2767
0
        _ => None,
2768
28
    }
2769
28
}
2770
2771
2.80k
fn validate_func(
2772
2.80k
    resolve: &Resolve,
2773
2.80k
    ty: &wasmparser::FuncType,
2774
2.80k
    func: &Function,
2775
2.80k
    abi: AbiVariant,
2776
2.80k
) -> Result<()> {
2777
2.80k
    validate_func_sig(
2778
2.80k
        &func.name,
2779
2.80k
        &wasm_sig_to_func_type(resolve.wasm_signature(abi, func)),
2780
2.80k
        ty,
2781
    )
2782
2.80k
}
2783
2784
1.44k
fn validate_post_return(
2785
1.44k
    resolve: &Resolve,
2786
1.44k
    ty: &wasmparser::FuncType,
2787
1.44k
    func: &Function,
2788
1.44k
) -> Result<()> {
2789
    // The expected signature of a post-return function is to take all the
2790
    // parameters that are returned by the guest function and then return no
2791
    // results. Model this by calculating the signature of `func` and then
2792
    // moving its results into the parameters list while emptying out the
2793
    // results.
2794
1.44k
    let mut sig = resolve.wasm_signature(AbiVariant::GuestExport, func);
2795
1.44k
    sig.params = mem::take(&mut sig.results);
2796
1.44k
    validate_func_sig(
2797
1.44k
        &format!("{} post-return", func.name),
2798
1.44k
        &wasm_sig_to_func_type(sig),
2799
1.44k
        ty,
2800
    )
2801
1.44k
}
2802
2803
9.62k
fn validate_func_sig(name: &str, expected: &FuncType, ty: &wasmparser::FuncType) -> Result<()> {
2804
9.62k
    if ty != expected {
2805
0
        bail!(
2806
            "type mismatch for function `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`",
2807
            name,
2808
0
            expected.params(),
2809
0
            expected.results(),
2810
0
            ty.params(),
2811
0
            ty.results()
2812
        );
2813
9.62k
    }
2814
2815
9.62k
    Ok(())
2816
9.62k
}
2817
2818
/// Matches `name` as `[${prefix}S]...`, and if found returns `("S", "...")`
2819
4.98k
fn prefixed_intrinsic<'a>(name: &'a str, prefix: &str) -> Option<(&'a str, &'a str)> {
2820
4.98k
    assert!(prefix.starts_with("["));
2821
4.98k
    assert!(prefix.ends_with("-"));
2822
4.98k
    let suffix = name.strip_prefix(prefix)?;
2823
1.21k
    let index = suffix.find(']')?;
2824
1.21k
    let rest = &suffix[index + 1..];
2825
1.21k
    Some((&suffix[..index], rest))
2826
4.98k
}
2827
2828
/// Parses a `[context-get-<N>]` / `[context-set-<N>]` style name, optionally
2829
/// carrying a type width infix: `[context-get-i64-<N>]`.
2830
///
2831
/// Returns the value type together with the numeric slot. Additional type
2832
/// widths can be added here by extending the match below.
2833
1.20k
fn parse_context_name(name: &str, prefix: &str) -> Option<(ValType, u32)> {
2834
1.20k
    let (suffix, rest) = prefixed_intrinsic(name, prefix)?;
2835
332
    if !rest.is_empty() {
2836
0
        return None;
2837
332
    }
2838
332
    let (ty, slot) = match suffix.split_once('-') {
2839
0
        Some(("i64", slot)) => (ValType::I64, slot),
2840
0
        Some(("i32", slot)) => (ValType::I32, slot),
2841
332
        _ => (ValType::I32, suffix),
2842
    };
2843
332
    let slot = slot.parse().ok()?;
2844
332
    Some((ty, slot))
2845
1.20k
}
2846
2847
1.55k
fn get_function<'a>(
2848
1.55k
    resolve: &'a Resolve,
2849
1.55k
    world: &'a World,
2850
1.55k
    name: &str,
2851
1.55k
    interface: Option<InterfaceId>,
2852
1.55k
    imported: bool,
2853
1.55k
) -> Result<&'a Function> {
2854
1.55k
    let function = if let Some(id) = interface {
2855
1.52k
        return resolve.interfaces[id]
2856
1.52k
            .functions
2857
1.52k
            .get(name)
2858
1.52k
            .ok_or_else(|| anyhow!("no export `{name}` found"));
2859
35
    } else if imported {
2860
14
        world.imports.get(&WorldKey::Name(name.to_string()))
2861
    } else {
2862
21
        world.exports.get(&WorldKey::Name(name.to_string()))
2863
    };
2864
35
    let Some(WorldItem::Function(function)) = function else {
2865
0
        bail!("no export `{name}` found");
2866
    };
2867
35
    Ok(function)
2868
1.55k
}