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