Skip to main content

wasmtime_environ/
module.rs

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