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)]
8extern crate alloc;
11
12#[cfg(feature = "std")]
13extern crate std;
14
15#[cfg(feature = "logging")]
17#[allow(clippy::single_component_path_imports, unused_imports)]
18use log;
19
20#[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#[derive(Default, Debug)]
44pub struct Parser {}
45
46impl Parser {
47 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 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 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 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}