1 use crate::error::{OutOfMemory, Result, bail}; 2 use crate::module::{ 3 FuncRefIndex, Initializer, MemoryInitialization, MemoryInitializer, Module, TableSegment, 4 TableSegmentElements, 5 }; 6 use crate::prelude::*; 7 use crate::{ 8 ConstExpr, ConstOp, DataIndex, DefinedFuncIndex, ElemIndex, EngineOrModuleTypeIndex, 9 EntityIndex, EntityType, FuncIndex, FuncKey, GlobalIndex, IndexType, InitMemory, MemoryIndex, 10 ModuleInternedTypeIndex, ModuleTypesBuilder, PanicOnOom as _, PrimaryMap, SizeOverflow, 11 StaticMemoryInitializer, StaticModuleIndex, TableIndex, TableInitialValue, Tag, TagIndex, 12 Tunables, TypeConvert, TypeIndex, WasmError, WasmHeapTopType, WasmHeapType, WasmResult, 13 WasmValType, WasmparserTypeConverter, 14 }; 15 use cranelift_entity::SecondaryMap; 16 use cranelift_entity::packed_option::ReservedValue; 17 use std::borrow::Cow; 18 use std::collections::HashMap; 19 use std::mem; 20 use std::path::PathBuf; 21 use std::sync::Arc; 22 use wasmparser::{ 23 CustomSectionReader, DataKind, ElementItems, ElementKind, Encoding, ExternalKind, 24 FuncToValidate, FunctionBody, KnownCustom, NameSectionReader, Naming, Parser, Payload, TypeRef, 25 Validator, ValidatorResources, types::Types, 26 }; 27 28 /// Object containing the standalone environment information. 29 pub struct ModuleEnvironment<'a, 'data> { 30 /// The current module being translated 31 result: ModuleTranslation<'data>, 32 33 /// Intern'd types for this entire translation, shared by all modules. 34 types: &'a mut ModuleTypesBuilder, 35 36 // Various bits and pieces of configuration 37 validator: &'a mut Validator, 38 tunables: &'a Tunables, 39 } 40 41 /// The result of translating via `ModuleEnvironment`. 42 /// 43 /// Function bodies are not yet translated, and data initializers have not yet 44 /// been copied out of the original buffer. 45 pub struct ModuleTranslation<'data> { 46 /// Module information. 47 pub module: Module, 48 49 /// The input wasm binary. 50 /// 51 /// This can be useful, for example, when modules are parsed from a 52 /// component and the embedder wants access to the raw wasm modules 53 /// themselves. 54 pub wasm: &'data [u8], 55 56 /// The byte offset of this module's Wasm binary within the outer 57 /// binary (e.g. a component). For standalone modules this is 0. 58 /// This is used to convert component-relative source locations to 59 /// module-relative source locations. 60 pub wasm_module_offset: u64, 61 62 /// References to the function bodies. 63 pub function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>, 64 65 /// For each imported function, the single statically-known function that 66 /// always satisfies that import, if any. 67 /// 68 /// This is used to turn what would otherwise be indirect calls through the 69 /// imports table into direct calls, when possible. 70 /// 71 /// When filled in, this only ever contains 72 /// `FuncKey::DefinedWasmFunction(..)`s and `FuncKey::Intrinsic(..)`s. 73 pub known_imported_functions: SecondaryMap<FuncIndex, Option<FuncKey>>, 74 75 /// A list of type signatures which are considered exported from this 76 /// module, or those that can possibly be called. This list is sorted, and 77 /// trampolines for each of these signatures are required. 78 pub exported_signatures: Vec<ModuleInternedTypeIndex>, 79 80 /// DWARF debug information, if enabled, parsed from the module. 81 pub debuginfo: DebugInfoData<'data>, 82 83 /// Set if debuginfo was found but it was not parsed due to `Tunables` 84 /// configuration. 85 pub has_unparsed_debuginfo: bool, 86 87 /// List of data segments found in this module which should be concatenated 88 /// together for the final compiled artifact. 89 /// 90 /// These data segments, when concatenated, are indexed by the 91 /// `MemoryInitializer` type. 92 pub data: Vec<Cow<'data, [u8]>>, 93 94 /// The desired alignment of `data` in the final data section of the object 95 /// file that we'll emit. 96 /// 97 /// Note that this is 1 by default but `MemoryInitialization::Static` might 98 /// switch this to a higher alignment to facilitate mmap-ing data from 99 /// an object file into a linear memory. 100 pub data_align: Option<u64>, 101 102 /// Total size of all data pushed onto `data` so far. 103 total_data: u32, 104 105 /// List of passive element segments found in this module which will get 106 /// concatenated for the final artifact. 107 pub passive_data: Vec<&'data [u8]>, 108 109 /// Total size of all passive data pushed into `passive_data` so far. 110 total_passive_data: u32, 111 112 /// When we're parsing the code section this will be incremented so we know 113 /// which function is currently being defined. 114 code_index: u32, 115 116 /// The type information of the current module made available at the end of the 117 /// validation process. 118 types: Option<Types>, 119 } 120 121 impl<'data> ModuleTranslation<'data> { 122 /// Create a new translation for the module with the given index. new(module_index: StaticModuleIndex) -> Self123 pub fn new(module_index: StaticModuleIndex) -> Self { 124 Self { 125 module: Module::new(module_index), 126 wasm: &[], 127 wasm_module_offset: 0, 128 function_body_inputs: PrimaryMap::default(), 129 known_imported_functions: SecondaryMap::default(), 130 exported_signatures: Vec::default(), 131 debuginfo: DebugInfoData::default(), 132 has_unparsed_debuginfo: false, 133 data: Vec::default(), 134 data_align: None, 135 total_data: 0, 136 passive_data: Vec::default(), 137 total_passive_data: 0, 138 code_index: 0, 139 types: None, 140 } 141 } 142 143 /// Returns a reference to the type information of the current module. get_types(&self) -> &Types144 pub fn get_types(&self) -> &Types { 145 self.types 146 .as_ref() 147 .expect("module type information to be available") 148 } 149 150 /// Get this translation's module's index. module_index(&self) -> StaticModuleIndex151 pub fn module_index(&self) -> StaticModuleIndex { 152 self.module.module_index 153 } 154 } 155 156 /// Contains function data: byte code and its offset in the module. 157 pub struct FunctionBodyData<'a> { 158 /// The body of the function, containing code and locals. 159 pub body: FunctionBody<'a>, 160 /// Validator for the function body 161 pub validator: FuncToValidate<ValidatorResources>, 162 } 163 164 #[derive(Debug, Default)] 165 #[expect(missing_docs, reason = "self-describing fields")] 166 pub struct DebugInfoData<'a> { 167 pub dwarf: Dwarf<'a>, 168 pub name_section: NameSection<'a>, 169 pub wasm_file: WasmFileInfo, 170 pub debug_loc: gimli::DebugLoc<Reader<'a>>, 171 pub debug_loclists: gimli::DebugLocLists<Reader<'a>>, 172 pub debug_ranges: gimli::DebugRanges<Reader<'a>>, 173 pub debug_rnglists: gimli::DebugRngLists<Reader<'a>>, 174 pub debug_cu_index: gimli::DebugCuIndex<Reader<'a>>, 175 pub debug_tu_index: gimli::DebugTuIndex<Reader<'a>>, 176 } 177 178 #[expect(missing_docs, reason = "self-describing")] 179 pub type Dwarf<'input> = gimli::Dwarf<Reader<'input>>; 180 181 type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>; 182 183 #[derive(Debug, Default)] 184 #[expect(missing_docs, reason = "self-describing fields")] 185 pub struct NameSection<'a> { 186 pub module_name: Option<&'a str>, 187 pub func_names: HashMap<FuncIndex, &'a str>, 188 pub locals_names: HashMap<FuncIndex, HashMap<u32, &'a str>>, 189 } 190 191 #[derive(Debug, Default)] 192 #[expect(missing_docs, reason = "self-describing fields")] 193 pub struct WasmFileInfo { 194 pub path: Option<PathBuf>, 195 pub code_section_offset: u64, 196 pub imported_func_count: u32, 197 pub funcs: Vec<FunctionMetadata>, 198 } 199 200 #[derive(Debug)] 201 #[expect(missing_docs, reason = "self-describing fields")] 202 pub struct FunctionMetadata { 203 pub params: Box<[WasmValType]>, 204 pub locals: Box<[(u32, WasmValType)]>, 205 } 206 207 impl<'a, 'data> ModuleEnvironment<'a, 'data> { 208 /// Allocates the environment data structures. new( tunables: &'a Tunables, validator: &'a mut Validator, types: &'a mut ModuleTypesBuilder, module_index: StaticModuleIndex, ) -> Self209 pub fn new( 210 tunables: &'a Tunables, 211 validator: &'a mut Validator, 212 types: &'a mut ModuleTypesBuilder, 213 module_index: StaticModuleIndex, 214 ) -> Self { 215 Self { 216 result: ModuleTranslation::new(module_index), 217 types, 218 tunables, 219 validator, 220 } 221 } 222 223 /// Translate a wasm module using this environment. 224 /// 225 /// This function will translate the `data` provided with `parser`, 226 /// validating everything along the way with this environment's validator. 227 /// 228 /// The result of translation, [`ModuleTranslation`], contains everything 229 /// necessary to compile functions afterwards as well as learn type 230 /// information about the module at runtime. translate( mut self, parser: Parser, data: &'data [u8], ) -> Result<ModuleTranslation<'data>>231 pub fn translate( 232 mut self, 233 parser: Parser, 234 data: &'data [u8], 235 ) -> Result<ModuleTranslation<'data>> { 236 self.result.wasm = data; 237 238 for payload in parser.parse_all(data) { 239 self.translate_payload(payload?)?; 240 } 241 242 Ok(self.result) 243 } 244 translate_payload(&mut self, payload: Payload<'data>) -> Result<()>245 fn translate_payload(&mut self, payload: Payload<'data>) -> Result<()> { 246 match payload { 247 Payload::Version { 248 num, 249 encoding, 250 range, 251 } => { 252 self.validator.version(num, encoding, &range)?; 253 match encoding { 254 Encoding::Module => {} 255 Encoding::Component => { 256 bail!("expected a WebAssembly module but was given a WebAssembly component") 257 } 258 } 259 } 260 261 Payload::End(offset) => { 262 self.result.types = Some(self.validator.end(offset)?); 263 264 // With the `escaped_funcs` set of functions finished 265 // we can calculate the set of signatures that are exported as 266 // the set of exported functions' signatures. 267 self.result.exported_signatures = self 268 .result 269 .module 270 .functions 271 .iter() 272 .filter_map(|(_, func)| { 273 if func.is_escaping() { 274 Some(func.signature.unwrap_module_type_index()) 275 } else { 276 None 277 } 278 }) 279 .collect(); 280 self.result.exported_signatures.sort_unstable(); 281 self.result.exported_signatures.dedup(); 282 } 283 284 Payload::TypeSection(types) => { 285 self.validator.type_section(&types)?; 286 287 let count = self.validator.types(0).unwrap().core_type_count_in_module(); 288 log::trace!("interning {count} Wasm types"); 289 290 let capacity = usize::try_from(count).unwrap(); 291 self.result.module.types.reserve(capacity)?; 292 self.types.reserve_wasm_signatures(capacity); 293 294 // Iterate over each *rec group* -- not type -- defined in the 295 // types section. Rec groups are the unit of canonicalization 296 // and therefore the unit at which we need to process at a 297 // time. `wasmparser` has already done the hard work of 298 // de-duplicating and canonicalizing the rec groups within the 299 // module for us, we just need to translate them into our data 300 // structures. Note that, if the Wasm defines duplicate rec 301 // groups, we need copy the duplicates over (shallowly) as well, 302 // so that our types index space doesn't have holes. 303 let mut type_index = 0; 304 while type_index < count { 305 let validator_types = self.validator.types(0).unwrap(); 306 307 // Get the rec group for the current type index, which is 308 // always the first type defined in a rec group. 309 log::trace!("looking up wasmparser type for index {type_index}"); 310 let core_type_id = validator_types.core_type_at_in_module(type_index); 311 log::trace!( 312 " --> {core_type_id:?} = {:?}", 313 validator_types[core_type_id], 314 ); 315 let rec_group_id = validator_types.rec_group_id_of(core_type_id); 316 debug_assert_eq!( 317 validator_types 318 .rec_group_elements(rec_group_id) 319 .position(|id| id == core_type_id), 320 Some(0) 321 ); 322 323 // Intern the rec group and then fill in this module's types 324 // index space. 325 let interned = self.types.intern_rec_group(validator_types, rec_group_id)?; 326 let elems = self.types.rec_group_elements(interned); 327 let len = elems.len(); 328 self.result.module.types.reserve(len)?; 329 for ty in elems { 330 self.result.module.types.push(ty.into())?; 331 } 332 333 // Advance `type_index` to the start of the next rec group. 334 type_index += u32::try_from(len).unwrap(); 335 } 336 } 337 338 Payload::ImportSection(imports) => { 339 self.validator.import_section(&imports)?; 340 341 let cnt = usize::try_from(imports.count()).unwrap(); 342 self.result.module.initializers.reserve(cnt)?; 343 344 for entry in imports.into_imports() { 345 let import = entry?; 346 let ty = match import.ty { 347 TypeRef::Func(index) => { 348 let index = TypeIndex::from_u32(index); 349 let interned_index = self.result.module.types[index]; 350 self.result.module.num_imported_funcs += 1; 351 self.result.debuginfo.wasm_file.imported_func_count += 1; 352 EntityType::Function(interned_index) 353 } 354 TypeRef::Memory(ty) => { 355 self.result.module.num_imported_memories += 1; 356 EntityType::Memory(ty.into()) 357 } 358 TypeRef::Global(ty) => { 359 self.result.module.num_imported_globals += 1; 360 EntityType::Global(self.convert_global_type(&ty)?) 361 } 362 TypeRef::Table(ty) => { 363 self.result.module.num_imported_tables += 1; 364 EntityType::Table(self.convert_table_type(&ty)?) 365 } 366 TypeRef::Tag(ty) => { 367 let index = TypeIndex::from_u32(ty.func_type_idx); 368 let signature = self.result.module.types[index]; 369 let exception = self.types.define_exception_type_for_tag( 370 signature.unwrap_module_type_index(), 371 ); 372 let tag = Tag { 373 signature, 374 exception: EngineOrModuleTypeIndex::Module(exception), 375 }; 376 self.result.module.num_imported_tags += 1; 377 EntityType::Tag(tag) 378 } 379 TypeRef::FuncExact(_) => { 380 bail!("custom-descriptors proposal not implemented yet"); 381 } 382 }; 383 self.declare_import(import.module, import.name, ty)?; 384 } 385 } 386 387 Payload::FunctionSection(functions) => { 388 self.validator.function_section(&functions)?; 389 390 let cnt = usize::try_from(functions.count()).unwrap(); 391 self.result.module.functions.reserve_exact(cnt)?; 392 393 for entry in functions { 394 let sigindex = entry?; 395 let ty = TypeIndex::from_u32(sigindex); 396 let interned_index = self.result.module.types[ty]; 397 self.result.module.push_function(interned_index); 398 } 399 } 400 401 Payload::TableSection(tables) => { 402 self.validator.table_section(&tables)?; 403 let cnt = usize::try_from(tables.count()).unwrap(); 404 self.result.module.tables.reserve_exact(cnt)?; 405 406 for entry in tables { 407 let wasmparser::Table { ty, init } = entry?; 408 let table = self.convert_table_type(&ty)?; 409 self.result.module.needs_gc_heap |= table.ref_type.is_vmgcref_type(); 410 self.result.module.tables.push(table)?; 411 let init = match init { 412 wasmparser::TableInit::RefNull => TableInitialValue::Null { 413 precomputed: TryVec::new(), 414 }, 415 wasmparser::TableInit::Expr(expr) => { 416 let (init, escaped) = ConstExpr::from_wasmparser(self, expr)?; 417 for f in escaped { 418 self.flag_func_escaped(f); 419 } 420 TableInitialValue::Expr(init) 421 } 422 }; 423 self.result 424 .module 425 .table_initialization 426 .initial_values 427 .push(init)?; 428 } 429 } 430 431 Payload::MemorySection(memories) => { 432 self.validator.memory_section(&memories)?; 433 434 let cnt = usize::try_from(memories.count()).unwrap(); 435 self.result.module.memories.reserve_exact(cnt)?; 436 437 for entry in memories { 438 let memory = entry?; 439 self.result.module.memories.push(memory.into())?; 440 } 441 } 442 443 Payload::TagSection(tags) => { 444 self.validator.tag_section(&tags)?; 445 446 for entry in tags { 447 let sigindex = entry?.func_type_idx; 448 let ty = TypeIndex::from_u32(sigindex); 449 let interned_index = self.result.module.types[ty]; 450 let exception = self 451 .types 452 .define_exception_type_for_tag(interned_index.unwrap_module_type_index()); 453 self.result.module.push_tag(interned_index, exception); 454 } 455 } 456 457 Payload::GlobalSection(globals) => { 458 self.validator.global_section(&globals)?; 459 460 let cnt = usize::try_from(globals.count()).unwrap(); 461 self.result.module.globals.reserve_exact(cnt)?; 462 463 for entry in globals { 464 let wasmparser::Global { ty, init_expr } = entry?; 465 let (initializer, escaped) = ConstExpr::from_wasmparser(self, init_expr)?; 466 for f in escaped { 467 self.flag_func_escaped(f); 468 } 469 let ty = self.convert_global_type(&ty)?; 470 self.result.module.globals.push(ty)?; 471 self.result.module.global_initializers.push(initializer)?; 472 } 473 } 474 475 Payload::ExportSection(exports) => { 476 self.validator.export_section(&exports)?; 477 478 let cnt = usize::try_from(exports.count()).unwrap(); 479 self.result.module.exports.reserve(cnt)?; 480 481 for entry in exports { 482 let wasmparser::Export { name, kind, index } = entry?; 483 let entity = match kind { 484 ExternalKind::Func | ExternalKind::FuncExact => { 485 let index = FuncIndex::from_u32(index); 486 self.flag_func_escaped(index); 487 EntityIndex::Function(index) 488 } 489 ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)), 490 ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(index)), 491 ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(index)), 492 ExternalKind::Tag => EntityIndex::Tag(TagIndex::from_u32(index)), 493 }; 494 let name = self.result.module.strings.insert(name)?; 495 self.result.module.exports.insert(name, entity)?; 496 } 497 } 498 499 Payload::StartSection { func, range } => { 500 self.validator.start_section(func, &range)?; 501 502 let func_index = FuncIndex::from_u32(func); 503 self.flag_func_escaped(func_index); 504 debug_assert!(self.result.module.start_func.is_none()); 505 self.result.module.start_func = Some(func_index); 506 } 507 508 Payload::ElementSection(elements) => { 509 self.validator.element_section(&elements)?; 510 511 for (index, entry) in elements.into_iter().enumerate() { 512 let wasmparser::Element { 513 kind, 514 items, 515 range: _, 516 } = entry?; 517 518 // Build up a list of `FuncIndex` corresponding to all the 519 // entries listed in this segment. Note that it's not 520 // possible to create anything other than a `ref.null 521 // extern` for externref segments, so those just get 522 // translated to the reserved value of `FuncIndex`. 523 let elements = match items { 524 ElementItems::Functions(funcs) => { 525 let mut elems = 526 Vec::with_capacity(usize::try_from(funcs.count()).unwrap()); 527 for func in funcs { 528 let func = FuncIndex::from_u32(func?); 529 self.flag_func_escaped(func); 530 elems.push(func); 531 } 532 TableSegmentElements::Functions(elems.into()) 533 } 534 ElementItems::Expressions(_ty, items) => { 535 let mut exprs = 536 Vec::with_capacity(usize::try_from(items.count()).unwrap()); 537 for expr in items { 538 let (expr, escaped) = ConstExpr::from_wasmparser(self, expr?)?; 539 exprs.push(expr); 540 for func in escaped { 541 self.flag_func_escaped(func); 542 } 543 } 544 TableSegmentElements::Expressions(exprs.into()) 545 } 546 }; 547 548 match kind { 549 ElementKind::Active { 550 table_index, 551 offset_expr, 552 } => { 553 let table_index = TableIndex::from_u32(table_index.unwrap_or(0)); 554 let (offset, escaped) = ConstExpr::from_wasmparser(self, offset_expr)?; 555 debug_assert!(escaped.is_empty()); 556 557 self.result.module.table_initialization.segments.push( 558 TableSegment { 559 table_index, 560 offset, 561 elements, 562 }, 563 )?; 564 } 565 566 ElementKind::Passive => { 567 let elem_index = ElemIndex::from_u32(index as u32); 568 let index = self.result.module.passive_elements.len(); 569 self.result.module.passive_elements.push(elements)?; 570 self.result 571 .module 572 .passive_elements_map 573 .insert(elem_index, index); 574 } 575 576 ElementKind::Declared => {} 577 } 578 } 579 } 580 581 Payload::CodeSectionStart { count, range, .. } => { 582 self.validator.code_section_start(&range)?; 583 let cnt = usize::try_from(count).unwrap(); 584 self.result.function_body_inputs.reserve_exact(cnt); 585 self.result.debuginfo.wasm_file.code_section_offset = range.start as u64; 586 } 587 588 Payload::CodeSectionEntry(body) => { 589 let validator = self.validator.code_section_entry(&body)?; 590 let func_index = 591 self.result.code_index + self.result.module.num_imported_funcs as u32; 592 let func_index = FuncIndex::from_u32(func_index); 593 594 if self.tunables.debug_native { 595 let sig_index = self.result.module.functions[func_index] 596 .signature 597 .unwrap_module_type_index(); 598 let sig = self.types[sig_index].unwrap_func(); 599 let mut locals = Vec::new(); 600 for pair in body.get_locals_reader()? { 601 let (cnt, ty) = pair?; 602 let ty = self.convert_valtype(ty)?; 603 locals.push((cnt, ty)); 604 } 605 self.result 606 .debuginfo 607 .wasm_file 608 .funcs 609 .push(FunctionMetadata { 610 locals: locals.into_boxed_slice(), 611 params: sig.params().into(), 612 }); 613 } 614 if self.tunables.debug_guest { 615 // All functions are potentially reachable and 616 // callable by the guest debugger, so they must 617 // all be flagged as escaping. 618 self.flag_func_escaped(func_index); 619 } 620 self.result 621 .function_body_inputs 622 .push(FunctionBodyData { validator, body }); 623 self.result.code_index += 1; 624 } 625 626 Payload::DataSection(data) => { 627 self.validator.data_section(&data)?; 628 629 let initializers = match &mut self.result.module.memory_initialization { 630 MemoryInitialization::Segmented(i) => i, 631 _ => unreachable!(), 632 }; 633 634 let cnt = usize::try_from(data.count()).unwrap(); 635 initializers.reserve_exact(cnt)?; 636 self.result.data.reserve_exact(cnt); 637 638 for (index, entry) in data.into_iter().enumerate() { 639 let wasmparser::Data { 640 kind, 641 data, 642 range: _, 643 } = entry?; 644 let mk_range = |total: &mut u32| -> Result<_, WasmError> { 645 let range = u32::try_from(data.len()) 646 .ok() 647 .and_then(|size| { 648 let start = *total; 649 let end = start.checked_add(size)?; 650 Some(start..end) 651 }) 652 .ok_or_else(|| { 653 WasmError::Unsupported(format!( 654 "more than 4 gigabytes of data in wasm module", 655 )) 656 })?; 657 *total += range.end - range.start; 658 Ok(range) 659 }; 660 match kind { 661 DataKind::Active { 662 memory_index, 663 offset_expr, 664 } => { 665 let range = mk_range(&mut self.result.total_data)?; 666 let memory_index = MemoryIndex::from_u32(memory_index); 667 let (offset, escaped) = ConstExpr::from_wasmparser(self, offset_expr)?; 668 debug_assert!(escaped.is_empty()); 669 670 let initializers = match &mut self.result.module.memory_initialization { 671 MemoryInitialization::Segmented(i) => i, 672 _ => unreachable!(), 673 }; 674 initializers.push(MemoryInitializer { 675 memory_index, 676 offset, 677 data: range, 678 })?; 679 self.result.data.push(data.into()); 680 } 681 DataKind::Passive => { 682 let data_index = DataIndex::from_u32(index as u32); 683 let range = mk_range(&mut self.result.total_passive_data)?; 684 self.result.passive_data.push(data); 685 self.result 686 .module 687 .passive_data_map 688 .insert(data_index, range); 689 } 690 } 691 } 692 } 693 694 Payload::DataCountSection { count, range } => { 695 self.validator.data_count_section(count, &range)?; 696 697 // Note: the count passed in here is the *total* segment count 698 // There is no way to reserve for just the passive segments as 699 // they are discovered when iterating the data section entries 700 // Given that the total segment count might be much larger than 701 // the passive count, do not reserve anything here. 702 } 703 704 Payload::CustomSection(s) 705 if s.name() == "webidl-bindings" || s.name() == "wasm-interface-types" => 706 { 707 bail!( 708 "\ 709 Support for interface types has temporarily been removed from `wasmtime`. 710 711 For more information about this temporary change you can read on the issue online: 712 713 https://github.com/bytecodealliance/wasmtime/issues/1271 714 715 and for re-adding support for interface types you can see this issue: 716 717 https://github.com/bytecodealliance/wasmtime/issues/677 718 " 719 ) 720 } 721 722 Payload::CustomSection(s) => { 723 self.register_custom_section(&s); 724 } 725 726 // It's expected that validation will probably reject other 727 // payloads such as `UnknownSection` or those related to the 728 // component model. If, however, something gets past validation then 729 // that's a bug in Wasmtime as we forgot to implement something. 730 other => { 731 self.validator.payload(&other)?; 732 panic!("unimplemented section in wasm file {other:?}"); 733 } 734 } 735 Ok(()) 736 } 737 register_custom_section(&mut self, section: &CustomSectionReader<'data>)738 fn register_custom_section(&mut self, section: &CustomSectionReader<'data>) { 739 match section.as_known() { 740 KnownCustom::Name(name) => { 741 let result = self.name_section(name); 742 if let Err(e) = result { 743 log::warn!("failed to parse name section {e:?}"); 744 } 745 } 746 _ => { 747 let name = section.name().trim_end_matches(".dwo"); 748 if name.starts_with(".debug_") { 749 self.dwarf_section(name, section); 750 } 751 } 752 } 753 } 754 dwarf_section(&mut self, name: &str, section: &CustomSectionReader<'data>)755 fn dwarf_section(&mut self, name: &str, section: &CustomSectionReader<'data>) { 756 if !self.tunables.debug_native && !self.tunables.parse_wasm_debuginfo { 757 self.result.has_unparsed_debuginfo = true; 758 return; 759 } 760 let info = &mut self.result.debuginfo; 761 let dwarf = &mut info.dwarf; 762 let endian = gimli::LittleEndian; 763 let data = section.data(); 764 let slice = gimli::EndianSlice::new(data, endian); 765 766 match name { 767 // `gimli::Dwarf` fields. 768 ".debug_abbrev" => dwarf.debug_abbrev = gimli::DebugAbbrev::new(data, endian), 769 ".debug_addr" => dwarf.debug_addr = gimli::DebugAddr::from(slice), 770 ".debug_info" => { 771 dwarf.debug_info = gimli::DebugInfo::new(data, endian); 772 } 773 ".debug_line" => dwarf.debug_line = gimli::DebugLine::new(data, endian), 774 ".debug_line_str" => dwarf.debug_line_str = gimli::DebugLineStr::from(slice), 775 ".debug_str" => dwarf.debug_str = gimli::DebugStr::new(data, endian), 776 ".debug_str_offsets" => dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(slice), 777 ".debug_str_sup" => { 778 let mut dwarf_sup: Dwarf<'data> = Default::default(); 779 dwarf_sup.debug_str = gimli::DebugStr::from(slice); 780 dwarf.sup = Some(Arc::new(dwarf_sup)); 781 } 782 ".debug_types" => dwarf.debug_types = gimli::DebugTypes::from(slice), 783 784 // Additional fields. 785 ".debug_loc" => info.debug_loc = gimli::DebugLoc::from(slice), 786 ".debug_loclists" => info.debug_loclists = gimli::DebugLocLists::from(slice), 787 ".debug_ranges" => info.debug_ranges = gimli::DebugRanges::new(data, endian), 788 ".debug_rnglists" => info.debug_rnglists = gimli::DebugRngLists::new(data, endian), 789 790 // DWARF package fields 791 ".debug_cu_index" => info.debug_cu_index = gimli::DebugCuIndex::new(data, endian), 792 ".debug_tu_index" => info.debug_tu_index = gimli::DebugTuIndex::new(data, endian), 793 794 // We don't use these at the moment. 795 ".debug_aranges" | ".debug_pubnames" | ".debug_pubtypes" => return, 796 other => { 797 log::warn!("unknown debug section `{other}`"); 798 return; 799 } 800 } 801 802 dwarf.ranges = gimli::RangeLists::new(info.debug_ranges, info.debug_rnglists); 803 dwarf.locations = gimli::LocationLists::new(info.debug_loc, info.debug_loclists); 804 } 805 806 /// Declares a new import with the `module` and `field` names, importing the 807 /// `ty` specified. 808 /// 809 /// Note that this method is somewhat tricky due to the implementation of 810 /// the module linking proposal. In the module linking proposal two-level 811 /// imports are recast as single-level imports of instances. That recasting 812 /// happens here by recording an import of an instance for the first time 813 /// we see a two-level import. 814 /// 815 /// When the module linking proposal is disabled, however, disregard this 816 /// logic and instead work directly with two-level imports since no 817 /// instances are defined. declare_import( &mut self, module: &'data str, field: &'data str, ty: EntityType, ) -> Result<(), OutOfMemory>818 fn declare_import( 819 &mut self, 820 module: &'data str, 821 field: &'data str, 822 ty: EntityType, 823 ) -> Result<(), OutOfMemory> { 824 let index = self.push_type(ty); 825 self.result.module.initializers.push(Initializer::Import { 826 name: self.result.module.strings.insert(module)?, 827 field: self.result.module.strings.insert(field)?, 828 index, 829 })?; 830 Ok(()) 831 } 832 push_type(&mut self, ty: EntityType) -> EntityIndex833 fn push_type(&mut self, ty: EntityType) -> EntityIndex { 834 match ty { 835 EntityType::Function(ty) => EntityIndex::Function({ 836 let func_index = self 837 .result 838 .module 839 .push_function(ty.unwrap_module_type_index()); 840 // Imported functions can escape; in fact, they've already done 841 // so to get here. 842 self.flag_func_escaped(func_index); 843 func_index 844 }), 845 EntityType::Table(ty) => { 846 EntityIndex::Table(self.result.module.tables.push(ty).panic_on_oom()) 847 } 848 EntityType::Memory(ty) => { 849 EntityIndex::Memory(self.result.module.memories.push(ty).panic_on_oom()) 850 } 851 EntityType::Global(ty) => { 852 EntityIndex::Global(self.result.module.globals.push(ty).panic_on_oom()) 853 } 854 EntityType::Tag(ty) => { 855 EntityIndex::Tag(self.result.module.tags.push(ty).panic_on_oom()) 856 } 857 } 858 } 859 flag_func_escaped(&mut self, func: FuncIndex)860 fn flag_func_escaped(&mut self, func: FuncIndex) { 861 let ty = &mut self.result.module.functions[func]; 862 // If this was already assigned a funcref index no need to re-assign it. 863 if ty.is_escaping() { 864 return; 865 } 866 let index = self.result.module.num_escaped_funcs as u32; 867 ty.func_ref = FuncRefIndex::from_u32(index); 868 self.result.module.num_escaped_funcs += 1; 869 } 870 871 /// Parses the Name section of the wasm module. name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()>872 fn name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()> { 873 for subsection in names { 874 match subsection? { 875 wasmparser::Name::Function(names) => { 876 for name in names { 877 let Naming { index, name } = name?; 878 // Skip this naming if it's naming a function that 879 // doesn't actually exist. 880 if (index as usize) >= self.result.module.functions.len() { 881 continue; 882 } 883 884 // Store the name unconditionally, regardless of 885 // whether we're parsing debuginfo, since function 886 // names are almost always present in the 887 // final compilation artifact. 888 let index = FuncIndex::from_u32(index); 889 self.result 890 .debuginfo 891 .name_section 892 .func_names 893 .insert(index, name); 894 } 895 } 896 wasmparser::Name::Module { name, .. } => { 897 self.result.module.name = 898 Some(self.result.module.strings.insert(name).panic_on_oom()); 899 if self.tunables.debug_native { 900 self.result.debuginfo.name_section.module_name = Some(name); 901 } 902 } 903 wasmparser::Name::Local(reader) => { 904 if !self.tunables.debug_native { 905 continue; 906 } 907 for f in reader { 908 let f = f?; 909 // Skip this naming if it's naming a function that 910 // doesn't actually exist. 911 if (f.index as usize) >= self.result.module.functions.len() { 912 continue; 913 } 914 for name in f.names { 915 let Naming { index, name } = name?; 916 917 self.result 918 .debuginfo 919 .name_section 920 .locals_names 921 .entry(FuncIndex::from_u32(f.index)) 922 .or_insert(HashMap::new()) 923 .insert(index, name); 924 } 925 } 926 } 927 wasmparser::Name::Label(_) 928 | wasmparser::Name::Type(_) 929 | wasmparser::Name::Table(_) 930 | wasmparser::Name::Global(_) 931 | wasmparser::Name::Memory(_) 932 | wasmparser::Name::Element(_) 933 | wasmparser::Name::Data(_) 934 | wasmparser::Name::Tag(_) 935 | wasmparser::Name::Field(_) 936 | wasmparser::Name::Unknown { .. } => {} 937 } 938 } 939 Ok(()) 940 } 941 } 942 943 impl TypeConvert for ModuleEnvironment<'_, '_> { lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType944 fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType { 945 WasmparserTypeConverter::new(&self.types, |idx| { 946 self.result.module.types[idx].unwrap_module_type_index() 947 }) 948 .lookup_heap_type(index) 949 } 950 lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex951 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex { 952 WasmparserTypeConverter::new(&self.types, |idx| { 953 self.result.module.types[idx].unwrap_module_type_index() 954 }) 955 .lookup_type_index(index) 956 } 957 } 958 959 impl ModuleTranslation<'_> { 960 /// Attempts to convert segmented memory initialization into static 961 /// initialization for the module that this translation represents. 962 /// 963 /// If this module's memory initialization is not compatible with paged 964 /// initialization then this won't change anything. Otherwise if it is 965 /// compatible then the `memory_initialization` field will be updated. 966 /// 967 /// Takes a `page_size` argument in order to ensure that all 968 /// initialization is page-aligned for mmap-ability, and 969 /// `max_image_size_always_allowed` to control how we decide 970 /// whether to use static init. 971 /// 972 /// We will try to avoid generating very sparse images, which are 973 /// possible if e.g. a module has an initializer at offset 0 and a 974 /// very high offset (say, 1 GiB). To avoid this, we use a dual 975 /// condition: we always allow images less than 976 /// `max_image_size_always_allowed`, and the embedder of Wasmtime 977 /// can set this if desired to ensure that static init should 978 /// always be done if the size of the module or its heaps is 979 /// otherwise bounded by the system. We also allow images with 980 /// static init data bigger than that, but only if it is "dense", 981 /// defined as having at least half (50%) of its pages with some 982 /// data. 983 /// 984 /// We could do something slightly better by building a dense part 985 /// and keeping a sparse list of outlier/leftover segments (see 986 /// issue #3820). This would also allow mostly-static init of 987 /// modules that have some dynamically-placed data segments. But, 988 /// for now, this is sufficient to allow a system that "knows what 989 /// it's doing" to always get static init. try_static_init(&mut self, page_size: u64, max_image_size_always_allowed: u64)990 pub fn try_static_init(&mut self, page_size: u64, max_image_size_always_allowed: u64) { 991 // This method only attempts to transform a `Segmented` memory init 992 // into a `Static` one, no other state. 993 if !self.module.memory_initialization.is_segmented() { 994 return; 995 } 996 997 // First a dry run of memory initialization is performed. This 998 // collects information about the extent of memory initialized for each 999 // memory as well as the size of all data segments being copied in. 1000 struct Memory { 1001 data_size: u64, 1002 min_addr: u64, 1003 max_addr: u64, 1004 // The `usize` here is a pointer into `self.data` which is the list 1005 // of data segments corresponding to what was found in the original 1006 // wasm module. 1007 segments: Vec<(usize, StaticMemoryInitializer)>, 1008 } 1009 let mut info = PrimaryMap::with_capacity(self.module.memories.len()); 1010 for _ in 0..self.module.memories.len() { 1011 info.push(Memory { 1012 data_size: 0, 1013 min_addr: u64::MAX, 1014 max_addr: 0, 1015 segments: Vec::new(), 1016 }); 1017 } 1018 1019 struct InitMemoryAtCompileTime<'a> { 1020 module: &'a Module, 1021 info: &'a mut PrimaryMap<MemoryIndex, Memory>, 1022 idx: usize, 1023 } 1024 impl InitMemory for InitMemoryAtCompileTime<'_> { 1025 fn memory_size_in_bytes( 1026 &mut self, 1027 memory_index: MemoryIndex, 1028 ) -> Result<u64, SizeOverflow> { 1029 self.module.memories[memory_index].minimum_byte_size() 1030 } 1031 1032 fn eval_offset(&mut self, memory_index: MemoryIndex, expr: &ConstExpr) -> Option<u64> { 1033 match (expr.ops(), self.module.memories[memory_index].idx_type) { 1034 (&[ConstOp::I32Const(offset)], IndexType::I32) => { 1035 Some(offset.cast_unsigned().into()) 1036 } 1037 (&[ConstOp::I64Const(offset)], IndexType::I64) => Some(offset.cast_unsigned()), 1038 _ => None, 1039 } 1040 } 1041 1042 fn write(&mut self, memory: MemoryIndex, init: &StaticMemoryInitializer) -> bool { 1043 // Currently `Static` only applies to locally-defined memories, 1044 // so if a data segment references an imported memory then 1045 // transitioning to a `Static` memory initializer is not 1046 // possible. 1047 if self.module.defined_memory_index(memory).is_none() { 1048 return false; 1049 }; 1050 let info = &mut self.info[memory]; 1051 let data_len = u64::from(init.data.end - init.data.start); 1052 if data_len > 0 { 1053 info.data_size += data_len; 1054 info.min_addr = info.min_addr.min(init.offset); 1055 info.max_addr = info.max_addr.max(init.offset + data_len); 1056 info.segments.push((self.idx, init.clone())); 1057 } 1058 self.idx += 1; 1059 true 1060 } 1061 } 1062 let ok = self 1063 .module 1064 .memory_initialization 1065 .init_memory(&mut InitMemoryAtCompileTime { 1066 idx: 0, 1067 module: &self.module, 1068 info: &mut info, 1069 }); 1070 if !ok { 1071 return; 1072 } 1073 1074 // Validate that the memory information collected is indeed valid for 1075 // static memory initialization. 1076 for (i, info) in info.iter().filter(|(_, info)| info.data_size > 0) { 1077 let image_size = info.max_addr - info.min_addr; 1078 1079 // Simplify things for now by bailing out entirely if any memory has 1080 // a page size smaller than the host's page size. This fixes a case 1081 // where currently initializers are created in host-page-size units 1082 // of length which means that a larger-than-the-entire-memory 1083 // initializer can be created. This can be handled technically but 1084 // would require some more changes to help fix the assert elsewhere 1085 // that this protects against. 1086 if self.module.memories[i].page_size() < page_size { 1087 return; 1088 } 1089 1090 // If the range of memory being initialized is less than twice the 1091 // total size of the data itself then it's assumed that static 1092 // initialization is ok. This means we'll at most double memory 1093 // consumption during the memory image creation process, which is 1094 // currently assumed to "probably be ok" but this will likely need 1095 // tweaks over time. 1096 if image_size < info.data_size.saturating_mul(2) { 1097 continue; 1098 } 1099 1100 // If the memory initialization image is larger than the size of all 1101 // data, then we still allow memory initialization if the image will 1102 // be of a relatively modest size, such as 1MB here. 1103 if image_size < max_image_size_always_allowed { 1104 continue; 1105 } 1106 1107 // At this point memory initialization is concluded to be too 1108 // expensive to do at compile time so it's entirely deferred to 1109 // happen at runtime. 1110 return; 1111 } 1112 1113 // Here's where we've now committed to changing to static memory. The 1114 // memory initialization image is built here from the page data and then 1115 // it's converted to a single initializer. 1116 let data = mem::replace(&mut self.data, Vec::new()); 1117 let mut map = TryPrimaryMap::with_capacity(info.len()).panic_on_oom(); 1118 let mut module_data_size = 0u32; 1119 for (memory, info) in info.iter() { 1120 // Create the in-memory `image` which is the initialized contents of 1121 // this linear memory. 1122 let extent = if info.segments.len() > 0 { 1123 (info.max_addr - info.min_addr) as usize 1124 } else { 1125 0 1126 }; 1127 let mut image = Vec::with_capacity(extent); 1128 for (idx, init) in info.segments.iter() { 1129 let data = &data[*idx]; 1130 assert_eq!(data.len(), init.data.len()); 1131 let offset = usize::try_from(init.offset - info.min_addr).unwrap(); 1132 if image.len() < offset { 1133 image.resize(offset, 0u8); 1134 image.extend_from_slice(data); 1135 } else { 1136 image.splice( 1137 offset..(offset + data.len()).min(image.len()), 1138 data.iter().copied(), 1139 ); 1140 } 1141 } 1142 assert_eq!(image.len(), extent); 1143 assert_eq!(image.capacity(), extent); 1144 let mut offset = if info.segments.len() > 0 { 1145 info.min_addr 1146 } else { 1147 0 1148 }; 1149 1150 // Chop off trailing zeros from the image as memory is already 1151 // zero-initialized. Note that `i` is the position of a nonzero 1152 // entry here, so to not lose it we truncate to `i + 1`. 1153 if let Some(i) = image.iter().rposition(|i| *i != 0) { 1154 image.truncate(i + 1); 1155 } 1156 1157 // Also chop off leading zeros, if any. 1158 if let Some(i) = image.iter().position(|i| *i != 0) { 1159 offset += i as u64; 1160 image.drain(..i); 1161 } 1162 let mut len = u64::try_from(image.len()).unwrap(); 1163 1164 // The goal is to enable mapping this image directly into memory, so 1165 // the offset into linear memory must be a multiple of the page 1166 // size. If that's not already the case then the image is padded at 1167 // the front and back with extra zeros as necessary 1168 if offset % page_size != 0 { 1169 let zero_padding = offset % page_size; 1170 self.data.push(vec![0; zero_padding as usize].into()); 1171 offset -= zero_padding; 1172 len += zero_padding; 1173 } 1174 self.data.push(image.into()); 1175 if len % page_size != 0 { 1176 let zero_padding = page_size - (len % page_size); 1177 self.data.push(vec![0; zero_padding as usize].into()); 1178 len += zero_padding; 1179 } 1180 1181 // Offset/length should now always be page-aligned. 1182 assert!(offset % page_size == 0); 1183 assert!(len % page_size == 0); 1184 1185 // Create the `StaticMemoryInitializer` which describes this image, 1186 // only needed if the image is actually present and has a nonzero 1187 // length. The `offset` has been calculates above, originally 1188 // sourced from `info.min_addr`. The `data` field is the extent 1189 // within the final data segment we'll emit to an ELF image, which 1190 // is the concatenation of `self.data`, so here it's the size of 1191 // the section-so-far plus the current segment we're appending. 1192 let len = u32::try_from(len).unwrap(); 1193 let init = if len > 0 { 1194 Some(StaticMemoryInitializer { 1195 offset, 1196 data: module_data_size..module_data_size + len, 1197 }) 1198 } else { 1199 None 1200 }; 1201 let idx = map.push(init).panic_on_oom(); 1202 assert_eq!(idx, memory); 1203 module_data_size += len; 1204 } 1205 self.data_align = Some(page_size); 1206 self.module.memory_initialization = MemoryInitialization::Static { map }; 1207 } 1208 1209 /// Attempts to convert the module's table initializers to 1210 /// FuncTable form where possible. This enables lazy table 1211 /// initialization later by providing a one-to-one map of initial 1212 /// table values, without having to parse all segments. try_func_table_init(&mut self)1213 pub fn try_func_table_init(&mut self) { 1214 // This should be large enough to support very large Wasm 1215 // modules with huge funcref tables, but small enough to avoid 1216 // OOMs or DoS on truly sparse tables. 1217 const MAX_FUNC_TABLE_SIZE: u64 = 1024 * 1024; 1218 1219 // First convert any element-initialized tables to images of just that 1220 // single function if the minimum size of the table allows doing so. 1221 for ((_, init), (_, table)) in self 1222 .module 1223 .table_initialization 1224 .initial_values 1225 .iter_mut() 1226 .zip( 1227 self.module 1228 .tables 1229 .iter() 1230 .skip(self.module.num_imported_tables), 1231 ) 1232 { 1233 let table_size = table.limits.min; 1234 if table_size > MAX_FUNC_TABLE_SIZE { 1235 continue; 1236 } 1237 if let TableInitialValue::Expr(expr) = init { 1238 if let [ConstOp::RefFunc(f)] = expr.ops() { 1239 *init = TableInitialValue::Null { 1240 precomputed: try_vec![*f; table_size as usize].panic_on_oom(), 1241 }; 1242 } 1243 } 1244 } 1245 1246 let mut segments = mem::take(&mut self.module.table_initialization.segments) 1247 .into_iter() 1248 .peekable(); 1249 1250 // The goal of this loop is to interpret a table segment and apply it 1251 // "statically" to a local table. This will iterate over segments and 1252 // apply them one-by-one to each table. 1253 // 1254 // If any segment can't be applied, however, then this loop exits and 1255 // all remaining segments are placed back into the segment list. This is 1256 // because segments are supposed to be initialized one-at-a-time which 1257 // means that intermediate state is visible with respect to traps. If 1258 // anything isn't statically known to not trap it's pessimistically 1259 // assumed to trap meaning all further segment initializers must be 1260 // applied manually at instantiation time. 1261 while let Some(segment) = segments.peek() { 1262 let defined_index = match self.module.defined_table_index(segment.table_index) { 1263 Some(index) => index, 1264 // Skip imported tables: we can't provide a preconstructed 1265 // table for them, because their values depend on the 1266 // imported table overlaid with whatever segments we have. 1267 None => break, 1268 }; 1269 1270 // If the base of this segment is dynamic, then we can't 1271 // include it in the statically-built array of initial 1272 // contents. 1273 let offset = match segment.offset.ops() { 1274 &[ConstOp::I32Const(offset)] => u64::from(offset.cast_unsigned()), 1275 &[ConstOp::I64Const(offset)] => offset.cast_unsigned(), 1276 _ => break, 1277 }; 1278 1279 // Get the end of this segment. If out-of-bounds, or too 1280 // large for our dense table representation, then skip the 1281 // segment. 1282 let top = match offset.checked_add(segment.elements.len()) { 1283 Some(top) => top, 1284 None => break, 1285 }; 1286 let table_size = self.module.tables[segment.table_index].limits.min; 1287 if top > table_size || top > MAX_FUNC_TABLE_SIZE { 1288 break; 1289 } 1290 1291 match self.module.tables[segment.table_index] 1292 .ref_type 1293 .heap_type 1294 .top() 1295 { 1296 WasmHeapTopType::Func => {} 1297 // If this is not a funcref table, then we can't support a 1298 // pre-computed table of function indices. Technically this 1299 // initializer won't trap so we could continue processing 1300 // segments, but that's left as a future optimization if 1301 // necessary. 1302 WasmHeapTopType::Any 1303 | WasmHeapTopType::Extern 1304 | WasmHeapTopType::Cont 1305 | WasmHeapTopType::Exn => break, 1306 } 1307 1308 // Function indices can be optimized here, but fully general 1309 // expressions are deferred to get evaluated at runtime. 1310 let function_elements = match &segment.elements { 1311 TableSegmentElements::Functions(indices) => indices, 1312 TableSegmentElements::Expressions(_) => break, 1313 }; 1314 1315 let precomputed = 1316 match &mut self.module.table_initialization.initial_values[defined_index] { 1317 TableInitialValue::Null { precomputed } => precomputed, 1318 1319 // If this table is still listed as an initial value here 1320 // then that means the initial size of the table doesn't 1321 // support a precomputed function list, so skip this. 1322 // Technically this won't trap so it's possible to process 1323 // further initializers, but that's left as a future 1324 // optimization. 1325 TableInitialValue::Expr(_) => break, 1326 }; 1327 1328 // At this point we're committing to pre-initializing the table 1329 // with the `segment` that's being iterated over. This segment is 1330 // applied to the `precomputed` list for the table by ensuring 1331 // it's large enough to hold the segment and then copying the 1332 // segment into the precomputed list. 1333 if precomputed.len() < top as usize { 1334 precomputed 1335 .resize(top as usize, FuncIndex::reserved_value()) 1336 .panic_on_oom(); 1337 } 1338 let dst = &mut precomputed[offset as usize..top as usize]; 1339 dst.copy_from_slice(&function_elements); 1340 1341 // advance the iterator to see the next segment 1342 let _ = segments.next(); 1343 } 1344 self.module.table_initialization.segments = segments.try_collect().panic_on_oom(); 1345 } 1346 } 1347