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