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