Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/module.rs
3068 views
1
//! Data structures for representing decoded wasm modules.
2
3
use crate::prelude::*;
4
use crate::*;
5
use alloc::collections::BTreeMap;
6
use core::ops::Range;
7
use cranelift_entity::{EntityRef, packed_option::ReservedValue};
8
use serde_derive::{Deserialize, Serialize};
9
10
/// A WebAssembly linear memory initializer.
11
#[derive(Clone, Debug, Serialize, Deserialize)]
12
pub 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)]
27
pub 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)]
38
pub 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(Vec<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: PrimaryMap<MemoryIndex, Option<StaticMemoryInitializer>>,
86
},
87
}
88
89
impl Default for MemoryInitialization {
90
fn default() -> Self {
91
Self::Segmented(Vec::new())
92
}
93
}
94
95
impl 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.
200
pub 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)]
219
pub 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: PrimaryMap<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: Vec<TableSegment>,
237
}
238
239
/// Initial value for all elements in a table.
240
#[derive(Clone, Debug, Serialize, Deserialize)]
241
pub 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: Vec<FuncIndex>,
253
},
254
/// An arbitrary const expression.
255
Expr(ConstExpr),
256
}
257
258
/// A WebAssembly table initializer segment.
259
#[derive(Clone, Debug, Serialize, Deserialize)]
260
pub 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
/// Elements of a table segment, either a list of functions or list of arbitrary
270
/// expressions.
271
#[derive(Clone, Debug, Serialize, Deserialize)]
272
pub enum TableSegmentElements {
273
/// A sequential list of functions where `FuncIndex::reserved_value()`
274
/// indicates a null function.
275
Functions(Box<[FuncIndex]>),
276
/// Arbitrary expressions, aka either functions, null or a load of a global.
277
Expressions(Box<[ConstExpr]>),
278
}
279
280
impl TableSegmentElements {
281
/// Returns the number of elements in this segment.
282
pub fn len(&self) -> u64 {
283
match self {
284
Self::Functions(s) => u64::try_from(s.len()).unwrap(),
285
Self::Expressions(s) => u64::try_from(s.len()).unwrap(),
286
}
287
}
288
}
289
290
/// A translated WebAssembly module, excluding the function bodies and
291
/// memory initializers.
292
#[derive(Debug, Serialize, Deserialize)]
293
pub struct Module {
294
/// This module's index.
295
pub module_index: StaticModuleIndex,
296
297
/// The name of this wasm module, often found in the wasm file.
298
pub name: Option<String>,
299
300
/// All import records, in the order they are declared in the module.
301
pub initializers: Vec<Initializer>,
302
303
/// Exported entities.
304
pub exports: IndexMap<String, EntityIndex>,
305
306
/// The module "start" function, if present.
307
pub start_func: Option<FuncIndex>,
308
309
/// WebAssembly table initialization data, per table.
310
pub table_initialization: TableInitialization,
311
312
/// WebAssembly linear memory initializer.
313
pub memory_initialization: MemoryInitialization,
314
315
/// WebAssembly passive elements.
316
pub passive_elements: Vec<TableSegmentElements>,
317
318
/// The map from passive element index (element segment index space) to index in `passive_elements`.
319
pub passive_elements_map: BTreeMap<ElemIndex, usize>,
320
321
/// The map from passive data index (data segment index space) to index in `passive_data`.
322
pub passive_data_map: BTreeMap<DataIndex, Range<u32>>,
323
324
/// Types declared in the wasm module.
325
pub types: PrimaryMap<TypeIndex, EngineOrModuleTypeIndex>,
326
327
/// Number of imported or aliased functions in the module.
328
pub num_imported_funcs: usize,
329
330
/// Number of imported or aliased tables in the module.
331
pub num_imported_tables: usize,
332
333
/// Number of imported or aliased memories in the module.
334
pub num_imported_memories: usize,
335
336
/// Number of imported or aliased globals in the module.
337
pub num_imported_globals: usize,
338
339
/// Number of imported or aliased tags in the module.
340
pub num_imported_tags: usize,
341
342
/// Does this module need a GC heap to run?
343
pub needs_gc_heap: bool,
344
345
/// Number of functions that "escape" from this module may need to have a
346
/// `VMFuncRef` constructed for them.
347
///
348
/// This is also the number of functions in the `functions` array below with
349
/// an `func_ref` index (and is the maximum func_ref index).
350
pub num_escaped_funcs: usize,
351
352
/// Types of functions, imported and local.
353
pub functions: PrimaryMap<FuncIndex, FunctionType>,
354
355
/// WebAssembly tables.
356
pub tables: PrimaryMap<TableIndex, Table>,
357
358
/// WebAssembly linear memory plans.
359
pub memories: PrimaryMap<MemoryIndex, Memory>,
360
361
/// WebAssembly global variables.
362
pub globals: PrimaryMap<GlobalIndex, Global>,
363
364
/// WebAssembly global initializers for locally-defined globals.
365
pub global_initializers: PrimaryMap<DefinedGlobalIndex, ConstExpr>,
366
367
/// WebAssembly exception and control tags.
368
pub tags: PrimaryMap<TagIndex, Tag>,
369
}
370
371
/// Initialization routines for creating an instance, encompassing imports,
372
/// modules, instances, aliases, etc.
373
#[derive(Debug, Serialize, Deserialize)]
374
pub enum Initializer {
375
/// An imported item is required to be provided.
376
Import {
377
/// Name of this import
378
name: String,
379
/// The field name projection of this import
380
field: String,
381
/// Where this import will be placed, which also has type information
382
/// about the import.
383
index: EntityIndex,
384
},
385
}
386
387
impl Module {
388
/// Allocates the module data structures.
389
pub fn new(module_index: StaticModuleIndex) -> Self {
390
Self {
391
module_index,
392
name: Default::default(),
393
initializers: Default::default(),
394
exports: Default::default(),
395
start_func: Default::default(),
396
table_initialization: Default::default(),
397
memory_initialization: Default::default(),
398
passive_elements: Default::default(),
399
passive_elements_map: Default::default(),
400
passive_data_map: Default::default(),
401
types: Default::default(),
402
num_imported_funcs: Default::default(),
403
num_imported_tables: Default::default(),
404
num_imported_memories: Default::default(),
405
num_imported_globals: Default::default(),
406
num_imported_tags: Default::default(),
407
needs_gc_heap: Default::default(),
408
num_escaped_funcs: Default::default(),
409
functions: Default::default(),
410
tables: Default::default(),
411
memories: Default::default(),
412
globals: Default::default(),
413
global_initializers: Default::default(),
414
tags: Default::default(),
415
}
416
}
417
418
/// Convert a `DefinedFuncIndex` into a `FuncIndex`.
419
#[inline]
420
pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
421
FuncIndex::new(self.num_imported_funcs + defined_func.index())
422
}
423
424
/// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the
425
/// index is an imported function.
426
#[inline]
427
pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
428
if func.index() < self.num_imported_funcs {
429
None
430
} else {
431
Some(DefinedFuncIndex::new(
432
func.index() - self.num_imported_funcs,
433
))
434
}
435
}
436
437
/// Test whether the given function index is for an imported function.
438
#[inline]
439
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
440
index.index() < self.num_imported_funcs
441
}
442
443
/// Convert a `DefinedTableIndex` into a `TableIndex`.
444
#[inline]
445
pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
446
TableIndex::new(self.num_imported_tables + defined_table.index())
447
}
448
449
/// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
450
/// index is an imported table.
451
#[inline]
452
pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
453
if table.index() < self.num_imported_tables {
454
None
455
} else {
456
Some(DefinedTableIndex::new(
457
table.index() - self.num_imported_tables,
458
))
459
}
460
}
461
462
/// Test whether the given table index is for an imported table.
463
#[inline]
464
pub fn is_imported_table(&self, index: TableIndex) -> bool {
465
index.index() < self.num_imported_tables
466
}
467
468
/// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
469
#[inline]
470
pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
471
MemoryIndex::new(self.num_imported_memories + defined_memory.index())
472
}
473
474
/// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
475
/// index is an imported memory.
476
#[inline]
477
pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
478
if memory.index() < self.num_imported_memories {
479
None
480
} else {
481
Some(DefinedMemoryIndex::new(
482
memory.index() - self.num_imported_memories,
483
))
484
}
485
}
486
487
/// Convert a `DefinedMemoryIndex` into an `OwnedMemoryIndex`. Returns None
488
/// if the index is an imported memory.
489
#[inline]
490
pub fn owned_memory_index(&self, memory: DefinedMemoryIndex) -> OwnedMemoryIndex {
491
assert!(
492
memory.index() < self.memories.len(),
493
"non-shared memory must have an owned index"
494
);
495
496
// Once we know that the memory index is not greater than the number of
497
// plans, we can iterate through the plans up to the memory index and
498
// count how many are not shared (i.e., owned).
499
let owned_memory_index = self
500
.memories
501
.iter()
502
.skip(self.num_imported_memories)
503
.take(memory.index())
504
.filter(|(_, mp)| !mp.shared)
505
.count();
506
OwnedMemoryIndex::new(owned_memory_index)
507
}
508
509
/// Test whether the given memory index is for an imported memory.
510
#[inline]
511
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
512
index.index() < self.num_imported_memories
513
}
514
515
/// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
516
#[inline]
517
pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
518
GlobalIndex::new(self.num_imported_globals + defined_global.index())
519
}
520
521
/// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
522
/// index is an imported global.
523
#[inline]
524
pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
525
if global.index() < self.num_imported_globals {
526
None
527
} else {
528
Some(DefinedGlobalIndex::new(
529
global.index() - self.num_imported_globals,
530
))
531
}
532
}
533
534
/// Test whether the given global index is for an imported global.
535
#[inline]
536
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
537
index.index() < self.num_imported_globals
538
}
539
540
/// Test whether the given tag index is for an imported tag.
541
#[inline]
542
pub fn is_imported_tag(&self, index: TagIndex) -> bool {
543
index.index() < self.num_imported_tags
544
}
545
546
/// Convert a `DefinedTagIndex` into a `TagIndex`.
547
#[inline]
548
pub fn tag_index(&self, defined_tag: DefinedTagIndex) -> TagIndex {
549
TagIndex::new(self.num_imported_tags + defined_tag.index())
550
}
551
552
/// Convert a `TagIndex` into a `DefinedTagIndex`. Returns None if the
553
/// index is an imported tag.
554
#[inline]
555
pub fn defined_tag_index(&self, tag: TagIndex) -> Option<DefinedTagIndex> {
556
if tag.index() < self.num_imported_tags {
557
None
558
} else {
559
Some(DefinedTagIndex::new(tag.index() - self.num_imported_tags))
560
}
561
}
562
563
/// Returns an iterator of all the imports in this module, along with their
564
/// module name, field name, and type that's being imported.
565
pub fn imports(&self) -> impl ExactSizeIterator<Item = (&str, &str, EntityType)> {
566
self.initializers.iter().map(move |i| match i {
567
Initializer::Import { name, field, index } => {
568
(name.as_str(), field.as_str(), 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((name, field, self.type_of(*index))),
577
}
578
}
579
580
/// Returns the type of an item based on its index
581
pub fn type_of(&self, index: EntityIndex) -> EntityType {
582
match index {
583
EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
584
EntityIndex::Table(i) => EntityType::Table(self.tables[i]),
585
EntityIndex::Memory(i) => EntityType::Memory(self.memories[i]),
586
EntityIndex::Function(i) => EntityType::Function(self.functions[i].signature),
587
EntityIndex::Tag(i) => EntityType::Tag(self.tags[i]),
588
}
589
}
590
591
/// Appends a new tag to this module with the given type information.
592
pub fn push_tag(
593
&mut self,
594
signature: impl Into<EngineOrModuleTypeIndex>,
595
exception: impl Into<EngineOrModuleTypeIndex>,
596
) -> TagIndex {
597
let signature = signature.into();
598
let exception = exception.into();
599
self.tags.push(Tag {
600
signature,
601
exception,
602
})
603
}
604
605
/// Appends a new function to this module with the given type information,
606
/// used for functions that either don't escape or aren't certain whether
607
/// they escape yet.
608
pub fn push_function(&mut self, signature: impl Into<EngineOrModuleTypeIndex>) -> FuncIndex {
609
let signature = signature.into();
610
self.functions.push(FunctionType {
611
signature,
612
func_ref: FuncRefIndex::reserved_value(),
613
})
614
}
615
616
/// Returns an iterator over all of the defined function indices in this
617
/// module.
618
pub fn defined_func_indices(&self) -> impl ExactSizeIterator<Item = DefinedFuncIndex> + use<> {
619
(0..self.functions.len() - self.num_imported_funcs).map(|i| DefinedFuncIndex::new(i))
620
}
621
622
/// Returns the number of functions defined by this module itself: all
623
/// functions minus imported functions.
624
pub fn num_defined_funcs(&self) -> usize {
625
self.functions.len() - self.num_imported_funcs
626
}
627
628
/// Returns the number of tables defined by this module itself: all tables
629
/// minus imported tables.
630
pub fn num_defined_tables(&self) -> usize {
631
self.tables.len() - self.num_imported_tables
632
}
633
634
/// Returns the number of memories defined by this module itself: all
635
/// memories minus imported memories.
636
pub fn num_defined_memories(&self) -> usize {
637
self.memories.len() - self.num_imported_memories
638
}
639
640
/// Returns the number of globals defined by this module itself: all
641
/// globals minus imported globals.
642
pub fn num_defined_globals(&self) -> usize {
643
self.globals.len() - self.num_imported_globals
644
}
645
646
/// Returns the number of tags defined by this module itself: all tags
647
/// minus imported tags.
648
pub fn num_defined_tags(&self) -> usize {
649
self.tags.len() - self.num_imported_tags
650
}
651
652
/// Tests whether `index` is valid for this module.
653
pub fn is_valid(&self, index: EntityIndex) -> bool {
654
match index {
655
EntityIndex::Function(i) => self.functions.is_valid(i),
656
EntityIndex::Table(i) => self.tables.is_valid(i),
657
EntityIndex::Memory(i) => self.memories.is_valid(i),
658
EntityIndex::Global(i) => self.globals.is_valid(i),
659
EntityIndex::Tag(i) => self.tags.is_valid(i),
660
}
661
}
662
}
663
664
impl TypeTrace for Module {
665
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
666
where
667
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
668
{
669
// NB: Do not `..` elide unmodified fields so that we get compile errors
670
// when adding new fields that might need re-canonicalization.
671
let Self {
672
module_index: _,
673
name: _,
674
initializers: _,
675
exports: _,
676
start_func: _,
677
table_initialization: _,
678
memory_initialization: _,
679
passive_elements: _,
680
passive_elements_map: _,
681
passive_data_map: _,
682
types,
683
num_imported_funcs: _,
684
num_imported_tables: _,
685
num_imported_memories: _,
686
num_imported_globals: _,
687
num_imported_tags: _,
688
num_escaped_funcs: _,
689
needs_gc_heap: _,
690
functions,
691
tables,
692
memories: _,
693
globals,
694
global_initializers: _,
695
tags,
696
} = self;
697
698
for t in types.values().copied() {
699
func(t)?;
700
}
701
for f in functions.values() {
702
f.trace(func)?;
703
}
704
for t in tables.values() {
705
t.trace(func)?;
706
}
707
for g in globals.values() {
708
g.trace(func)?;
709
}
710
for t in tags.values() {
711
t.trace(func)?;
712
}
713
Ok(())
714
}
715
716
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
717
where
718
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
719
{
720
// NB: Do not `..` elide unmodified fields so that we get compile errors
721
// when adding new fields that might need re-canonicalization.
722
let Self {
723
module_index: _,
724
name: _,
725
initializers: _,
726
exports: _,
727
start_func: _,
728
table_initialization: _,
729
memory_initialization: _,
730
passive_elements: _,
731
passive_elements_map: _,
732
passive_data_map: _,
733
types,
734
num_imported_funcs: _,
735
num_imported_tables: _,
736
num_imported_memories: _,
737
num_imported_globals: _,
738
num_imported_tags: _,
739
num_escaped_funcs: _,
740
needs_gc_heap: _,
741
functions,
742
tables,
743
memories: _,
744
globals,
745
global_initializers: _,
746
tags,
747
} = self;
748
749
for t in types.values_mut() {
750
func(t)?;
751
}
752
for f in functions.values_mut() {
753
f.trace_mut(func)?;
754
}
755
for t in tables.values_mut() {
756
t.trace_mut(func)?;
757
}
758
for g in globals.values_mut() {
759
g.trace_mut(func)?;
760
}
761
for t in tags.values_mut() {
762
t.trace_mut(func)?;
763
}
764
Ok(())
765
}
766
}
767
768
/// Type information about functions in a wasm module.
769
#[derive(Debug, Serialize, Deserialize)]
770
pub struct FunctionType {
771
/// The type of this function, indexed into the module-wide type tables for
772
/// a module compilation.
773
pub signature: EngineOrModuleTypeIndex,
774
/// The index into the funcref table, if present. Note that this is
775
/// `reserved_value()` if the function does not escape from a module.
776
pub func_ref: FuncRefIndex,
777
}
778
779
impl TypeTrace for FunctionType {
780
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
781
where
782
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
783
{
784
func(self.signature)
785
}
786
787
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
788
where
789
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
790
{
791
func(&mut self.signature)
792
}
793
}
794
795
impl FunctionType {
796
/// Returns whether this function's type is one that "escapes" the current
797
/// module, meaning that the function is exported, used in `ref.func`, used
798
/// in a table, etc.
799
pub fn is_escaping(&self) -> bool {
800
!self.func_ref.is_reserved_value()
801
}
802
}
803
804
/// Index into the funcref table within a VMContext for a function.
805
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
806
pub struct FuncRefIndex(u32);
807
cranelift_entity::entity_impl!(FuncRefIndex);
808
809