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