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(TryVec<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: TryPrimaryMap<MemoryIndex, Option<StaticMemoryInitializer>>, 86 }, 87 } 88 89 impl Default for MemoryInitialization { default() -> Self90 fn default() -> Self { 91 Self::Segmented(TryVec::new()) 92 } 93 } 94 95 impl MemoryInitialization { 96 /// Returns whether this initialization is of the form 97 /// `MemoryInitialization::Segmented`. is_segmented(&self) -> bool98 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. init_memory(&self, state: &mut dyn InitMemory) -> bool119 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. memory_size_in_bytes(&mut self, memory_index: MemoryIndex) -> Result<u64, SizeOverflow>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. eval_offset(&mut self, memory_index: MemoryIndex, expr: &ConstExpr) -> Option<u64>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. write(&mut self, memory_index: MemoryIndex, init: &StaticMemoryInitializer) -> bool214 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: TryPrimaryMap<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: TryVec<TableSegment>, 237 } 238 239 /// Initial value for all elements in a table. 240 #[derive(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: TryVec<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. len(&self) -> u64282 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: TryVec<Initializer>, 305 306 /// Exported entities. 307 pub exports: TryIndexMap<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: TryVec<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: TryPrimaryMap<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: TryPrimaryMap<FuncIndex, FunctionType>, 357 358 /// WebAssembly tables. 359 pub tables: TryPrimaryMap<TableIndex, Table>, 360 361 /// WebAssembly linear memory plans. 362 pub memories: TryPrimaryMap<MemoryIndex, Memory>, 363 364 /// WebAssembly global variables. 365 pub globals: TryPrimaryMap<GlobalIndex, Global>, 366 367 /// WebAssembly global initializers for locally-defined globals. 368 pub global_initializers: TryPrimaryMap<DefinedGlobalIndex, ConstExpr>, 369 370 /// WebAssembly exception and control tags. 371 pub tags: TryPrimaryMap<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. new(module_index: StaticModuleIndex) -> Self392 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] func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex424 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] defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex>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] is_imported_function(&self, index: FuncIndex) -> bool443 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] table_index(&self, defined_table: DefinedTableIndex) -> TableIndex449 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] defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex>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] is_imported_table(&self, index: TableIndex) -> bool468 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] memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex474 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] defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex>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] owned_memory_index(&self, memory: DefinedMemoryIndex) -> OwnedMemoryIndex494 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] is_imported_memory(&self, index: MemoryIndex) -> bool515 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] global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex521 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] defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex>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] is_imported_global(&self, index: GlobalIndex) -> bool540 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] is_imported_tag(&self, index: TagIndex) -> bool546 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] tag_index(&self, defined_tag: DefinedTagIndex) -> TagIndex552 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] defined_tag_index(&self, tag: TagIndex) -> Option<DefinedTagIndex>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. imports(&self) -> impl ExactSizeIterator<Item = (&str, &str, EntityType)>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. import(&self, i: usize) -> Option<(&str, &str, EntityType)>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 type_of(&self, index: EntityIndex) -> EntityType590 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. push_tag( &mut self, signature: impl Into<EngineOrModuleTypeIndex>, exception: impl Into<EngineOrModuleTypeIndex>, ) -> TagIndex601 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 609 .push(Tag { 610 signature, 611 exception, 612 }) 613 .panic_on_oom() 614 } 615 616 /// Appends a new function to this module with the given type information, 617 /// used for functions that either don't escape or aren't certain whether 618 /// they escape yet. push_function(&mut self, signature: impl Into<EngineOrModuleTypeIndex>) -> FuncIndex619 pub fn push_function(&mut self, signature: impl Into<EngineOrModuleTypeIndex>) -> FuncIndex { 620 let signature = signature.into(); 621 self.functions 622 .push(FunctionType { 623 signature, 624 func_ref: FuncRefIndex::reserved_value(), 625 }) 626 .panic_on_oom() 627 } 628 629 /// Returns an iterator over all of the defined function indices in this 630 /// module. defined_func_indices(&self) -> impl ExactSizeIterator<Item = DefinedFuncIndex> + use<>631 pub fn defined_func_indices(&self) -> impl ExactSizeIterator<Item = DefinedFuncIndex> + use<> { 632 (0..self.functions.len() - self.num_imported_funcs).map(|i| DefinedFuncIndex::new(i)) 633 } 634 635 /// Returns the number of functions defined by this module itself: all 636 /// functions minus imported functions. num_defined_funcs(&self) -> usize637 pub fn num_defined_funcs(&self) -> usize { 638 self.functions.len() - self.num_imported_funcs 639 } 640 641 /// Returns the number of tables defined by this module itself: all tables 642 /// minus imported tables. num_defined_tables(&self) -> usize643 pub fn num_defined_tables(&self) -> usize { 644 self.tables.len() - self.num_imported_tables 645 } 646 647 /// Returns the number of memories defined by this module itself: all 648 /// memories minus imported memories. num_defined_memories(&self) -> usize649 pub fn num_defined_memories(&self) -> usize { 650 self.memories.len() - self.num_imported_memories 651 } 652 653 /// Returns the number of globals defined by this module itself: all 654 /// globals minus imported globals. num_defined_globals(&self) -> usize655 pub fn num_defined_globals(&self) -> usize { 656 self.globals.len() - self.num_imported_globals 657 } 658 659 /// Returns the number of tags defined by this module itself: all tags 660 /// minus imported tags. num_defined_tags(&self) -> usize661 pub fn num_defined_tags(&self) -> usize { 662 self.tags.len() - self.num_imported_tags 663 } 664 665 /// Tests whether `index` is valid for this module. is_valid(&self, index: EntityIndex) -> bool666 pub fn is_valid(&self, index: EntityIndex) -> bool { 667 match index { 668 EntityIndex::Function(i) => self.functions.is_valid(i), 669 EntityIndex::Table(i) => self.tables.is_valid(i), 670 EntityIndex::Memory(i) => self.memories.is_valid(i), 671 EntityIndex::Global(i) => self.globals.is_valid(i), 672 EntityIndex::Tag(i) => self.tags.is_valid(i), 673 } 674 } 675 } 676 677 impl TypeTrace for Module { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,678 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 679 where 680 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 681 { 682 // NB: Do not `..` elide unmodified fields so that we get compile errors 683 // when adding new fields that might need re-canonicalization. 684 let Self { 685 module_index: _, 686 strings: _, 687 name: _, 688 initializers: _, 689 exports: _, 690 start_func: _, 691 table_initialization: _, 692 memory_initialization: _, 693 passive_elements: _, 694 passive_elements_map: _, 695 passive_data_map: _, 696 types, 697 num_imported_funcs: _, 698 num_imported_tables: _, 699 num_imported_memories: _, 700 num_imported_globals: _, 701 num_imported_tags: _, 702 num_escaped_funcs: _, 703 needs_gc_heap: _, 704 functions, 705 tables, 706 memories: _, 707 globals, 708 global_initializers: _, 709 tags, 710 } = self; 711 712 for t in types.values().copied() { 713 func(t)?; 714 } 715 for f in functions.values() { 716 f.trace(func)?; 717 } 718 for t in tables.values() { 719 t.trace(func)?; 720 } 721 for g in globals.values() { 722 g.trace(func)?; 723 } 724 for t in tags.values() { 725 t.trace(func)?; 726 } 727 Ok(()) 728 } 729 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,730 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 731 where 732 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 733 { 734 // NB: Do not `..` elide unmodified fields so that we get compile errors 735 // when adding new fields that might need re-canonicalization. 736 let Self { 737 module_index: _, 738 strings: _, 739 name: _, 740 initializers: _, 741 exports: _, 742 start_func: _, 743 table_initialization: _, 744 memory_initialization: _, 745 passive_elements: _, 746 passive_elements_map: _, 747 passive_data_map: _, 748 types, 749 num_imported_funcs: _, 750 num_imported_tables: _, 751 num_imported_memories: _, 752 num_imported_globals: _, 753 num_imported_tags: _, 754 num_escaped_funcs: _, 755 needs_gc_heap: _, 756 functions, 757 tables, 758 memories: _, 759 globals, 760 global_initializers: _, 761 tags, 762 } = self; 763 764 for t in types.values_mut() { 765 func(t)?; 766 } 767 for f in functions.values_mut() { 768 f.trace_mut(func)?; 769 } 770 for t in tables.values_mut() { 771 t.trace_mut(func)?; 772 } 773 for g in globals.values_mut() { 774 g.trace_mut(func)?; 775 } 776 for t in tags.values_mut() { 777 t.trace_mut(func)?; 778 } 779 Ok(()) 780 } 781 } 782 783 /// Type information about functions in a wasm module. 784 #[derive(Debug, Serialize, Deserialize)] 785 pub struct FunctionType { 786 /// The type of this function, indexed into the module-wide type tables for 787 /// a module compilation. 788 pub signature: EngineOrModuleTypeIndex, 789 /// The index into the funcref table, if present. Note that this is 790 /// `reserved_value()` if the function does not escape from a module. 791 pub func_ref: FuncRefIndex, 792 } 793 794 impl TypeTrace for FunctionType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,795 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 796 where 797 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 798 { 799 func(self.signature) 800 } 801 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,802 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 803 where 804 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 805 { 806 func(&mut self.signature) 807 } 808 } 809 810 impl FunctionType { 811 /// Returns whether this function's type is one that "escapes" the current 812 /// module, meaning that the function is exported, used in `ref.func`, used 813 /// in a table, etc. is_escaping(&self) -> bool814 pub fn is_escaping(&self) -> bool { 815 !self.func_ref.is_reserved_value() 816 } 817 } 818 819 /// Index into the funcref table within a VMContext for a function. 820 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 821 pub struct FuncRefIndex(u32); 822 cranelift_entity::entity_impl!(FuncRefIndex); 823