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);