bwasm/
lib.rs

1use core::convert::{TryFrom, TryInto};
2use core::fmt;
3use core::iter::{self, FromIterator};
4
5use parity_wasm::elements as pwasm;
6
7pub use parity_wasm::elements::{
8    BlockType, BrTableData, CustomSection, ExportEntry, External, GlobalType, ImportEntry,
9    Instruction, Internal, MemoryType, ResizableLimits, TableElementType, TableType, ValueType,
10};
11pub use parity_wasm::SerializationError;
12pub use wasmi_validation::Error as ValidationError;
13
14pub const PAGE_SIZE: u32 = 64 * 1024; // 64 KiB
15
16#[derive(Debug)]
17pub enum LoadError {
18    SerializationError(SerializationError),
19    ValidationError(ValidationError),
20}
21
22impl fmt::Display for LoadError {
23    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24        match self {
25            Self::SerializationError(error) => write!(f, "Error while serializing file: {}", error),
26            Self::ValidationError(error) => write!(f, "Error while validating file: {}", error),
27        }
28    }
29}
30
31impl From<SerializationError> for LoadError {
32    fn from(error: SerializationError) -> Self {
33        Self::SerializationError(error)
34    }
35}
36
37impl From<ValidationError> for LoadError {
38    fn from(error: ValidationError) -> Self {
39        Self::ValidationError(error)
40    }
41}
42
43#[derive(Clone, PartialEq, Eq, Hash, Debug)]
44pub struct FunctionType {
45    type_ref: u32,
46    params: Vec<ValueType>,
47    return_type: Option<ValueType>,
48}
49
50impl FunctionType {
51    fn new(type_ref: u32, func_type: &mut pwasm::FunctionType) -> Self {
52        FunctionType {
53            type_ref,
54            params: take(func_type.params_mut()),
55            return_type: take(func_type.return_type_mut()),
56        }
57    }
58    pub const fn type_ref(&self) -> u32 {
59        self.type_ref
60    }
61    pub fn params(&self) -> &[ValueType] {
62        &self.params
63    }
64    pub fn param_count(&self) -> u32 {
65        self.params.len() as u32
66    }
67    pub const fn return_type(&self) -> Option<ValueType> {
68        self.return_type
69    }
70}
71
72impl fmt::Display for FunctionType {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        let params = self
75            .params
76            .iter()
77            .map(|t| t.to_string())
78            .collect::<Vec<String>>()
79            .join(", ");
80        let return_type = match self.return_type {
81            Some(return_type) => return_type.to_string(),
82            None => String::from("()"),
83        };
84        write!(f, "fn ({}) -> {}", params, return_type)
85    }
86}
87
88#[derive(Clone, PartialEq, Eq, Hash, Debug)]
89pub struct Function {
90    name: String,
91    func_type: FunctionType,
92    is_imported: bool,
93    locals: Vec<ValueType>,
94    instructions: Vec<Instruction>,
95}
96
97impl Function {
98    const fn new(
99        name: String,
100        func_type: FunctionType,
101        locals: Vec<ValueType>,
102        instructions: Vec<Instruction>,
103    ) -> Self {
104        Function {
105            name,
106            func_type,
107            is_imported: false,
108            locals,
109            instructions,
110        }
111    }
112
113    fn new_imported(name: String, func_type: FunctionType) -> Self {
114        Function {
115            name,
116            func_type,
117            is_imported: true,
118            locals: Vec::new(),
119            instructions: Vec::new(),
120        }
121    }
122
123    pub fn name(&self) -> &str {
124        &self.name
125    }
126    pub const fn func_type(&self) -> &FunctionType {
127        &self.func_type
128    }
129    pub const fn type_ref(&self) -> u32 {
130        self.func_type.type_ref()
131    }
132    pub fn params(&self) -> &[ValueType] {
133        self.func_type.params()
134    }
135    pub fn param_count(&self) -> u32 {
136        self.func_type.param_count()
137    }
138    pub const fn return_type(&self) -> Option<ValueType> {
139        self.func_type().return_type()
140    }
141    pub const fn is_imported(&self) -> bool {
142        self.is_imported
143    }
144    pub fn locals(&self) -> &[ValueType] {
145        &self.locals
146    }
147    pub fn instructions(&self) -> &[Instruction] {
148        &self.instructions
149    }
150}
151
152impl fmt::Display for Function {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        write!(f, "fn {}{}", self.name, &self.func_type.to_string()[3..])
155    }
156}
157
158#[derive(Clone, Copy, PartialEq, Debug)]
159pub enum InitExpr {
160    I32Const(i32),
161    I64Const(i64),
162    F32Const(u32),
163    F64Const(u64),
164    Global(u32),
165}
166
167impl TryFrom<&pwasm::InitExpr> for InitExpr {
168    type Error = String;
169
170    fn try_from(init_expr: &pwasm::InitExpr) -> Result<Self, Self::Error> {
171        let instrs = init_expr.code();
172        if instrs.len() != 2 {
173            return Err(format!("Init expr has invalid length: {}", instrs.len()));
174        }
175        if instrs[1] != Instruction::End {
176            return Err("Init expr has multiple instructions".to_string());
177        }
178        match &instrs[0] {
179            Instruction::I32Const(val) => Ok(InitExpr::I32Const(*val)),
180            Instruction::I64Const(val) => Ok(InitExpr::I64Const(*val)),
181            Instruction::F32Const(val) => Ok(InitExpr::F32Const(*val)),
182            Instruction::F64Const(val) => Ok(InitExpr::F64Const(*val)),
183            Instruction::GetGlobal(index) => Ok(InitExpr::Global(*index)),
184            other => Err(format!("Invalid instruction in init expr: {}", other)),
185        }
186    }
187}
188
189#[derive(Clone, PartialEq, Debug)]
190pub struct Global {
191    name: String,
192    is_imported: bool,
193    is_mutable: bool,
194    value_type: ValueType,
195    init_expr: InitExpr,
196}
197
198impl Global {
199    fn from_parity(name: String, global: &pwasm::GlobalEntry) -> Self {
200        let global_type = global.global_type();
201        Global {
202            name,
203            is_imported: false,
204            is_mutable: global_type.is_mutable(),
205            value_type: global_type.content_type(),
206            init_expr: global.init_expr().try_into().unwrap(),
207        }
208    }
209    fn from_import(name: String, index: u32, global_type: pwasm::GlobalType) -> Self {
210        Global {
211            name,
212            is_imported: true,
213            is_mutable: global_type.is_mutable(),
214            value_type: global_type.content_type(),
215            init_expr: InitExpr::Global(index),
216        }
217    }
218    pub fn name(&self) -> &str {
219        &self.name
220    }
221    pub const fn is_imported(&self) -> bool {
222        self.is_imported
223    }
224    pub const fn is_mutable(&self) -> bool {
225        self.is_mutable
226    }
227    pub const fn value_type(&self) -> ValueType {
228        self.value_type
229    }
230    pub const fn init_expr(&self) -> &InitExpr {
231        &self.init_expr
232    }
233}
234
235#[derive(Clone, Copy, PartialEq, Debug)]
236pub struct Table {
237    is_imported: bool,
238    elem_type: TableElementType,
239    limits: ResizableLimits,
240}
241
242impl Table {
243    pub const fn is_imported(&self) -> bool {
244        self.is_imported
245    }
246    pub const fn elem_type(&self) -> TableElementType {
247        self.elem_type
248    }
249    pub const fn limits(&self) -> &ResizableLimits {
250        &self.limits
251    }
252}
253
254#[derive(Clone, Copy, PartialEq, Debug)]
255pub struct Memory {
256    is_imported: bool,
257    limits: ResizableLimits,
258}
259
260impl Memory {
261    pub const fn is_imported(&self) -> bool {
262        self.is_imported
263    }
264    pub const fn limits(&self) -> &ResizableLimits {
265        &self.limits
266    }
267}
268
269#[derive(Clone, PartialEq, Debug)]
270pub struct TableInit {
271    index: u32,
272    offset: InitExpr,
273    entries: Vec<u32>,
274}
275
276impl TableInit {
277    pub const fn index(&self) -> u32 {
278        self.index
279    }
280    pub const fn offset(&self) -> &InitExpr {
281        &self.offset
282    }
283    pub fn entries(&self) -> &[u32] {
284        &self.entries
285    }
286}
287
288#[derive(Clone, PartialEq, Debug)]
289pub struct MemoryInit {
290    index: u32,
291    offset: InitExpr,
292    data: Vec<u8>,
293}
294
295impl MemoryInit {
296    pub const fn index(&self) -> u32 {
297        self.index
298    }
299    pub const fn offset(&self) -> &InitExpr {
300        &self.offset
301    }
302    pub fn data(&self) -> &[u8] {
303        &self.data
304    }
305}
306
307#[derive(Clone, PartialEq, Debug)]
308pub struct Module {
309    types: Vec<FunctionType>,
310    functions: Vec<Function>,
311    globals: Vec<Global>,
312    tables: Vec<Table>,
313    memories: Vec<Memory>,
314    table_inits: Vec<TableInit>,
315    memory_inits: Vec<MemoryInit>,
316    imports: Vec<ImportEntry>,
317    exports: Vec<ExportEntry>,
318    start_func: Option<u32>,
319    custom_sections: Vec<CustomSection>,
320}
321
322impl Module {
323    pub fn from_file<P: AsRef<::std::path::Path>>(path: P) -> Result<Self, LoadError> {
324        let module = parity_wasm::deserialize_file(path)?;
325        wasmi_validation::validate_module::<wasmi_validation::PlainValidator>(&module)?;
326        Ok(Module::from_parity_module(module))
327    }
328
329    fn from_parity_module(module: pwasm::Module) -> Self {
330        // TODO: What happens when multiple functions have the same name?
331        let mut module = match module.parse_names() {
332            Ok(module) => module,
333            Err((_, module)) => module,
334        };
335
336        let types = get_types(&mut module);
337
338        let mut globals = Vec::new();
339        let mut functions = Vec::new();
340        let mut tables = Vec::new();
341        let mut memories = Vec::new();
342        let mut imports = Vec::new();
343        let mut exports = Vec::new();
344
345        if let Some(import_sec) = module.import_section_mut() {
346            for entry in import_sec.entries() {
347                let name = format!("{}.{}", entry.module(), entry.field());
348                match entry.external() {
349                    External::Function(type_ref) => {
350                        let func_type = types[*type_ref as usize].clone();
351                        functions.push(Function::new_imported(name, func_type))
352                    }
353                    External::Global(global_type) => globals.push(Global::from_import(
354                        name,
355                        globals.len() as u32,
356                        *global_type,
357                    )),
358                    External::Table(table_type) => tables.push(Table {
359                        is_imported: true,
360                        elem_type: table_type.elem_type(),
361                        limits: *table_type.limits(),
362                    }),
363                    External::Memory(memory_type) => memories.push(Memory {
364                        is_imported: true,
365                        limits: *memory_type.limits(),
366                    }),
367                }
368            }
369            imports = take(import_sec.entries_mut());
370        }
371
372        handle_global_section(&mut globals, &module);
373        handle_function_section(&mut functions, &module, &types);
374        handle_table_section(&mut tables, &mut module);
375        handle_memory_section(&mut memories, &mut module);
376
377        if let Some(export_sec) = module.export_section_mut() {
378            for export in export_sec.entries() {
379                match export.internal() {
380                    Internal::Function(index) => {
381                        functions[*index as usize].name = export.field().to_string()
382                    }
383                    Internal::Global(index) => {
384                        globals[*index as usize].name = export.field().to_string()
385                    }
386                    _ => (),
387                }
388            }
389            exports = take(export_sec.entries_mut());
390        }
391
392        if let Some(name_sec) = module.names_section() {
393            if let Some(func_names) = name_sec.functions() {
394                for (i, name) in func_names.names() {
395                    functions[i as usize].name = name.clone();
396                }
397            }
398        }
399
400        Module {
401            types,
402            functions,
403            globals,
404            tables,
405            memories,
406            table_inits: get_table_inits(&mut module),
407            memory_inits: get_memory_inits(&mut module),
408            imports,
409            exports,
410            start_func: module.start_section(),
411            custom_sections: Vec::from_iter(module.custom_sections().cloned()),
412        }
413    }
414
415    pub fn types(&self) -> &[FunctionType] {
416        &self.types
417    }
418    pub fn functions(&self) -> &[Function] {
419        &self.functions
420    }
421    pub fn func(&self, index: u32) -> &Function {
422        &self.functions[index as usize]
423    }
424    pub fn get_func(&self, index: u32) -> Option<&Function> {
425        self.functions.get(index as usize)
426    }
427    pub fn globals(&self) -> &[Global] {
428        &self.globals
429    }
430    pub fn tables(&self) -> &[Table] {
431        &self.tables
432    }
433    pub fn memories(&self) -> &[Memory] {
434        &self.memories
435    }
436    pub fn table_inits(&self) -> &[TableInit] {
437        &self.table_inits
438    }
439    pub fn memory_inits(&self) -> &[MemoryInit] {
440        &self.memory_inits
441    }
442    pub fn imports(&self) -> &[ImportEntry] {
443        &self.imports
444    }
445    pub fn exports(&self) -> &[ExportEntry] {
446        &self.exports
447    }
448    pub const fn start_func(&self) -> Option<u32> {
449        self.start_func
450    }
451    pub fn custom_sections(&self) -> &[CustomSection] {
452        &self.custom_sections
453    }
454}
455
456fn get_types(module: &mut pwasm::Module) -> Vec<FunctionType> {
457    match module.type_section_mut() {
458        Some(type_sec) => type_sec
459            .types_mut()
460            .iter_mut()
461            .enumerate()
462            .map(|(i, t)| {
463                let pwasm::Type::Function(func_type) = t;
464                FunctionType::new(i as u32, func_type)
465            })
466            .collect(),
467        None => Vec::new(),
468    }
469}
470
471fn handle_global_section(globals: &mut Vec<Global>, module: &pwasm::Module) {
472    if let Some(global_sec) = module.global_section() {
473        for global in global_sec.entries() {
474            let name = format!("global_{}", globals.len());
475            globals.push(Global::from_parity(name, global));
476        }
477    }
478}
479
480fn handle_function_section(
481    functions: &mut Vec<Function>,
482    module: &pwasm::Module,
483    types: &[FunctionType],
484) {
485    if let Some(func_sec) = module.function_section() {
486        let func_bodies = module.code_section().map(|sec| sec.bodies()).unwrap_or(&[]);
487        for (type_ref, body) in func_sec.entries().iter().zip(func_bodies.iter()) {
488            let type_ref = type_ref.type_ref();
489            let name = format!("func_{}", functions.len());
490            let func_type = types[type_ref as usize].clone();
491            let locals = body
492                .locals()
493                .iter()
494                .flat_map(|locals| iter::repeat(locals.value_type()).take(locals.count() as usize))
495                .collect();
496            let instructions = body.code().elements().to_vec();
497            functions.push(Function::new(name, func_type, locals, instructions));
498        }
499    }
500}
501
502fn handle_table_section(tables: &mut Vec<Table>, module: &mut pwasm::Module) {
503    if let Some(table_sec) = module.table_section_mut() {
504        tables.extend(table_sec.entries_mut().drain(..).map(|table_type| Table {
505            is_imported: false,
506            elem_type: table_type.elem_type(),
507            limits: *table_type.limits(),
508        }));
509    }
510}
511
512fn handle_memory_section(memories: &mut Vec<Memory>, module: &mut pwasm::Module) {
513    if let Some(memory_sec) = module.memory_section_mut() {
514        memories.extend(
515            memory_sec
516                .entries_mut()
517                .drain(..)
518                .map(|memory_type| Memory {
519                    is_imported: false,
520                    limits: *memory_type.limits(),
521                }),
522        );
523    }
524}
525
526fn get_table_inits(module: &mut pwasm::Module) -> Vec<TableInit> {
527    let mut inits = Vec::new();
528    if let Some(elements_sec) = module.elements_section_mut() {
529        for init in elements_sec.entries_mut() {
530            inits.push(TableInit {
531                index: init.index(),
532                offset: init.offset().as_ref().unwrap().try_into().unwrap(),
533                entries: take(init.members_mut()),
534            });
535        }
536    }
537    inits
538}
539
540fn get_memory_inits(module: &mut pwasm::Module) -> Vec<MemoryInit> {
541    let mut inits = Vec::new();
542    if let Some(data_sec) = module.data_section_mut() {
543        for init in data_sec.entries_mut() {
544            inits.push(MemoryInit {
545                index: init.index(),
546                offset: init.offset().as_ref().unwrap().try_into().unwrap(),
547                data: take(init.value_mut()),
548            });
549        }
550    }
551    inits
552}
553
554fn take<T: Default>(t: &mut T) -> T {
555    std::mem::replace(t, T::default())
556}