cranelift_wasm/
sections_translator.rs

1//! Helper functions to gather information for each of the non-function sections of a
2//! WebAssembly module.
3//!
4//! The code of these helper functions is straightforward since they only read metadata
5//! about linear memories, tables, globals, etc. and store them for later use.
6//!
7//! The special case of the initialize expressions for table elements offsets or global variables
8//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
9//! interpreted on the fly.
10use crate::environ::ModuleEnvironment;
11use crate::wasm_unsupported;
12use crate::{
13    DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, Table,
14    TableIndex, Tag, TagIndex, TypeIndex, WasmError, WasmResult,
15};
16use cranelift_entity::packed_option::ReservedValue;
17use cranelift_entity::EntityRef;
18use std::boxed::Box;
19use std::vec::Vec;
20use wasmparser::{
21    self, Data, DataKind, DataSectionReader, Element, ElementItems, ElementKind,
22    ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
23    GlobalSectionReader, GlobalType, ImportSectionReader, MemorySectionReader, MemoryType,
24    NameSectionReader, Naming, Operator, TableSectionReader, TableType, TagSectionReader, TagType,
25    Type, TypeRef, TypeSectionReader,
26};
27
28fn memory(ty: MemoryType) -> Memory {
29    Memory {
30        minimum: ty.initial,
31        maximum: ty.maximum,
32        shared: ty.shared,
33        memory64: ty.memory64,
34    }
35}
36
37fn tag(e: TagType) -> Tag {
38    match e.kind {
39        wasmparser::TagKind::Exception => Tag {
40            ty: TypeIndex::from_u32(e.func_type_idx),
41        },
42    }
43}
44
45fn table(ty: TableType) -> WasmResult<Table> {
46    Ok(Table {
47        wasm_ty: ty.element_type.try_into()?,
48        minimum: ty.initial,
49        maximum: ty.maximum,
50    })
51}
52
53fn global(ty: GlobalType, initializer: GlobalInit) -> WasmResult<Global> {
54    Ok(Global {
55        wasm_ty: ty.content_type.try_into()?,
56        mutability: ty.mutable,
57        initializer,
58    })
59}
60
61/// Parses the Type section of the wasm module.
62pub fn parse_type_section<'a>(
63    types: TypeSectionReader<'a>,
64    environ: &mut dyn ModuleEnvironment<'a>,
65) -> WasmResult<()> {
66    let count = types.count();
67    environ.reserve_types(count)?;
68
69    for entry in types {
70        match entry? {
71            Type::Func(wasm_func_ty) => {
72                environ.declare_type_func(wasm_func_ty.clone().try_into()?)?;
73            }
74        }
75    }
76    Ok(())
77}
78
79/// Parses the Import section of the wasm module.
80pub fn parse_import_section<'data>(
81    imports: ImportSectionReader<'data>,
82    environ: &mut dyn ModuleEnvironment<'data>,
83) -> WasmResult<()> {
84    environ.reserve_imports(imports.count())?;
85
86    for entry in imports {
87        let import = entry?;
88        match import.ty {
89            TypeRef::Func(sig) => {
90                environ.declare_func_import(
91                    TypeIndex::from_u32(sig),
92                    import.module,
93                    import.name,
94                )?;
95            }
96            TypeRef::Memory(ty) => {
97                environ.declare_memory_import(memory(ty), import.module, import.name)?;
98            }
99            TypeRef::Tag(e) => {
100                environ.declare_tag_import(tag(e), import.module, import.name)?;
101            }
102            TypeRef::Global(ty) => {
103                let ty = global(ty, GlobalInit::Import)?;
104                environ.declare_global_import(ty, import.module, import.name)?;
105            }
106            TypeRef::Table(ty) => {
107                let ty = table(ty)?;
108                environ.declare_table_import(ty, import.module, import.name)?;
109            }
110        }
111    }
112
113    environ.finish_imports()?;
114    Ok(())
115}
116
117/// Parses the Function section of the wasm module.
118pub fn parse_function_section(
119    functions: FunctionSectionReader,
120    environ: &mut dyn ModuleEnvironment,
121) -> WasmResult<()> {
122    let num_functions = functions.count();
123    if num_functions == std::u32::MAX {
124        // We reserve `u32::MAX` for our own use in cranelift-entity.
125        return Err(WasmError::ImplLimitExceeded);
126    }
127
128    environ.reserve_func_types(num_functions)?;
129
130    for entry in functions {
131        let sigindex = entry?;
132        environ.declare_func_type(TypeIndex::from_u32(sigindex))?;
133    }
134
135    Ok(())
136}
137
138/// Parses the Table section of the wasm module.
139pub fn parse_table_section(
140    tables: TableSectionReader,
141    environ: &mut dyn ModuleEnvironment,
142) -> WasmResult<()> {
143    environ.reserve_tables(tables.count())?;
144
145    for entry in tables {
146        let ty = table(entry?.ty)?;
147        environ.declare_table(ty)?;
148    }
149
150    Ok(())
151}
152
153/// Parses the Memory section of the wasm module.
154pub fn parse_memory_section(
155    memories: MemorySectionReader,
156    environ: &mut dyn ModuleEnvironment,
157) -> WasmResult<()> {
158    environ.reserve_memories(memories.count())?;
159
160    for entry in memories {
161        let memory = memory(entry?);
162        environ.declare_memory(memory)?;
163    }
164
165    Ok(())
166}
167
168/// Parses the Tag section of the wasm module.
169pub fn parse_tag_section(
170    tags: TagSectionReader,
171    environ: &mut dyn ModuleEnvironment,
172) -> WasmResult<()> {
173    environ.reserve_tags(tags.count())?;
174
175    for entry in tags {
176        let tag = tag(entry?);
177        environ.declare_tag(tag)?;
178    }
179
180    Ok(())
181}
182
183/// Parses the Global section of the wasm module.
184pub fn parse_global_section(
185    globals: GlobalSectionReader,
186    environ: &mut dyn ModuleEnvironment,
187) -> WasmResult<()> {
188    environ.reserve_globals(globals.count())?;
189
190    for entry in globals {
191        let wasmparser::Global { ty, init_expr } = entry?;
192        let mut init_expr_reader = init_expr.get_binary_reader();
193        let initializer = match init_expr_reader.read_operator()? {
194            Operator::I32Const { value } => GlobalInit::I32Const(value),
195            Operator::I64Const { value } => GlobalInit::I64Const(value),
196            Operator::F32Const { value } => GlobalInit::F32Const(value.bits()),
197            Operator::F64Const { value } => GlobalInit::F64Const(value.bits()),
198            Operator::V128Const { value } => {
199                GlobalInit::V128Const(u128::from_le_bytes(*value.bytes()))
200            }
201            Operator::RefNull { hty: _ } => GlobalInit::RefNullConst,
202            Operator::RefFunc { function_index } => {
203                GlobalInit::RefFunc(FuncIndex::from_u32(function_index))
204            }
205            Operator::GlobalGet { global_index } => {
206                GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
207            }
208            ref s => {
209                return Err(wasm_unsupported!(
210                    "unsupported init expr in global section: {:?}",
211                    s
212                ));
213            }
214        };
215        let ty = global(ty, initializer)?;
216        environ.declare_global(ty)?;
217    }
218
219    Ok(())
220}
221
222/// Parses the Export section of the wasm module.
223pub fn parse_export_section<'data>(
224    exports: ExportSectionReader<'data>,
225    environ: &mut dyn ModuleEnvironment<'data>,
226) -> WasmResult<()> {
227    environ.reserve_exports(exports.count())?;
228
229    for entry in exports {
230        let Export {
231            name,
232            ref kind,
233            index,
234        } = entry?;
235
236        // The input has already been validated, so we should be able to
237        // assume valid UTF-8 and use `from_utf8_unchecked` if performance
238        // becomes a concern here.
239        let index = index as usize;
240        match *kind {
241            ExternalKind::Func => environ.declare_func_export(FuncIndex::new(index), name)?,
242            ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), name)?,
243            ExternalKind::Memory => environ.declare_memory_export(MemoryIndex::new(index), name)?,
244            ExternalKind::Tag => environ.declare_tag_export(TagIndex::new(index), name)?,
245            ExternalKind::Global => environ.declare_global_export(GlobalIndex::new(index), name)?,
246        }
247    }
248
249    environ.finish_exports()?;
250    Ok(())
251}
252
253/// Parses the Start section of the wasm module.
254pub fn parse_start_section(index: u32, environ: &mut dyn ModuleEnvironment) -> WasmResult<()> {
255    environ.declare_start_func(FuncIndex::from_u32(index))?;
256    Ok(())
257}
258
259fn read_elems(items: &ElementItems) -> WasmResult<Box<[FuncIndex]>> {
260    let mut elems = Vec::new();
261    match items {
262        ElementItems::Functions(funcs) => {
263            for func in funcs.clone() {
264                elems.push(FuncIndex::from_u32(func?));
265            }
266        }
267        ElementItems::Expressions(funcs) => {
268            for func in funcs.clone() {
269                let idx = match func?.get_binary_reader().read_operator()? {
270                    Operator::RefNull { .. } => FuncIndex::reserved_value(),
271                    Operator::RefFunc { function_index } => FuncIndex::from_u32(function_index),
272                    s => {
273                        return Err(WasmError::Unsupported(format!(
274                            "unsupported init expr in element section: {:?}",
275                            s
276                        )));
277                    }
278                };
279                elems.push(idx);
280            }
281        }
282    }
283    Ok(elems.into_boxed_slice())
284}
285
286/// Parses the Element section of the wasm module.
287pub fn parse_element_section<'data>(
288    elements: ElementSectionReader<'data>,
289    environ: &mut dyn ModuleEnvironment,
290) -> WasmResult<()> {
291    environ.reserve_table_elements(elements.count())?;
292
293    for (index, entry) in elements.into_iter().enumerate() {
294        let Element {
295            kind,
296            items,
297            ty: _,
298            range: _,
299        } = entry?;
300        let segments = read_elems(&items)?;
301        match kind {
302            ElementKind::Active {
303                table_index,
304                offset_expr,
305            } => {
306                let mut offset_expr_reader = offset_expr.get_binary_reader();
307                let (base, offset) = match offset_expr_reader.read_operator()? {
308                    Operator::I32Const { value } => (None, value as u32),
309                    Operator::GlobalGet { global_index } => {
310                        (Some(GlobalIndex::from_u32(global_index)), 0)
311                    }
312                    ref s => {
313                        return Err(wasm_unsupported!(
314                            "unsupported init expr in element section: {:?}",
315                            s
316                        ));
317                    }
318                };
319                environ.declare_table_elements(
320                    TableIndex::from_u32(table_index),
321                    base,
322                    offset,
323                    segments,
324                )?
325            }
326            ElementKind::Passive => {
327                let index = ElemIndex::from_u32(index as u32);
328                environ.declare_passive_element(index, segments)?;
329            }
330            ElementKind::Declared => {
331                environ.declare_elements(segments)?;
332            }
333        }
334    }
335    Ok(())
336}
337
338/// Parses the Data section of the wasm module.
339pub fn parse_data_section<'data>(
340    data: DataSectionReader<'data>,
341    environ: &mut dyn ModuleEnvironment<'data>,
342) -> WasmResult<()> {
343    environ.reserve_data_initializers(data.count())?;
344
345    for (index, entry) in data.into_iter().enumerate() {
346        let Data {
347            kind,
348            data,
349            range: _,
350        } = entry?;
351        match kind {
352            DataKind::Active {
353                memory_index,
354                offset_expr,
355            } => {
356                let mut offset_expr_reader = offset_expr.get_binary_reader();
357                let (base, offset) = match offset_expr_reader.read_operator()? {
358                    Operator::I32Const { value } => (None, value as u64),
359                    Operator::I64Const { value } => (None, value as u64),
360                    Operator::GlobalGet { global_index } => {
361                        (Some(GlobalIndex::from_u32(global_index)), 0)
362                    }
363                    ref s => {
364                        return Err(wasm_unsupported!(
365                            "unsupported init expr in data section: {:?}",
366                            s
367                        ))
368                    }
369                };
370                environ.declare_data_initialization(
371                    MemoryIndex::from_u32(memory_index),
372                    base,
373                    offset,
374                    data,
375                )?;
376            }
377            DataKind::Passive => {
378                let index = DataIndex::from_u32(index as u32);
379                environ.declare_passive_data(index, data)?;
380            }
381        }
382    }
383
384    Ok(())
385}
386
387/// Parses the Name section of the wasm module.
388pub fn parse_name_section<'data>(
389    names: NameSectionReader<'data>,
390    environ: &mut dyn ModuleEnvironment<'data>,
391) -> WasmResult<()> {
392    for subsection in names {
393        match subsection? {
394            wasmparser::Name::Function(names) => {
395                for name in names {
396                    let Naming { index, name } = name?;
397                    // We reserve `u32::MAX` for our own use in cranelift-entity.
398                    if index != u32::max_value() {
399                        environ.declare_func_name(FuncIndex::from_u32(index), name);
400                    }
401                }
402            }
403            wasmparser::Name::Module { name, .. } => {
404                environ.declare_module_name(name);
405            }
406            wasmparser::Name::Local(reader) => {
407                for f in reader {
408                    let f = f?;
409                    if f.index == u32::max_value() {
410                        continue;
411                    }
412                    for name in f.names {
413                        let Naming { index, name } = name?;
414                        environ.declare_local_name(FuncIndex::from_u32(f.index), index, name)
415                    }
416                }
417            }
418            wasmparser::Name::Label(_)
419            | wasmparser::Name::Type(_)
420            | wasmparser::Name::Table(_)
421            | wasmparser::Name::Global(_)
422            | wasmparser::Name::Memory(_)
423            | wasmparser::Name::Element(_)
424            | wasmparser::Name::Data(_)
425            | wasmparser::Name::Unknown { .. } => {}
426        }
427    }
428    Ok(())
429}