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::FuncExact(_) => {
132                return Err(WasmError::Generic(
133                    "custom-descriptors not implemented yet".to_string(),
134                ));
135            }
136            TypeRef::Tag(t) => {
137                environ.declare_tag_import(t, module_name, field_name)?;
138            }
139            TypeRef::Memory(WPMemoryType {
140                shared,
141                memory64,
142                initial,
143                maximum,
144                ..
145            }) => {
146                if memory64 {
147                    unimplemented!("64bit memory not implemented yet");
148                }
149                environ.declare_memory_import(
150                    MemoryType {
151                        minimum: Pages(initial as u32),
152                        maximum: maximum.map(|p| Pages(p as u32)),
153                        shared,
154                    },
155                    module_name,
156                    field_name,
157                )?;
158            }
159            TypeRef::Global(ref ty) => {
160                environ.declare_global_import(
161                    GlobalType {
162                        ty: wptype_to_type(ty.content_type)?,
163                        mutability: ty.mutable.into(),
164                    },
165                    module_name,
166                    field_name,
167                )?;
168            }
169            TypeRef::Table(ref tab) => {
170                environ.declare_table_import(
171                    TableType {
172                        ty: wpreftype_to_type(tab.element_type)?,
173                        minimum: tab.initial as u32,
174                        maximum: tab.maximum.map(|v| v as u32),
175                    },
176                    module_name,
177                    field_name,
178                )?;
179            }
180        }
181    }
182
183    environ.finish_imports()?;
184    Ok(())
185}
186
187/// Parses the Function section of the wasm module.
188pub fn parse_function_section(
189    functions: FunctionSectionReader,
190    environ: &mut ModuleEnvironment,
191) -> WasmResult<()> {
192    let num_functions = functions.count();
193    if num_functions == u32::MAX {
194        // We reserve `u32::MAX` for our own use.
195        return Err(WasmError::ImplLimitExceeded);
196    }
197
198    environ.reserve_func_types(num_functions)?;
199
200    for entry in functions {
201        let sigindex = entry.map_err(from_binaryreadererror_wasmerror)?;
202        environ.declare_func_type(SignatureIndex::from_u32(sigindex))?;
203    }
204
205    Ok(())
206}
207
208/// Parses the Table section of the wasm module.
209pub fn parse_table_section(
210    tables: TableSectionReader,
211    environ: &mut ModuleEnvironment,
212) -> WasmResult<()> {
213    environ.reserve_tables(tables.count())?;
214
215    for entry in tables {
216        let table = entry.map_err(from_binaryreadererror_wasmerror)?;
217        environ.declare_table(TableType {
218            ty: wpreftype_to_type(table.ty.element_type).unwrap(),
219            minimum: table.ty.initial as u32,
220            maximum: table.ty.maximum.map(|v| v as u32),
221        })?;
222    }
223
224    Ok(())
225}
226
227/// Parses the Memory section of the wasm module.
228pub fn parse_memory_section(
229    memories: MemorySectionReader,
230    environ: &mut ModuleEnvironment,
231) -> WasmResult<()> {
232    environ.reserve_memories(memories.count())?;
233
234    for entry in memories {
235        let WPMemoryType {
236            shared,
237            memory64,
238            initial,
239            maximum,
240            ..
241        } = entry.map_err(from_binaryreadererror_wasmerror)?;
242        if memory64 {
243            unimplemented!("64bit memory not implemented yet");
244        }
245        environ.declare_memory(MemoryType {
246            minimum: Pages(initial as u32),
247            maximum: maximum.map(|p| Pages(p as u32)),
248            shared,
249        })?;
250    }
251
252    Ok(())
253}
254
255/// Parser the Tag section of the wasm module.
256pub fn parse_tag_section(
257    tags: wasmparser::SectionLimited<wasmparser::TagType>,
258    environ: &mut ModuleEnvironment,
259) -> WasmResult<()> {
260    environ.reserve_tags(tags.count())?;
261
262    for entry in tags {
263        let WPTagType { func_type_idx, .. } = entry.map_err(from_binaryreadererror_wasmerror)?;
264        environ.declare_tag(SignatureIndex::from_u32(func_type_idx))?;
265    }
266
267    Ok(())
268}
269
270/// Parses the Global section of the wasm module.
271pub fn parse_global_section(
272    globals: GlobalSectionReader,
273    environ: &mut ModuleEnvironment,
274) -> WasmResult<()> {
275    environ.reserve_globals(globals.count())?;
276
277    for entry in globals {
278        let wasmparser::Global {
279            ty:
280                WPGlobalType {
281                    content_type,
282                    mutable,
283                    ..
284                },
285            init_expr,
286        } = entry.map_err(from_binaryreadererror_wasmerror)?;
287        let mut init_expr_reader = init_expr.get_operators_reader();
288        let initializer = match init_expr_reader
289            .read()
290            .map_err(from_binaryreadererror_wasmerror)?
291        {
292            Operator::I32Const { value } => GlobalInit::I32Const(value),
293            Operator::I64Const { value } => GlobalInit::I64Const(value),
294            Operator::F32Const { value } => GlobalInit::F32Const(f32::from_bits(value.bits())),
295            Operator::F64Const { value } => GlobalInit::F64Const(f64::from_bits(value.bits())),
296            Operator::V128Const { value } => GlobalInit::V128Const(V128::from(*value.bytes())),
297            Operator::RefNull { hty: _ } => {
298                // TODO: Do we need to handle different heap types here?
299                GlobalInit::RefNullConst
300            }
301            Operator::RefFunc { function_index } => {
302                GlobalInit::RefFunc(FunctionIndex::from_u32(function_index))
303            }
304            Operator::GlobalGet { global_index } => {
305                GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
306            }
307            ref s => {
308                return Err(wasm_unsupported!(
309                    "unsupported init expr in global section: {:?}",
310                    s
311                ));
312            }
313        };
314        let global = GlobalType {
315            ty: wptype_to_type(content_type).unwrap(),
316            mutability: mutable.into(),
317        };
318        environ.declare_global(global, initializer)?;
319    }
320
321    Ok(())
322}
323
324/// Parses the Export section of the wasm module.
325pub fn parse_export_section<'data>(
326    exports: ExportSectionReader<'data>,
327    environ: &mut ModuleEnvironment<'data>,
328) -> WasmResult<()> {
329    environ.reserve_exports(exports.count())?;
330
331    for entry in exports {
332        let Export {
333            name: field,
334            ref kind,
335            index,
336        } = entry.map_err(from_binaryreadererror_wasmerror)?;
337
338        // The input has already been validated, so we should be able to
339        // assume valid UTF-8 and use `from_utf8_unchecked` if performance
340        // becomes a concern here.
341        let index = index as usize;
342        match *kind {
343            ExternalKind::Func => environ.declare_func_export(FunctionIndex::new(index), field)?,
344            ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), field)?,
345            ExternalKind::Memory => {
346                environ.declare_memory_export(MemoryIndex::new(index), field)?
347            }
348            ExternalKind::Global => {
349                environ.declare_global_export(GlobalIndex::new(index), field)?
350            }
351            ExternalKind::Tag => environ.declare_tag_export(TagIndex::new(index), field)?,
352            ExternalKind::FuncExact => unimplemented!("custom-descriptors not implemented yet"),
353        }
354    }
355
356    environ.finish_exports()?;
357    Ok(())
358}
359
360/// Parses the Start section of the wasm module.
361pub fn parse_start_section(index: u32, environ: &mut ModuleEnvironment) -> WasmResult<()> {
362    environ.declare_start_function(FunctionIndex::from_u32(index))?;
363    Ok(())
364}
365
366fn read_elems(items: &ElementItems) -> WasmResult<Box<[FunctionIndex]>> {
367    let mut out = Vec::new();
368
369    match items {
370        ElementItems::Functions(funcs) => {
371            for res in funcs.clone().into_iter() {
372                let func_index = res.map_err(from_binaryreadererror_wasmerror)?;
373                out.push(FunctionIndex::from_u32(func_index));
374            }
375        }
376        ElementItems::Expressions(ty, section) => {
377            // TODO: check type is supported
378            if !(ty.is_extern_ref() || ty.is_func_ref()) {
379                return Err(wasm_unsupported!(
380                    "unsupported element type in element section: {:?}",
381                    ty
382                ));
383            }
384
385            for res in section.clone().into_iter() {
386                let expr = res.map_err(from_binaryreadererror_wasmerror)?;
387
388                let op = expr
389                    .get_operators_reader()
390                    .read()
391                    .map_err(from_binaryreadererror_wasmerror)?;
392                match op {
393                    Operator::RefNull { .. } => out.push(FunctionIndex::reserved_value()),
394                    Operator::RefFunc { function_index } => {
395                        out.push(FunctionIndex::from_u32(function_index))
396                    }
397                    other => {
398                        return Err(WasmError::Unsupported(format!(
399                            "unsupported init expr in element section: {other:?}",
400                        )));
401                    }
402                }
403            }
404        }
405    }
406
407    Ok(out.into_boxed_slice())
408}
409
410/// Parses the Element section of the wasm module.
411pub fn parse_element_section(
412    elements: ElementSectionReader<'_>,
413    environ: &mut ModuleEnvironment,
414) -> WasmResult<()> {
415    environ.reserve_table_initializers(elements.count())?;
416
417    for (index, elem) in elements.into_iter().enumerate() {
418        let Element {
419            kind,
420            items,
421            range: _,
422        } = elem.map_err(from_binaryreadererror_wasmerror)?;
423
424        let segments = read_elems(&items)?;
425        match kind {
426            ElementKind::Active {
427                table_index,
428                offset_expr,
429            } => {
430                let table_index = TableIndex::from_u32(table_index.unwrap_or(0));
431
432                let mut init_expr_reader = offset_expr.get_operators_reader();
433                let (base, offset) = match init_expr_reader
434                    .read()
435                    .map_err(from_binaryreadererror_wasmerror)?
436                {
437                    Operator::I32Const { value } => (None, value as u32 as usize),
438                    Operator::GlobalGet { global_index } => {
439                        (Some(GlobalIndex::from_u32(global_index)), 0)
440                    }
441                    ref s => {
442                        return Err(wasm_unsupported!(
443                            "unsupported init expr in element section: {:?}",
444                            s
445                        ));
446                    }
447                };
448                environ.declare_table_initializers(table_index, base, offset, segments)?
449            }
450            ElementKind::Passive => {
451                let index = ElemIndex::from_u32(index as u32);
452                environ.declare_passive_element(index, segments)?;
453            }
454            ElementKind::Declared => (),
455        }
456    }
457    Ok(())
458}
459
460/// Parses the Data section of the wasm module.
461pub fn parse_data_section<'data>(
462    data: DataSectionReader<'data>,
463    environ: &mut ModuleEnvironment<'data>,
464) -> WasmResult<()> {
465    environ.reserve_data_initializers(data.count())?;
466
467    for (index, entry) in data.into_iter().enumerate() {
468        let Data {
469            kind,
470            data,
471            range: _,
472        } = entry.map_err(from_binaryreadererror_wasmerror)?;
473        match kind {
474            DataKind::Active {
475                memory_index,
476                offset_expr,
477            } => {
478                let mut init_expr_reader = offset_expr.get_operators_reader();
479                let (base, offset) = match init_expr_reader
480                    .read()
481                    .map_err(from_binaryreadererror_wasmerror)?
482                {
483                    Operator::I32Const { value } => (None, value as u32 as usize),
484                    Operator::GlobalGet { global_index } => {
485                        (Some(GlobalIndex::from_u32(global_index)), 0)
486                    }
487                    ref s => {
488                        return Err(wasm_unsupported!(
489                            "unsupported init expr in data section: {:?}",
490                            s
491                        ));
492                    }
493                };
494                environ.declare_data_initialization(
495                    MemoryIndex::from_u32(memory_index),
496                    base,
497                    offset,
498                    data,
499                )?;
500            }
501            DataKind::Passive => {
502                let index = DataIndex::from_u32(index as u32);
503                environ.declare_passive_data(index, data)?;
504            }
505        }
506    }
507
508    Ok(())
509}
510
511/// Parses the Name section of the wasm module.
512pub fn parse_name_section<'data>(
513    names: NameSectionReader<'data>,
514    environ: &mut ModuleEnvironment<'data>,
515) -> WasmResult<()> {
516    for res in names {
517        let subsection = if let Ok(subsection) = res {
518            subsection
519        } else {
520            // Should we log / warn here?
521            continue;
522        };
523        match subsection {
524            wasmparser::Name::Function(function_subsection) => {
525                for naming in function_subsection.into_iter().flatten() {
526                    if naming.index != u32::MAX {
527                        environ.declare_function_name(
528                            FunctionIndex::from_u32(naming.index),
529                            naming.name,
530                        )?;
531                    }
532                }
533            }
534            wasmparser::Name::Module {
535                name,
536                name_range: _,
537            } => {
538                environ.declare_module_name(name)?;
539            }
540            wasmparser::Name::Local(_) => {}
541            wasmparser::Name::Label(_)
542            | wasmparser::Name::Type(_)
543            | wasmparser::Name::Table(_)
544            | wasmparser::Name::Memory(_)
545            | wasmparser::Name::Global(_)
546            | wasmparser::Name::Element(_)
547            | wasmparser::Name::Data(_)
548            | wasmparser::Name::Unknown { .. }
549            | wasmparser::Name::Field(_)
550            | wasmparser::Name::Tag(..) => {}
551        }
552    }
553
554    Ok(())
555}