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