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