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