wasmer_compiler/translator/
sections.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Helper functions to gather information for each of the non-function sections of a
5//! WebAssembly module.
6//!
7//! The code of these helper functions is straightforward since they only read metadata
8//! about linear memories, tables, globals, etc. and store them for later use.
9//!
10//! The special case of the initialize expressions for table elements offsets or global variables
11//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
12//! interpreted on the fly.
13
14use super::environ::ModuleEnvironment;
15use super::error::from_binaryreadererror_wasmerror;
16use super::state::ModuleTranslationState;
17use std::boxed::Box;
18use std::collections::{HashMap, HashSet};
19use std::vec::Vec;
20use wasmer_types::entity::EntityRef;
21use wasmer_types::entity::packed_option::ReservedValue;
22use wasmer_types::{
23    DataIndex, ElemIndex, FunctionIndex, FunctionType, GlobalIndex, GlobalInit, GlobalType,
24    MemoryIndex, MemoryType, Pages, SignatureIndex, TableIndex, TableType, TagIndex, Type, V128,
25};
26use wasmer_types::{WasmError, WasmResult};
27use wasmparser::{
28    self, Data, DataKind, DataSectionReader, Element, ElementItems, ElementKind,
29    ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
30    GlobalSectionReader, GlobalType as WPGlobalType, ImportSectionReader, Imports,
31    MemorySectionReader, MemoryType as WPMemoryType, NameSectionReader, Operator,
32    TableSectionReader, TagType as WPTagType, TypeRef, TypeSectionReader,
33};
34
35/// Helper function translating wasmparser types to Wasm Type.
36pub fn wptype_to_type(ty: wasmparser::ValType) -> WasmResult<Type> {
37    match ty {
38        wasmparser::ValType::I32 => Ok(Type::I32),
39        wasmparser::ValType::I64 => Ok(Type::I64),
40        wasmparser::ValType::F32 => Ok(Type::F32),
41        wasmparser::ValType::F64 => Ok(Type::F64),
42        wasmparser::ValType::V128 => Ok(Type::V128),
43        wasmparser::ValType::Ref(ty) => wpreftype_to_type(ty),
44    }
45}
46
47/// Converts a wasmparser ref type to a Wasm Type.
48pub fn wpreftype_to_type(ty: wasmparser::RefType) -> WasmResult<Type> {
49    if ty.is_extern_ref() {
50        Ok(Type::ExternRef)
51    } else if ty.is_func_ref() {
52        Ok(Type::FuncRef)
53    } else if ty == wasmparser::RefType::EXNREF || ty == wasmparser::RefType::EXN {
54        // no `.is_exnref` yet
55        Ok(Type::ExceptionRef)
56    } else {
57        Err(wasm_unsupported!("unsupported reference type: {:?}", ty))
58    }
59}
60
61/// Converts a wasmparser heap type to a Wasm Type.
62pub fn wpheaptype_to_type(ty: wasmparser::HeapType) -> WasmResult<Type> {
63    match ty {
64        wasmparser::HeapType::Abstract { ty, .. } => match ty {
65            wasmparser::AbstractHeapType::Func => Ok(Type::FuncRef),
66            wasmparser::AbstractHeapType::Extern => Ok(Type::ExternRef),
67            wasmparser::AbstractHeapType::Exn => Ok(Type::ExceptionRef),
68            other => Err(wasm_unsupported!("unsupported reference type: {other:?}")),
69        },
70        other => Err(wasm_unsupported!("unsupported reference type: {other:?}")),
71    }
72}
73
74/// Parses the Type section of the wasm module.
75pub fn parse_type_section(
76    types: TypeSectionReader,
77    module_translation_state: &mut ModuleTranslationState,
78    environ: &mut ModuleEnvironment,
79) -> WasmResult<()> {
80    let count = types.count();
81    environ.reserve_signatures(count)?;
82
83    for res in types.into_iter_err_on_gc_types() {
84        let functype = res.map_err(from_binaryreadererror_wasmerror)?;
85
86        let params = functype.params();
87        let returns = functype.results();
88        let sig_params: Box<[Type]> = params
89            .iter()
90            .map(|ty| {
91                wptype_to_type(*ty)
92                    .expect("only numeric types are supported in function signatures")
93            })
94            .collect();
95        let sig_returns: Box<[Type]> = returns
96            .iter()
97            .map(|ty| {
98                wptype_to_type(*ty)
99                    .expect("only numeric types are supported in function signatures")
100            })
101            .collect();
102        let sig = FunctionType::new(sig_params, sig_returns);
103        environ.declare_signature(sig)?;
104        module_translation_state
105            .wasm_types
106            .push((params.to_vec().into(), returns.to_vec().into()));
107    }
108
109    Ok(())
110}
111
112/// Parses the Import section of the wasm module.
113pub fn parse_import_section<'data>(
114    imports: ImportSectionReader<'data>,
115    environ: &mut ModuleEnvironment<'data>,
116) -> WasmResult<()> {
117    environ.reserve_imports(imports.count())?;
118
119    for entry in imports {
120        let import = entry.map_err(from_binaryreadererror_wasmerror)?;
121        let Imports::Single(_index, import) = import else {
122            return Err(WasmError::Generic(
123                "non-Single section Imports not implemented yet".to_string(),
124            ));
125        };
126        let module_name = import.module;
127        let field_name = import.name;
128
129        match import.ty {
130            TypeRef::Func(sig) => {
131                environ.declare_func_import(
132                    SignatureIndex::from_u32(sig),
133                    module_name,
134                    field_name,
135                )?;
136            }
137            TypeRef::FuncExact(_) => {
138                return Err(WasmError::Generic(
139                    "custom-descriptors not implemented yet".to_string(),
140                ));
141            }
142            TypeRef::Tag(t) => {
143                environ.declare_tag_import(t, module_name, field_name)?;
144            }
145            TypeRef::Memory(WPMemoryType {
146                shared,
147                memory64,
148                initial,
149                maximum,
150                ..
151            }) => {
152                if memory64 {
153                    unimplemented!("64bit memory not implemented yet");
154                }
155                environ.declare_memory_import(
156                    MemoryType {
157                        minimum: Pages(initial as u32),
158                        maximum: maximum.map(|p| Pages(p as u32)),
159                        shared,
160                    },
161                    module_name,
162                    field_name,
163                )?;
164            }
165            TypeRef::Global(ref ty) => {
166                environ.declare_global_import(
167                    GlobalType {
168                        ty: wptype_to_type(ty.content_type)?,
169                        mutability: ty.mutable.into(),
170                    },
171                    module_name,
172                    field_name,
173                )?;
174            }
175            TypeRef::Table(ref tab) => {
176                environ.declare_table_import(
177                    TableType {
178                        ty: wpreftype_to_type(tab.element_type)?,
179                        minimum: tab.initial as u32,
180                        maximum: tab.maximum.map(|v| v as u32),
181                    },
182                    module_name,
183                    field_name,
184                )?;
185            }
186        }
187    }
188
189    environ.finish_imports()?;
190    Ok(())
191}
192
193/// Parses the Function section of the wasm module.
194pub fn parse_function_section(
195    functions: FunctionSectionReader,
196    environ: &mut ModuleEnvironment,
197) -> WasmResult<()> {
198    let num_functions = functions.count();
199    if num_functions == u32::MAX {
200        // We reserve `u32::MAX` for our own use.
201        return Err(WasmError::ImplLimitExceeded);
202    }
203
204    environ.reserve_func_types(num_functions)?;
205
206    for entry in functions {
207        let sigindex = entry.map_err(from_binaryreadererror_wasmerror)?;
208        environ.declare_func_type(SignatureIndex::from_u32(sigindex))?;
209    }
210
211    Ok(())
212}
213
214/// Parses the Table section of the wasm module.
215pub fn parse_table_section(
216    tables: TableSectionReader,
217    environ: &mut ModuleEnvironment,
218) -> WasmResult<()> {
219    environ.reserve_tables(tables.count())?;
220
221    for entry in tables {
222        let table = entry.map_err(from_binaryreadererror_wasmerror)?;
223        environ.declare_table(TableType {
224            ty: wpreftype_to_type(table.ty.element_type).unwrap(),
225            minimum: table.ty.initial as u32,
226            maximum: table.ty.maximum.map(|v| v as u32),
227        })?;
228    }
229
230    Ok(())
231}
232
233/// Parses the Memory section of the wasm module.
234pub fn parse_memory_section(
235    memories: MemorySectionReader,
236    environ: &mut ModuleEnvironment,
237) -> WasmResult<()> {
238    environ.reserve_memories(memories.count())?;
239
240    for entry in memories {
241        let WPMemoryType {
242            shared,
243            memory64,
244            initial,
245            maximum,
246            ..
247        } = entry.map_err(from_binaryreadererror_wasmerror)?;
248        if memory64 {
249            unimplemented!("64bit memory not implemented yet");
250        }
251        environ.declare_memory(MemoryType {
252            minimum: Pages(initial as u32),
253            maximum: maximum.map(|p| Pages(p as u32)),
254            shared,
255        })?;
256    }
257
258    Ok(())
259}
260
261/// Parser the Tag section of the wasm module.
262pub fn parse_tag_section(
263    tags: wasmparser::SectionLimited<wasmparser::TagType>,
264    environ: &mut ModuleEnvironment,
265) -> WasmResult<()> {
266    environ.reserve_tags(tags.count())?;
267
268    for entry in tags {
269        let WPTagType { func_type_idx, .. } = entry.map_err(from_binaryreadererror_wasmerror)?;
270        environ.declare_tag(SignatureIndex::from_u32(func_type_idx))?;
271    }
272
273    Ok(())
274}
275
276/// Parses the Global section of the wasm module.
277pub fn parse_global_section(
278    globals: GlobalSectionReader,
279    environ: &mut ModuleEnvironment,
280) -> WasmResult<()> {
281    environ.reserve_globals(globals.count())?;
282
283    for entry in globals {
284        let wasmparser::Global {
285            ty:
286                WPGlobalType {
287                    content_type,
288                    mutable,
289                    ..
290                },
291            init_expr,
292        } = entry.map_err(from_binaryreadererror_wasmerror)?;
293        let mut init_expr_reader = init_expr.get_operators_reader();
294        let initializer = match init_expr_reader
295            .read()
296            .map_err(from_binaryreadererror_wasmerror)?
297        {
298            Operator::I32Const { value } => GlobalInit::I32Const(value),
299            Operator::I64Const { value } => GlobalInit::I64Const(value),
300            Operator::F32Const { value } => GlobalInit::F32Const(f32::from_bits(value.bits())),
301            Operator::F64Const { value } => GlobalInit::F64Const(f64::from_bits(value.bits())),
302            Operator::V128Const { value } => GlobalInit::V128Const(V128::from(*value.bytes())),
303            Operator::RefNull { hty: _ } => {
304                // TODO: Do we need to handle different heap types here?
305                GlobalInit::RefNullConst
306            }
307            Operator::RefFunc { function_index } => {
308                GlobalInit::RefFunc(FunctionIndex::from_u32(function_index))
309            }
310            Operator::GlobalGet { global_index } => {
311                GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
312            }
313            ref s => {
314                return Err(wasm_unsupported!(
315                    "unsupported init expr in global section: {:?}",
316                    s
317                ));
318            }
319        };
320        let global = GlobalType {
321            ty: wptype_to_type(content_type).unwrap(),
322            mutability: mutable.into(),
323        };
324        environ.declare_global(global, initializer)?;
325    }
326
327    Ok(())
328}
329
330/// Parses the Export section of the wasm module.
331pub fn parse_export_section<'data>(
332    exports: ExportSectionReader<'data>,
333    environ: &mut ModuleEnvironment<'data>,
334) -> WasmResult<()> {
335    environ.reserve_exports(exports.count())?;
336
337    for entry in exports {
338        let Export {
339            name: field,
340            ref kind,
341            index,
342        } = entry.map_err(from_binaryreadererror_wasmerror)?;
343
344        // The input has already been validated, so we should be able to
345        // assume valid UTF-8 and use `from_utf8_unchecked` if performance
346        // becomes a concern here.
347        let index = index as usize;
348        match *kind {
349            ExternalKind::Func => environ.declare_func_export(FunctionIndex::new(index), field)?,
350            ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), field)?,
351            ExternalKind::Memory => {
352                environ.declare_memory_export(MemoryIndex::new(index), field)?
353            }
354            ExternalKind::Global => {
355                environ.declare_global_export(GlobalIndex::new(index), field)?
356            }
357            ExternalKind::Tag => environ.declare_tag_export(TagIndex::new(index), field)?,
358            ExternalKind::FuncExact => unimplemented!("custom-descriptors not implemented yet"),
359        }
360    }
361
362    environ.finish_exports()?;
363    Ok(())
364}
365
366/// Parses the Start section of the wasm module.
367pub fn parse_start_section(index: u32, environ: &mut ModuleEnvironment) -> WasmResult<()> {
368    environ.declare_start_function(FunctionIndex::from_u32(index))?;
369    Ok(())
370}
371
372fn read_elems(items: &ElementItems) -> WasmResult<Box<[FunctionIndex]>> {
373    let mut out = Vec::new();
374
375    match items {
376        ElementItems::Functions(funcs) => {
377            for res in funcs.clone().into_iter() {
378                let func_index = res.map_err(from_binaryreadererror_wasmerror)?;
379                out.push(FunctionIndex::from_u32(func_index));
380            }
381        }
382        ElementItems::Expressions(ty, section) => {
383            // TODO: check type is supported
384            if !(ty.is_extern_ref() || ty.is_func_ref()) {
385                return Err(wasm_unsupported!(
386                    "unsupported element type in element section: {:?}",
387                    ty
388                ));
389            }
390
391            for res in section.clone().into_iter() {
392                let expr = res.map_err(from_binaryreadererror_wasmerror)?;
393
394                let op = expr
395                    .get_operators_reader()
396                    .read()
397                    .map_err(from_binaryreadererror_wasmerror)?;
398                match op {
399                    Operator::RefNull { .. } => out.push(FunctionIndex::reserved_value()),
400                    Operator::RefFunc { function_index } => {
401                        out.push(FunctionIndex::from_u32(function_index))
402                    }
403                    other => {
404                        return Err(WasmError::Unsupported(format!(
405                            "unsupported init expr in element section: {other:?}",
406                        )));
407                    }
408                }
409            }
410        }
411    }
412
413    Ok(out.into_boxed_slice())
414}
415
416/// Parses the Element section of the wasm module.
417pub fn parse_element_section(
418    elements: ElementSectionReader<'_>,
419    environ: &mut ModuleEnvironment,
420) -> WasmResult<()> {
421    environ.reserve_table_initializers(elements.count())?;
422
423    for (index, elem) in elements.into_iter().enumerate() {
424        let Element {
425            kind,
426            items,
427            range: _,
428        } = elem.map_err(from_binaryreadererror_wasmerror)?;
429
430        let segments = read_elems(&items)?;
431        match kind {
432            ElementKind::Active {
433                table_index,
434                offset_expr,
435            } => {
436                let table_index = TableIndex::from_u32(table_index.unwrap_or(0));
437
438                let mut init_expr_reader = offset_expr.get_operators_reader();
439                let (base, offset) = match init_expr_reader
440                    .read()
441                    .map_err(from_binaryreadererror_wasmerror)?
442                {
443                    Operator::I32Const { value } => (None, value as u32 as usize),
444                    Operator::GlobalGet { global_index } => {
445                        (Some(GlobalIndex::from_u32(global_index)), 0)
446                    }
447                    ref s => {
448                        return Err(wasm_unsupported!(
449                            "unsupported init expr in element section: {:?}",
450                            s
451                        ));
452                    }
453                };
454                environ.declare_table_initializers(table_index, base, offset, segments)?
455            }
456            ElementKind::Passive => {
457                let index = ElemIndex::from_u32(index as u32);
458                environ.declare_passive_element(index, segments)?;
459            }
460            ElementKind::Declared => (),
461        }
462    }
463    Ok(())
464}
465
466/// Parses the Data section of the wasm module.
467pub fn parse_data_section<'data>(
468    data: DataSectionReader<'data>,
469    environ: &mut ModuleEnvironment<'data>,
470) -> WasmResult<()> {
471    environ.reserve_data_initializers(data.count())?;
472
473    for (index, entry) in data.into_iter().enumerate() {
474        let Data {
475            kind,
476            data,
477            range: _,
478        } = entry.map_err(from_binaryreadererror_wasmerror)?;
479        match kind {
480            DataKind::Active {
481                memory_index,
482                offset_expr,
483            } => {
484                let mut init_expr_reader = offset_expr.get_operators_reader();
485                let (base, offset) = match init_expr_reader
486                    .read()
487                    .map_err(from_binaryreadererror_wasmerror)?
488                {
489                    Operator::I32Const { value } => (None, value as u32 as usize),
490                    Operator::GlobalGet { global_index } => {
491                        (Some(GlobalIndex::from_u32(global_index)), 0)
492                    }
493                    ref s => {
494                        return Err(wasm_unsupported!(
495                            "unsupported init expr in data section: {:?}",
496                            s
497                        ));
498                    }
499                };
500                environ.declare_data_initialization(
501                    MemoryIndex::from_u32(memory_index),
502                    base,
503                    offset,
504                    data,
505                )?;
506            }
507            DataKind::Passive => {
508                let index = DataIndex::from_u32(index as u32);
509                environ.declare_passive_data(index, data)?;
510            }
511        }
512    }
513
514    Ok(())
515}
516
517/// Parses the Name section of the wasm module.
518pub fn parse_name_section<'data>(
519    names: NameSectionReader<'data>,
520    environ: &mut ModuleEnvironment<'data>,
521) -> WasmResult<()> {
522    let mut functions = HashMap::new();
523    let mut names_set = HashSet::new();
524
525    for res in names {
526        let subsection = if let Ok(subsection) = res {
527            subsection
528        } else {
529            // Should we log / warn here?
530            continue;
531        };
532        match subsection {
533            wasmparser::Name::Function(function_subsection) => {
534                let mut unique_name_map = HashMap::new();
535                for naming in function_subsection.into_iter().flatten() {
536                    if naming.index != u32::MAX {
537                        let mut name = naming.name.to_string();
538                        // In very rare cases a function name can have duplicates.
539                        if names_set.contains(&name) {
540                            let index = unique_name_map
541                                .entry(name.clone())
542                                .and_modify(|e| *e += 1)
543                                .or_insert(0);
544                            let alternative = format!("{name}.{index}");
545                            name = alternative;
546                        }
547                        names_set.insert(name.clone());
548                        functions.insert(FunctionIndex::from_u32(naming.index), name);
549                    }
550                }
551            }
552            wasmparser::Name::Module {
553                name,
554                name_range: _,
555            } => {
556                environ.declare_module_name(name)?;
557            }
558            wasmparser::Name::Local(_) => {}
559            wasmparser::Name::Label(_)
560            | wasmparser::Name::Type(_)
561            | wasmparser::Name::Table(_)
562            | wasmparser::Name::Memory(_)
563            | wasmparser::Name::Global(_)
564            | wasmparser::Name::Element(_)
565            | wasmparser::Name::Data(_)
566            | wasmparser::Name::Unknown { .. }
567            | wasmparser::Name::Field(_)
568            | wasmparser::Name::Tag(..) => {}
569        }
570    }
571
572    environ.declare_function_names(functions)
573}