dusk_wasmtime_environ/
module.rs

1//! Data structures for representing decoded wasm modules.
2
3use crate::{PrimaryMap, Tunables, WASM_PAGE_SIZE};
4use cranelift_entity::{packed_option::ReservedValue, EntityRef};
5use indexmap::IndexMap;
6use serde_derive::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use std::ops::Range;
9use wasmtime_types::*;
10
11/// Implementation styles for WebAssembly linear memory.
12#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
13pub enum MemoryStyle {
14    /// The actual memory can be resized and moved.
15    Dynamic {
16        /// Extra space to reserve when a memory must be moved due to growth.
17        reserve: u64,
18    },
19    /// Address space is allocated up front.
20    Static {
21        /// The number of mapped and unmapped pages.
22        bound: u64,
23    },
24}
25
26impl MemoryStyle {
27    /// Decide on an implementation style for the given `Memory`.
28    pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) {
29        // A heap with a maximum that doesn't exceed the static memory bound specified by the
30        // tunables make it static.
31        //
32        // If the module doesn't declare an explicit maximum treat it as 4GiB when not
33        // requested to use the static memory bound itself as the maximum.
34        let absolute_max_pages = if memory.memory64 {
35            crate::WASM64_MAX_PAGES
36        } else {
37            crate::WASM32_MAX_PAGES
38        };
39        let maximum = std::cmp::min(
40            memory.maximum.unwrap_or(absolute_max_pages),
41            if tunables.static_memory_bound_is_maximum {
42                std::cmp::min(tunables.static_memory_bound, absolute_max_pages)
43            } else {
44                absolute_max_pages
45            },
46        );
47
48        // Ensure the minimum is less than the maximum; the minimum might exceed the maximum
49        // when the memory is artificially bounded via `static_memory_bound_is_maximum` above
50        if memory.minimum <= maximum && maximum <= tunables.static_memory_bound {
51            return (
52                Self::Static {
53                    bound: tunables.static_memory_bound,
54                },
55                tunables.static_memory_offset_guard_size,
56            );
57        }
58
59        // Otherwise, make it dynamic.
60        (
61            Self::Dynamic {
62                reserve: tunables.dynamic_memory_growth_reserve,
63            },
64            tunables.dynamic_memory_offset_guard_size,
65        )
66    }
67}
68
69/// A WebAssembly linear memory description along with our chosen style for
70/// implementing it.
71#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
72pub struct MemoryPlan {
73    /// The WebAssembly linear memory description.
74    pub memory: Memory,
75    /// Our chosen implementation style.
76    pub style: MemoryStyle,
77    /// Chosen size of a guard page before the linear memory allocation.
78    pub pre_guard_size: u64,
79    /// Our chosen offset-guard size.
80    pub offset_guard_size: u64,
81}
82
83impl MemoryPlan {
84    /// Draw up a plan for implementing a `Memory`.
85    pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self {
86        let (style, offset_guard_size) = MemoryStyle::for_memory(memory, tunables);
87        Self {
88            memory,
89            style,
90            offset_guard_size,
91            pre_guard_size: if tunables.guard_before_linear_memory {
92                offset_guard_size
93            } else {
94                0
95            },
96        }
97    }
98}
99
100/// A WebAssembly linear memory initializer.
101#[derive(Clone, Debug, Serialize, Deserialize)]
102pub struct MemoryInitializer {
103    /// The index of a linear memory to initialize.
104    pub memory_index: MemoryIndex,
105    /// Optionally, a global variable giving a base index.
106    pub base: Option<GlobalIndex>,
107    /// The offset to add to the base.
108    pub offset: u64,
109    /// The range of the data to write within the linear memory.
110    ///
111    /// This range indexes into a separately stored data section which will be
112    /// provided with the compiled module's code as well.
113    pub data: Range<u32>,
114}
115
116/// Similar to the above `MemoryInitializer` but only used when memory
117/// initializers are statically known to be valid.
118#[derive(Clone, Debug, Serialize, Deserialize)]
119pub struct StaticMemoryInitializer {
120    /// The 64-bit offset, in bytes, of where this initializer starts.
121    pub offset: u64,
122
123    /// The range of data to write at `offset`, where these indices are indexes
124    /// into the compiled wasm module's data section.
125    pub data: Range<u32>,
126}
127
128/// The type of WebAssembly linear memory initialization to use for a module.
129#[derive(Debug, Serialize, Deserialize)]
130pub enum MemoryInitialization {
131    /// Memory initialization is segmented.
132    ///
133    /// Segmented initialization can be used for any module, but it is required
134    /// if:
135    ///
136    /// * A data segment referenced an imported memory.
137    /// * A data segment uses a global base.
138    ///
139    /// Segmented initialization is performed by processing the complete set of
140    /// data segments when the module is instantiated.
141    ///
142    /// This is the default memory initialization type.
143    Segmented(Vec<MemoryInitializer>),
144
145    /// Memory initialization is statically known and involves a single `memcpy`
146    /// or otherwise simply making the defined data visible.
147    ///
148    /// To be statically initialized everything must reference a defined memory
149    /// and all data segments have a statically known in-bounds base (no
150    /// globals).
151    ///
152    /// This form of memory initialization is a more optimized version of
153    /// `Segmented` where memory can be initialized with one of a few methods:
154    ///
155    /// * First it could be initialized with a single `memcpy` of data from the
156    ///   module to the linear memory.
157    /// * Otherwise techniques like `mmap` are also possible to make this data,
158    ///   which might reside in a compiled module on disk, available immediately
159    ///   in a linear memory's address space.
160    ///
161    /// To facilitate the latter of these techniques the `try_static_init`
162    /// function below, which creates this variant, takes a host page size
163    /// argument which can page-align everything to make mmap-ing possible.
164    Static {
165        /// The initialization contents for each linear memory.
166        ///
167        /// This array has, for each module's own linear memory, the contents
168        /// necessary to initialize it. If the memory has a `None` value then no
169        /// initialization is necessary (it's zero-filled). Otherwise with
170        /// `Some` the first element of the tuple is the offset in memory to
171        /// start the initialization and the `Range` is the range within the
172        /// final data section of the compiled module of bytes to copy into the
173        /// memory.
174        ///
175        /// The offset, range base, and range end are all guaranteed to be page
176        /// aligned to the page size passed in to `try_static_init`.
177        map: PrimaryMap<MemoryIndex, Option<StaticMemoryInitializer>>,
178    },
179}
180
181impl Default for MemoryInitialization {
182    fn default() -> Self {
183        Self::Segmented(Vec::new())
184    }
185}
186
187impl MemoryInitialization {
188    /// Returns whether this initialization is of the form
189    /// `MemoryInitialization::Segmented`.
190    pub fn is_segmented(&self) -> bool {
191        match self {
192            MemoryInitialization::Segmented(_) => true,
193            _ => false,
194        }
195    }
196
197    /// Performs the memory initialization steps for this set of initializers.
198    ///
199    /// This will perform wasm initialization in compliance with the wasm spec
200    /// and how data segments are processed. This doesn't need to necessarily
201    /// only be called as part of initialization, however, as it's structured to
202    /// allow learning about memory ahead-of-time at compile time possibly.
203    ///
204    /// The various callbacks provided here are used to drive the smaller bits
205    /// of initialization, such as:
206    ///
207    /// * `get_cur_size_in_pages` - gets the current size, in wasm pages, of the
208    ///   memory specified. For compile-time purposes this would be the memory
209    ///   type's minimum size.
210    ///
211    /// * `get_global` - gets the value of the global specified. This is
212    ///   statically, via validation, a pointer to the global of the correct
213    ///   type (either u32 or u64 depending on the memory), but the value
214    ///   returned here is `u64`. A `None` value can be returned to indicate
215    ///   that the global's value isn't known yet.
216    ///
217    /// * `write` - a callback used to actually write data. This indicates that
218    ///   the specified memory must receive the specified range of data at the
219    ///   specified offset. This can internally return an false error if it
220    ///   wants to fail.
221    ///
222    /// This function will return true if all memory initializers are processed
223    /// successfully. If any initializer hits an error or, for example, a
224    /// global value is needed but `None` is returned, then false will be
225    /// returned. At compile-time this typically means that the "error" in
226    /// question needs to be deferred to runtime, and at runtime this means
227    /// that an invalid initializer has been found and a trap should be
228    /// generated.
229    pub fn init_memory<T>(
230        &self,
231        state: &mut T,
232        init: InitMemory<'_, T>,
233        mut write: impl FnMut(&mut T, MemoryIndex, &StaticMemoryInitializer) -> bool,
234    ) -> bool {
235        let initializers = match self {
236            // Fall through below to the segmented memory one-by-one
237            // initialization.
238            MemoryInitialization::Segmented(list) => list,
239
240            // If previously switched to static initialization then pass through
241            // all those parameters here to the `write` callback.
242            //
243            // Note that existence of `Static` already guarantees that all
244            // indices are in-bounds.
245            MemoryInitialization::Static { map } => {
246                for (index, init) in map {
247                    if let Some(init) = init {
248                        let result = write(state, index, init);
249                        if !result {
250                            return result;
251                        }
252                    }
253                }
254                return true;
255            }
256        };
257
258        for initializer in initializers {
259            let MemoryInitializer {
260                memory_index,
261                base,
262                offset,
263                ref data,
264            } = *initializer;
265
266            // First up determine the start/end range and verify that they're
267            // in-bounds for the initial size of the memory at `memory_index`.
268            // Note that this can bail if we don't have access to globals yet
269            // (e.g. this is a task happening before instantiation at
270            // compile-time).
271            let base = match base {
272                Some(index) => match &init {
273                    InitMemory::Runtime {
274                        get_global_as_u64, ..
275                    } => get_global_as_u64(state, index),
276                    InitMemory::CompileTime(_) => return false,
277                },
278                None => 0,
279            };
280            let start = match base.checked_add(offset) {
281                Some(start) => start,
282                None => return false,
283            };
284            let len = u64::try_from(data.len()).unwrap();
285            let end = match start.checked_add(len) {
286                Some(end) => end,
287                None => return false,
288            };
289
290            let cur_size_in_pages = match &init {
291                InitMemory::CompileTime(module) => module.memory_plans[memory_index].memory.minimum,
292                InitMemory::Runtime {
293                    memory_size_in_pages,
294                    ..
295                } => memory_size_in_pages(state, memory_index),
296            };
297
298            // Note that this `minimum` can overflow if `minimum` is
299            // `1 << 48`, the maximum number of minimum pages for 64-bit
300            // memories. If this overflow happens, though, then there's no need
301            // to check the `end` value since `end` fits in a `u64` and it is
302            // naturally less than the overflowed value.
303            //
304            // This is a bit esoteric though because it's impossible to actually
305            // create a memory of `u64::MAX + 1` bytes, so this is largely just
306            // here to avoid having the multiplication here overflow in debug
307            // mode.
308            if let Some(max) = cur_size_in_pages.checked_mul(u64::from(WASM_PAGE_SIZE)) {
309                if end > max {
310                    return false;
311                }
312            }
313
314            // The limits of the data segment have been validated at this point
315            // so the `write` callback is called with the range of data being
316            // written. Any erroneous result is propagated upwards.
317            let init = StaticMemoryInitializer {
318                offset: start,
319                data: data.clone(),
320            };
321            let result = write(state, memory_index, &init);
322            if !result {
323                return result;
324            }
325        }
326
327        return true;
328    }
329}
330
331/// Argument to [`MemoryInitialization::init_memory`] indicating the current
332/// status of the instance.
333pub enum InitMemory<'a, T> {
334    /// This evaluation of memory initializers is happening at compile time.
335    /// This means that the current state of memories is whatever their initial
336    /// state is, and additionally globals are not available if data segments
337    /// have global offsets.
338    CompileTime(&'a Module),
339
340    /// Evaluation of memory initializers is happening at runtime when the
341    /// instance is available, and callbacks are provided to learn about the
342    /// instance's state.
343    Runtime {
344        /// Returns the size, in wasm pages, of the the memory specified.
345        memory_size_in_pages: &'a dyn Fn(&mut T, MemoryIndex) -> u64,
346        /// Returns the value of the global, as a `u64`. Note that this may
347        /// involve zero-extending a 32-bit global to a 64-bit number.
348        get_global_as_u64: &'a dyn Fn(&mut T, GlobalIndex) -> u64,
349    },
350}
351
352/// Implementation styles for WebAssembly tables.
353#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
354pub enum TableStyle {
355    /// Signatures are stored in the table and checked in the caller.
356    CallerChecksSignature,
357}
358
359impl TableStyle {
360    /// Decide on an implementation style for the given `Table`.
361    pub fn for_table(_table: Table, _tunables: &Tunables) -> Self {
362        Self::CallerChecksSignature
363    }
364}
365
366/// A WebAssembly table description along with our chosen style for
367/// implementing it.
368#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
369pub struct TablePlan {
370    /// The WebAssembly table description.
371    pub table: Table,
372    /// Our chosen implementation style.
373    pub style: TableStyle,
374}
375
376impl TablePlan {
377    /// Draw up a plan for implementing a `Table`.
378    pub fn for_table(table: Table, tunables: &Tunables) -> Self {
379        let style = TableStyle::for_table(table, tunables);
380        Self { table, style }
381    }
382}
383
384/// Table initialization data for all tables in the module.
385#[derive(Debug, Default, Serialize, Deserialize)]
386pub struct TableInitialization {
387    /// Initial values for tables defined within the module itself.
388    ///
389    /// This contains the initial values and initializers for tables defined
390    /// within a wasm, so excluding imported tables. This initializer can
391    /// represent null-initialized tables, element-initialized tables (e.g. with
392    /// the function-references proposal), or precomputed images of table
393    /// initialization. For example table initializers to a table that are all
394    /// in-bounds will get removed from `segment` and moved into
395    /// `initial_values` here.
396    pub initial_values: PrimaryMap<DefinedTableIndex, TableInitialValue>,
397
398    /// Element segments present in the initial wasm module which are executed
399    /// at instantiation time.
400    ///
401    /// These element segments are iterated over during instantiation to apply
402    /// any segments that weren't already moved into `initial_values` above.
403    pub segments: Vec<TableSegment>,
404}
405
406/// Initial value for all elements in a table.
407#[derive(Clone, Debug, Serialize, Deserialize)]
408pub enum TableInitialValue {
409    /// Initialize each table element to null, optionally setting some elements
410    /// to non-null given the precomputed image.
411    Null {
412        /// A precomputed image of table initializers for this table.
413        ///
414        /// This image is constructed during `try_func_table_init` and
415        /// null-initialized elements are represented with
416        /// `FuncIndex::reserved_value()`. Note that this image is empty by
417        /// default and may not encompass the entire span of the table in which
418        /// case the elements are initialized to null.
419        precomputed: Vec<FuncIndex>,
420    },
421
422    /// Initialize each table element to the function reference given
423    /// by the `FuncIndex`.
424    FuncRef(FuncIndex),
425
426    /// At instantiation time this global is loaded and the funcref value is
427    /// used to initialize the table.
428    GlobalGet(GlobalIndex),
429
430    /// Initialize the table element to an `i31ref` of the given value.
431    I31Ref(i32),
432}
433
434/// A WebAssembly table initializer segment.
435#[derive(Clone, Debug, Serialize, Deserialize)]
436pub struct TableSegment {
437    /// The index of a table to initialize.
438    pub table_index: TableIndex,
439    /// Optionally, a global variable giving a base index.
440    pub base: Option<GlobalIndex>,
441    /// The offset to add to the base.
442    pub offset: u32,
443    /// The values to write into the table elements.
444    pub elements: TableSegmentElements,
445}
446
447/// Elements of a table segment, either a list of functions or list of arbitrary
448/// expressions.
449#[derive(Clone, Debug, Serialize, Deserialize)]
450pub enum TableSegmentElements {
451    /// A sequential list of functions where `FuncIndex::reserved_value()`
452    /// indicates a null function.
453    Functions(Box<[FuncIndex]>),
454    /// Arbitrary expressions, aka either functions, null or a load of a global.
455    Expressions(Box<[TableElementExpression]>),
456}
457
458impl TableSegmentElements {
459    /// Returns the number of elements in this segment.
460    pub fn len(&self) -> u32 {
461        match self {
462            Self::Functions(s) => s.len() as u32,
463            Self::Expressions(s) => s.len() as u32,
464        }
465    }
466}
467
468/// Different kinds of expression that can initialize table elements.
469#[derive(Clone, Debug, Serialize, Deserialize)]
470pub enum TableElementExpression {
471    /// `ref.func $f`
472    Function(FuncIndex),
473    /// `global.get $g`
474    GlobalGet(GlobalIndex),
475    /// `ref.null $ty`
476    Null,
477}
478
479/// Different types that can appear in a module.
480///
481/// Note that each of these variants are intended to index further into a
482/// separate table.
483#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
484#[allow(missing_docs)]
485pub enum ModuleType {
486    Function(ModuleInternedTypeIndex),
487}
488
489impl ModuleType {
490    /// Asserts this is a `ModuleType::Function`, returning the underlying
491    /// `SignatureIndex`.
492    pub fn unwrap_function(&self) -> ModuleInternedTypeIndex {
493        match self {
494            ModuleType::Function(f) => *f,
495        }
496    }
497}
498
499/// A translated WebAssembly module, excluding the function bodies and
500/// memory initializers.
501#[derive(Default, Debug, Serialize, Deserialize)]
502pub struct Module {
503    /// The name of this wasm module, often found in the wasm file.
504    pub name: Option<String>,
505
506    /// All import records, in the order they are declared in the module.
507    pub initializers: Vec<Initializer>,
508
509    /// Exported entities.
510    pub exports: IndexMap<String, EntityIndex>,
511
512    /// The module "start" function, if present.
513    pub start_func: Option<FuncIndex>,
514
515    /// WebAssembly table initialization data, per table.
516    pub table_initialization: TableInitialization,
517
518    /// WebAssembly linear memory initializer.
519    pub memory_initialization: MemoryInitialization,
520
521    /// WebAssembly passive elements.
522    pub passive_elements: Vec<TableSegmentElements>,
523
524    /// The map from passive element index (element segment index space) to index in `passive_elements`.
525    pub passive_elements_map: BTreeMap<ElemIndex, usize>,
526
527    /// The map from passive data index (data segment index space) to index in `passive_data`.
528    pub passive_data_map: BTreeMap<DataIndex, Range<u32>>,
529
530    /// Types declared in the wasm module.
531    pub types: PrimaryMap<TypeIndex, ModuleType>,
532
533    /// Number of imported or aliased functions in the module.
534    pub num_imported_funcs: usize,
535
536    /// Number of imported or aliased tables in the module.
537    pub num_imported_tables: usize,
538
539    /// Number of imported or aliased memories in the module.
540    pub num_imported_memories: usize,
541
542    /// Number of imported or aliased globals in the module.
543    pub num_imported_globals: usize,
544
545    /// Number of functions that "escape" from this module may need to have a
546    /// `VMFuncRef` constructed for them.
547    ///
548    /// This is also the number of functions in the `functions` array below with
549    /// an `func_ref` index (and is the maximum func_ref index).
550    pub num_escaped_funcs: usize,
551
552    /// Types of functions, imported and local.
553    pub functions: PrimaryMap<FuncIndex, FunctionType>,
554
555    /// WebAssembly tables.
556    pub table_plans: PrimaryMap<TableIndex, TablePlan>,
557
558    /// WebAssembly linear memory plans.
559    pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
560
561    /// WebAssembly global variables.
562    pub globals: PrimaryMap<GlobalIndex, Global>,
563
564    /// WebAssembly global initializers for locally-defined globals.
565    pub global_initializers: PrimaryMap<DefinedGlobalIndex, GlobalInit>,
566}
567
568/// Initialization routines for creating an instance, encompassing imports,
569/// modules, instances, aliases, etc.
570#[derive(Debug, Serialize, Deserialize)]
571pub enum Initializer {
572    /// An imported item is required to be provided.
573    Import {
574        /// Name of this import
575        name: String,
576        /// The field name projection of this import
577        field: String,
578        /// Where this import will be placed, which also has type information
579        /// about the import.
580        index: EntityIndex,
581    },
582}
583
584impl Module {
585    /// Allocates the module data structures.
586    pub fn new() -> Self {
587        Module::default()
588    }
589
590    /// Convert a `DefinedFuncIndex` into a `FuncIndex`.
591    #[inline]
592    pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
593        FuncIndex::new(self.num_imported_funcs + defined_func.index())
594    }
595
596    /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the
597    /// index is an imported function.
598    #[inline]
599    pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
600        if func.index() < self.num_imported_funcs {
601            None
602        } else {
603            Some(DefinedFuncIndex::new(
604                func.index() - self.num_imported_funcs,
605            ))
606        }
607    }
608
609    /// Test whether the given function index is for an imported function.
610    #[inline]
611    pub fn is_imported_function(&self, index: FuncIndex) -> bool {
612        index.index() < self.num_imported_funcs
613    }
614
615    /// Convert a `DefinedTableIndex` into a `TableIndex`.
616    #[inline]
617    pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
618        TableIndex::new(self.num_imported_tables + defined_table.index())
619    }
620
621    /// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
622    /// index is an imported table.
623    #[inline]
624    pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
625        if table.index() < self.num_imported_tables {
626            None
627        } else {
628            Some(DefinedTableIndex::new(
629                table.index() - self.num_imported_tables,
630            ))
631        }
632    }
633
634    /// Test whether the given table index is for an imported table.
635    #[inline]
636    pub fn is_imported_table(&self, index: TableIndex) -> bool {
637        index.index() < self.num_imported_tables
638    }
639
640    /// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
641    #[inline]
642    pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
643        MemoryIndex::new(self.num_imported_memories + defined_memory.index())
644    }
645
646    /// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
647    /// index is an imported memory.
648    #[inline]
649    pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
650        if memory.index() < self.num_imported_memories {
651            None
652        } else {
653            Some(DefinedMemoryIndex::new(
654                memory.index() - self.num_imported_memories,
655            ))
656        }
657    }
658
659    /// Convert a `DefinedMemoryIndex` into an `OwnedMemoryIndex`. Returns None
660    /// if the index is an imported memory.
661    #[inline]
662    pub fn owned_memory_index(&self, memory: DefinedMemoryIndex) -> OwnedMemoryIndex {
663        assert!(
664            memory.index() < self.memory_plans.len(),
665            "non-shared memory must have an owned index"
666        );
667
668        // Once we know that the memory index is not greater than the number of
669        // plans, we can iterate through the plans up to the memory index and
670        // count how many are not shared (i.e., owned).
671        let owned_memory_index = self
672            .memory_plans
673            .iter()
674            .skip(self.num_imported_memories)
675            .take(memory.index())
676            .filter(|(_, mp)| !mp.memory.shared)
677            .count();
678        OwnedMemoryIndex::new(owned_memory_index)
679    }
680
681    /// Test whether the given memory index is for an imported memory.
682    #[inline]
683    pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
684        index.index() < self.num_imported_memories
685    }
686
687    /// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
688    #[inline]
689    pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
690        GlobalIndex::new(self.num_imported_globals + defined_global.index())
691    }
692
693    /// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
694    /// index is an imported global.
695    #[inline]
696    pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
697        if global.index() < self.num_imported_globals {
698            None
699        } else {
700            Some(DefinedGlobalIndex::new(
701                global.index() - self.num_imported_globals,
702            ))
703        }
704    }
705
706    /// Test whether the given global index is for an imported global.
707    #[inline]
708    pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
709        index.index() < self.num_imported_globals
710    }
711
712    /// Returns an iterator of all the imports in this module, along with their
713    /// module name, field name, and type that's being imported.
714    pub fn imports(&self) -> impl ExactSizeIterator<Item = (&str, &str, EntityType)> {
715        self.initializers.iter().map(move |i| match i {
716            Initializer::Import { name, field, index } => {
717                (name.as_str(), field.as_str(), self.type_of(*index))
718            }
719        })
720    }
721
722    /// Returns the type of an item based on its index
723    pub fn type_of(&self, index: EntityIndex) -> EntityType {
724        match index {
725            EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
726            EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
727            EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
728            EntityIndex::Function(i) => {
729                EntityType::Function(EngineOrModuleTypeIndex::Module(self.functions[i].signature))
730            }
731        }
732    }
733
734    /// Appends a new function to this module with the given type information,
735    /// used for functions that either don't escape or aren't certain whether
736    /// they escape yet.
737    pub fn push_function(&mut self, signature: ModuleInternedTypeIndex) -> FuncIndex {
738        self.functions.push(FunctionType {
739            signature,
740            func_ref: FuncRefIndex::reserved_value(),
741        })
742    }
743
744    /// Appends a new function to this module with the given type information.
745    pub fn push_escaped_function(
746        &mut self,
747        signature: ModuleInternedTypeIndex,
748        func_ref: FuncRefIndex,
749    ) -> FuncIndex {
750        self.functions.push(FunctionType {
751            signature,
752            func_ref,
753        })
754    }
755}
756
757/// Type information about functions in a wasm module.
758#[derive(Debug, Serialize, Deserialize)]
759pub struct FunctionType {
760    /// The type of this function, indexed into the module-wide type tables for
761    /// a module compilation.
762    pub signature: ModuleInternedTypeIndex,
763    /// The index into the funcref table, if present. Note that this is
764    /// `reserved_value()` if the function does not escape from a module.
765    pub func_ref: FuncRefIndex,
766}
767
768impl FunctionType {
769    /// Returns whether this function's type is one that "escapes" the current
770    /// module, meaning that the function is exported, used in `ref.func`, used
771    /// in a table, etc.
772    pub fn is_escaping(&self) -> bool {
773        !self.func_ref.is_reserved_value()
774    }
775}
776
777/// Index into the funcref table within a VMContext for a function.
778#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
779pub struct FuncRefIndex(u32);
780cranelift_entity::entity_impl!(FuncRefIndex);