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 653 impl TypeTrace for Module { 654 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 655 where 656 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 657 { 658 // NB: Do not `..` elide unmodified fields so that we get compile errors 659 // when adding new fields that might need re-canonicalization. 660 let Self { 661 module_index: _, 662 name: _, 663 initializers: _, 664 exports: _, 665 start_func: _, 666 table_initialization: _, 667 memory_initialization: _, 668 passive_elements: _, 669 passive_elements_map: _, 670 passive_data_map: _, 671 types, 672 num_imported_funcs: _, 673 num_imported_tables: _, 674 num_imported_memories: _, 675 num_imported_globals: _, 676 num_imported_tags: _, 677 num_escaped_funcs: _, 678 needs_gc_heap: _, 679 functions, 680 tables, 681 memories: _, 682 globals, 683 global_initializers: _, 684 tags, 685 } = self; 686 687 for t in types.values().copied() { 688 func(t)?; 689 } 690 for f in functions.values() { 691 f.trace(func)?; 692 } 693 for t in tables.values() { 694 t.trace(func)?; 695 } 696 for g in globals.values() { 697 g.trace(func)?; 698 } 699 for t in tags.values() { 700 t.trace(func)?; 701 } 702 Ok(()) 703 } 704 705 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 706 where 707 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 708 { 709 // NB: Do not `..` elide unmodified fields so that we get compile errors 710 // when adding new fields that might need re-canonicalization. 711 let Self { 712 module_index: _, 713 name: _, 714 initializers: _, 715 exports: _, 716 start_func: _, 717 table_initialization: _, 718 memory_initialization: _, 719 passive_elements: _, 720 passive_elements_map: _, 721 passive_data_map: _, 722 types, 723 num_imported_funcs: _, 724 num_imported_tables: _, 725 num_imported_memories: _, 726 num_imported_globals: _, 727 num_imported_tags: _, 728 num_escaped_funcs: _, 729 needs_gc_heap: _, 730 functions, 731 tables, 732 memories: _, 733 globals, 734 global_initializers: _, 735 tags, 736 } = self; 737 738 for t in types.values_mut() { 739 func(t)?; 740 } 741 for f in functions.values_mut() { 742 f.trace_mut(func)?; 743 } 744 for t in tables.values_mut() { 745 t.trace_mut(func)?; 746 } 747 for g in globals.values_mut() { 748 g.trace_mut(func)?; 749 } 750 for t in tags.values_mut() { 751 t.trace_mut(func)?; 752 } 753 Ok(()) 754 } 755 } 756 757 /// Type information about functions in a wasm module. 758 #[derive(Debug, Serialize, Deserialize)] 759 pub struct FunctionType { 760 /// The type of this function, indexed into the module-wide type tables for 761 /// a module compilation. 762 pub signature: EngineOrModuleTypeIndex, 763 /// The index into the funcref table, if present. Note that this is 764 /// `reserved_value()` if the function does not escape from a module. 765 pub func_ref: FuncRefIndex, 766 } 767 768 impl TypeTrace for FunctionType { 769 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 770 where 771 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 772 { 773 func(self.signature) 774 } 775 776 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 777 where 778 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 779 { 780 func(&mut self.signature) 781 } 782 } 783 784 impl FunctionType { 785 /// Returns whether this function's type is one that "escapes" the current 786 /// module, meaning that the function is exported, used in `ref.func`, used 787 /// in a table, etc. 788 pub fn is_escaping(&self) -> bool { 789 !self.func_ref.is_reserved_value() 790 } 791 } 792 793 /// Index into the funcref table within a VMContext for a function. 794 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 795 pub struct FuncRefIndex(u32); 796 cranelift_entity::entity_impl!(FuncRefIndex); 797