Coverage Report

Created: 2025-01-06 07:43

/src/wasm-tools/crates/wit-component/src/validation.rs
Line
Count
Source (jump to first uncovered line)
1
use crate::encoding::{Instance, Item, LibraryInfo, MainOrAdapter};
2
use crate::{ComponentEncoder, StringEncoding};
3
use anyhow::{bail, Context, Result};
4
use indexmap::{map::Entry, IndexMap, IndexSet};
5
use std::hash::{Hash, Hasher};
6
use std::mem;
7
use wasm_encoder::ExportKind;
8
use wasmparser::names::{ComponentName, ComponentNameKind};
9
use wasmparser::{
10
    types::TypesRef, Encoding, ExternalKind, FuncType, Parser, Payload, TypeRef, ValType,
11
    ValidPayload, Validator,
12
};
13
use wit_parser::{
14
    abi::{AbiVariant, WasmSignature, WasmType},
15
    Function, InterfaceId, PackageName, Resolve, TypeDefKind, TypeId, World, WorldId, WorldItem,
16
    WorldKey,
17
};
18
19
55.6k
fn wasm_sig_to_func_type(signature: WasmSignature) -> FuncType {
20
134k
    fn from_wasm_type(ty: &WasmType) -> ValType {
21
134k
        match ty {
22
94.4k
            WasmType::I32 => ValType::I32,
23
1.19k
            WasmType::I64 => ValType::I64,
24
1.47k
            WasmType::F32 => ValType::F32,
25
17.0k
            WasmType::F64 => ValType::F64,
26
19.2k
            WasmType::Pointer => ValType::I32,
27
0
            WasmType::PointerOrI64 => ValType::I64,
28
833
            WasmType::Length => ValType::I32,
29
        }
30
134k
    }
31
32
55.6k
    FuncType::new(
33
55.6k
        signature.params.iter().map(from_wasm_type),
34
55.6k
        signature.results.iter().map(from_wasm_type),
35
55.6k
    )
36
55.6k
}
37
38
/// Metadata about a validated module and what was found internally.
39
///
40
/// This structure houses information about `imports` and `exports` to the
41
/// module. Each of these specialized types contains "connection" information
42
/// between a module's imports/exports and the WIT or component-level constructs
43
/// they correspond to.
44
45
#[derive(Default)]
46
pub struct ValidatedModule {
47
    /// Information about a module's imports.
48
    pub imports: ImportMap,
49
50
    /// Information about a module's exports.
51
    pub exports: ExportMap,
52
}
53
54
impl ValidatedModule {
55
6.26k
    fn new(
56
6.26k
        encoder: &ComponentEncoder,
57
6.26k
        bytes: &[u8],
58
6.26k
        exports: &IndexSet<WorldKey>,
59
6.26k
        info: Option<&LibraryInfo>,
60
6.26k
    ) -> Result<ValidatedModule> {
61
6.26k
        let mut validator = Validator::new();
62
6.26k
        let mut ret = ValidatedModule::default();
63
64
110k
        for payload in Parser::new(0).parse_all(bytes) {
65
110k
            let payload = payload?;
66
110k
            if let ValidPayload::End(_) = validator.payload(&payload)? {
67
6.26k
                break;
68
103k
            }
69
103k
70
103k
            let types = validator.types(0).unwrap();
71
72
6.26k
            match payload {
73
6.26k
                Payload::Version { encoding, .. } if encoding != Encoding::Module => {
74
0
                    bail!("data is not a WebAssembly module");
75
                }
76
3.06k
                Payload::ImportSection(s) => {
77
30.9k
                    for import in s {
78
27.8k
                        let import = import?;
79
27.8k
                        ret.imports.add(import, encoder, info, types)?;
80
                    }
81
                }
82
6.26k
                Payload::ExportSection(s) => {
83
69.5k
                    for export in s {
84
63.3k
                        let export = export?;
85
63.3k
                        ret.exports.add(export, encoder, &exports, types)?;
86
                    }
87
                }
88
94.6k
                _ => continue,
89
            }
90
        }
91
92
6.26k
        ret.exports.validate(encoder, exports)?;
93
94
6.26k
        Ok(ret)
95
6.26k
    }
96
}
97
98
/// Metadata information about a module's imports.
99
///
100
/// This structure maintains the connection between component model "things" and
101
/// core wasm "things" by ensuring that all imports to the core wasm module are
102
/// classified by the `Import` enumeration.
103
#[derive(Default)]
104
pub struct ImportMap {
105
    /// The first level of the map here is the module namespace of the import
106
    /// and the second level of the map is the field namespace. The item is then
107
    /// how the import is satisfied.
108
    names: IndexMap<String, ImportInstance>,
109
}
110
111
pub enum ImportInstance {
112
    /// This import is satisfied by an entire instance of another
113
    /// adapter/module.
114
    Whole(MainOrAdapter),
115
116
    /// This import is satisfied by filling out each name possibly differently.
117
    Names(IndexMap<String, Import>),
118
}
119
120
/// Represents metadata about a `stream<T>` or `future<T>` type for a specific
121
/// payload type `T`.
122
///
123
/// Currently, the name mangling scheme we use to represent `stream` and
124
/// `future` intrinsics as core module function imports refers to a specific
125
/// `stream` or `future` type by naming an imported or exported component
126
/// function which has that type as a parameter or return type (where the
127
/// specific type is refered to using an ordinal numbering scheme).  Not only
128
/// does this approach unambiguously indicate the type of interest, but it
129
/// allows us to reuse the `realloc`, string encoding, memory, etc. used by that
130
/// function when emitting intrinsic declarations.
131
///
132
/// TODO: Rather than reusing the same canon opts as the function in which the
133
/// type appears, consider encoding them in the name mangling stream on an
134
/// individual basis, similar to how we encode `error-context.*` built-in
135
/// imports.
136
#[derive(Debug, Eq, PartialEq, Clone)]
137
pub struct PayloadInfo {
138
    /// The original, mangled import name used to import this built-in
139
    /// (currently used only for hashing and debugging).
140
    pub name: String,
141
    /// The resolved type id for the `stream` or `future` type of interest.
142
    pub ty: TypeId,
143
    /// The component-level function import or export where the type appeared as
144
    /// a parameter or result type.
145
    pub function: Function,
146
    /// The world key representing the import or export context of `function`.
147
    pub key: WorldKey,
148
    /// The interface that `function` was imported from or exported in, if any.
149
    pub interface: Option<InterfaceId>,
150
    /// Whether `function` is being imported or exported.
151
    ///
152
    /// This may affect how we emit the declaration of the built-in, e.g. if the
153
    /// payload type is an exported resource.
154
    pub imported: bool,
155
}
156
157
impl Hash for PayloadInfo {
158
    /// We derive `Hash` for this type by hand and exclude the `function` field
159
    /// because (A) `Function` doesn't implement `Hash` and (B) the other fields
160
    /// are sufficient to uniquely identify the type of interest, which function
161
    /// it appeared in, and which parameter or return type we found it in.
162
0
    fn hash<H: Hasher>(&self, state: &mut H) {
163
0
        self.name.hash(state);
164
0
        self.ty.hash(state);
165
0
        self.key.hash(state);
166
0
        self.interface.hash(state);
167
0
        self.imported.hash(state);
168
0
    }
169
}
170
171
/// The different kinds of items that a module or an adapter can import.
172
///
173
/// This is intended to be an exhaustive definition of what can be imported into
174
/// core modules within a component that wit-component supports.
175
#[derive(Debug, Clone)]
176
pub enum Import {
177
    /// A top-level world function, with the name provided here, is imported
178
    /// into the module.
179
    WorldFunc(WorldKey, String, AbiVariant),
180
181
    /// An interface's function is imported into the module.
182
    ///
183
    /// The `WorldKey` here is the name of the interface in the world in
184
    /// question. The `InterfaceId` is the interface that was imported from and
185
    /// `String` is the WIT name of the function.
186
    InterfaceFunc(WorldKey, InterfaceId, String, AbiVariant),
187
188
    /// An imported resource's destructor is imported.
189
    ///
190
    /// The key provided indicates whether it's for the top-level types of the
191
    /// world (`None`) or an interface (`Some` with the name of the interface).
192
    /// The `TypeId` is what resource is being dropped.
193
    ImportedResourceDrop(WorldKey, Option<InterfaceId>, TypeId),
194
195
    /// A `canon resource.drop` intrinsic for an exported item is being
196
    /// imported.
197
    ///
198
    /// This lists the key of the interface that's exporting the resource plus
199
    /// the id within that interface.
200
    ExportedResourceDrop(WorldKey, TypeId),
201
202
    /// A `canon resource.new` intrinsic for an exported item is being
203
    /// imported.
204
    ///
205
    /// This lists the key of the interface that's exporting the resource plus
206
    /// the id within that interface.
207
    ExportedResourceNew(WorldKey, TypeId),
208
209
    /// A `canon resource.rep` intrinsic for an exported item is being
210
    /// imported.
211
    ///
212
    /// This lists the key of the interface that's exporting the resource plus
213
    /// the id within that interface.
214
    ExportedResourceRep(WorldKey, TypeId),
215
216
    /// An export of an adapter is being imported with the specified type.
217
    ///
218
    /// This is used for when the main module imports an adapter function. The
219
    /// adapter name and function name match the module's own import, and the
220
    /// type must match that listed here.
221
    AdapterExport(FuncType),
222
223
    /// An adapter is importing the memory of the main module.
224
    ///
225
    /// (should be combined with `MainModuleExport` below one day)
226
    MainModuleMemory,
227
228
    /// An adapter is importing an arbitrary item from the main module.
229
    MainModuleExport { name: String, kind: ExportKind },
230
231
    /// An arbitrary item from either the main module or an adapter is being
232
    /// imported.
233
    ///
234
    /// (should probably subsume `MainModule*` and maybe `AdapterExport` above
235
    /// one day.
236
    Item(Item),
237
238
    /// A `canon task.return` intrinsic for an exported function.
239
    ///
240
    /// This allows an exported function to return a value and then continue
241
    /// running.
242
    ///
243
    /// As of this writing, only async-lifted exports use `task.return`, but the
244
    /// plan is to also support it for sync-lifted exports in the future as
245
    /// well.
246
    ExportedTaskReturn(Function),
247
248
    /// A `canon task.backpressure` intrinsic.
249
    ///
250
    /// This allows the guest to dynamically indicate whether it's ready for
251
    /// additional concurrent calls.
252
    TaskBackpressure,
253
254
    /// A `canon task.wait` intrinsic.
255
    ///
256
    /// This allows the guest to wait for any pending calls to async-lowered
257
    /// imports and/or `stream` and `future` operations to complete without
258
    /// unwinding the current Wasm stack.
259
    TaskWait { async_: bool },
260
261
    /// A `canon task.poll` intrinsic.
262
    ///
263
    /// This allows the guest to check whether any pending calls to
264
    /// async-lowered imports and/or `stream` and `future` operations have
265
    /// completed without unwinding the current Wasm stack and without blocking.
266
    TaskPoll { async_: bool },
267
268
    /// A `canon task.wait` intrinsic.
269
    ///
270
    /// This allows the guest to yield (e.g. during an computationally-intensive
271
    /// operation) and allow other subtasks to make progress.
272
    TaskYield { async_: bool },
273
274
    /// A `canon subtask.drop` intrinsic.
275
    ///
276
    /// This allows the guest to release its handle to an completed subtask.
277
    SubtaskDrop,
278
279
    /// A `canon stream.new` intrinsic.
280
    ///
281
    /// This allows the guest to create a new `stream` of the specified type.
282
    StreamNew(PayloadInfo),
283
284
    /// A `canon stream.read` intrinsic.
285
    ///
286
    /// This allows the guest to read the next values (if any) from the specifed
287
    /// stream.
288
    StreamRead { async_: bool, info: PayloadInfo },
289
290
    /// A `canon stream.write` intrinsic.
291
    ///
292
    /// This allows the guest to write one or more values to the specifed
293
    /// stream.
294
    StreamWrite { async_: bool, info: PayloadInfo },
295
296
    /// A `canon stream.cancel-read` intrinsic.
297
    ///
298
    /// This allows the guest to cancel a pending read it initiated earlier (but
299
    /// which may have already partially or entirely completed).
300
    StreamCancelRead {
301
        ty: TypeId,
302
        imported: bool,
303
        async_: bool,
304
    },
305
306
    /// A `canon stream.cancel-write` intrinsic.
307
    ///
308
    /// This allows the guest to cancel a pending write it initiated earlier
309
    /// (but which may have already partially or entirely completed).
310
    StreamCancelWrite {
311
        ty: TypeId,
312
        imported: bool,
313
        async_: bool,
314
    },
315
316
    /// A `canon stream.close-readable` intrinsic.
317
    ///
318
    /// This allows the guest to close the readable end of a `stream`.
319
    StreamCloseReadable { ty: TypeId, imported: bool },
320
321
    /// A `canon stream.close-writable` intrinsic.
322
    ///
323
    /// This allows the guest to close the writable end of a `stream`.
324
    StreamCloseWritable { ty: TypeId, imported: bool },
325
326
    /// A `canon future.new` intrinsic.
327
    ///
328
    /// This allows the guest to create a new `future` of the specified type.
329
    FutureNew(PayloadInfo),
330
331
    /// A `canon future.read` intrinsic.
332
    ///
333
    /// This allows the guest to read the value (if any) from the specifed
334
    /// future.
335
    FutureRead { async_: bool, info: PayloadInfo },
336
337
    /// A `canon future.write` intrinsic.
338
    ///
339
    /// This allows the guest to write a value to the specifed future.
340
    FutureWrite { async_: bool, info: PayloadInfo },
341
342
    /// A `canon future.cancel-read` intrinsic.
343
    ///
344
    /// This allows the guest to cancel a pending read it initiated earlier (but
345
    /// which may have already completed).
346
    FutureCancelRead {
347
        ty: TypeId,
348
        imported: bool,
349
        async_: bool,
350
    },
351
352
    /// A `canon future.cancel-write` intrinsic.
353
    ///
354
    /// This allows the guest to cancel a pending write it initiated earlier
355
    /// (but which may have already completed).
356
    FutureCancelWrite {
357
        ty: TypeId,
358
        imported: bool,
359
        async_: bool,
360
    },
361
362
    /// A `canon future.close-readable` intrinsic.
363
    ///
364
    /// This allows the guest to close the readable end of a `future`.
365
    FutureCloseReadable { ty: TypeId, imported: bool },
366
367
    /// A `canon future.close-writable` intrinsic.
368
    ///
369
    /// This allows the guest to close the writable end of a `future`.
370
    FutureCloseWritable { ty: TypeId, imported: bool },
371
372
    /// A `canon error-context.new` intrinsic.
373
    ///
374
    /// This allows the guest to create a new `error-context` instance with a
375
    /// specified debug message.
376
    ErrorContextNew { encoding: StringEncoding },
377
378
    /// A `canon error-context.debug-message` intrinsic.
379
    ///
380
    /// This allows the guest to retrieve the debug message from a
381
    /// `error-context` instance.  Note that the content of this message might
382
    /// not be identical to what was passed in to `error-context.new`.
383
    ErrorContextDebugMessage {
384
        encoding: StringEncoding,
385
        realloc: String,
386
    },
387
388
    /// A `canon error-context.drop` intrinsic.
389
    ///
390
    /// This allows the guest to release its handle to the specified
391
    /// `error-context` instance.
392
    ErrorContextDrop,
393
}
394
395
impl ImportMap {
396
    /// Returns whether the top-level world function `func` is imported.
397
4.99k
    pub fn uses_toplevel_func(&self, func: &str) -> bool {
398
58.0k
        self.imports().any(|(_, _, item)| match item {
399
37.9k
            Import::WorldFunc(_, name, _) => func == name,
400
20.0k
            _ => false,
401
58.0k
        })
402
4.99k
    }
403
404
    /// Returns whether the interface function specified is imported.
405
12.7k
    pub fn uses_interface_func(&self, interface: InterfaceId, func: &str) -> bool {
406
108k
        self.imports().any(|(_, _, import)| match import {
407
96.7k
            Import::InterfaceFunc(_, id, name, _) => *id == interface && name == func,
408
12.2k
            _ => false,
409
108k
        })
410
12.7k
    }
411
412
    /// Returns whether the specified resource's drop method is needed to import.
413
14.9k
    pub fn uses_imported_resource_drop(&self, resource: TypeId) -> bool {
414
122k
        self.imports().any(|(_, _, import)| match import {
415
10.8k
            Import::ImportedResourceDrop(_, _, id) => resource == *id,
416
111k
            _ => false,
417
122k
        })
418
14.9k
    }
419
420
    /// Returns the list of items that the adapter named `name` must export.
421
0
    pub fn required_from_adapter(&self, name: &str) -> IndexMap<String, FuncType> {
422
0
        let names = match self.names.get(name) {
423
0
            Some(ImportInstance::Names(names)) => names,
424
0
            _ => return IndexMap::new(),
425
        };
426
0
        names
427
0
            .iter()
428
0
            .map(|(name, import)| {
429
0
                (
430
0
                    name.clone(),
431
0
                    match import {
432
0
                        Import::AdapterExport(ty) => ty.clone(),
433
0
                        _ => unreachable!(),
434
                    },
435
                )
436
0
            })
437
0
            .collect()
438
0
    }
439
440
    /// Returns an iterator over all individual imports registered in this map.
441
    ///
442
    /// Note that this doesn't iterate over the "whole instance" imports.
443
45.1k
    pub fn imports(&self) -> impl Iterator<Item = (&str, &str, &Import)> + '_ {
444
45.1k
        self.names
445
45.1k
            .iter()
446
58.0k
            .filter_map(|(module, m)| match m {
447
58.0k
                ImportInstance::Names(names) => Some((module, names)),
448
0
                ImportInstance::Whole(_) => None,
449
58.0k
            })
450
58.0k
            .flat_map(|(module, m)| {
451
58.0k
                m.iter()
452
345k
                    .map(move |(field, import)| (module.as_str(), field.as_str(), import))
453
58.0k
            })
454
45.1k
    }
455
456
    /// Returns the map for how all imports must be satisfied.
457
6.26k
    pub fn modules(&self) -> &IndexMap<String, ImportInstance> {
458
6.26k
        &self.names
459
6.26k
    }
460
461
    /// Helper function used during validation to build up this `ImportMap`.
462
27.8k
    fn add(
463
27.8k
        &mut self,
464
27.8k
        import: wasmparser::Import<'_>,
465
27.8k
        encoder: &ComponentEncoder,
466
27.8k
        library_info: Option<&LibraryInfo>,
467
27.8k
        types: TypesRef<'_>,
468
27.8k
    ) -> Result<()> {
469
27.8k
        if self.classify_import_with_library(import, library_info)? {
470
0
            return Ok(());
471
27.8k
        }
472
27.8k
        let item = self.classify(import, encoder, types).with_context(|| {
473
0
            format!(
474
0
                "failed to resolve import `{}::{}`",
475
0
                import.module, import.name,
476
0
            )
477
27.8k
        })?;
478
27.8k
        self.insert_import(import, item)
479
27.8k
    }
480
481
27.8k
    fn classify(
482
27.8k
        &self,
483
27.8k
        import: wasmparser::Import<'_>,
484
27.8k
        encoder: &ComponentEncoder,
485
27.8k
        types: TypesRef<'_>,
486
27.8k
    ) -> Result<Import> {
487
27.8k
        // Special-case the main module's memory imported into adapters which
488
27.8k
        // currently with `wasm-ld` is not easily configurable.
489
27.8k
        if import.module == "env" && import.name == "memory" {
490
0
            return Ok(Import::MainModuleMemory);
491
27.8k
        }
492
27.8k
493
27.8k
        // Special-case imports from the main module into adapters.
494
27.8k
        if import.module == "__main_module__" {
495
            return Ok(Import::MainModuleExport {
496
0
                name: import.name.to_string(),
497
0
                kind: match import.ty {
498
0
                    TypeRef::Func(_) => ExportKind::Func,
499
0
                    TypeRef::Table(_) => ExportKind::Table,
500
0
                    TypeRef::Memory(_) => ExportKind::Memory,
501
0
                    TypeRef::Global(_) => ExportKind::Global,
502
0
                    TypeRef::Tag(_) => ExportKind::Tag,
503
                },
504
            });
505
27.8k
        }
506
507
27.8k
        let ty_index = match import.ty {
508
27.8k
            TypeRef::Func(ty) => ty,
509
0
            _ => bail!("module is only allowed to import functions"),
510
        };
511
27.8k
        let ty = types[types.core_type_at_in_module(ty_index)].unwrap_func();
512
27.8k
513
27.8k
        // Handle main module imports that match known adapters and set it up as
514
27.8k
        // an import of an adapter export.
515
27.8k
        if encoder.adapters.contains_key(import.module) {
516
0
            return Ok(Import::AdapterExport(ty.clone()));
517
27.8k
        }
518
519
27.8k
        let (module, names) = match import.module.strip_prefix("cm32p2") {
520
7.74k
            Some(suffix) => (suffix, STANDARD),
521
0
            None if encoder.reject_legacy_names => (import.module, STANDARD),
522
20.1k
            None => (import.module, LEGACY),
523
        };
524
27.8k
        self.classify_component_model_import(module, import.name, encoder, ty, names)
525
27.8k
    }
526
527
    /// Attempts to classify the import `{module}::{name}` with the rules
528
    /// specified in WebAssembly/component-model#378
529
27.8k
    fn classify_component_model_import(
530
27.8k
        &self,
531
27.8k
        module: &str,
532
27.8k
        name: &str,
533
27.8k
        encoder: &ComponentEncoder,
534
27.8k
        ty: &FuncType,
535
27.8k
        names: &dyn NameMangling,
536
27.8k
    ) -> Result<Import> {
537
27.8k
        let resolve = &encoder.metadata.resolve;
538
27.8k
        let world_id = encoder.metadata.world;
539
27.8k
        let world = &resolve.worlds[world_id];
540
541
27.8k
        if let Some(import) = names.payload_import(module, name, resolve, world, ty)? {
542
0
            return Ok(import);
543
27.8k
        }
544
27.8k
545
27.8k
        let async_import_for_export = |interface: Option<(WorldKey, InterfaceId)>| {
546
13.8k
            Ok::<_, anyhow::Error>(if let Some(function_name) = names.task_return_name(name) {
547
0
                let interface_id = interface.as_ref().map(|(_, id)| *id);
548
0
                let func = get_function(resolve, world, function_name, interface_id, false)?;
549
                // Note that we can't statically validate the type signature of
550
                // a `task.return` built-in since we can't know which export
551
                // it's associated with in general.  Instead, the host will
552
                // compare it with the expected type at runtime and trap if
553
                // necessary.
554
0
                Some(Import::ExportedTaskReturn(func))
555
            } else {
556
13.8k
                None
557
            })
558
13.8k
        };
559
560
27.8k
        let (abi, name) = if let Some(name) = names.async_name(name) {
561
6.57k
            (AbiVariant::GuestImportAsync, name)
562
        } else {
563
21.3k
            (AbiVariant::GuestImport, name)
564
        };
565
566
27.8k
        if module == names.import_root() {
567
5.81k
            if Some(name) == names.error_context_drop() {
568
0
                let expected = FuncType::new([ValType::I32], []);
569
0
                validate_func_sig(name, &expected, ty)?;
570
0
                return Ok(Import::ErrorContextDrop);
571
5.81k
            }
572
5.81k
573
5.81k
            if Some(name) == names.task_backpressure() {
574
0
                let expected = FuncType::new([ValType::I32], []);
575
0
                validate_func_sig(name, &expected, ty)?;
576
0
                return Ok(Import::TaskBackpressure);
577
5.81k
            }
578
5.81k
579
5.81k
            if Some(name) == names.task_wait() {
580
0
                let expected = FuncType::new([ValType::I32], [ValType::I32]);
581
0
                validate_func_sig(name, &expected, ty)?;
582
0
                return Ok(Import::TaskWait {
583
0
                    async_: abi == AbiVariant::GuestImportAsync,
584
0
                });
585
5.81k
            }
586
5.81k
587
5.81k
            if Some(name) == names.task_poll() {
588
0
                let expected = FuncType::new([ValType::I32], [ValType::I32]);
589
0
                validate_func_sig(name, &expected, ty)?;
590
0
                return Ok(Import::TaskPoll {
591
0
                    async_: abi == AbiVariant::GuestImportAsync,
592
0
                });
593
5.81k
            }
594
5.81k
595
5.81k
            if Some(name) == names.task_yield() {
596
0
                let expected = FuncType::new([], []);
597
0
                validate_func_sig(name, &expected, ty)?;
598
0
                return Ok(Import::TaskYield {
599
0
                    async_: abi == AbiVariant::GuestImportAsync,
600
0
                });
601
5.81k
            }
602
5.81k
603
5.81k
            if Some(name) == names.subtask_drop() {
604
0
                let expected = FuncType::new([ValType::I32], []);
605
0
                validate_func_sig(name, &expected, ty)?;
606
0
                return Ok(Import::SubtaskDrop);
607
5.81k
            }
608
609
5.81k
            if let Some(encoding) = names.error_context_new(name) {
610
0
                let expected = FuncType::new([ValType::I32; 2], [ValType::I32]);
611
0
                validate_func_sig(name, &expected, ty)?;
612
0
                return Ok(Import::ErrorContextNew { encoding });
613
5.81k
            }
614
615
5.81k
            if let Some((encoding, realloc)) = names.error_context_debug_message(name) {
616
0
                let expected = FuncType::new([ValType::I32; 2], []);
617
0
                validate_func_sig(name, &expected, ty)?;
618
0
                return Ok(Import::ErrorContextDebugMessage {
619
0
                    encoding,
620
0
                    realloc: realloc.to_owned(),
621
0
                });
622
5.81k
            }
623
624
5.81k
            if let Some(import) = async_import_for_export(None)? {
625
0
                return Ok(import);
626
5.81k
            }
627
5.81k
628
5.81k
            let key = WorldKey::Name(name.to_string());
629
5.81k
            if let Some(WorldItem::Function(func)) = world.imports.get(&key) {
630
4.99k
                validate_func(resolve, ty, func, abi)?;
631
4.99k
                return Ok(Import::WorldFunc(key, func.name.clone(), abi));
632
820
            }
633
820
634
820
            let get_resource = resource_test_for_world(resolve, world_id);
635
820
            if let Some(resource) = names.resource_drop_name(name) {
636
820
                if let Some(id) = get_resource(resource) {
637
820
                    let expected = FuncType::new([ValType::I32], []);
638
820
                    validate_func_sig(name, &expected, ty)?;
639
820
                    return Ok(Import::ImportedResourceDrop(key, None, id));
640
0
                }
641
0
            }
642
643
0
            match world.imports.get(&key) {
644
0
                Some(_) => bail!("expected world top-level import `{name}` to be a function"),
645
0
                None => bail!("no top-level imported function `{name}` specified"),
646
            }
647
22.0k
        }
648
649
22.0k
        let interface = match module.strip_prefix(names.import_non_root_prefix()) {
650
22.0k
            Some(name) => name,
651
0
            None => bail!("unknown or invalid component model import syntax"),
652
        };
653
654
22.0k
        if let Some(interface) = interface.strip_prefix(names.import_exported_intrinsic_prefix()) {
655
8.01k
            if let Some(import) = async_import_for_export(Some(names.module_to_interface(
656
8.01k
                interface,
657
8.01k
                resolve,
658
8.01k
                &world.exports,
659
8.01k
            )?))? {
660
0
                return Ok(import);
661
8.01k
            }
662
663
8.01k
            let (key, id) = names.module_to_interface(interface, resolve, &world.exports)?;
664
665
8.01k
            let get_resource = resource_test_for_interface(resolve, id);
666
8.01k
            if let Some(name) = names.resource_drop_name(name) {
667
2.67k
                if let Some(id) = get_resource(name) {
668
2.67k
                    let expected = FuncType::new([ValType::I32], []);
669
2.67k
                    validate_func_sig(name, &expected, ty)?;
670
2.67k
                    return Ok(Import::ExportedResourceDrop(key, id));
671
0
                }
672
5.34k
            }
673
5.34k
            if let Some(name) = names.resource_new_name(name) {
674
2.67k
                if let Some(id) = get_resource(name) {
675
2.67k
                    let expected = FuncType::new([ValType::I32], [ValType::I32]);
676
2.67k
                    validate_func_sig(name, &expected, ty)?;
677
2.67k
                    return Ok(Import::ExportedResourceNew(key, id));
678
0
                }
679
2.67k
            }
680
2.67k
            if let Some(name) = names.resource_rep_name(name) {
681
2.67k
                if let Some(id) = get_resource(name) {
682
2.67k
                    let expected = FuncType::new([ValType::I32], [ValType::I32]);
683
2.67k
                    validate_func_sig(name, &expected, ty)?;
684
2.67k
                    return Ok(Import::ExportedResourceRep(key, id));
685
0
                }
686
0
            }
687
0
            bail!("unknown function `{name}`")
688
14.0k
        }
689
690
14.0k
        let (key, id) = names.module_to_interface(interface, resolve, &world.imports)?;
691
14.0k
        let interface = &resolve.interfaces[id];
692
14.0k
        let get_resource = resource_test_for_interface(resolve, id);
693
14.0k
        if let Some(f) = interface.functions.get(name) {
694
12.7k
            validate_func(resolve, ty, f, abi).with_context(|| {
695
0
                let name = resolve.name_world_key(&key);
696
0
                format!("failed to validate import interface `{name}`")
697
12.7k
            })?;
698
12.7k
            return Ok(Import::InterfaceFunc(key, id, f.name.clone(), abi));
699
1.28k
        } else if let Some(resource) = names.resource_drop_name(name) {
700
1.28k
            if let Some(resource) = get_resource(resource) {
701
1.28k
                let expected = FuncType::new([ValType::I32], []);
702
1.28k
                validate_func_sig(name, &expected, ty)?;
703
1.28k
                return Ok(Import::ImportedResourceDrop(key, Some(id), resource));
704
0
            }
705
0
        }
706
0
        bail!(
707
0
            "import interface `{module}` is missing function \
708
0
             `{name}` that is required by the module",
709
0
        )
710
27.8k
    }
711
712
27.8k
    fn classify_import_with_library(
713
27.8k
        &mut self,
714
27.8k
        import: wasmparser::Import<'_>,
715
27.8k
        library_info: Option<&LibraryInfo>,
716
27.8k
    ) -> Result<bool> {
717
27.8k
        let info = match library_info {
718
0
            Some(info) => info,
719
27.8k
            None => return Ok(false),
720
        };
721
0
        let Some((_, instance)) = info
722
0
            .arguments
723
0
            .iter()
724
0
            .find(|(name, _items)| *name == import.module)
725
        else {
726
0
            return Ok(false);
727
        };
728
0
        match instance {
729
0
            Instance::MainOrAdapter(module) => match self.names.get(import.module) {
730
0
                Some(ImportInstance::Whole(which)) => {
731
0
                    if which != module {
732
0
                        bail!("different whole modules imported under the same name");
733
0
                    }
734
                }
735
                Some(ImportInstance::Names(_)) => {
736
0
                    bail!("cannot mix individual imports and whole module imports")
737
                }
738
0
                None => {
739
0
                    let instance = ImportInstance::Whole(module.clone());
740
0
                    self.names.insert(import.module.to_string(), instance);
741
0
                }
742
            },
743
0
            Instance::Items(items) => {
744
0
                let Some(item) = items.iter().find(|i| i.alias == import.name) else {
745
0
                    return Ok(false);
746
                };
747
0
                self.insert_import(import, Import::Item(item.clone()))?;
748
            }
749
        }
750
0
        Ok(true)
751
27.8k
    }
752
753
27.8k
    fn insert_import(&mut self, import: wasmparser::Import<'_>, item: Import) -> Result<()> {
754
27.8k
        let entry = self
755
27.8k
            .names
756
27.8k
            .entry(import.module.to_string())
757
27.8k
            .or_insert(ImportInstance::Names(IndexMap::default()));
758
27.8k
        let names = match entry {
759
27.8k
            ImportInstance::Names(names) => names,
760
0
            _ => bail!("cannot mix individual imports with module imports"),
761
        };
762
27.8k
        let entry = match names.entry(import.name.to_string()) {
763
            Entry::Occupied(_) => {
764
0
                bail!(
765
0
                    "module has duplicate import for `{}::{}`",
766
0
                    import.module,
767
0
                    import.name
768
0
                );
769
            }
770
27.8k
            Entry::Vacant(v) => v,
771
27.8k
        };
772
27.8k
        log::trace!(
773
0
            "classifying import `{}::{} as {item:?}",
774
            import.module,
775
            import.name
776
        );
777
27.8k
        entry.insert(item);
778
27.8k
        Ok(())
779
27.8k
    }
780
}
781
782
/// Dual of `ImportMap` except describes the exports of a module instead of the
783
/// imports.
784
#[derive(Default)]
785
pub struct ExportMap {
786
    names: IndexMap<String, Export>,
787
    raw_exports: IndexMap<String, FuncType>,
788
}
789
790
/// All possible (known) exports from a core wasm module that are recognized and
791
/// handled during the componentization process.
792
#[derive(Debug)]
793
pub enum Export {
794
    /// An export of a top-level function of a world, where the world function
795
    /// is named here.
796
    WorldFunc(WorldKey, String, AbiVariant),
797
798
    /// A post-return for a top-level function of a world.
799
    WorldFuncPostReturn(WorldKey),
800
801
    /// An export of a function in an interface.
802
    InterfaceFunc(WorldKey, InterfaceId, String, AbiVariant),
803
804
    /// A post-return for the above function.
805
    InterfaceFuncPostReturn(WorldKey, String),
806
807
    /// A destructor for an exported resource.
808
    ResourceDtor(TypeId),
809
810
    /// Memory, typically for an adapter.
811
    Memory,
812
813
    /// `cabi_realloc`
814
    GeneralPurposeRealloc,
815
816
    /// `cabi_export_realloc`
817
    GeneralPurposeExportRealloc,
818
819
    /// `cabi_import_realloc`
820
    GeneralPurposeImportRealloc,
821
822
    /// `_initialize`
823
    Initialize,
824
825
    /// `cabi_realloc_adapter`
826
    ReallocForAdapter,
827
828
    WorldFuncCallback(WorldKey),
829
830
    InterfaceFuncCallback(WorldKey, String),
831
}
832
833
impl ExportMap {
834
63.3k
    fn add(
835
63.3k
        &mut self,
836
63.3k
        export: wasmparser::Export<'_>,
837
63.3k
        encoder: &ComponentEncoder,
838
63.3k
        exports: &IndexSet<WorldKey>,
839
63.3k
        types: TypesRef<'_>,
840
63.3k
    ) -> Result<()> {
841
63.3k
        if let Some(item) = self.classify(export, encoder, exports, types)? {
842
63.3k
            log::debug!("classifying export `{}` as {item:?}", export.name);
843
63.3k
            let prev = self.names.insert(export.name.to_string(), item);
844
63.3k
            assert!(prev.is_none());
845
0
        }
846
63.3k
        Ok(())
847
63.3k
    }
848
849
63.3k
    fn classify(
850
63.3k
        &mut self,
851
63.3k
        export: wasmparser::Export<'_>,
852
63.3k
        encoder: &ComponentEncoder,
853
63.3k
        exports: &IndexSet<WorldKey>,
854
63.3k
        types: TypesRef<'_>,
855
63.3k
    ) -> Result<Option<Export>> {
856
63.3k
        match export.kind {
857
57.0k
            ExternalKind::Func => {
858
57.0k
                let ty = types[types.core_function_at(export.index)].unwrap_func();
859
57.0k
                self.raw_exports.insert(export.name.to_string(), ty.clone());
860
57.0k
            }
861
6.26k
            _ => {}
862
        }
863
864
        // Handle a few special-cased names first.
865
63.3k
        if export.name == "canonical_abi_realloc" {
866
0
            return Ok(Some(Export::GeneralPurposeRealloc));
867
63.3k
        } else if export.name == "cabi_import_realloc" {
868
0
            return Ok(Some(Export::GeneralPurposeImportRealloc));
869
63.3k
        } else if export.name == "cabi_export_realloc" {
870
0
            return Ok(Some(Export::GeneralPurposeExportRealloc));
871
63.3k
        } else if export.name == "cabi_realloc_adapter" {
872
0
            return Ok(Some(Export::ReallocForAdapter));
873
63.3k
        }
874
875
63.3k
        let (name, names) = match export.name.strip_prefix("cm32p2") {
876
19.0k
            Some(name) => (name, STANDARD),
877
0
            None if encoder.reject_legacy_names => return Ok(None),
878
44.2k
            None => (export.name, LEGACY),
879
        };
880
881
63.3k
        if let Some(export) = self
882
63.3k
            .classify_component_export(names, name, &export, encoder, exports, types)
883
63.3k
            .with_context(|| format!("failed to classify export `{}`", export.name))?
884
        {
885
63.3k
            return Ok(Some(export));
886
0
        }
887
0
        log::debug!("unknown export `{}`", export.name);
888
0
        Ok(None)
889
63.3k
    }
890
891
63.3k
    fn classify_component_export(
892
63.3k
        &mut self,
893
63.3k
        names: &dyn NameMangling,
894
63.3k
        name: &str,
895
63.3k
        export: &wasmparser::Export<'_>,
896
63.3k
        encoder: &ComponentEncoder,
897
63.3k
        exports: &IndexSet<WorldKey>,
898
63.3k
        types: TypesRef<'_>,
899
63.3k
    ) -> Result<Option<Export>> {
900
63.3k
        let resolve = &encoder.metadata.resolve;
901
63.3k
        let world = encoder.metadata.world;
902
63.3k
        match export.kind {
903
57.0k
            ExternalKind::Func => {}
904
            ExternalKind::Memory => {
905
6.26k
                if name == names.export_memory() {
906
6.26k
                    return Ok(Some(Export::Memory));
907
0
                }
908
0
                return Ok(None);
909
            }
910
0
            _ => return Ok(None),
911
        }
912
57.0k
        let ty = types[types.core_function_at(export.index)].unwrap_func();
913
57.0k
914
57.0k
        // Handle a few special-cased names first.
915
57.0k
        if name == names.export_realloc() {
916
6.26k
            let expected = FuncType::new([ValType::I32; 4], [ValType::I32]);
917
6.26k
            validate_func_sig(name, &expected, ty)?;
918
6.26k
            return Ok(Some(Export::GeneralPurposeRealloc));
919
50.8k
        } else if name == names.export_initialize() {
920
6.26k
            let expected = FuncType::new([], []);
921
6.26k
            validate_func_sig(name, &expected, ty)?;
922
6.26k
            return Ok(Some(Export::Initialize));
923
44.5k
        }
924
44.5k
925
44.5k
        let full_name = name;
926
44.5k
        let (abi, name) = if let Some(name) = names.async_name(name) {
927
4.39k
            (AbiVariant::GuestExportAsync, name)
928
40.1k
        } else if let Some(name) = names.async_stackful_name(name) {
929
973
            (AbiVariant::GuestExportAsyncStackful, name)
930
        } else {
931
39.1k
            (AbiVariant::GuestExport, name)
932
        };
933
934
        // Try to match this to a known WIT export that `exports` allows.
935
44.5k
        if let Some((key, id, f)) = names.match_wit_export(name, resolve, world, exports) {
936
21.3k
            validate_func(resolve, ty, f, abi).with_context(|| {
937
0
                let key = resolve.name_world_key(key);
938
0
                format!("failed to validate export for `{key}`")
939
21.3k
            })?;
940
21.3k
            match id {
941
20.5k
                Some(id) => {
942
20.5k
                    return Ok(Some(Export::InterfaceFunc(
943
20.5k
                        key.clone(),
944
20.5k
                        id,
945
20.5k
                        f.name.clone(),
946
20.5k
                        abi,
947
20.5k
                    )));
948
                }
949
                None => {
950
874
                    return Ok(Some(Export::WorldFunc(key.clone(), f.name.clone(), abi)));
951
                }
952
            }
953
23.1k
        }
954
955
        // See if this is a post-return for any known WIT export.
956
23.1k
        if let Some(remaining) = names.strip_post_return(name) {
957
16.5k
            if let Some((key, id, f)) = names.match_wit_export(remaining, resolve, world, exports) {
958
16.5k
                validate_post_return(resolve, ty, f).with_context(|| {
959
0
                    let key = resolve.name_world_key(key);
960
0
                    format!("failed to validate export for `{key}`")
961
16.5k
                })?;
962
16.5k
                match id {
963
15.8k
                    Some(_id) => {
964
15.8k
                        return Ok(Some(Export::InterfaceFuncPostReturn(
965
15.8k
                            key.clone(),
966
15.8k
                            f.name.clone(),
967
15.8k
                        )));
968
                    }
969
                    None => {
970
693
                        return Ok(Some(Export::WorldFuncPostReturn(key.clone())));
971
                    }
972
                }
973
0
            }
974
6.63k
        }
975
976
6.63k
        if let Some(suffix) = names.callback_name(full_name) {
977
3.96k
            if let Some((key, id, f)) = names.match_wit_export(suffix, resolve, world, exports) {
978
3.96k
                validate_func_sig(
979
3.96k
                    full_name,
980
3.96k
                    &FuncType::new([ValType::I32; 4], [ValType::I32]),
981
3.96k
                    ty,
982
3.96k
                )?;
983
3.96k
                return Ok(Some(if id.is_some() {
984
3.82k
                    Export::InterfaceFuncCallback(key.clone(), f.name.clone())
985
                } else {
986
140
                    Export::WorldFuncCallback(key.clone())
987
                }));
988
0
            }
989
2.67k
        }
990
991
        // And, finally, see if it matches a known destructor.
992
2.67k
        if let Some(dtor) = names.match_wit_resource_dtor(name, resolve, world, exports) {
993
2.67k
            let expected = FuncType::new([ValType::I32], []);
994
2.67k
            validate_func_sig(full_name, &expected, ty)?;
995
2.67k
            return Ok(Some(Export::ResourceDtor(dtor)));
996
0
        }
997
0
998
0
        Ok(None)
999
63.3k
    }
1000
1001
    /// Returns the name of the post-return export, if any, for the `key` and
1002
    /// `func` combo.
1003
21.3k
    pub fn post_return(&self, key: &WorldKey, func: &Function) -> Option<&str> {
1004
863k
        self.find(|m| match m {
1005
2.46k
            Export::WorldFuncPostReturn(k) => k == key,
1006
260k
            Export::InterfaceFuncPostReturn(k, f) => k == key && func.name == *f,
1007
600k
            _ => false,
1008
863k
        })
1009
21.3k
    }
1010
1011
    /// Returns the name of the async callback export, if any, for the `key` and
1012
    /// `func` combo.
1013
21.3k
    pub fn callback(&self, key: &WorldKey, func: &Function) -> Option<&str> {
1014
1.30M
        self.find(|m| match m {
1015
631
            Export::WorldFuncCallback(k) => k == key,
1016
65.6k
            Export::InterfaceFuncCallback(k, f) => k == key && func.name == *f,
1017
1.23M
            _ => false,
1018
1.30M
        })
1019
21.3k
    }
1020
1021
21.3k
    pub fn abi(&self, key: &WorldKey, func: &Function) -> Option<AbiVariant> {
1022
674k
        self.names.values().find_map(|m| match m {
1023
3.24k
            Export::WorldFunc(k, f, abi) if k == key && func.name == *f => Some(*abi),
1024
341k
            Export::InterfaceFunc(k, _, f, abi) if k == key && func.name == *f => Some(*abi),
1025
653k
            _ => None,
1026
674k
        })
1027
21.3k
    }
1028
1029
    /// Returns the realloc that the exported function `interface` and `func`
1030
    /// are using.
1031
21.3k
    pub fn export_realloc_for(&self, key: &WorldKey, func: &Function) -> Option<&str> {
1032
21.3k
        // TODO: This realloc detection should probably be improved with
1033
21.3k
        // some sort of scheme to have per-function reallocs like
1034
21.3k
        // `cabi_realloc_{name}` or something like that.
1035
21.3k
        let _ = (key, func);
1036
1037
1.44M
        if let Some(name) = self.find(|m| matches!(m, Export::GeneralPurposeExportRealloc)) {
1038
0
            return Some(name);
1039
21.3k
        }
1040
21.3k
        self.general_purpose_realloc()
1041
21.3k
    }
1042
1043
    /// Returns the realloc that the imported function `interface` and `func`
1044
    /// are using.
1045
5.66k
    pub fn import_realloc_for(&self, interface: Option<InterfaceId>, func: &str) -> Option<&str> {
1046
5.66k
        // TODO: This realloc detection should probably be improved with
1047
5.66k
        // some sort of scheme to have per-function reallocs like
1048
5.66k
        // `cabi_realloc_{name}` or something like that.
1049
5.66k
        let _ = (interface, func);
1050
1051
118k
        if let Some(name) = self.find(|m| matches!(m, Export::GeneralPurposeImportRealloc)) {
1052
0
            return Some(name);
1053
5.66k
        }
1054
5.66k
        self.general_purpose_realloc()
1055
5.66k
    }
1056
1057
    /// Returns the realloc that the main module is exporting into the adapter.
1058
0
    pub fn realloc_to_import_into_adapter(&self) -> Option<&str> {
1059
0
        if let Some(name) = self.find(|m| matches!(m, Export::ReallocForAdapter)) {
1060
0
            return Some(name);
1061
0
        }
1062
0
        self.general_purpose_realloc()
1063
0
    }
1064
1065
27.0k
    fn general_purpose_realloc(&self) -> Option<&str> {
1066
1.53M
        self.find(|m| matches!(m, Export::GeneralPurposeRealloc))
1067
27.0k
    }
1068
1069
    /// Returns the memory, if exported, for this module.
1070
6.26k
    pub fn memory(&self) -> Option<&str> {
1071
50.8k
        self.find(|m| matches!(m, Export::Memory))
1072
6.26k
    }
1073
1074
    /// Returns the `_initialize` intrinsic, if exported, for this module.
1075
6.26k
    pub fn initialize(&self) -> Option<&str> {
1076
63.3k
        self.find(|m| matches!(m, Export::Initialize))
1077
6.26k
    }
1078
1079
    /// Returns destructor for the exported resource `ty`, if it was listed.
1080
2.67k
    pub fn resource_dtor(&self, ty: TypeId) -> Option<&str> {
1081
116k
        self.find(|m| match m {
1082
7.35k
            Export::ResourceDtor(t) => *t == ty,
1083
109k
            _ => false,
1084
116k
        })
1085
2.67k
    }
1086
1087
    /// NB: this is a linear search and if that's ever a problem this should
1088
    /// build up an inverse map during construction to accelerate it.
1089
133k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
6.17M
        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
1090
1.18k
        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
1090
673k
        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
1090
63.3k
        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
1090
863k
        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
1090
116k
        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
1090
1.44M
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::import_realloc_for::{closure#0}>::{closure#0}
Line
Count
Source
1090
118k
        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
1090
1.53M
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
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
1090
50.8k
        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
1090
1.30M
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
84.1k
        Some(name)
1092
133k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#2}::{closure#0}>
Line
Count
Source
1089
874
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
874
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
874
        Some(name)
1092
874
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::validate::{closure#1}::{closure#0}>
Line
Count
Source
1089
20.5k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
20.5k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
20.5k
        Some(name)
1092
20.5k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::initialize::{closure#0}>
Line
Count
Source
1089
6.26k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
6.26k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
6.26k
        Some(name)
1092
6.26k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::post_return::{closure#0}>
Line
Count
Source
1089
21.3k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
21.3k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
16.5k
        Some(name)
1092
21.3k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::resource_dtor::{closure#0}>
Line
Count
Source
1089
2.67k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
2.67k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
2.67k
        Some(name)
1092
2.67k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::export_realloc_for::{closure#0}>
Line
Count
Source
1089
21.3k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
21.3k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
0
        Some(name)
1092
21.3k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::import_realloc_for::{closure#0}>
Line
Count
Source
1089
5.66k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
5.66k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
0
        Some(name)
1092
5.66k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::general_purpose_realloc::{closure#0}>
Line
Count
Source
1089
27.0k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
27.0k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
27.0k
        Some(name)
1092
27.0k
    }
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
1089
6.26k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
6.26k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
6.26k
        Some(name)
1092
6.26k
    }
<wit_component::validation::ExportMap>::find::<<wit_component::validation::ExportMap>::callback::{closure#0}>
Line
Count
Source
1089
21.3k
    fn find(&self, f: impl Fn(&Export) -> bool) -> Option<&str> {
1090
21.3k
        let (name, _) = self.names.iter().filter(|(_, m)| f(m)).next()?;
1091
3.96k
        Some(name)
1092
21.3k
    }
1093
1094
    /// Iterates over all exports of this module.
1095
9.80k
    pub fn iter(&self) -> impl Iterator<Item = (&str, &Export)> + '_ {
1096
118k
        self.names.iter().map(|(n, e)| (n.as_str(), e))
1097
9.80k
    }
1098
1099
6.26k
    fn validate(&self, encoder: &ComponentEncoder, exports: &IndexSet<WorldKey>) -> Result<()> {
1100
6.26k
        let resolve = &encoder.metadata.resolve;
1101
6.26k
        let world = encoder.metadata.world;
1102
6.26k
        // Multi-memory isn't supported because otherwise we don't know what
1103
6.26k
        // memory to put things in.
1104
6.26k
        if self
1105
6.26k
            .names
1106
6.26k
            .values()
1107
63.3k
            .filter(|m| matches!(m, Export::Memory))
1108
6.26k
            .count()
1109
6.26k
            > 1
1110
        {
1111
0
            bail!("cannot componentize module that exports multiple memories")
1112
6.26k
        }
1113
1114
        // All of `exports` must be exported and found within this module.
1115
16.8k
        for export in exports {
1116
20.5k
            let require_interface_func = |interface: InterfaceId, name: &str| -> Result<()> {
1117
673k
                let result = self.find(|e| match e {
1118
341k
                    Export::InterfaceFunc(_, id, s, _) => interface == *id && name == s,
1119
331k
                    _ => false,
1120
673k
                });
1121
20.5k
                if result.is_some() {
1122
20.5k
                    Ok(())
1123
                } else {
1124
0
                    let export = resolve.name_world_key(export);
1125
0
                    bail!("failed to find export of interface `{export}` function `{name}`")
1126
                }
1127
20.5k
            };
1128
10.5k
            let require_world_func = |name: &str| -> Result<()> {
1129
1.18k
                let result = self.find(|e| match e {
1130
1.03k
                    Export::WorldFunc(_, s, _) => name == s,
1131
151
                    _ => false,
1132
1.18k
                });
1133
874
                if result.is_some() {
1134
874
                    Ok(())
1135
                } else {
1136
0
                    bail!("failed to find export of function `{name}`")
1137
                }
1138
874
            };
1139
10.5k
            match &resolve.worlds[world].exports[export] {
1140
9.72k
                WorldItem::Interface { id, .. } => {
1141
20.5k
                    for (name, _) in resolve.interfaces[*id].functions.iter() {
1142
20.5k
                        require_interface_func(*id, name)?;
1143
                    }
1144
                }
1145
874
                WorldItem::Function(f) => {
1146
874
                    require_world_func(&f.name)?;
1147
                }
1148
0
                WorldItem::Type(_) => unreachable!(),
1149
            }
1150
        }
1151
1152
6.26k
        Ok(())
1153
6.26k
    }
1154
}
1155
1156
/// Trait dispatch and definition for parsing and interpreting "mangled names"
1157
/// which show up in imports and exports of the component model.
1158
///
1159
/// This trait is used to implement classification of imports and exports in the
1160
/// component model. The methods on `ImportMap` and `ExportMap` will use this to
1161
/// determine what an import is and how it's lifted/lowered in the world being
1162
/// bound.
1163
///
1164
/// This trait has a bit of history behind it as well. Before
1165
/// WebAssembly/component-model#378 there was no standard naming scheme for core
1166
/// wasm imports or exports when componenitizing. This meant that
1167
/// `wit-component` implemented a particular scheme which mostly worked but was
1168
/// mostly along the lines of "this at least works" rather than "someone sat
1169
/// down and designed this". Since then, however, an standard naming scheme has
1170
/// now been specified which was indeed designed.
1171
///
1172
/// This trait serves as the bridge between these two. The historical naming
1173
/// scheme is still supported for now through the `Legacy` implementation below
1174
/// and will be for some time. The transition plan at this time is to support
1175
/// the new scheme, eventually get it supported in bindings generators, and once
1176
/// that's all propagated remove support for the legacy scheme.
1177
trait NameMangling {
1178
    fn import_root(&self) -> &str;
1179
    fn import_non_root_prefix(&self) -> &str;
1180
    fn import_exported_intrinsic_prefix(&self) -> &str;
1181
    fn export_memory(&self) -> &str;
1182
    fn export_initialize(&self) -> &str;
1183
    fn export_realloc(&self) -> &str;
1184
    fn resource_drop_name<'a>(&self, s: &'a str) -> Option<&'a str>;
1185
    fn resource_new_name<'a>(&self, s: &'a str) -> Option<&'a str>;
1186
    fn resource_rep_name<'a>(&self, s: &'a str) -> Option<&'a str>;
1187
    fn task_return_name<'a>(&self, s: &'a str) -> Option<&'a str>;
1188
    fn task_backpressure(&self) -> Option<&str>;
1189
    fn task_wait(&self) -> Option<&str>;
1190
    fn task_poll(&self) -> Option<&str>;
1191
    fn task_yield(&self) -> Option<&str>;
1192
    fn subtask_drop(&self) -> Option<&str>;
1193
    fn callback_name<'a>(&self, s: &'a str) -> Option<&'a str>;
1194
    fn async_name<'a>(&self, s: &'a str) -> Option<&'a str>;
1195
    fn async_stackful_name<'a>(&self, s: &'a str) -> Option<&'a str>;
1196
    fn error_context_new(&self, s: &str) -> Option<StringEncoding>;
1197
    fn error_context_debug_message<'a>(&self, s: &'a str) -> Option<(StringEncoding, &'a str)>;
1198
    fn error_context_drop(&self) -> Option<&str>;
1199
    fn payload_import(
1200
        &self,
1201
        module: &str,
1202
        name: &str,
1203
        resolve: &Resolve,
1204
        world: &World,
1205
        ty: &FuncType,
1206
    ) -> Result<Option<Import>>;
1207
    fn module_to_interface(
1208
        &self,
1209
        module: &str,
1210
        resolve: &Resolve,
1211
        items: &IndexMap<WorldKey, WorldItem>,
1212
    ) -> Result<(WorldKey, InterfaceId)>;
1213
    fn strip_post_return<'a>(&self, s: &'a str) -> Option<&'a str>;
1214
    fn match_wit_export<'a>(
1215
        &self,
1216
        export_name: &str,
1217
        resolve: &'a Resolve,
1218
        world: WorldId,
1219
        exports: &'a IndexSet<WorldKey>,
1220
    ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)>;
1221
    fn match_wit_resource_dtor<'a>(
1222
        &self,
1223
        export_name: &str,
1224
        resolve: &'a Resolve,
1225
        world: WorldId,
1226
        exports: &'a IndexSet<WorldKey>,
1227
    ) -> Option<TypeId>;
1228
}
1229
1230
/// Definition of the "standard" naming scheme which currently starts with
1231
/// "cm32p2". Note that wasm64 is not supported at this time.
1232
struct Standard;
1233
1234
const STANDARD: &'static dyn NameMangling = &Standard;
1235
1236
impl NameMangling for Standard {
1237
7.74k
    fn import_root(&self) -> &str {
1238
7.74k
        ""
1239
7.74k
    }
1240
6.12k
    fn import_non_root_prefix(&self) -> &str {
1241
6.12k
        "|"
1242
6.12k
    }
1243
6.12k
    fn import_exported_intrinsic_prefix(&self) -> &str {
1244
6.12k
        "_ex_"
1245
6.12k
    }
1246
1.53k
    fn export_memory(&self) -> &str {
1247
1.53k
        "_memory"
1248
1.53k
    }
1249
15.9k
    fn export_initialize(&self) -> &str {
1250
15.9k
        "_initialize"
1251
15.9k
    }
1252
17.5k
    fn export_realloc(&self) -> &str {
1253
17.5k
        "_realloc"
1254
17.5k
    }
1255
2.91k
    fn resource_drop_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1256
2.91k
        s.strip_suffix("_drop")
1257
2.91k
    }
1258
1.56k
    fn resource_new_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1259
1.56k
        s.strip_suffix("_new")
1260
1.56k
    }
1261
783
    fn resource_rep_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1262
783
        s.strip_suffix("_rep")
1263
783
    }
1264
3.96k
    fn task_return_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1265
3.96k
        _ = s;
1266
3.96k
        None
1267
3.96k
    }
1268
1.61k
    fn task_backpressure(&self) -> Option<&str> {
1269
1.61k
        None
1270
1.61k
    }
1271
1.61k
    fn task_wait(&self) -> Option<&str> {
1272
1.61k
        None
1273
1.61k
    }
1274
1.61k
    fn task_poll(&self) -> Option<&str> {
1275
1.61k
        None
1276
1.61k
    }
1277
1.61k
    fn task_yield(&self) -> Option<&str> {
1278
1.61k
        None
1279
1.61k
    }
1280
1.61k
    fn subtask_drop(&self) -> Option<&str> {
1281
1.61k
        None
1282
1.61k
    }
1283
783
    fn callback_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1284
783
        _ = s;
1285
783
        None
1286
783
    }
1287
22.2k
    fn async_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1288
22.2k
        _ = s;
1289
22.2k
        None
1290
22.2k
    }
1291
14.4k
    fn async_stackful_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1292
14.4k
        _ = s;
1293
14.4k
        None
1294
14.4k
    }
1295
1.61k
    fn error_context_new(&self, s: &str) -> Option<StringEncoding> {
1296
1.61k
        _ = s;
1297
1.61k
        None
1298
1.61k
    }
1299
1.61k
    fn error_context_debug_message<'a>(&self, s: &'a str) -> Option<(StringEncoding, &'a str)> {
1300
1.61k
        _ = s;
1301
1.61k
        None
1302
1.61k
    }
1303
1.61k
    fn error_context_drop(&self) -> Option<&str> {
1304
1.61k
        None
1305
1.61k
    }
1306
7.74k
    fn payload_import(
1307
7.74k
        &self,
1308
7.74k
        module: &str,
1309
7.74k
        name: &str,
1310
7.74k
        resolve: &Resolve,
1311
7.74k
        world: &World,
1312
7.74k
        ty: &FuncType,
1313
7.74k
    ) -> Result<Option<Import>> {
1314
7.74k
        _ = (module, name, resolve, world, ty);
1315
7.74k
        Ok(None)
1316
7.74k
    }
1317
8.47k
    fn module_to_interface(
1318
8.47k
        &self,
1319
8.47k
        interface: &str,
1320
8.47k
        resolve: &Resolve,
1321
8.47k
        items: &IndexMap<WorldKey, WorldItem>,
1322
8.47k
    ) -> Result<(WorldKey, InterfaceId)> {
1323
23.5k
        for (key, item) in items.iter() {
1324
23.5k
            let id = match key {
1325
                // Bare keys are matched exactly against `interface`
1326
19.7k
                WorldKey::Name(name) => match item {
1327
15.5k
                    WorldItem::Interface { id, .. } if name == interface => *id,
1328
14.2k
                    _ => continue,
1329
                },
1330
                // ID-identified keys are matched with their "canonical name"
1331
3.79k
                WorldKey::Interface(id) => {
1332
3.79k
                    if resolve.canonicalized_id_of(*id).as_deref() != Some(interface) {
1333
825
                        continue;
1334
2.96k
                    }
1335
2.96k
                    *id
1336
                }
1337
            };
1338
8.47k
            return Ok((key.clone(), id));
1339
        }
1340
0
        bail!("failed to find world item corresponding to interface `{interface}`")
1341
8.47k
    }
1342
7.62k
    fn strip_post_return<'a>(&self, s: &'a str) -> Option<&'a str> {
1343
7.62k
        s.strip_suffix("_post")
1344
7.62k
    }
1345
21.3k
    fn match_wit_export<'a>(
1346
21.3k
        &self,
1347
21.3k
        export_name: &str,
1348
21.3k
        resolve: &'a Resolve,
1349
21.3k
        world: WorldId,
1350
21.3k
        exports: &'a IndexSet<WorldKey>,
1351
21.3k
    ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)> {
1352
21.3k
        if let Some(world_export_name) = export_name.strip_prefix("||") {
1353
699
            let key = exports.get(&WorldKey::Name(world_export_name.to_string()))?;
1354
466
            match &resolve.worlds[world].exports[key] {
1355
466
                WorldItem::Function(f) => return Some((key, None, f)),
1356
0
                _ => return None,
1357
            }
1358
20.6k
        }
1359
1360
20.6k
        let (key, id, func_name) =
1361
20.6k
            self.match_wit_interface(export_name, resolve, world, exports)?;
1362
20.6k
        let func = resolve.interfaces[id].functions.get(func_name)?;
1363
13.2k
        Some((key, Some(id), func))
1364
21.3k
    }
1365
1366
783
    fn match_wit_resource_dtor<'a>(
1367
783
        &self,
1368
783
        export_name: &str,
1369
783
        resolve: &'a Resolve,
1370
783
        world: WorldId,
1371
783
        exports: &'a IndexSet<WorldKey>,
1372
783
    ) -> Option<TypeId> {
1373
783
        let (_key, id, name) =
1374
783
            self.match_wit_interface(export_name.strip_suffix("_dtor")?, resolve, world, exports)?;
1375
783
        let ty = *resolve.interfaces[id].types.get(name)?;
1376
783
        match resolve.types[ty].kind {
1377
783
            TypeDefKind::Resource => Some(ty),
1378
0
            _ => None,
1379
        }
1380
783
    }
1381
}
1382
1383
impl Standard {
1384
21.3k
    fn match_wit_interface<'a, 'b>(
1385
21.3k
        &self,
1386
21.3k
        export_name: &'b str,
1387
21.3k
        resolve: &'a Resolve,
1388
21.3k
        world: WorldId,
1389
21.3k
        exports: &'a IndexSet<WorldKey>,
1390
21.3k
    ) -> Option<(&'a WorldKey, InterfaceId, &'b str)> {
1391
21.3k
        let world = &resolve.worlds[world];
1392
21.3k
        let export_name = export_name.strip_prefix("|")?;
1393
1394
65.5k
        for export in exports {
1395
65.5k
            let id = match &world.exports[export] {
1396
62.7k
                WorldItem::Interface { id, .. } => *id,
1397
2.87k
                WorldItem::Function(_) => continue,
1398
0
                WorldItem::Type(_) => unreachable!(),
1399
            };
1400
62.7k
            let remaining = match export {
1401
60.9k
                WorldKey::Name(name) => export_name.strip_prefix(name),
1402
                WorldKey::Interface(_) => {
1403
1.77k
                    let prefix = resolve.canonicalized_id_of(id).unwrap();
1404
1.77k
                    export_name.strip_prefix(&prefix)
1405
                }
1406
            };
1407
62.7k
            let item_name = match remaining.and_then(|s| s.strip_prefix("|")) {
1408
21.3k
                Some(name) => name,
1409
41.3k
                None => continue,
1410
            };
1411
21.3k
            return Some((export, id, item_name));
1412
        }
1413
1414
0
        None
1415
21.3k
    }
1416
}
1417
1418
/// Definition of wit-component's "legacy" naming scheme which predates
1419
/// WebAssembly/component-model#378.
1420
struct Legacy;
1421
1422
const LEGACY: &'static dyn NameMangling = &Legacy;
1423
1424
impl NameMangling for Legacy {
1425
20.1k
    fn import_root(&self) -> &str {
1426
20.1k
        "$root"
1427
20.1k
    }
1428
15.9k
    fn import_non_root_prefix(&self) -> &str {
1429
15.9k
        ""
1430
15.9k
    }
1431
15.9k
    fn import_exported_intrinsic_prefix(&self) -> &str {
1432
15.9k
        "[export]"
1433
15.9k
    }
1434
4.73k
    fn export_memory(&self) -> &str {
1435
4.73k
        "memory"
1436
4.73k
    }
1437
34.8k
    fn export_initialize(&self) -> &str {
1438
34.8k
        "_initialize"
1439
34.8k
    }
1440
39.5k
    fn export_realloc(&self) -> &str {
1441
39.5k
        "cabi_realloc"
1442
39.5k
    }
1443
7.20k
    fn resource_drop_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1444
7.20k
        s.strip_prefix("[resource-drop]")
1445
7.20k
    }
1446
3.77k
    fn resource_new_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1447
3.77k
        s.strip_prefix("[resource-new]")
1448
3.77k
    }
1449
1.88k
    fn resource_rep_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1450
1.88k
        s.strip_prefix("[resource-rep]")
1451
1.88k
    }
1452
9.85k
    fn task_return_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1453
9.85k
        s.strip_prefix("[task-return]")
1454
9.85k
    }
1455
4.19k
    fn task_backpressure(&self) -> Option<&str> {
1456
4.19k
        Some("[task-backpressure]")
1457
4.19k
    }
1458
4.19k
    fn task_wait(&self) -> Option<&str> {
1459
4.19k
        Some("[task-wait]")
1460
4.19k
    }
1461
4.19k
    fn task_poll(&self) -> Option<&str> {
1462
4.19k
        Some("[task-poll]")
1463
4.19k
    }
1464
4.19k
    fn task_yield(&self) -> Option<&str> {
1465
4.19k
        Some("[task-yield]")
1466
4.19k
    }
1467
4.19k
    fn subtask_drop(&self) -> Option<&str> {
1468
4.19k
        Some("[subtask-drop]")
1469
4.19k
    }
1470
5.85k
    fn callback_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1471
5.85k
        s.strip_prefix("[callback][async]")
1472
5.85k
    }
1473
50.2k
    fn async_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1474
50.2k
        s.strip_prefix("[async]")
1475
50.2k
    }
1476
25.6k
    fn async_stackful_name<'a>(&self, s: &'a str) -> Option<&'a str> {
1477
25.6k
        s.strip_prefix("[async-stackful]")
1478
25.6k
    }
1479
4.19k
    fn error_context_new(&self, s: &str) -> Option<StringEncoding> {
1480
4.19k
        parse_encoding(
1481
4.19k
            s.strip_prefix("[error-context-new;encoding=")?
1482
0
                .strip_suffix("]")?,
1483
        )
1484
4.19k
    }
1485
4.19k
    fn error_context_debug_message<'a>(&self, s: &'a str) -> Option<(StringEncoding, &'a str)> {
1486
4.19k
        let mut suffix = s.strip_prefix("[error-context-debug-message;")?;
1487
0
        let mut encoding = None;
1488
0
        let mut realloc = None;
1489
        loop {
1490
0
            if let Some(index) = suffix.find(';').or_else(|| suffix.find(']')) {
1491
0
                if let Some(suffix) = suffix[..index].strip_prefix("encoding=") {
1492
0
                    if encoding.is_some() {
1493
0
                        return None;
1494
0
                    }
1495
0
                    encoding = parse_encoding(suffix)
1496
0
                } else if let Some(suffix) = suffix[..index].strip_prefix("realloc=") {
1497
0
                    if realloc.is_some() {
1498
0
                        return None;
1499
0
                    }
1500
0
                    realloc = Some(suffix);
1501
                } else {
1502
0
                    return None;
1503
                }
1504
0
                suffix = &suffix[index + 1..];
1505
            } else {
1506
0
                break;
1507
0
            }
1508
0
        }
1509
0
        Some((encoding?, realloc?))
1510
4.19k
    }
1511
4.19k
    fn error_context_drop(&self) -> Option<&str> {
1512
4.19k
        Some("[error-context-drop]")
1513
4.19k
    }
1514
20.1k
    fn payload_import(
1515
20.1k
        &self,
1516
20.1k
        module: &str,
1517
20.1k
        name: &str,
1518
20.1k
        resolve: &Resolve,
1519
20.1k
        world: &World,
1520
20.1k
        ty: &FuncType,
1521
20.1k
    ) -> Result<Option<Import>> {
1522
        Ok(
1523
20.1k
            if let Some((suffix, imported)) = module
1524
20.1k
                .strip_prefix("[import-payload]")
1525
20.1k
                .map(|v| (v, true))
1526
20.1k
                .or_else(|| module.strip_prefix("[export-payload]").map(|v| (v, false)))
1527
            {
1528
0
                let (key, interface) = if suffix == self.import_root() {
1529
0
                    (WorldKey::Name(name.to_string()), None)
1530
                } else {
1531
0
                    let (key, id) = self.module_to_interface(
1532
0
                        suffix,
1533
0
                        resolve,
1534
0
                        if imported {
1535
0
                            &world.imports
1536
                        } else {
1537
0
                            &world.exports
1538
                        },
1539
0
                    )?;
1540
0
                    (key, Some(id))
1541
                };
1542
1543
0
                let orig_name = name;
1544
1545
0
                let (name, async_) = if let Some(name) = self.async_name(name) {
1546
0
                    (name, true)
1547
                } else {
1548
0
                    (name, false)
1549
                };
1550
1551
0
                let info = |payload_key| {
1552
0
                    let (function, ty) = get_future_or_stream_type(
1553
0
                        resolve,
1554
0
                        world,
1555
0
                        &payload_key,
1556
0
                        interface,
1557
0
                        imported,
1558
0
                    )?;
1559
0
                    Ok::<_, anyhow::Error>(PayloadInfo {
1560
0
                        name: orig_name.to_string(),
1561
0
                        ty,
1562
0
                        function,
1563
0
                        key: key.clone(),
1564
0
                        interface,
1565
0
                        imported,
1566
0
                    })
1567
0
                };
1568
1569
                Some(
1570
0
                    if let Some(key) = match_payload_prefix(name, "[future-new-") {
1571
0
                        if async_ {
1572
0
                            bail!("async `future.new` calls not supported");
1573
0
                        }
1574
0
                        validate_func_sig(name, &FuncType::new([], [ValType::I32]), ty)?;
1575
0
                        Import::FutureNew(info(key)?)
1576
0
                    } else if let Some(key) = match_payload_prefix(name, "[future-write-") {
1577
0
                        validate_func_sig(
1578
0
                            name,
1579
0
                            &FuncType::new([ValType::I32; 2], [ValType::I32]),
1580
0
                            ty,
1581
0
                        )?;
1582
                        Import::FutureWrite {
1583
0
                            async_,
1584
0
                            info: info(key)?,
1585
                        }
1586
0
                    } else if let Some(key) = match_payload_prefix(name, "[future-read-") {
1587
0
                        validate_func_sig(
1588
0
                            name,
1589
0
                            &FuncType::new([ValType::I32; 2], [ValType::I32]),
1590
0
                            ty,
1591
0
                        )?;
1592
                        Import::FutureRead {
1593
0
                            async_,
1594
0
                            info: info(key)?,
1595
                        }
1596
0
                    } else if let Some(key) = match_payload_prefix(name, "[future-cancel-write-") {
1597
0
                        validate_func_sig(
1598
0
                            name,
1599
0
                            &FuncType::new([ValType::I32], [ValType::I32]),
1600
0
                            ty,
1601
0
                        )?;
1602
0
                        let info = info(key)?;
1603
0
                        Import::FutureCancelWrite {
1604
0
                            async_,
1605
0
                            ty: info.ty,
1606
0
                            imported: info.imported,
1607
0
                        }
1608
0
                    } else if let Some(key) = match_payload_prefix(name, "[future-cancel-read-") {
1609
0
                        validate_func_sig(
1610
0
                            name,
1611
0
                            &FuncType::new([ValType::I32], [ValType::I32]),
1612
0
                            ty,
1613
0
                        )?;
1614
0
                        let info = info(key)?;
1615
0
                        Import::FutureCancelRead {
1616
0
                            async_,
1617
0
                            ty: info.ty,
1618
0
                            imported: info.imported,
1619
0
                        }
1620
0
                    } else if let Some(key) = match_payload_prefix(name, "[future-close-writable-")
1621
                    {
1622
0
                        if async_ {
1623
0
                            bail!("async `future.close-writable` calls not supported");
1624
0
                        }
1625
0
                        validate_func_sig(name, &FuncType::new([ValType::I32; 2], []), ty)?;
1626
0
                        let info = info(key)?;
1627
0
                        Import::FutureCloseWritable {
1628
0
                            ty: info.ty,
1629
0
                            imported: info.imported,
1630
0
                        }
1631
0
                    } else if let Some(key) = match_payload_prefix(name, "[future-close-readable-")
1632
                    {
1633
0
                        if async_ {
1634
0
                            bail!("async `future.close-readable` calls not supported");
1635
0
                        }
1636
0
                        validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?;
1637
0
                        let info = info(key)?;
1638
0
                        Import::FutureCloseReadable {
1639
0
                            ty: info.ty,
1640
0
                            imported: info.imported,
1641
0
                        }
1642
0
                    } else if let Some(key) = match_payload_prefix(name, "[stream-new-") {
1643
0
                        if async_ {
1644
0
                            bail!("async `stream.new` calls not supported");
1645
0
                        }
1646
0
                        validate_func_sig(name, &FuncType::new([], [ValType::I32]), ty)?;
1647
0
                        Import::StreamNew(info(key)?)
1648
0
                    } else if let Some(key) = match_payload_prefix(name, "[stream-write-") {
1649
0
                        validate_func_sig(
1650
0
                            name,
1651
0
                            &FuncType::new([ValType::I32; 3], [ValType::I32]),
1652
0
                            ty,
1653
0
                        )?;
1654
                        Import::StreamWrite {
1655
0
                            async_,
1656
0
                            info: info(key)?,
1657
                        }
1658
0
                    } else if let Some(key) = match_payload_prefix(name, "[stream-read-") {
1659
0
                        validate_func_sig(
1660
0
                            name,
1661
0
                            &FuncType::new([ValType::I32; 3], [ValType::I32]),
1662
0
                            ty,
1663
0
                        )?;
1664
                        Import::StreamRead {
1665
0
                            async_,
1666
0
                            info: info(key)?,
1667
                        }
1668
0
                    } else if let Some(key) = match_payload_prefix(name, "[stream-cancel-write-") {
1669
0
                        validate_func_sig(
1670
0
                            name,
1671
0
                            &FuncType::new([ValType::I32], [ValType::I32]),
1672
0
                            ty,
1673
0
                        )?;
1674
0
                        let info = info(key)?;
1675
0
                        Import::StreamCancelWrite {
1676
0
                            async_,
1677
0
                            ty: info.ty,
1678
0
                            imported: info.imported,
1679
0
                        }
1680
0
                    } else if let Some(key) = match_payload_prefix(name, "[stream-cancel-read-") {
1681
0
                        validate_func_sig(
1682
0
                            name,
1683
0
                            &FuncType::new([ValType::I32], [ValType::I32]),
1684
0
                            ty,
1685
0
                        )?;
1686
0
                        let info = info(key)?;
1687
0
                        Import::StreamCancelRead {
1688
0
                            async_,
1689
0
                            ty: info.ty,
1690
0
                            imported: info.imported,
1691
0
                        }
1692
0
                    } else if let Some(key) = match_payload_prefix(name, "[stream-close-writable-")
1693
                    {
1694
0
                        if async_ {
1695
0
                            bail!("async `stream.close-writable` calls not supported");
1696
0
                        }
1697
0
                        validate_func_sig(name, &FuncType::new([ValType::I32; 2], []), ty)?;
1698
0
                        let info = info(key)?;
1699
0
                        Import::StreamCloseWritable {
1700
0
                            ty: info.ty,
1701
0
                            imported: info.imported,
1702
0
                        }
1703
0
                    } else if let Some(key) = match_payload_prefix(name, "[stream-close-readable-")
1704
                    {
1705
0
                        if async_ {
1706
0
                            bail!("async `stream.close-readable` calls not supported");
1707
0
                        }
1708
0
                        validate_func_sig(name, &FuncType::new([ValType::I32], []), ty)?;
1709
0
                        let info = info(key)?;
1710
0
                        Import::StreamCloseReadable {
1711
0
                            ty: info.ty,
1712
0
                            imported: info.imported,
1713
0
                        }
1714
                    } else {
1715
0
                        bail!("unrecognized payload import: {name}");
1716
                    },
1717
                )
1718
            } else {
1719
20.1k
                None
1720
            },
1721
        )
1722
20.1k
    }
1723
21.6k
    fn module_to_interface(
1724
21.6k
        &self,
1725
21.6k
        module: &str,
1726
21.6k
        resolve: &Resolve,
1727
21.6k
        items: &IndexMap<WorldKey, WorldItem>,
1728
21.6k
    ) -> Result<(WorldKey, InterfaceId)> {
1729
21.6k
        // First see if this is a bare name
1730
21.6k
        let bare_name = WorldKey::Name(module.to_string());
1731
21.6k
        if let Some(WorldItem::Interface { id, .. }) = items.get(&bare_name) {
1732
12.7k
            return Ok((bare_name, *id));
1733
8.82k
        }
1734
8.82k
1735
8.82k
        // ... and if this isn't a bare name then it's time to do some parsing
1736
8.82k
        // related to interfaces, versions, and such. First up the `module` name
1737
8.82k
        // is parsed as a normal component name from `wasmparser` to see if it's
1738
8.82k
        // of the "interface kind". If it's not then that means the above match
1739
8.82k
        // should have been a hit but it wasn't, so an error is returned.
1740
8.82k
        let kebab_name = ComponentName::new(module, 0);
1741
8.82k
        let name = match kebab_name.as_ref().map(|k| k.kind()) {
1742
8.82k
            Ok(ComponentNameKind::Interface(name)) => name,
1743
0
            _ => bail!("module requires an import interface named `{module}`"),
1744
        };
1745
1746
        // Prioritize an exact match based on versions, so try that first.
1747
8.82k
        let pkgname = PackageName {
1748
8.82k
            namespace: name.namespace().to_string(),
1749
8.82k
            name: name.package().to_string(),
1750
8.82k
            version: name.version(),
1751
8.82k
        };
1752
8.82k
        if let Some(pkg) = resolve.package_names.get(&pkgname) {
1753
8.82k
            if let Some(id) = resolve.packages[*pkg]
1754
8.82k
                .interfaces
1755
8.82k
                .get(name.interface().as_str())
1756
            {
1757
8.82k
                let key = WorldKey::Interface(*id);
1758
8.82k
                if items.contains_key(&key) {
1759
8.82k
                    return Ok((key, *id));
1760
0
                }
1761
0
            }
1762
0
        }
1763
1764
        // If an exact match wasn't found then instead search for the first
1765
        // match based on versions. This means that a core wasm import for
1766
        // "1.2.3" might end up matching an interface at "1.2.4", for example.
1767
        // (or "1.2.2", depending on what's available).
1768
0
        for (key, _) in items {
1769
0
            let id = match key {
1770
0
                WorldKey::Interface(id) => *id,
1771
0
                WorldKey::Name(_) => continue,
1772
            };
1773
            // Make sure the interface names match
1774
0
            let interface = &resolve.interfaces[id];
1775
0
            if interface.name.as_ref().unwrap() != name.interface().as_str() {
1776
0
                continue;
1777
0
            }
1778
0
1779
0
            // Make sure the package name (without version) matches
1780
0
            let pkg = &resolve.packages[interface.package.unwrap()];
1781
0
            if pkg.name.namespace != pkgname.namespace || pkg.name.name != pkgname.name {
1782
0
                continue;
1783
0
            }
1784
1785
0
            let module_version = match name.version() {
1786
0
                Some(version) => version,
1787
0
                None => continue,
1788
            };
1789
0
            let pkg_version = match &pkg.name.version {
1790
0
                Some(version) => version,
1791
0
                None => continue,
1792
            };
1793
1794
            // Test if the two semver versions are compatible
1795
0
            let module_compat = PackageName::version_compat_track(&module_version);
1796
0
            let pkg_compat = PackageName::version_compat_track(pkg_version);
1797
0
            if module_compat == pkg_compat {
1798
0
                return Ok((key.clone(), id));
1799
0
            }
1800
        }
1801
1802
0
        bail!("module requires an import interface named `{module}`")
1803
21.6k
    }
1804
15.5k
    fn strip_post_return<'a>(&self, s: &'a str) -> Option<&'a str> {
1805
15.5k
        s.strip_prefix("cabi_post_")
1806
15.5k
    }
1807
43.7k
    fn match_wit_export<'a>(
1808
43.7k
        &self,
1809
43.7k
        export_name: &str,
1810
43.7k
        resolve: &'a Resolve,
1811
43.7k
        world: WorldId,
1812
43.7k
        exports: &'a IndexSet<WorldKey>,
1813
43.7k
    ) -> Option<(&'a WorldKey, Option<InterfaceId>, &'a Function)> {
1814
43.7k
        let world = &resolve.worlds[world];
1815
165k
        for name in exports {
1816
149k
            match &world.exports[name] {
1817
6.32k
                WorldItem::Function(f) => {
1818
6.32k
                    if f.legacy_core_export_name(None) == export_name {
1819
1.24k
                        return Some((name, None, f));
1820
5.08k
                    }
1821
                }
1822
143k
                WorldItem::Interface { id, .. } => {
1823
143k
                    let string = resolve.name_world_key(name);
1824
916k
                    for (_, func) in resolve.interfaces[*id].functions.iter() {
1825
916k
                        if func.legacy_core_export_name(Some(&string)) == export_name {
1826
26.9k
                            return Some((name, Some(*id), func));
1827
889k
                        }
1828
                    }
1829
                }
1830
1831
0
                WorldItem::Type(_) => unreachable!(),
1832
            }
1833
        }
1834
1835
15.5k
        None
1836
43.7k
    }
1837
1838
1.88k
    fn match_wit_resource_dtor<'a>(
1839
1.88k
        &self,
1840
1.88k
        export_name: &str,
1841
1.88k
        resolve: &'a Resolve,
1842
1.88k
        world: WorldId,
1843
1.88k
        exports: &'a IndexSet<WorldKey>,
1844
1.88k
    ) -> Option<TypeId> {
1845
1.88k
        let world = &resolve.worlds[world];
1846
5.90k
        for name in exports {
1847
5.90k
            let id = match &world.exports[name] {
1848
5.65k
                WorldItem::Interface { id, .. } => *id,
1849
257
                WorldItem::Function(_) => continue,
1850
0
                WorldItem::Type(_) => unreachable!(),
1851
            };
1852
5.65k
            let name = resolve.name_world_key(name);
1853
5.65k
            let resource = match export_name
1854
5.65k
                .strip_prefix(&name)
1855
5.65k
                .and_then(|s| s.strip_prefix("#[dtor]"))
1856
5.65k
                .and_then(|r| resolve.interfaces[id].types.get(r))
1857
            {
1858
1.88k
                Some(id) => *id,
1859
3.76k
                None => continue,
1860
            };
1861
1862
1.88k
            match resolve.types[resource].kind {
1863
1.88k
                TypeDefKind::Resource => {}
1864
0
                _ => continue,
1865
            }
1866
1867
1.88k
            return Some(resource);
1868
        }
1869
1870
0
        None
1871
1.88k
    }
1872
}
1873
1874
/// This function validates the following:
1875
///
1876
/// * The `bytes` represent a valid core WebAssembly module.
1877
/// * The module's imports are all satisfied by the given `imports` interfaces
1878
///   or the `adapters` set.
1879
/// * The given default and exported interfaces are satisfied by the module's
1880
///   exports.
1881
///
1882
/// The `ValidatedModule` return value contains the metadata which describes the
1883
/// input module on success. This is then further used to generate a component
1884
/// for this module.
1885
6.26k
pub fn validate_module(encoder: &ComponentEncoder, bytes: &[u8]) -> Result<ValidatedModule> {
1886
6.26k
    ValidatedModule::new(encoder, bytes, &encoder.main_module_exports, None)
1887
6.26k
}
1888
1889
/// This function will validate the `bytes` provided as a wasm adapter module.
1890
/// Notably this will validate the wasm module itself in addition to ensuring
1891
/// that it has the "shape" of an adapter module. Current constraints are:
1892
///
1893
/// * The adapter module can import only one memory
1894
/// * The adapter module can only import from the name of `interface` specified,
1895
///   and all function imports must match the `required` types which correspond
1896
///   to the lowered types of the functions in `interface`.
1897
///
1898
/// The wasm module passed into this function is the output of the GC pass of an
1899
/// adapter module's original source. This means that the adapter module is
1900
/// already minimized and this is a double-check that the minimization pass
1901
/// didn't accidentally break the wasm module.
1902
///
1903
/// If `is_library` is true, we waive some of the constraints described above,
1904
/// allowing the module to import tables and globals, as well as import
1905
/// functions at the world level, not just at the interface level.
1906
0
pub fn validate_adapter_module(
1907
0
    encoder: &ComponentEncoder,
1908
0
    bytes: &[u8],
1909
0
    required_by_import: &IndexMap<String, FuncType>,
1910
0
    exports: &IndexSet<WorldKey>,
1911
0
    library_info: Option<&LibraryInfo>,
1912
0
) -> Result<ValidatedModule> {
1913
0
    let ret = ValidatedModule::new(encoder, bytes, exports, library_info)?;
1914
1915
0
    for (name, required_ty) in required_by_import {
1916
0
        let actual = match ret.exports.raw_exports.get(name) {
1917
0
            Some(ty) => ty,
1918
0
            None => bail!("adapter module did not export `{name}`"),
1919
        };
1920
0
        validate_func_sig(name, required_ty, &actual)?;
1921
    }
1922
1923
0
    Ok(ret)
1924
0
}
1925
1926
22.0k
fn resource_test_for_interface<'a>(
1927
22.0k
    resolve: &'a Resolve,
1928
22.0k
    id: InterfaceId,
1929
22.0k
) -> impl Fn(&str) -> Option<TypeId> + 'a {
1930
22.0k
    let interface = &resolve.interfaces[id];
1931
9.29k
    move |name: &str| {
1932
9.29k
        let ty = match interface.types.get(name) {
1933
9.29k
            Some(ty) => *ty,
1934
0
            None => return None,
1935
        };
1936
9.29k
        if matches!(resolve.types[ty].kind, TypeDefKind::Resource) {
1937
9.29k
            Some(ty)
1938
        } else {
1939
0
            None
1940
        }
1941
9.29k
    }
1942
22.0k
}
1943
1944
820
fn resource_test_for_world<'a>(
1945
820
    resolve: &'a Resolve,
1946
820
    id: WorldId,
1947
820
) -> impl Fn(&str) -> Option<TypeId> + 'a {
1948
820
    let world = &resolve.worlds[id];
1949
820
    move |name: &str| match world.imports.get(&WorldKey::Name(name.to_string()))? {
1950
820
        WorldItem::Type(r) => {
1951
820
            if matches!(resolve.types[*r].kind, TypeDefKind::Resource) {
1952
820
                Some(*r)
1953
            } else {
1954
0
                None
1955
            }
1956
        }
1957
0
        _ => None,
1958
820
    }
1959
820
}
1960
1961
39.1k
fn validate_func(
1962
39.1k
    resolve: &Resolve,
1963
39.1k
    ty: &wasmparser::FuncType,
1964
39.1k
    func: &Function,
1965
39.1k
    abi: AbiVariant,
1966
39.1k
) -> Result<()> {
1967
39.1k
    validate_func_sig(
1968
39.1k
        &func.name,
1969
39.1k
        &wasm_sig_to_func_type(resolve.wasm_signature(abi, func)),
1970
39.1k
        ty,
1971
39.1k
    )
1972
39.1k
}
1973
1974
16.5k
fn validate_post_return(
1975
16.5k
    resolve: &Resolve,
1976
16.5k
    ty: &wasmparser::FuncType,
1977
16.5k
    func: &Function,
1978
16.5k
) -> Result<()> {
1979
16.5k
    // The expected signature of a post-return function is to take all the
1980
16.5k
    // parameters that are returned by the guest function and then return no
1981
16.5k
    // results. Model this by calculating the signature of `func` and then
1982
16.5k
    // moving its results into the parameters list while emptying out the
1983
16.5k
    // results.
1984
16.5k
    let mut sig = resolve.wasm_signature(AbiVariant::GuestExport, func);
1985
16.5k
    sig.params = mem::take(&mut sig.results);
1986
16.5k
    validate_func_sig(
1987
16.5k
        &format!("{} post-return", func.name),
1988
16.5k
        &wasm_sig_to_func_type(sig),
1989
16.5k
        ty,
1990
16.5k
    )
1991
16.5k
}
1992
1993
84.9k
fn validate_func_sig(name: &str, expected: &FuncType, ty: &wasmparser::FuncType) -> Result<()> {
1994
84.9k
    if ty != expected {
1995
0
        bail!(
1996
0
            "type mismatch for function `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`",
1997
0
            name,
1998
0
            expected.params(),
1999
0
            expected.results(),
2000
0
            ty.params(),
2001
0
            ty.results()
2002
0
        );
2003
84.9k
    }
2004
84.9k
2005
84.9k
    Ok(())
2006
84.9k
}
2007
2008
0
fn match_payload_prefix(name: &str, prefix: &str) -> Option<(String, usize)> {
2009
0
    let suffix = name.strip_prefix(prefix)?;
2010
0
    let index = suffix.find(']')?;
2011
    Some((
2012
0
        suffix[index + 1..].to_owned(),
2013
0
        suffix[..index].parse().ok()?,
2014
    ))
2015
0
}
2016
2017
/// Retrieve the specified function from the specified world or interface, along
2018
/// with the future or stream type at the specified index.
2019
///
2020
/// The index refers to the entry in the list returned by
2021
/// `Function::find_futures_and_streams`.
2022
0
fn get_future_or_stream_type(
2023
0
    resolve: &Resolve,
2024
0
    world: &World,
2025
0
    (name, index): &(String, usize),
2026
0
    interface: Option<InterfaceId>,
2027
0
    imported: bool,
2028
0
) -> Result<(Function, TypeId)> {
2029
0
    let function = get_function(resolve, world, name, interface, imported)?;
2030
0
    let ty = function.find_futures_and_streams(resolve)[*index];
2031
0
    Ok((function, ty))
2032
0
}
2033
2034
0
fn get_function(
2035
0
    resolve: &Resolve,
2036
0
    world: &World,
2037
0
    name: &str,
2038
0
    interface: Option<InterfaceId>,
2039
0
    imported: bool,
2040
0
) -> Result<Function> {
2041
0
    let function = if let Some(id) = interface {
2042
0
        resolve.interfaces[id]
2043
0
            .functions
2044
0
            .get(name)
2045
0
            .cloned()
2046
0
            .map(WorldItem::Function)
2047
0
    } else if imported {
2048
0
        world
2049
0
            .imports
2050
0
            .get(&WorldKey::Name(name.to_string()))
2051
0
            .cloned()
2052
    } else {
2053
0
        world
2054
0
            .exports
2055
0
            .get(&WorldKey::Name(name.to_string()))
2056
0
            .cloned()
2057
    };
2058
0
    let Some(WorldItem::Function(function)) = function else {
2059
0
        bail!("no export `{name}` found");
2060
    };
2061
0
    Ok(function)
2062
0
}
2063
2064
0
fn parse_encoding(s: &str) -> Option<StringEncoding> {
2065
0
    match s {
2066
0
        "utf8" => Some(StringEncoding::UTF8),
2067
0
        "utf16" => Some(StringEncoding::UTF16),
2068
0
        "compact-utf16" => Some(StringEncoding::CompactUTF16),
2069
0
        _ => None,
2070
    }
2071
0
}