Coverage Report

Created: 2026-10-12 08:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wit-component/src/encoding/fixup.rs
Line
Count
Source
1
use crate::ComponentEncoder;
2
use crate::encoding::{CustomModule, EncodingState, Shim, ShimKind, Shims};
3
use crate::validation::Export;
4
use anyhow::{Result, bail};
5
use indexmap::IndexMap;
6
use std::collections::HashMap;
7
use std::mem;
8
use wasm_encoder::*;
9
use wit_parser::WorldItem;
10
use wit_parser::abi::{AbiVariant, WasmSignature};
11
12
#[derive(Default)]
13
pub struct FixupModule {
14
    // Incrementally-built-up sections of the module-to-be.
15
    types: TypeSection,
16
    imports: ImportSection,
17
    function_names: NameMap,
18
    global_names: NameMap,
19
    elements: ElementSection,
20
    data: DataSection,
21
22
    /// Interning map for type signatures.
23
    type_map: HashMap<(Vec<ValType>, Vec<ValType>), u32>,
24
25
    /// The start function, if needed. This is split between "ABI details" and
26
    /// "user funcs" where ABI details must happen first and user funcs need to
27
    /// be wrapped in task intrinsics if provided.
28
    start_abi_details: Vec<StartAction>,
29
    start_user_funcs: Vec<StartAction>,
30
31
    /// Entries used to initialize the imported shim's table, if necessary.
32
    shims_in_table: Vec<ShimFill>,
33
34
    /// Entries to go into a declared element segment.
35
    declared_funcs: Vec<ShimFill>,
36
37
    /// The instance names that are imported into this module, and what those
38
    /// instances correspond to.
39
    imported_instances: IndexMap<String, ImportedInstance>,
40
41
    /// Interning map for imports.
42
    imported_items: HashMap<(String, String), (u32, EntityType)>,
43
44
    /// Functions that will be defined in this module itself.
45
    ///
46
    /// This is deferred to get emitted until the very end so all imports have
47
    /// been settled and then these import indices can be known.
48
    defined_functions: Vec<(u32, DefinedFunction)>,
49
50
    // Current counters for index spaces.
51
    funcs: u32,
52
    globals: u32,
53
    tables: u32,
54
    memories: u32,
55
}
56
57
/// Different instances that can be imported into the fixup module.
58
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
59
pub enum ImportedInstance {
60
    /// The original "shim" module with stubs that need to be filled in.
61
    Shim,
62
    /// The "main" module.
63
    Main,
64
    /// A synthetic instance created by collecting actual implementations of
65
    /// shims from various locations.
66
    Actual,
67
    /// A named adapter module.
68
    Adapter(String),
69
}
70
71
pub enum StartAction {
72
    /// Sets `$global` to `ref.func $shim`
73
    InitGlobal {
74
        /// The index in `declared_funcs` of the function to fill in.
75
        shim: usize,
76
        /// The global being set.
77
        global: u32,
78
    },
79
    /// Calls the specified function index.
80
    Call(u32),
81
    /// Used during the `link`-part of `wit-component` this adds the
82
    /// `memory_base` and `export` globals together and stores the result in
83
    /// `dest`.
84
    InitializeAddress {
85
        memory_base: u32,
86
        export: u32,
87
        dest: AddressDest,
88
    },
89
    /// Invoke the `func` hook with the `hook` argument.
90
    CallHook { func: u32, hook: TaskHook },
91
}
92
93
pub enum AddressDest {
94
    /// A linear memory address that's statically known.
95
    LinearMemory { address: u32, memory: u32 },
96
    /// A global indexed by the argument here.
97
    Global(u32),
98
}
99
100
/// What can be used to fill in a shim that was originally defined in the shim
101
/// module.
102
///
103
/// Functions specified here either go into an element segment to fill in the
104
/// original shim module's table or they're inserted into globals.
105
enum ShimFill {
106
    /// An imported function.
107
    Import(u32),
108
    /// An imported function which is also known to be a resource destructor.
109
    /// Used to know when to insert task hooks.
110
    ImportedResourceDtor(u32),
111
    /// A `realloc` function which is exported from `instance` under the name
112
    /// `export` and imported here as `func`.
113
    Realloc {
114
        instance: ImportedInstance,
115
        export: String,
116
        func: u32,
117
    },
118
    /// A function defined in this fixup module.
119
    DefinedFunction(usize),
120
}
121
122
/// Different functions that can be defined in the fixup module, apart from the
123
/// `start` function.
124
enum DefinedFunction {
125
    /// A function which wraps a core export to ensure that task state is set up
126
    /// before the core export is called.
127
    HookedCoreExport {
128
        /// The task hook function.
129
        task_hook: u32,
130
        /// The core function to call and delegate to.
131
        to_wrap: u32,
132
        /// The number of parameters to forward to `export`.
133
        params: usize,
134
        /// The debug-related core name of this function it's hooking.
135
        core_name: String,
136
        /// If `Some` this is the instance that is is exported for which will
137
        /// get inserted into `EncodingState`'s export wrapper map.
138
        export: Option<ImportedInstance>,
139
        /// The kind of hook to use at the start of this function.
140
        start: Option<TaskHook>,
141
        /// The kind of hook to use at the end of this function.
142
        end: EndTaskHook,
143
    },
144
}
145
146
#[derive(Copy, Clone)]
147
pub enum TaskHook {
148
    /// A synchronous task has started.
149
    SyncStart = 0,
150
    /// A synchronous task has finished.
151
    SyncFinish = 1,
152
    /// An async task has started.
153
    AsyncStart = 2,
154
    /// An async has resumed in its `callback` option.
155
    AsyncResume = 3,
156
    /// An async blocked, but not yet completed, and it's returning from either
157
    /// the main entrypoint or the `callback` option.
158
    AsyncBlock = 4,
159
    /// An async task has finished, returning from either the main entrypoint or
160
    /// the `callback` option.
161
    AsyncFinish = 5,
162
    /// The `_initialize` function and other ctors are being called.
163
    InitializeStart = 6,
164
    /// The `_initialize` function and other ctors are finished.
165
    InitializeFinish = 7,
166
    /// A resource destructor is starting.
167
    ResourceDtorStart = 8,
168
    /// A resource destructor is finished.
169
    ResourceDtorFinish = 9,
170
    //
171
    // 10/11 intentionally missing
172
    //
173
    /// A `realloc` function used as a canonical ABI option is being invoked.
174
    ReallocStart = 12,
175
    /// A `realloc` function used as a canonical ABI option has finished.
176
    ReallocFinish = 13,
177
}
178
179
enum EndTaskHook {
180
    None,
181
    AsyncCode,
182
    Normal(TaskHook),
183
}
184
185
impl FixupModule {
186
    /// Returns the 0-parameter 0-result function type.
187
3.55k
    pub fn type_thunk(&mut self) -> u32 {
188
3.55k
        self.type_intern(Vec::new(), Vec::new())
189
3.55k
    }
190
191
    /// Interns `params`/`results` as a function type.
192
5.83k
    pub fn type_intern(&mut self, params: Vec<ValType>, results: Vec<ValType>) -> u32 {
193
5.83k
        *self
194
5.83k
            .type_map
195
5.83k
            .entry((params.clone(), results.clone()))
196
5.83k
            .or_insert_with(|| {
197
4.25k
                let index = self.types.len();
198
4.25k
                self.types
199
4.25k
                    .ty()
200
4.25k
                    .function(params.iter().copied(), results.iter().copied());
201
4.25k
                index
202
4.25k
            })
203
5.83k
    }
204
205
2.28k
    fn type_index(&mut self, sig: &WasmSignature) -> u32 {
206
2.28k
        self.type_intern(
207
2.28k
            sig.params.iter().map(super::to_val_type).collect(),
208
2.28k
            sig.results.iter().map(super::to_val_type).collect(),
209
        )
210
2.28k
    }
211
212
7.10k
    fn import_instance(&mut self, kind: &ImportedInstance) -> Result<String> {
213
7.10k
        let name = match kind {
214
1.57k
            ImportedInstance::Shim => "shim",
215
3.24k
            ImportedInstance::Main => "main",
216
2.28k
            ImportedInstance::Actual => "actual",
217
0
            ImportedInstance::Adapter(name) => name.as_str(),
218
        };
219
7.10k
        let name = name.to_string();
220
7.10k
        if let Some(prev) = self.imported_instances.insert(name.clone(), kind.clone()) {
221
2.20k
            if prev != *kind {
222
0
                bail!("instance `{name}` was already imported as a different kind: {prev:?}");
223
2.20k
            }
224
4.90k
        }
225
7.10k
        Ok(name)
226
7.10k
    }
227
228
    /// Imports a new item into this fixup module to get used by later
229
    /// functions/etc.
230
    ///
231
    /// Returns the index that the item is imported as.
232
7.10k
    pub fn import(
233
7.10k
        &mut self,
234
7.10k
        instance: &ImportedInstance,
235
7.10k
        name: &str,
236
7.10k
        ty: EntityType,
237
7.10k
    ) -> Result<u32> {
238
7.10k
        let module = self.import_instance(instance)?;
239
7.10k
        let key = (module, name.to_string());
240
7.10k
        if let Some((index, prev)) = self.imported_items.get(&key) {
241
0
            if *prev != ty {
242
0
                bail!(
243
                    "`{}::{name}` was already imported with a different type",
244
                    key.0
245
                );
246
0
            }
247
0
            return Ok(*index);
248
7.10k
        }
249
        // Note that debug names are recorded here, rather than in the helpers
250
        // below, so that a name is only recorded for a freshly created import.
251
7.10k
        let ret = match ty {
252
            EntityType::Function(_) => {
253
5.52k
                let index = inc(&mut self.funcs);
254
5.52k
                self.function_names.append(index, name);
255
5.52k
                index
256
            }
257
            EntityType::Global(_) => {
258
1.06k
                let index = inc(&mut self.globals);
259
1.06k
                self.global_names.append(index, name);
260
1.06k
                index
261
            }
262
518
            EntityType::Table(_) => inc(&mut self.tables),
263
0
            EntityType::Memory(_) => inc(&mut self.memories),
264
0
            EntityType::Tag(_) | EntityType::FunctionExact(_) => unimplemented!(),
265
        };
266
7.10k
        self.imports.import(&key.0, name, ty);
267
7.10k
        self.imported_items.insert(key, (ret, ty));
268
7.10k
        Ok(ret)
269
7.10k
    }
270
271
    /// Helper over `self.import(...)`
272
1.06k
    pub fn import_global(
273
1.06k
        &mut self,
274
1.06k
        instance: &ImportedInstance,
275
1.06k
        name: &str,
276
1.06k
        ty: GlobalType,
277
1.06k
    ) -> Result<u32> {
278
1.06k
        self.import(instance, name, ty.into())
279
1.06k
    }
280
281
    /// Helper over `self.import(...)`
282
5.52k
    pub fn import_func(&mut self, instance: &ImportedInstance, name: &str, ty: u32) -> Result<u32> {
283
5.52k
        self.import(instance, name, EntityType::Function(ty))
284
5.52k
    }
285
286
    /// Appends to the `start` function that'll get generated.
287
0
    pub fn add_start_abi_detail(&mut self, action: StartAction) {
288
0
        self.start_abi_details.push(action);
289
0
    }
290
291
    /// Appends to the `start` function that'll get generated.
292
0
    pub fn add_start_user_func(&mut self, action: StartAction) {
293
0
        self.start_user_funcs.push(action);
294
0
    }
295
296
    /// Returns the underlying element section.
297
0
    pub fn elements(&mut self) -> &mut ElementSection {
298
0
        &mut self.elements
299
0
    }
300
301
    /// Returns the underlying data section.
302
0
    pub fn data(&mut self) -> &mut DataSection {
303
0
        &mut self.data
304
0
    }
305
306
    /// Adds `shim` to this fixup module to get filled in.
307
2.28k
    pub(super) fn add_shim(&mut self, opts: &ComponentEncoder, shim: &Shim) -> Result<()> {
308
2.28k
        let type_index = self.type_index(&shim.sig);
309
2.28k
        let func = self.import_func(&ImportedInstance::Actual, &shim.name, type_index)?;
310
311
2.28k
        let shim_fill = match &shim.kind {
312
0
            ShimKind::Realloc { module, export } => ShimFill::Realloc {
313
0
                instance: module.to_imported_instance(),
314
0
                export: export.to_string(),
315
0
                func,
316
0
            },
317
234
            ShimKind::ResourceDtor { .. } => ShimFill::ImportedResourceDtor(func),
318
2.05k
            _ => ShimFill::Import(func),
319
        };
320
321
2.28k
        if opts.shim_return_call_ref {
322
1.06k
            let global = self.import_global(
323
1.06k
                &ImportedInstance::Shim,
324
1.06k
                &format!("g{}", &shim.name),
325
1.06k
                GlobalType {
326
1.06k
                    val_type: RefType {
327
1.06k
                        heap_type: HeapType::Concrete(type_index),
328
1.06k
                        nullable: false,
329
1.06k
                    }
330
1.06k
                    .into(),
331
1.06k
                    mutable: true,
332
1.06k
                    shared: false,
333
1.06k
                },
334
0
            )?;
335
336
1.06k
            self.start_abi_details.push(StartAction::InitGlobal {
337
1.06k
                shim: self.declared_funcs.len(),
338
1.06k
                global,
339
1.06k
            });
340
1.06k
            self.declared_funcs.push(shim_fill);
341
1.22k
        } else {
342
1.22k
            self.shims_in_table.push(shim_fill);
343
1.22k
        }
344
2.28k
        Ok(())
345
2.28k
    }
346
347
    /// Ensures there's a `start` function which invokes `initialize`.
348
3.24k
    pub fn add_initialize(&mut self, initialize: &str) -> Result<()> {
349
3.24k
        let ty = self.type_thunk();
350
3.24k
        let func = self.import_func(&ImportedInstance::Main, initialize, ty)?;
351
3.24k
        self.start_user_funcs.push(StartAction::Call(func));
352
3.24k
        Ok(())
353
3.24k
    }
354
355
3.24k
    pub(super) fn instantiate(
356
3.24k
        &mut self,
357
3.24k
        shims: &Shims<'_>,
358
3.24k
        state: &mut EncodingState<'_>,
359
3.24k
    ) -> Result<()> {
360
        // First inject task hooks as necessary throughout the module which is
361
        // the final step before actually encoding the module.
362
3.24k
        self.prepare_task_hooks(state)?;
363
364
        // Generate the module, and this'll return `None` if the module isn't
365
        // necessary in which case we bail out.
366
3.24k
        let Some(module) = self.encode(state)? else {
367
0
            return Ok(());
368
        };
369
370
        // Embed this fixup module in the component.
371
3.24k
        let module_index = state
372
3.24k
            .component
373
3.24k
            .core_module(Some("wit-component-fixup"), &module);
374
375
        // Prepare the arguments used to instantiate this component based on the
376
        // `imported_instances` map that's been generated.
377
3.24k
        let mut instance_args = Vec::new();
378
4.90k
        for (name, instance) in self.imported_instances.iter() {
379
4.90k
            let index = match instance {
380
3.24k
                ImportedInstance::Main => state
381
3.24k
                    .instance_index
382
3.24k
                    .expect("must have an instantiated module"),
383
0
                ImportedInstance::Adapter(name) => state.adapter_instances[name.as_str()],
384
830
                ImportedInstance::Shim => state
385
830
                    .shim_instance_index
386
830
                    .expect("must have an instantiated shim"),
387
                ImportedInstance::Actual => {
388
830
                    let mut actual = Vec::new();
389
2.28k
                    for shim in shims.shims.values() {
390
2.28k
                        let core_func_index = state.encode_shim(shims, shim)?;
391
2.28k
                        actual.push((shim.name.as_str(), ExportKind::Func, core_func_index));
392
                    }
393
830
                    state
394
830
                        .component
395
830
                        .core_instantiate_exports(Some("actual"), actual)
396
                }
397
            };
398
4.90k
            instance_args.push((name.as_str(), ModuleArg::Instance(index)));
399
        }
400
401
        // The side-effectful instantiation of the fixup instance.
402
3.24k
        let instance = state
403
3.24k
            .component
404
3.24k
            .core_instantiate(Some("fixup"), module_index, instance_args);
405
406
        // If there are hooked core exports in this module then register within
407
        // `state` that they should be preferred over their raw brethren.
408
3.24k
        for (i, (_ty, func)) in self.defined_functions.iter().enumerate() {
409
0
            match func {
410
                DefinedFunction::HookedCoreExport {
411
0
                    core_name,
412
0
                    export: Some(from),
413
                    ..
414
0
                } => {
415
0
                    let name = format!("hook{i}");
416
0
                    let wrapper =
417
0
                        state.core_alias_export(Some(&name), instance, &name, ExportKind::Func);
418
0
                    state
419
0
                        .export_task_initialization_wrappers
420
0
                        .insert((from.clone(), core_name.clone()), wrapper);
421
0
                }
422
0
                DefinedFunction::HookedCoreExport { export: None, .. } => {}
423
            }
424
        }
425
3.24k
        Ok(())
426
3.24k
    }
427
428
3.24k
    fn encode(&mut self, state: &EncodingState<'_>) -> Result<Option<Module>> {
429
3.24k
        let opts = &state.info.encoder;
430
3.24k
        if self.start_abi_details.is_empty()
431
2.92k
            && self.start_user_funcs.is_empty()
432
0
            && self.shims_in_table.is_empty()
433
0
            && self.declared_funcs.is_empty()
434
0
            && self.elements.is_empty()
435
0
            && self.data.is_empty()
436
0
            && self.defined_functions.is_empty()
437
        {
438
0
            return Ok(None);
439
3.24k
        }
440
441
3.24k
        let mut functions = FunctionSection::new();
442
3.24k
        let mut code = CodeSection::new();
443
3.24k
        let mut exports = ExportSection::new();
444
445
        // First define functions within this module now that the import space
446
        // for functions has settled.
447
3.24k
        let mut defined_func_indices = Vec::new();
448
3.24k
        for (i, (ty, func)) in self.defined_functions.iter().enumerate() {
449
0
            functions.function(*ty);
450
0
            let index = inc(&mut self.funcs);
451
0
            defined_func_indices.push(index);
452
0
            let mut func = match func {
453
                DefinedFunction::HookedCoreExport {
454
0
                    task_hook,
455
0
                    to_wrap,
456
0
                    params,
457
0
                    core_name,
458
0
                    export,
459
0
                    start,
460
0
                    end,
461
                } => {
462
0
                    let mut locals = Vec::new();
463
0
                    if let EndTaskHook::AsyncCode = end {
464
0
                        locals.push((1, ValType::I32));
465
0
                    }
466
0
                    let mut f = Function::new(locals);
467
0
                    if let Some(start) = *start {
468
0
                        f.instructions().i32_const(start as i32).call(*task_hook);
469
0
                    }
470
0
                    for i in 0..*params {
471
0
                        f.instructions().local_get(i as u32);
472
0
                    }
473
0
                    f.instructions().call(*to_wrap);
474
0
                    match end {
475
0
                        EndTaskHook::AsyncCode => {
476
0
                            f.instructions()
477
0
                                .local_set(*params as u32)
478
0
                                // 0 == EXIT, 1/2 == yield/wait, so delegate to
479
0
                                // appropriate code.
480
0
                                //
481
0
                                // First thing pushed on the stack is "if true",
482
0
                                // which in this case nonzero means blocking.
483
0
                                // Second thing is "if false" meaning if 0
484
0
                                // meaning EXIT meaning "done". Final thing is
485
0
                                // what to test, the return code.
486
0
                                .i32_const(TaskHook::AsyncBlock as i32)
487
0
                                .i32_const(TaskHook::AsyncFinish as i32)
488
0
                                .local_get(*params as u32)
489
0
                                .select()
490
0
                                .call(*task_hook)
491
0
                                // put the return code back on the stack to
492
0
                                // actually return
493
0
                                .local_get(*params as u32);
494
0
                        }
495
0
                        EndTaskHook::Normal(hook) => {
496
0
                            f.instructions().i32_const(*hook as i32).call(*task_hook);
497
0
                        }
498
0
                        EndTaskHook::None => {}
499
                    }
500
0
                    if export.is_some() {
501
0
                        exports.export(&format!("hook{i}"), ExportKind::Func, index);
502
0
                    }
503
0
                    self.function_names
504
0
                        .append(index, &format!("hook-{core_name}"));
505
0
                    f
506
                }
507
            };
508
0
            func.instructions().end();
509
0
            code.function(&func);
510
        }
511
512
        // Using our defined functions above it's possible to implement
513
        // resolution of `ShimFill` items.
514
3.34k
        let resolve_shim = |shim: &ShimFill| match shim {
515
3.05k
            ShimFill::Import(func) => *func,
516
287
            ShimFill::ImportedResourceDtor(func) => *func,
517
0
            ShimFill::DefinedFunction(i) => defined_func_indices[*i],
518
0
            ShimFill::Realloc { func, .. } => *func,
519
3.34k
        };
520
521
        // If this fixup is filling in a table, then import the table and use an
522
        // element segment for its initialization.
523
3.24k
        if !self.shims_in_table.is_empty() {
524
518
            let table_type = TableType {
525
518
                element_type: RefType::FUNCREF,
526
518
                minimum: self.shims_in_table.len() as u64,
527
518
                maximum: Some(self.shims_in_table.len() as u64),
528
518
                table64: false,
529
518
                shared: false,
530
518
            };
531
518
            let table = self.import(
532
518
                &ImportedInstance::Shim,
533
518
                super::INDIRECT_TABLE_NAME,
534
518
                table_type.into(),
535
0
            )?;
536
518
            self.elements.active(
537
518
                if table == 0 { None } else { Some(table) },
538
518
                &ConstExpr::i32_const(0),
539
518
                Elements::Functions(
540
518
                    self.shims_in_table
541
518
                        .iter()
542
518
                        .map(resolve_shim)
543
518
                        .collect::<Vec<_>>()
544
518
                        .into(),
545
518
                ),
546
            );
547
2.72k
        }
548
549
        // Codegen the start function here. Special case an empty start function
550
        // or just a single function call as the start function, otherwise each
551
        // item needs to be handled individually.
552
3.24k
        let start = match (&self.start_abi_details[..], &self.start_user_funcs[..]) {
553
3.24k
            ([], []) => None,
554
2.92k
            ([], [StartAction::Call(func)]) => Some(*func),
555
312
            (first, second) => {
556
312
                let mut start = Function::new(Vec::new());
557
1.37k
                for action in first.iter().chain(second.iter()) {
558
1.37k
                    match action {
559
1.06k
                        StartAction::InitGlobal { shim, global } => {
560
1.06k
                            let func = resolve_shim(&self.declared_funcs[*shim]);
561
1.06k
                            start.instructions().ref_func(func).global_set(*global);
562
1.06k
                        }
563
312
                        StartAction::Call(func) => {
564
312
                            start.instructions().call(*func);
565
312
                        }
566
0
                        StartAction::CallHook { func, hook } => {
567
0
                            start.instructions().i32_const(*hook as i32).call(*func);
568
0
                        }
569
                        StartAction::InitializeAddress {
570
0
                            memory_base,
571
0
                            export,
572
0
                            dest,
573
0
                        } => match dest {
574
0
                            AddressDest::LinearMemory { address, memory } => {
575
0
                                start
576
0
                                    .instructions()
577
0
                                    .i32_const(*address as i32)
578
0
                                    .global_get(*memory_base)
579
0
                                    .global_get(*export)
580
0
                                    .i32_add()
581
0
                                    .i32_store(MemArg {
582
0
                                        offset: 0,
583
0
                                        align: 2,
584
0
                                        memory_index: *memory,
585
0
                                    });
586
0
                            }
587
0
                            AddressDest::Global(global) => {
588
0
                                start
589
0
                                    .instructions()
590
0
                                    .global_get(*memory_base)
591
0
                                    .global_get(*export)
592
0
                                    .i32_add()
593
0
                                    .global_set(*global);
594
0
                            }
595
                        },
596
                    }
597
                }
598
312
                start.instructions().end();
599
312
                let index = inc(&mut self.funcs);
600
312
                let ty = self.type_thunk();
601
312
                functions.function(ty);
602
312
                code.function(&start);
603
312
                self.function_names.append(index, "start");
604
312
                Some(index)
605
            }
606
        };
607
608
        // Fill in the declared element segment, if necessary, used for
609
        // `ref.func` in the start function above.
610
3.24k
        if !self.declared_funcs.is_empty() {
611
312
            self.elements.declared(Elements::Functions(
612
312
                self.declared_funcs
613
312
                    .iter()
614
312
                    .map(resolve_shim)
615
312
                    .collect::<Vec<_>>()
616
312
                    .into(),
617
312
            ));
618
2.92k
        }
619
620
        // Now that we've got all the pieces weave everything into a `Module`.
621
3.24k
        let mut module = Module::default();
622
3.24k
        if !self.types.is_empty() {
623
3.24k
            module.section(&self.types);
624
3.24k
        }
625
3.24k
        if !self.imports.is_empty() {
626
3.24k
            module.section(&self.imports);
627
3.24k
        }
628
3.24k
        if !functions.is_empty() {
629
312
            module.section(&functions);
630
2.92k
        }
631
3.24k
        if !exports.is_empty() {
632
0
            module.section(&exports);
633
3.24k
        }
634
3.24k
        if let Some(start) = start {
635
3.24k
            module.section(&StartSection {
636
3.24k
                function_index: start,
637
3.24k
            });
638
3.24k
        }
639
3.24k
        if !self.elements.is_empty() {
640
830
            module.section(&self.elements);
641
2.41k
        }
642
3.24k
        if !code.is_empty() {
643
312
            module.section(&code);
644
2.92k
        }
645
3.24k
        if !self.data.is_empty() {
646
0
            module.section(&self.data);
647
3.24k
        }
648
3.24k
        module.section(&RawCustomSection(
649
3.24k
            &crate::base_producers().raw_custom_section(),
650
3.24k
        ));
651
652
3.24k
        if opts.debug_names {
653
477
            let mut names = NameSection::new();
654
477
            names.module("wit-component:fixups");
655
477
            if !self.function_names.is_empty() {
656
477
                names.functions(&self.function_names);
657
477
            }
658
477
            if !self.global_names.is_empty() {
659
249
                names.globals(&self.global_names);
660
249
            }
661
477
            module.section(&names);
662
2.76k
        }
663
664
3.24k
        Ok(Some(module))
665
3.24k
    }
666
667
    /// Prepares task hooks, like `__wasm_init_task`, to be configured in
668
    /// various locations throughout this fixup module.
669
    ///
670
    /// This handles:
671
    ///
672
    /// * The `_initialize` function (needs task setup first)
673
    /// * Hooks for all exports which are lifted.
674
3.24k
    fn prepare_task_hooks(&mut self, state: &mut EncodingState<'_>) -> Result<()> {
675
3.24k
        let info_main = state.info.exports_for(CustomModule::Main);
676
3.24k
        let Some(task_hook) = info_main.wasm_task_hook() else {
677
3.24k
            return Ok(());
678
        };
679
0
        let ty = self.type_intern(vec![ValType::I32], Vec::new());
680
0
        let task_hook = self.import_func(&ImportedInstance::Main, task_hook, ty)?;
681
682
        // Handle the start function first where if there's something registered
683
        // in the "user function" area we need to configure that to work.
684
0
        if !self.start_user_funcs.is_empty() {
685
0
            self.start_user_funcs.insert(
686
0
                0,
687
0
                StartAction::CallHook {
688
0
                    func: task_hook,
689
0
                    hook: TaskHook::InitializeStart,
690
0
                },
691
0
            );
692
0
            self.start_user_funcs.push(StartAction::CallHook {
693
0
                func: task_hook,
694
0
                hook: TaskHook::InitializeFinish,
695
0
            });
696
0
        }
697
698
        // Afterwards handle all exports for the main/adapter modules.
699
0
        self.prepare_export_task_hooks_for(state, CustomModule::Main, task_hook)?;
700
0
        for adapter in state.info.adapters.keys() {
701
0
            self.prepare_export_task_hooks_for(state, CustomModule::Adapter(adapter), task_hook)?;
702
        }
703
704
        // Next up generate wrappers for all `realloc` functions which might be
705
        // used as a canonical option.
706
0
        let mut reallocs = HashMap::new();
707
0
        self.prepare_realloc_task_hooks_for(state, CustomModule::Main, task_hook, &mut reallocs)?;
708
0
        for adapter in state.info.adapters.keys() {
709
0
            self.prepare_realloc_task_hooks_for(
710
0
                state,
711
0
                CustomModule::Adapter(adapter),
712
0
                task_hook,
713
0
                &mut reallocs,
714
0
            )?;
715
        }
716
717
        // And finally redirect/fixup shims that need hooks to defined versions
718
        // locally in this module.
719
0
        let mut shims_in_table = mem::take(&mut self.shims_in_table);
720
0
        let mut declared_funcs = mem::take(&mut self.declared_funcs);
721
0
        for shim in shims_in_table.iter_mut().chain(declared_funcs.iter_mut()) {
722
0
            match shim {
723
                // Resource destructors are handled here directly inline.
724
0
                ShimFill::ImportedResourceDtor(func) => {
725
0
                    let func = *func;
726
0
                    let ty = self.type_intern(vec![ValType::I32], Vec::new());
727
0
                    *shim = ShimFill::DefinedFunction(self.defined_functions.len());
728
0
                    self.defined_functions.push((
729
0
                        ty,
730
0
                        DefinedFunction::HookedCoreExport {
731
0
                            task_hook,
732
0
                            to_wrap: func,
733
0
                            params: 1,
734
0
                            start: Some(TaskHook::ResourceDtorStart),
735
0
                            end: EndTaskHook::Normal(TaskHook::ResourceDtorFinish),
736
0
                            export: None,
737
0
                            core_name: format!("resource-dtor"),
738
0
                        },
739
0
                    ));
740
0
                }
741
742
                // All realloc functions were hooked above in
743
                // `prepare_realloc_task_hooks_for` so this just looks up in the
744
                // map built there to figure out which function to redirect to.
745
                ShimFill::Realloc {
746
0
                    instance, export, ..
747
0
                } => {
748
0
                    let func = reallocs
749
0
                        .get(&(instance.clone(), export.clone()))
750
0
                        .copied()
751
0
                        .expect("realloc wrapper should have been generated");
752
0
                    *shim = ShimFill::DefinedFunction(func);
753
0
                }
754
755
0
                ShimFill::Import(_) | ShimFill::DefinedFunction(_) => {}
756
            };
757
        }
758
759
0
        self.shims_in_table = shims_in_table;
760
0
        self.declared_funcs = declared_funcs;
761
762
0
        Ok(())
763
3.24k
    }
764
765
    /// Generates a task hook wrapper for each `realloc` function exported by
766
    /// `for_module` which may be used as a canonical option.
767
    ///
768
    /// These wrappers are themselves imported from the fixup module for use in
769
    /// lifts-of-exports, and then additionally these are used to fill in
770
    /// `ShimFill::Realloc` later on.
771
0
    fn prepare_realloc_task_hooks_for(
772
0
        &mut self,
773
0
        state: &mut EncodingState<'_>,
774
0
        for_module: CustomModule<'_>,
775
0
        task_hook: u32,
776
0
        reallocs: &mut HashMap<(ImportedInstance, String), usize>,
777
0
    ) -> Result<()> {
778
0
        let instance = for_module.to_imported_instance();
779
0
        for name in state.info.exports_for(for_module).reallocs() {
780
0
            let ty = self.type_intern(vec![ValType::I32; 4], vec![ValType::I32]);
781
0
            let func = self.import_func(&instance, name, ty)?;
782
0
            let index = self.defined_functions.len();
783
0
            self.defined_functions.push((
784
0
                ty,
785
0
                DefinedFunction::HookedCoreExport {
786
0
                    task_hook,
787
0
                    to_wrap: func,
788
0
                    params: 4,
789
0
                    start: Some(TaskHook::ReallocStart),
790
0
                    end: EndTaskHook::Normal(TaskHook::ReallocFinish),
791
0
                    export: Some(instance.clone()),
792
0
                    core_name: name.to_string(),
793
0
                },
794
0
            ));
795
0
            reallocs.insert((instance.clone(), name.to_string()), index);
796
        }
797
0
        Ok(())
798
0
    }
799
800
    /// Looks over the exports of `for_module` and adds any functions as
801
    /// necessary to `self.defined_functions` which hook the original export to
802
    /// ensure it's got task state set up when called first.
803
0
    fn prepare_export_task_hooks_for(
804
0
        &mut self,
805
0
        state: &mut EncodingState<'_>,
806
0
        for_module: CustomModule<'_>,
807
0
        task_hook: u32,
808
0
    ) -> Result<()> {
809
0
        let resolve = &state.info.encoder.metadata.resolve;
810
0
        let world = &resolve.worlds[state.info.encoder.metadata.world];
811
0
        let info = state.info.exports_for(for_module);
812
0
        let imported_instance = for_module.to_imported_instance();
813
814
0
        for (core_name, export) in info.iter() {
815
0
            let (key, f, abi) = match export {
816
0
                Export::WorldFunc(key, _, abi) => match &world.exports[key] {
817
0
                    WorldItem::Function(f) => (key, f, abi),
818
0
                    _ => continue,
819
                },
820
0
                Export::InterfaceFunc(key, id, func_name, abi) => {
821
0
                    let func = &resolve.interfaces[*id].functions[func_name.as_str()];
822
0
                    (key, func, abi)
823
                }
824
0
                _ => continue,
825
            };
826
827
0
            let sig = resolve.wasm_signature(*abi, f);
828
0
            let ty = self.type_index(&sig);
829
0
            let func = self.import_func(&imported_instance, core_name, ty)?;
830
0
            let post_return = info.post_return(key, f);
831
0
            self.defined_functions.push((
832
0
                ty,
833
                DefinedFunction::HookedCoreExport {
834
0
                    task_hook,
835
0
                    to_wrap: func,
836
0
                    export: Some(imported_instance.clone()),
837
0
                    params: sig.params.len(),
838
0
                    core_name: core_name.to_string(),
839
0
                    start: Some(if abi.is_async() {
840
0
                        TaskHook::AsyncStart
841
                    } else {
842
0
                        TaskHook::SyncStart
843
                    }),
844
0
                    end: if abi.is_async() {
845
0
                        assert!(post_return.is_none());
846
0
                        if *abi == AbiVariant::GuestExportAsyncStackful {
847
0
                            EndTaskHook::Normal(TaskHook::AsyncFinish)
848
                        } else {
849
0
                            EndTaskHook::AsyncCode
850
                        }
851
                    } else {
852
0
                        if post_return.is_some() {
853
0
                            EndTaskHook::None
854
                        } else {
855
0
                            EndTaskHook::Normal(TaskHook::SyncFinish)
856
                        }
857
                    },
858
                },
859
            ));
860
861
0
            if let Some(post_return) = info.post_return(key, f) {
862
0
                let mut post_return_sig = sig.clone();
863
0
                post_return_sig.params = mem::take(&mut post_return_sig.results);
864
0
                let ty = self.type_index(&post_return_sig);
865
0
                let func = self.import_func(&imported_instance, post_return, ty)?;
866
0
                self.defined_functions.push((
867
0
                    ty,
868
0
                    DefinedFunction::HookedCoreExport {
869
0
                        task_hook,
870
0
                        to_wrap: func,
871
0
                        export: Some(imported_instance.clone()),
872
0
                        params: post_return_sig.params.len(),
873
0
                        core_name: post_return.to_string(),
874
0
                        start: None,
875
0
                        end: EndTaskHook::Normal(TaskHook::SyncFinish),
876
0
                    },
877
0
                ));
878
0
            }
879
880
0
            if let Some(callback) = info.callback(key, f) {
881
0
                let ty = self.type_intern(vec![ValType::I32; 3], vec![ValType::I32]);
882
0
                let func = self.import_func(&imported_instance, callback, ty)?;
883
0
                self.defined_functions.push((
884
0
                    ty,
885
0
                    DefinedFunction::HookedCoreExport {
886
0
                        task_hook,
887
0
                        to_wrap: func,
888
0
                        export: Some(imported_instance.clone()),
889
0
                        params: 3,
890
0
                        core_name: callback.to_string(),
891
0
                        start: Some(TaskHook::AsyncResume),
892
0
                        end: EndTaskHook::AsyncCode,
893
0
                    },
894
0
                ));
895
0
            }
896
        }
897
898
0
        Ok(())
899
0
    }
900
}
901
902
7.41k
fn inc(i: &mut u32) -> u32 {
903
7.41k
    let ret = *i;
904
7.41k
    *i += 1;
905
7.41k
    ret
906
7.41k
}