1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#![no_std]
#![forbid(unsafe_code)]
#![cfg_attr(not(feature = "std"), feature(error_in_core))]

mod std;
extern crate alloc;

// log for logging (optional).
#[cfg(feature = "logging")]
#[allow(clippy::single_component_path_imports)]
use log;

#[cfg(not(feature = "logging"))]
mod log {
    macro_rules! debug    ( ($($tt:tt)*) => {{}} );
    pub(crate) use debug;
}

mod conversion;
mod error;
mod module;
use alloc::vec::Vec;
pub use error::*;
use module::ModuleReader;
use tinywasm_types::Function;
use wasmparser::Validator;

pub use tinywasm_types::TinyWasmModule;

#[derive(Default)]
pub struct Parser {}

impl Parser {
    pub fn new() -> Self {
        Self {}
    }

    pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result<TinyWasmModule> {
        let wasm = wasm.as_ref();
        let mut validator = Validator::new();
        let mut reader = ModuleReader::new();

        for payload in wasmparser::Parser::new(0).parse_all(wasm) {
            reader.process_payload(payload?, &mut validator)?;
        }

        if !reader.end_reached {
            return Err(ParseError::EndNotReached);
        }

        reader.try_into()
    }

    #[cfg(feature = "std")]
    pub fn parse_module_file(&self, path: impl AsRef<crate::std::path::Path> + Clone) -> Result<TinyWasmModule> {
        use alloc::format;
        let f = crate::std::fs::File::open(path.clone())
            .map_err(|e| ParseError::Other(format!("Error opening file {:?}: {}", path.as_ref(), e)))?;

        let mut reader = crate::std::io::BufReader::new(f);
        self.parse_module_stream(&mut reader)
    }

    #[cfg(feature = "std")]
    pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result<TinyWasmModule> {
        use alloc::format;

        let mut validator = Validator::new();
        let mut reader = ModuleReader::new();
        let mut buffer = Vec::new();
        let mut parser = wasmparser::Parser::new(0);
        let mut eof = false;

        loop {
            match parser.parse(&buffer, eof)? {
                wasmparser::Chunk::NeedMoreData(hint) => {
                    let len = buffer.len();
                    buffer.extend((0..hint).map(|_| 0u8));
                    let read_bytes = stream
                        .read(&mut buffer[len..])
                        .map_err(|e| ParseError::Other(format!("Error reading from stream: {}", e)))?;
                    buffer.truncate(len + read_bytes);
                    eof = read_bytes == 0;
                }
                wasmparser::Chunk::Parsed { consumed, payload } => {
                    reader.process_payload(payload, &mut validator)?;
                    buffer.drain(..consumed);
                    if eof || reader.end_reached {
                        return reader.try_into();
                    }
                }
            };
        }
    }
}

impl TryFrom<ModuleReader> for TinyWasmModule {
    type Error = ParseError;

    fn try_from(reader: ModuleReader) -> Result<Self> {
        if !reader.end_reached {
            return Err(ParseError::EndNotReached);
        }

        let func_types = reader.function_section;
        let funcs = reader
            .code_section
            .into_iter()
            .zip(func_types)
            .map(|(f, ty)| Function {
                instructions: f.body,
                locals: f.locals,
                ty,
            })
            .collect::<Vec<_>>();

        Ok(TinyWasmModule {
            version: reader.version,
            start_func: reader.start_func,
            types: reader.type_section.into_boxed_slice(),
            funcs: funcs.into_boxed_slice(),
            exports: reader.export_section.into_boxed_slice(),
        })
    }
}