1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
//! Functions within a wasm module.

use std::cmp;
use std::collections::BTreeMap;

use anyhow::{bail, Context};
use wasm_encoder::Encode;
use wasmparser::{FuncValidator, FunctionBody, Range, ValidatorResources};

#[cfg(feature = "parallel")]
use rayon::prelude::*;

mod local_function;

use crate::emit::{Emit, EmitContext};
use crate::error::Result;
use crate::ir::InstrLocId;
use crate::module::imports::ImportId;
use crate::module::Module;
use crate::parse::IndicesToIds;
use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
use crate::ty::TypeId;
use crate::ty::ValType;
use crate::{ExportItem, FunctionBuilder, InstrSeqBuilder, LocalId, Memory, MemoryId};

pub use self::local_function::LocalFunction;

/// A function identifier.
pub type FunctionId = Id<Function>;

/// Parameter(s) to a function
pub type FuncParams = Vec<ValType>;

/// Result(s) of a given function
pub type FuncResults = Vec<ValType>;

/// A wasm function.
///
/// Either defined locally or externally and then imported; see `FunctionKind`.
#[derive(Debug)]
pub struct Function {
    // NB: Not public so that it can't get out of sync with the arena that this
    // function lives within.
    id: FunctionId,

    /// The kind of function this is.
    pub kind: FunctionKind,

    /// An optional name associated with this function
    pub name: Option<String>,
}

impl Tombstone for Function {
    fn on_delete(&mut self) {
        let ty = self.ty();
        self.kind = FunctionKind::Uninitialized(ty);
        self.name = None;
    }
}

impl Function {
    fn new_uninitialized(id: FunctionId, ty: TypeId) -> Function {
        Function {
            id,
            kind: FunctionKind::Uninitialized(ty),
            name: None,
        }
    }

    /// Get this function's identifier.
    pub fn id(&self) -> FunctionId {
        self.id
    }

    /// Get this function's type's identifier.
    pub fn ty(&self) -> TypeId {
        match &self.kind {
            FunctionKind::Local(l) => l.ty(),
            FunctionKind::Import(i) => i.ty,
            FunctionKind::Uninitialized(t) => *t,
        }
    }
}

/// The local- or external-specific bits of a function.
#[derive(Debug)]
pub enum FunctionKind {
    /// An externally defined, imported wasm function.
    Import(ImportedFunction),

    /// A locally defined wasm function.
    Local(LocalFunction),

    /// A locally defined wasm function that we haven't parsed yet (but have
    /// reserved its id and associated it with its original input wasm module
    /// index). This should only exist within
    /// `ModuleFunctions::add_local_functions`.
    Uninitialized(TypeId),
}

impl FunctionKind {
    /// Get the underlying `FunctionKind::Import` or panic if this is not an
    /// import function
    pub fn unwrap_import(&self) -> &ImportedFunction {
        match self {
            FunctionKind::Import(import) => import,
            _ => panic!("not an import function"),
        }
    }

    /// Get the underlying `FunctionKind::Local` or panic if this is not a local
    /// function.
    pub fn unwrap_local(&self) -> &LocalFunction {
        match self {
            FunctionKind::Local(l) => l,
            _ => panic!("not a local function"),
        }
    }

    /// Get the underlying `FunctionKind::Import` or panic if this is not an
    /// import function
    pub fn unwrap_import_mut(&mut self) -> &mut ImportedFunction {
        match self {
            FunctionKind::Import(import) => import,
            _ => panic!("not an import function"),
        }
    }

    /// Get the underlying `FunctionKind::Local` or panic if this is not a local
    /// function.
    pub fn unwrap_local_mut(&mut self) -> &mut LocalFunction {
        match self {
            FunctionKind::Local(l) => l,
            _ => panic!("not a local function"),
        }
    }
}

/// An externally defined, imported function.
#[derive(Debug)]
pub struct ImportedFunction {
    /// The import that brings this function into the module.
    pub import: ImportId,
    /// The type signature of this imported function.
    pub ty: TypeId,
}

/// The set of functions within a module.
#[derive(Debug, Default)]
pub struct ModuleFunctions {
    /// The arena containing this module's functions.
    arena: TombstoneArena<Function>,

    /// Original code section offset.
    pub(crate) code_section_offset: usize,
}

impl ModuleFunctions {
    /// Construct a new, empty set of functions for a module.
    pub fn new() -> ModuleFunctions {
        Default::default()
    }

    /// Create a new externally defined, imported function.
    pub fn add_import(&mut self, ty: TypeId, import: ImportId) -> FunctionId {
        self.arena.alloc_with_id(|id| Function {
            id,
            kind: FunctionKind::Import(ImportedFunction { import, ty }),
            name: None,
        })
    }

    /// Create a new internally defined function
    pub fn add_local(&mut self, func: LocalFunction) -> FunctionId {
        let func_name = func.builder().name.clone();
        self.arena.alloc_with_id(|id| Function {
            id,
            kind: FunctionKind::Local(func),
            name: func_name,
        })
    }

    /// Gets a reference to a function given its id
    pub fn get(&self, id: FunctionId) -> &Function {
        &self.arena[id]
    }

    /// Gets a reference to a function given its id
    pub fn get_mut(&mut self, id: FunctionId) -> &mut Function {
        &mut self.arena[id]
    }

    /// Get a function ID by its name.
    ///
    /// The name used is the "name" custom section name and *not* the export
    /// name, if a function happens to be exported.
    ///
    /// Note that function names are *not* guaranteed to be unique. This will
    /// return the first function in the module with the given name.
    pub fn by_name(&self, name: &str) -> Option<FunctionId> {
        self.arena.iter().find_map(|(id, f)| {
            if f.name.as_ref().map(|s| s.as_str()) == Some(name) {
                Some(id)
            } else {
                None
            }
        })
    }

    /// Removes a function from this module.
    ///
    /// It is up to you to ensure that any potential references to the deleted
    /// function are also removed, eg `call` expressions, exports, table
    /// elements, etc.
    pub fn delete(&mut self, id: FunctionId) {
        self.arena.delete(id);
    }

    /// Get a shared reference to this module's functions.
    pub fn iter(&self) -> impl Iterator<Item = &Function> {
        self.arena.iter().map(|(_, f)| f)
    }

    /// Get a shared reference to this module's functions.
    ///
    /// Requires the `parallel` feature of this crate to be enabled.
    #[cfg(feature = "parallel")]
    pub fn par_iter(&self) -> impl ParallelIterator<Item = &Function> {
        self.arena.par_iter().map(|(_, f)| f)
    }

    /// Get an iterator of this module's local functions
    pub fn iter_local(&self) -> impl Iterator<Item = (FunctionId, &LocalFunction)> {
        self.iter().filter_map(|f| match &f.kind {
            FunctionKind::Local(local) => Some((f.id(), local)),
            _ => None,
        })
    }

    /// Get a parallel iterator of this module's local functions
    ///
    /// Requires the `parallel` feature of this crate to be enabled.
    #[cfg(feature = "parallel")]
    pub fn par_iter_local(&self) -> impl ParallelIterator<Item = (FunctionId, &LocalFunction)> {
        self.par_iter().filter_map(|f| match &f.kind {
            FunctionKind::Local(local) => Some((f.id(), local)),
            _ => None,
        })
    }

    /// Get a mutable reference to this module's functions.
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Function> {
        self.arena.iter_mut().map(|(_, f)| f)
    }

    /// Get a mutable reference to this module's functions.
    ///
    /// Requires the `parallel` feature of this crate to be enabled.
    #[cfg(feature = "parallel")]
    pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut Function> {
        self.arena.par_iter_mut().map(|(_, f)| f)
    }

    /// Get an iterator of this module's local functions
    pub fn iter_local_mut(&mut self) -> impl Iterator<Item = (FunctionId, &mut LocalFunction)> {
        self.iter_mut().filter_map(|f| {
            let id = f.id();
            match &mut f.kind {
                FunctionKind::Local(local) => Some((id, local)),
                _ => None,
            }
        })
    }

    /// Get a parallel iterator of this module's local functions
    ///
    /// Requires the `parallel` feature of this crate to be enabled.
    #[cfg(feature = "parallel")]
    pub fn par_iter_local_mut(
        &mut self,
    ) -> impl ParallelIterator<Item = (FunctionId, &mut LocalFunction)> {
        self.par_iter_mut().filter_map(|f| {
            let id = f.id();
            match &mut f.kind {
                FunctionKind::Local(local) => Some((id, local)),
                _ => None,
            }
        })
    }

    pub(crate) fn emit_func_section(&self, cx: &mut EmitContext) {
        log::debug!("emit function section");
        let functions = used_local_functions(cx);
        if functions.len() == 0 {
            return;
        }
        let mut func_section = wasm_encoder::FunctionSection::new();
        for (id, function, _size) in functions {
            let index = cx.indices.get_type_index(function.ty());
            func_section.function(index);

            // Assign an index to all local defined functions before we start
            // translating them. While translating they may refer to future
            // functions, so we'll need to have an index for it by that point.
            // We're guaranteed the function section is emitted before the code
            // section so we should be covered here.
            cx.indices.push_func(id);
        }
        cx.wasm_module.section(&func_section);
    }
}

impl Module {
    /// Declare local functions after seeing the `function` section of a wasm
    /// executable.
    pub(crate) fn declare_local_functions(
        &mut self,
        section: wasmparser::FunctionSectionReader,
        ids: &mut IndicesToIds,
    ) -> Result<()> {
        log::debug!("parse function section");
        for func in section {
            let ty = ids.get_type(func?)?;
            let id = self
                .funcs
                .arena
                .alloc_with_id(|id| Function::new_uninitialized(id, ty));
            let idx = ids.push_func(id);
            if self.config.generate_synthetic_names_for_anonymous_items {
                self.funcs.get_mut(id).name = Some(format!("f{}", idx));
            }
        }

        Ok(())
    }

    /// Add the locally defined functions in the wasm module to this instance.
    pub(crate) fn parse_local_functions(
        &mut self,
        functions: Vec<(FunctionBody<'_>, FuncValidator<ValidatorResources>)>,
        indices: &mut IndicesToIds,
        on_instr_pos: Option<&(dyn Fn(&usize) -> InstrLocId + Sync + Send + 'static)>,
    ) -> Result<()> {
        log::debug!("parse code section");
        let num_imports = self.funcs.arena.len() - functions.len();

        // First up serially create corresponding `LocalId` instances for all
        // functions as well as extract the operators parser for each function.
        // This is pretty tough to parallelize, but we can look into it later if
        // necessary and it's a bottleneck!
        let mut bodies = Vec::with_capacity(functions.len());
        for (i, (body, mut validator)) in functions.into_iter().enumerate() {
            let index = (num_imports + i) as u32;
            let id = indices.get_func(index)?;
            let ty = match self.funcs.arena[id].kind {
                FunctionKind::Uninitialized(ty) => ty,
                _ => unreachable!(),
            };

            // First up, implicitly add locals for all function arguments. We also
            // record these in the function itself for later processing.
            let mut args = Vec::new();
            let type_ = self.types.get(ty);
            for ty in type_.params().iter() {
                let local_id = self.locals.add(*ty);
                let idx = indices.push_local(id, local_id);
                args.push(local_id);
                if self.config.generate_synthetic_names_for_anonymous_items {
                    let name = format!("arg{}", idx);
                    self.locals.get_mut(local_id).name = Some(name);
                }
            }

            // Ensure that there exists a `Type` for the function's entry
            // block. This is required because multi-value blocks reference a
            // `Type`, however function entry's type is implicit in the
            // encoding, and doesn't already exist in the `ModuleTypes`.
            let results = type_.results().to_vec();
            self.types.add_entry_ty(&results);

            // Next up comes all the locals of the function.
            let mut reader = body.get_binary_reader();
            for _ in 0..reader.read_var_u32()? {
                let pos = reader.original_position();
                let count = reader.read_var_u32()?;
                let ty = reader.read_type()?;
                validator.define_locals(pos, count, ty)?;
                let ty = ValType::parse(&ty)?;
                for _ in 0..count {
                    let local_id = self.locals.add(ty);
                    let idx = indices.push_local(id, local_id);
                    if self.config.generate_synthetic_names_for_anonymous_items {
                        let name = format!("l{}", idx);
                        self.locals.get_mut(local_id).name = Some(name);
                    }
                }
            }

            bodies.push((id, reader, args, ty, validator));
        }

        // Wasm modules can often have a lot of functions and this operation can
        // take some time, so parse all function bodies in parallel.
        let results = maybe_parallel!(bodies.(into_iter | into_par_iter))
            .map(|(id, body, args, ty, validator)| {
                (
                    id,
                    LocalFunction::parse(
                        self,
                        indices,
                        id,
                        ty,
                        args,
                        body,
                        on_instr_pos,
                        validator,
                    ),
                )
            })
            .collect::<Vec<_>>();

        // After all the function bodies are collected and finished push them
        // into our function arena.
        for (id, func) in results {
            let func = func?;
            self.funcs.arena[id].kind = FunctionKind::Local(func);
        }

        Ok(())
    }

    /// Retrieve the ID for the first exported memory.
    ///
    /// This method does not work in contexts with [multi-memory enabled](https://github.com/WebAssembly/multi-memory),
    /// and will error if more than one memory is present.
    pub fn get_memory_id(&self) -> Result<MemoryId> {
        if self.memories.len() > 1 {
            bail!("multiple memories unsupported")
        }

        self.memories
            .iter()
            .next()
            .map(Memory::id)
            .context("module does not export a memory")
    }

    /// Replace a single exported function with the result of the provided builder function.
    ///
    /// The builder function is provided a mutable reference to an [`InstrSeqBuilder`] which can be
    /// used to build the function as necessary.
    ///
    /// For example, if you wanted to replace an exported function with a no-op,
    ///
    /// ```ignore
    /// module.replace_exported_func(fid, |(body, arg_locals)| {
    ///     builder.func_body().unreachable();
    /// });
    /// ```
    ///
    /// The arguments passed to the original function will be passed to the
    /// new exported function that was built in your closure.
    ///
    /// This function returns the function ID of the *new* function,
    /// after it has been inserted into the module as an export.
    pub fn replace_exported_func(
        &mut self,
        fid: FunctionId,
        builder_fn: impl FnOnce((&mut InstrSeqBuilder, &Vec<LocalId>)),
    ) -> Result<FunctionId> {
        let original_export_id = self
            .exports
            .get_exported_func(fid)
            .map(|e| e.id())
            .with_context(|| format!("no exported function with ID [{fid:?}]"))?;

        if let Function {
            kind: FunctionKind::Local(lf),
            ..
        } = self.funcs.get(fid)
        {
            // Retrieve the params & result types for the exported (local) function
            let ty = self.types.get(lf.ty());
            let (params, results) = (ty.params().to_vec(), ty.results().to_vec());

            // Add the function produced by `fn_builder` as a local function
            let mut builder = FunctionBuilder::new(&mut self.types, &params, &results);
            let mut new_fn_body = builder.func_body();
            builder_fn((&mut new_fn_body, &lf.args));
            let func = builder.local_func(lf.args.clone());
            let new_fn_id = self.funcs.add_local(func);

            // Mutate the existing export to use the new local function
            let export = self.exports.get_mut(original_export_id);
            export.item = ExportItem::Function(new_fn_id);
            Ok(new_fn_id)
        } else {
            bail!("cannot replace function [{fid:?}], it is not an exported function");
        }
    }

    /// Replace a single imported function with the result of the provided builder function.
    ///
    /// The builder function is provided a mutable reference to an [`InstrSeqBuilder`] which can be
    /// used to build the function as necessary.
    ///
    /// For example, if you wanted to replace an imported function with a no-op,
    ///
    /// ```ignore
    /// module.replace_imported_func(fid, |(body, arg_locals)| {
    ///     builder.func_body().unreachable();
    /// });
    /// ```
    ///
    /// The arguments passed to the original function will be passed to the
    /// new exported function that was built in your closure.
    ///
    /// This function returns the function ID of the *new* function, and
    /// removes the existing import that has been replaced (the function will become local).
    pub fn replace_imported_func(
        &mut self,
        fid: FunctionId,
        builder_fn: impl FnOnce((&mut InstrSeqBuilder, &Vec<LocalId>)),
    ) -> Result<FunctionId> {
        let original_import_id = self
            .imports
            .get_imported_func(fid)
            .map(|import| import.id())
            .with_context(|| format!("no exported function with ID [{fid:?}]"))?;

        if let Function {
            kind: FunctionKind::Import(ImportedFunction { ty: tid, .. }),
            ..
        } = self.funcs.get(fid)
        {
            // Retrieve the params & result types for the imported function
            let ty = self.types.get(*tid);
            let (params, results) = (ty.params().to_vec(), ty.results().to_vec());

            // Build the list LocalIds used by args to match the original function
            let args = params
                .iter()
                .map(|ty| self.locals.add(*ty))
                .collect::<Vec<_>>();

            // Build the new function
            let mut builder = FunctionBuilder::new(&mut self.types, &params, &results);
            let mut new_fn_body = builder.func_body();
            builder_fn((&mut new_fn_body, &args));
            let new_func_kind = FunctionKind::Local(builder.local_func(args));

            // Mutate the existing function, changing it from a FunctionKind::ImportedFunction
            // to the local function produced by running the provided `fn_builder`
            let func = self.funcs.get_mut(fid);
            func.kind = new_func_kind;

            self.imports.delete(original_import_id);

            Ok(fid)
        } else {
            bail!("cannot replace function [{fid:?}], it is not an imported function");
        }
    }
}

fn used_local_functions<'a>(cx: &mut EmitContext<'a>) -> Vec<(FunctionId, &'a LocalFunction, u64)> {
    // Extract all local functions because imported ones were already
    // emitted as part of the import sectin. Find the size of each local
    // function. Sort imported functions in order so that we can get their
    // index in the function index space.
    let mut functions = Vec::new();
    for f in cx.module.funcs.iter() {
        match &f.kind {
            FunctionKind::Local(l) => functions.push((f.id(), l, l.size())),
            FunctionKind::Import(_) => {}
            FunctionKind::Uninitialized(_) => unreachable!(),
        }
    }

    // Sort local functions from largest to smallest; we will emit them in
    // this order. This helps load times, since wasm engines generally use
    // the function as their level of granularity for parallelism. We want
    // larger functions compiled before smaller ones because they will take
    // longer to compile.
    functions.sort_by_key(|(id, _, size)| (cmp::Reverse(*size), *id));

    functions
}

fn collect_non_default_code_offsets(
    code_transform: &mut BTreeMap<InstrLocId, usize>,
    code_offset: usize,
    map: Vec<(InstrLocId, usize)>,
) {
    for (src, dst) in map {
        let dst = dst + code_offset;
        if !src.is_default() {
            code_transform.insert(src, dst);
        }
    }
}

impl Emit for ModuleFunctions {
    fn emit(&self, cx: &mut EmitContext) {
        log::debug!("emit code section");
        let functions = used_local_functions(cx);
        if functions.len() == 0 {
            return;
        }

        let mut wasm_code_section = wasm_encoder::CodeSection::new();
        let generate_map = cx.module.config.preserve_code_transform;

        // Functions can typically take awhile to serialize, so serialize
        // everything in parallel. Afterwards we'll actually place all the
        // functions together.
        let bytes = maybe_parallel!(functions.(into_iter | into_par_iter))
            .map(|(id, func, _size)| {
                log::debug!("emit function {:?} {:?}", id, cx.module.funcs.get(id).name);
                let mut wasm = Vec::new();
                let mut map = if generate_map { Some(Vec::new()) } else { None };

                let (locals_types, used_locals, local_indices) = func.emit_locals(cx.module);
                let mut wasm_function = wasm_encoder::Function::new(locals_types);
                func.emit_instructions(
                    cx.indices,
                    &local_indices,
                    &mut wasm_function,
                    map.as_mut(),
                );
                wasm_function.encode(&mut wasm);
                (
                    wasm,
                    wasm_function.byte_len(),
                    id,
                    used_locals,
                    local_indices,
                    map,
                )
            })
            .collect::<Vec<_>>();

        let mut instruction_map = BTreeMap::new();
        cx.indices.locals.reserve(bytes.len());

        let mut offset_data = Vec::new();
        for (wasm, byte_len, id, used_locals, local_indices, map) in bytes {
            let leb_len = wasm.len() - byte_len;
            wasm_code_section.raw(&wasm[leb_len..]);
            cx.indices.locals.insert(id, local_indices);
            cx.locals.insert(id, used_locals);
            offset_data.push((byte_len, id, map, leb_len));
        }
        cx.wasm_module.section(&wasm_code_section);

        let code_section_start_offset =
            cx.wasm_module.as_slice().len() - wasm_code_section.byte_len();
        let mut cur_offset = code_section_start_offset;

        // update the map afterwards based on final offset differences
        for (byte_len, id, map, leb_len) in offset_data {
            // (this assumes the leb encodes the same)
            let code_start_offset = cur_offset + leb_len;
            cur_offset += leb_len + byte_len;
            if let Some(map) = map {
                collect_non_default_code_offsets(&mut instruction_map, code_start_offset, map);
            }
            cx.code_transform.function_ranges.push((
                id,
                Range {
                    // inclusive leb part
                    start: code_start_offset - leb_len,
                    end: cur_offset,
                },
            ));
        }
        cx.code_transform.function_ranges.sort_by_key(|i| i.0);
        // FIXME: code section start in DWARF debug information expects 2 bytes before actual code section start.
        cx.code_transform.code_section_start = code_section_start_offset - 2;
        cx.code_transform.instruction_map = instruction_map.into_iter().collect();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{Export, FunctionBuilder, Module};

    #[test]
    fn get_memory_id() {
        let mut module = Module::default();
        let expected_id = module.memories.add_local(false, 0, None);
        assert!(module.get_memory_id().is_ok_and(|id| id == expected_id));
    }

    /// Running `replace_exported_func` with a closure that builds
    /// a function should replace the existing function with the new one
    #[test]
    fn replace_exported_func() {
        let mut module = Module::default();

        // Create original function
        let mut builder = FunctionBuilder::new(&mut module.types, &[], &[]);
        builder.func_body().i32_const(1234).drop();
        let original_fn_id: FunctionId = builder.finish(vec![], &mut module.funcs);
        let original_export_id = module.exports.add("dummy", original_fn_id);

        // Replace the existing function with a new one with a reversed const value
        let new_fn_id = module
            .replace_exported_func(original_fn_id, |(body, _)| {
                body.i32_const(4321).drop();
            })
            .expect("function replacement worked");

        assert!(
            module.exports.get_exported_func(original_fn_id).is_none(),
            "replaced function cannot be gotten by ID"
        );

        // Ensure the function was replaced
        match module
            .exports
            .get_exported_func(new_fn_id)
            .expect("failed to unwrap exported func")
        {
            exp @ Export {
                item: ExportItem::Function(fid),
                ..
            } => {
                assert_eq!(*fid, new_fn_id, "retrieved function ID matches");
                assert_eq!(exp.id(), original_export_id, "export ID is unchanged");
            }
            _ => panic!("expected an Export with a Function inside"),
        }
    }

    /// Running `replace_exported_func` with a closure that returns None
    /// should replace the function with a generated no-op function
    #[test]
    fn replace_exported_func_generated_no_op() {
        let mut module = Module::default();

        // Create original function
        let mut builder = FunctionBuilder::new(&mut module.types, &[], &[]);
        builder.func_body().i32_const(1234).drop();
        let original_fn_id: FunctionId = builder.finish(vec![], &mut module.funcs);
        let original_export_id = module.exports.add("dummy", original_fn_id);

        // Replace the existing function with a new one with a reversed const value
        let new_fn_id = module
            .replace_exported_func(original_fn_id, |(body, _arg_locals)| {
                body.unreachable();
            })
            .expect("export function replacement worked");

        assert!(
            module.exports.get_exported_func(original_fn_id).is_none(),
            "replaced export function cannot be gotten by ID"
        );

        // Ensure the function was replaced
        match module
            .exports
            .get_exported_func(new_fn_id)
            .expect("failed to unwrap exported func")
        {
            exp @ Export {
                item: ExportItem::Function(fid),
                name,
                ..
            } => {
                assert_eq!(name, "dummy", "function name on export is unchanged");
                assert_eq!(*fid, new_fn_id, "retrieved function ID matches");
                assert_eq!(exp.id(), original_export_id, "export ID is unchanged");
            }
            _ => panic!("expected an Export with a Function inside"),
        }
    }

    /// Running `replace_imported_func` with a closure that builds
    /// a function should replace the existing function with the new one
    #[test]
    fn replace_imported_func() {
        let mut module = Module::default();

        // Create original import function
        let types = module.types.add(&[], &[]);
        let (original_fn_id, original_import_id) = module.add_import_func("mod", "dummy", types);

        // Replace the existing function with a new one with a reversed const value
        let new_fn_id = module
            .replace_imported_func(original_fn_id, |(body, _)| {
                body.i32_const(4321).drop();
            })
            .expect("import fn replacement worked");

        assert!(
            !module.imports.iter().any(|i| i.id() == original_import_id),
            "original import is missing",
        );

        assert!(
            module.imports.get_imported_func(original_fn_id).is_none(),
            "replaced import function cannot be gotten by ID"
        );

        assert!(
            module.imports.get_imported_func(new_fn_id).is_none(),
            "new import function cannot be gotten by ID (it is now local)"
        );

        assert!(
            matches!(module.funcs.get(new_fn_id).kind, FunctionKind::Local(_)),
            "new local function has the right kind"
        );
    }

    /// Running `replace_imported_func` with a closure that returns None
    /// should replace the function with a generated no-op function
    #[test]
    fn replace_imported_func_generated_no_op() {
        let mut module = Module::default();

        // Create original import function
        let types = module.types.add(&[], &[]);
        let (original_fn_id, original_import_id) = module.add_import_func("mod", "dummy", types);

        // Replace the existing function with a new one with a reversed const value
        let new_fn_id = module
            .replace_imported_func(original_fn_id, |(body, _arg_locals)| {
                body.unreachable();
            })
            .expect("import fn replacement worked");

        assert!(
            !module.imports.iter().any(|i| i.id() == original_import_id),
            "original import is missing",
        );

        assert!(
            module.imports.get_imported_func(original_fn_id).is_none(),
            "replaced import function cannot be gotten by ID"
        );

        assert!(
            module.imports.get_imported_func(new_fn_id).is_none(),
            "new import function cannot be gotten by ID (it is now local)"
        );

        assert!(
            matches!(module.funcs.get(new_fn_id).kind, FunctionKind::Local(_)),
            "new local function has the right kind"
        );
    }
}