tinywasm_parser/
lib.rs

1#![no_std]
2#![doc(test(
3    no_crate_inject,
4    attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables))
5))]
6#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)]
7#![forbid(unsafe_code)]
8//! See [`tinywasm`](https://docs.rs/tinywasm) for documentation.
9
10extern crate alloc;
11
12#[cfg(feature = "std")]
13extern crate std;
14
15// log for logging (optional).
16#[cfg(feature = "logging")]
17#[allow(clippy::single_component_path_imports, unused_imports)]
18use log;
19
20// noop fallback if logging is disabled.
21#[cfg(not(feature = "logging"))]
22#[allow(unused_imports, unused_macros)]
23pub(crate) mod log {
24    macro_rules! debug    ( ($($tt:tt)*) => {{}} );
25    macro_rules! info    ( ($($tt:tt)*) => {{}} );
26    macro_rules! error    ( ($($tt:tt)*) => {{}} );
27    pub(crate) use debug;
28    pub(crate) use error;
29    pub(crate) use info;
30}
31
32mod conversion;
33mod error;
34mod module;
35mod visit;
36pub use error::*;
37use module::ModuleReader;
38use wasmparser::{Validator, WasmFeaturesInflated};
39
40pub use tinywasm_types::TinyWasmModule;
41
42/// A WebAssembly parser
43#[derive(Default, Debug)]
44pub struct Parser {}
45
46impl Parser {
47    /// Create a new parser instance
48    pub fn new() -> Self {
49        Self {}
50    }
51
52    fn create_validator() -> Validator {
53        let features = WasmFeaturesInflated {
54            bulk_memory: true,
55            floats: true,
56            multi_value: true,
57            mutable_global: true,
58            reference_types: true,
59            sign_extension: true,
60            saturating_float_to_int: true,
61            function_references: true,
62            tail_call: true,
63            multi_memory: true,
64
65            gc_types: true,
66            component_model: false,
67            component_model_nested_names: false,
68            component_model_values: false,
69            component_model_more_flags: false,
70            exceptions: false,
71            extended_const: false,
72            gc: false,
73            memory64: false,
74            memory_control: false,
75            relaxed_simd: false,
76            simd: false,
77            threads: false,
78            custom_page_sizes: false,
79            shared_everything_threads: false,
80            component_model_multiple_returns: false,
81            legacy_exceptions: false,
82        };
83        Validator::new_with_features(features.into())
84    }
85
86    /// Parse a [`TinyWasmModule`] from bytes
87    pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result<TinyWasmModule> {
88        let wasm = wasm.as_ref();
89        let mut validator = Self::create_validator();
90        let mut reader = ModuleReader::new();
91
92        for payload in wasmparser::Parser::new(0).parse_all(wasm) {
93            reader.process_payload(payload?, &mut validator)?;
94        }
95
96        if !reader.end_reached {
97            return Err(ParseError::EndNotReached);
98        }
99
100        reader.into_module()
101    }
102
103    #[cfg(feature = "std")]
104    /// Parse a [`TinyWasmModule`] from a file. Requires `std` feature.
105    pub fn parse_module_file(&self, path: impl AsRef<crate::std::path::Path> + Clone) -> Result<TinyWasmModule> {
106        use alloc::format;
107        let f = crate::std::fs::File::open(path.clone())
108            .map_err(|e| ParseError::Other(format!("Error opening file {:?}: {}", path.as_ref(), e)))?;
109
110        let mut reader = crate::std::io::BufReader::new(f);
111        self.parse_module_stream(&mut reader)
112    }
113
114    #[cfg(feature = "std")]
115    /// Parse a [`TinyWasmModule`] from a stream. Requires `std` feature.
116    pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result<TinyWasmModule> {
117        use alloc::format;
118
119        let mut validator = Self::create_validator();
120        let mut reader = ModuleReader::new();
121        let mut buffer = alloc::vec::Vec::new();
122        let mut parser = wasmparser::Parser::new(0);
123        let mut eof = false;
124
125        loop {
126            match parser.parse(&buffer, eof)? {
127                wasmparser::Chunk::NeedMoreData(hint) => {
128                    let len = buffer.len();
129                    buffer.extend((0..hint).map(|_| 0u8));
130                    let read_bytes = stream
131                        .read(&mut buffer[len..])
132                        .map_err(|e| ParseError::Other(format!("Error reading from stream: {e}")))?;
133                    buffer.truncate(len + read_bytes);
134                    eof = read_bytes == 0;
135                }
136                wasmparser::Chunk::Parsed { consumed, payload } => {
137                    reader.process_payload(payload, &mut validator)?;
138                    buffer.drain(..consumed);
139                    if eof || reader.end_reached {
140                        return reader.into_module();
141                    }
142                }
143            };
144        }
145    }
146}
147
148impl TryFrom<ModuleReader> for TinyWasmModule {
149    type Error = ParseError;
150
151    fn try_from(reader: ModuleReader) -> Result<Self> {
152        reader.into_module()
153    }
154}