summaryrefslogtreecommitdiff
path: root/crates/parser
diff options
context:
space:
mode:
Diffstat (limited to 'crates/parser')
-rw-r--r--crates/parser/Cargo.toml2
-rw-r--r--crates/parser/src/conversion.rs178
-rw-r--r--crates/parser/src/error.rs1
-rw-r--r--crates/parser/src/lib.rs92
-rw-r--r--crates/parser/src/module.rs91
5 files changed, 247 insertions, 117 deletions
diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml
index 590b90c..b187886 100644
--- a/crates/parser/Cargo.toml
+++ b/crates/parser/Cargo.toml
@@ -7,7 +7,7 @@ edition="2021"
# fork of wasmparser with no_std support, see https://github.com/bytecodealliance/wasmtime/issues/3495
# TODO: create dependency free parser
wasmparser={version="0.100", package="wasmparser-nostd", default-features=false}
-tracing={version="0.1.38", default-features=false} # logging
+log="0.4.20"
tinywasm-types={path="../types"}
[features]
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index c75aaf1..ee30db9 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -1,18 +1,82 @@
-use alloc::{format, vec::Vec};
-use tinywasm_types::{BlockArgs, Instruction, MemArg, ValType};
+use alloc::{boxed::Box, format, string::ToString, vec::Vec};
+use tinywasm_types::{BlockArgs, Export, ExternalKind, FuncType, Instruction, MemArg, ValType};
-use crate::Result;
+use crate::{module::CodeSection, Result};
-fn convert_blocktype(blocktype: wasmparser::BlockType) -> BlockArgs {
+pub(crate) fn convert_module_export(export: wasmparser::Export) -> Result<Export> {
+ let kind = match export.kind {
+ wasmparser::ExternalKind::Func => ExternalKind::Func,
+ wasmparser::ExternalKind::Table => ExternalKind::Table,
+ wasmparser::ExternalKind::Memory => ExternalKind::Memory,
+ wasmparser::ExternalKind::Global => ExternalKind::Global,
+ wasmparser::ExternalKind::Tag => {
+ return Err(crate::ParseError::UnsupportedOperator(format!(
+ "Unsupported export kind: {:?}",
+ export.kind
+ )))
+ }
+ };
+
+ Ok(Export {
+ index: export.index,
+ name: Box::from(export.name),
+ kind,
+ })
+}
+
+pub(crate) fn convert_module_code(func: wasmparser::FunctionBody) -> Result<CodeSection> {
+ let locals_reader = func.get_locals_reader()?;
+ let count = locals_reader.get_count();
+ let mut locals = Vec::with_capacity(count as usize);
+ locals.extend(
+ locals_reader
+ .into_iter()
+ .filter_map(|l| l.ok())
+ .map(|l| convert_valtype(&l.1)),
+ );
+
+ if locals.len() != count as usize {
+ return Err(crate::ParseError::Other("Invalid local index".to_string()));
+ }
+
+ let body_reader = func.get_operators_reader()?;
+ let body = process_operators(body_reader.into_iter())?;
+
+ Ok(CodeSection {
+ locals: locals.into_boxed_slice(),
+ body,
+ })
+}
+
+pub(crate) fn convert_module_type(ty: wasmparser::Type) -> Result<FuncType> {
+ let wasmparser::Type::Func(ty) = ty;
+ let params = ty
+ .params()
+ .iter()
+ .map(|p| Ok(convert_valtype(p)))
+ .collect::<Result<Vec<ValType>>>()?
+ .into_boxed_slice();
+
+ let results = ty
+ .results()
+ .iter()
+ .map(|p| Ok(convert_valtype(p)))
+ .collect::<Result<Vec<ValType>>>()?
+ .into_boxed_slice();
+
+ Ok(FuncType { params, results })
+}
+
+pub(crate) fn convert_blocktype(blocktype: &wasmparser::BlockType) -> BlockArgs {
use wasmparser::BlockType::*;
match blocktype {
Empty => BlockArgs::Empty,
Type(ty) => BlockArgs::Type(convert_valtype(ty)),
- FuncType(ty) => BlockArgs::FuncType(ty),
+ FuncType(ty) => BlockArgs::FuncType(*ty),
}
}
-fn convert_valtype(valtype: wasmparser::ValType) -> ValType {
+pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType {
use wasmparser::ValType::*;
match valtype {
I32 => ValType::I32,
@@ -25,14 +89,38 @@ fn convert_valtype(valtype: wasmparser::ValType) -> ValType {
}
}
-fn convert_memarg(memarg: wasmparser::MemArg) -> MemArg {
+pub(crate) fn convert_memarg(memarg: wasmparser::MemArg) -> MemArg {
MemArg {
offset: memarg.offset,
align: memarg.align,
}
}
-pub fn process_operator(op: wasmparser::Operator<'_>) -> Result<Instruction> {
+pub fn process_operators<'a>(
+ ops: impl Iterator<Item = Result<wasmparser::Operator<'a>, wasmparser::BinaryReaderError>>,
+) -> Result<Box<[Instruction]>> {
+ let mut instructions = Vec::new();
+ for op in ops {
+ match op? {
+ wasmparser::Operator::BrTable { targets } => {
+ instructions.push(Instruction::BrTable(targets.default()));
+ instructions.extend(
+ targets
+ .targets()
+ .collect::<Result<Vec<u32>, wasmparser::BinaryReaderError>>()?
+ .into_iter()
+ .map(Instruction::Br),
+ );
+ }
+ op => instructions.push(process_operator(&op)?),
+ }
+ }
+
+ Ok(instructions.into_boxed_slice())
+}
+
+#[inline]
+pub(crate) fn process_operator(op: &wasmparser::Operator) -> Result<Instruction> {
use wasmparser::Operator::*;
let v = match op {
Unreachable => Instruction::Unreachable,
@@ -42,56 +130,48 @@ pub fn process_operator(op: wasmparser::Operator<'_>) -> Result<Instruction> {
If { blockty } => Instruction::If(convert_blocktype(blockty)),
Else => Instruction::Else,
End => Instruction::End,
- Br { relative_depth } => Instruction::Br(relative_depth),
- BrIf { relative_depth } => Instruction::BrIf(relative_depth),
- BrTable { targets } => {
- let default = targets.default();
- let targets = targets
- .targets()
- .map(|t| Ok(t?))
- .collect::<Result<Vec<u32>>>()?;
-
- Instruction::BrTable(targets, default)
- }
+ Br { relative_depth } => Instruction::Br(*relative_depth),
+ BrIf { relative_depth } => Instruction::BrIf(*relative_depth),
+ BrTable { targets } => Instruction::BrTable(targets.default()),
Return => Instruction::Return,
- Call { function_index } => Instruction::Call(function_index),
+ Call { function_index } => Instruction::Call(*function_index),
CallIndirect {
type_index,
table_index,
..
- } => Instruction::CallIndirect(type_index, table_index),
+ } => Instruction::CallIndirect(*type_index, *table_index),
Drop => Instruction::Drop,
Select => Instruction::Select,
- LocalGet { local_index } => Instruction::LocalGet(local_index),
- LocalSet { local_index } => Instruction::LocalSet(local_index),
- LocalTee { local_index } => Instruction::LocalTee(local_index),
- GlobalGet { global_index } => Instruction::GlobalGet(global_index),
- GlobalSet { global_index } => Instruction::GlobalSet(global_index),
+ LocalGet { local_index } => Instruction::LocalGet(*local_index),
+ LocalSet { local_index } => Instruction::LocalSet(*local_index),
+ LocalTee { local_index } => Instruction::LocalTee(*local_index),
+ GlobalGet { global_index } => Instruction::GlobalGet(*global_index),
+ GlobalSet { global_index } => Instruction::GlobalSet(*global_index),
MemorySize { .. } => Instruction::MemorySize,
MemoryGrow { .. } => Instruction::MemoryGrow,
- I32Load { memarg } => Instruction::I32Load(convert_memarg(memarg)),
- I64Load { memarg } => Instruction::I64Load(convert_memarg(memarg)),
- F32Load { memarg } => Instruction::F32Load(convert_memarg(memarg)),
- F64Load { memarg } => Instruction::F64Load(convert_memarg(memarg)),
- I32Load8S { memarg } => Instruction::I32Load8S(convert_memarg(memarg)),
- I32Load8U { memarg } => Instruction::I32Load8U(convert_memarg(memarg)),
- I32Load16S { memarg } => Instruction::I32Load16S(convert_memarg(memarg)),
- I32Load16U { memarg } => Instruction::I32Load16U(convert_memarg(memarg)),
- I64Load8S { memarg } => Instruction::I64Load8S(convert_memarg(memarg)),
- I64Load8U { memarg } => Instruction::I64Load8U(convert_memarg(memarg)),
- I64Load16S { memarg } => Instruction::I64Load16S(convert_memarg(memarg)),
- I64Load16U { memarg } => Instruction::I64Load16U(convert_memarg(memarg)),
- I64Load32S { memarg } => Instruction::I64Load32S(convert_memarg(memarg)),
- I64Load32U { memarg } => Instruction::I64Load32U(convert_memarg(memarg)),
- I32Store { memarg } => Instruction::I32Store(convert_memarg(memarg)),
- I64Store { memarg } => Instruction::I64Store(convert_memarg(memarg)),
- F32Store { memarg } => Instruction::F32Store(convert_memarg(memarg)),
- F64Store { memarg } => Instruction::F64Store(convert_memarg(memarg)),
- I32Store8 { memarg } => Instruction::I32Store8(convert_memarg(memarg)),
- I32Store16 { memarg } => Instruction::I32Store16(convert_memarg(memarg)),
- I64Store8 { memarg } => Instruction::I64Store8(convert_memarg(memarg)),
- I64Store16 { memarg } => Instruction::I64Store16(convert_memarg(memarg)),
- I64Store32 { memarg } => Instruction::I64Store32(convert_memarg(memarg)),
+ I32Load { memarg } => Instruction::I32Load(convert_memarg(*memarg)),
+ I64Load { memarg } => Instruction::I64Load(convert_memarg(*memarg)),
+ F32Load { memarg } => Instruction::F32Load(convert_memarg(*memarg)),
+ F64Load { memarg } => Instruction::F64Load(convert_memarg(*memarg)),
+ I32Load8S { memarg } => Instruction::I32Load8S(convert_memarg(*memarg)),
+ I32Load8U { memarg } => Instruction::I32Load8U(convert_memarg(*memarg)),
+ I32Load16S { memarg } => Instruction::I32Load16S(convert_memarg(*memarg)),
+ I32Load16U { memarg } => Instruction::I32Load16U(convert_memarg(*memarg)),
+ I64Load8S { memarg } => Instruction::I64Load8S(convert_memarg(*memarg)),
+ I64Load8U { memarg } => Instruction::I64Load8U(convert_memarg(*memarg)),
+ I64Load16S { memarg } => Instruction::I64Load16S(convert_memarg(*memarg)),
+ I64Load16U { memarg } => Instruction::I64Load16U(convert_memarg(*memarg)),
+ I64Load32S { memarg } => Instruction::I64Load32S(convert_memarg(*memarg)),
+ I64Load32U { memarg } => Instruction::I64Load32U(convert_memarg(*memarg)),
+ I32Store { memarg } => Instruction::I32Store(convert_memarg(*memarg)),
+ I64Store { memarg } => Instruction::I64Store(convert_memarg(*memarg)),
+ F32Store { memarg } => Instruction::F32Store(convert_memarg(*memarg)),
+ F64Store { memarg } => Instruction::F64Store(convert_memarg(*memarg)),
+ I32Store8 { memarg } => Instruction::I32Store8(convert_memarg(*memarg)),
+ I32Store16 { memarg } => Instruction::I32Store16(convert_memarg(*memarg)),
+ I64Store8 { memarg } => Instruction::I64Store8(convert_memarg(*memarg)),
+ I64Store16 { memarg } => Instruction::I64Store16(convert_memarg(*memarg)),
+ I64Store32 { memarg } => Instruction::I64Store32(convert_memarg(*memarg)),
I32Eqz => Instruction::I32Eqz,
I32Eq => Instruction::I32Eq,
I32Ne => Instruction::I32Ne,
diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs
index c99fa01..ee217c7 100644
--- a/crates/parser/src/error.rs
+++ b/crates/parser/src/error.rs
@@ -2,6 +2,7 @@ use alloc::string::{String, ToString};
use wasmparser::Encoding;
pub enum ParseError {
+ InvalidType,
UnsupportedSection(String),
DuplicateSection(String),
EmptySection(String),
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index d2b6d9c..5b17fb8 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -9,49 +9,101 @@ extern crate std;
mod conversion;
mod error;
mod module;
+use alloc::vec::Vec;
pub use error::*;
use module::ModuleReader;
-use tinywasm_types::TinyWasmModule;
+use tinywasm_types::{Function, TinyWasmModule};
+use wasmparser::Validator;
pub struct Parser {}
impl Parser {
pub fn parse_module_bytes(wasm: &[u8]) -> Result<TinyWasmModule> {
- let reader = ModuleReader::new();
- reader.try_into()
- }
+ 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);
+ }
- pub fn parse_module_file(file_name: &str) -> Result<TinyWasmModule> {
- let reader = ModuleReader::new();
reader.try_into()
}
#[cfg(feature = "std")]
- pub fn parse_module_stream(stream: impl std::io::Read) -> Result<TinyWasmModule> {
- let reader = ModuleReader::new();
- reader.try_into()
- }
+ pub fn parse_module_file(path: impl AsRef<crate::std::path::Path>) -> Result<TinyWasmModule> {
+ use alloc::format;
+ let f = crate::std::fs::File::open("log.txt").map_err(|e| {
+ ParseError::Other(format!("Error opening file {:?}: {}", path.as_ref(), e))
+ })?;
- pub fn read_module_bytes(bytes: &[u8]) -> Result<TinyWasmModule> {
- unimplemented!()
- }
- pub fn read_module_file(file_name: &str) -> Result<TinyWasmModule> {
- unimplemented!()
+ let mut reader = crate::std::io::BufReader::new(f);
+ Self::parse_module_stream(&mut reader)
}
+
#[cfg(feature = "std")]
- pub fn read_module_stream(stream: impl std::io::Read) -> Result<TinyWasmModule> {
- unimplemented!()
+ pub fn parse_module_stream(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 {
+impl TryFrom<ModuleReader> for TinyWasmModule {
type Error = ParseError;
- fn try_from(reader: ModuleReader<'_>) -> Result<Self> {
+ fn try_from(reader: ModuleReader) -> Result<Self> {
if !reader.end_reached {
return Err(ParseError::EndNotReached);
}
- unimplemented!()
+ let func_types = reader.function_section;
+ let funcs = reader
+ .code_section
+ .into_iter()
+ .zip(func_types)
+ .map(|(f, ty)| Function {
+ body: f.body,
+ locals: f.locals,
+ ty,
+ })
+ .collect::<Vec<_>>()
+ .into_boxed_slice();
+
+ Ok(TinyWasmModule {
+ version: reader.version,
+ start_func: reader.start_func,
+ types: reader.type_section.into_boxed_slice(),
+ funcs,
+ exports: reader.export_section.into_boxed_slice(),
+ })
}
}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index e2e1bde..268bad7 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -1,21 +1,26 @@
-use alloc::{format, vec::Vec};
+use alloc::{boxed::Box, format, vec::Vec};
use core::fmt::Debug;
-use tracing::debug;
-use wasmparser::{
- ExportSectionReader, FunctionBody, FunctionSectionReader, Payload, TypeSectionReader, Validator,
-};
+use log::debug;
+use tinywasm_types::{Export, FuncType, Instruction, ValType};
+use wasmparser::{Payload, Validator};
-use crate::{ParseError, Result};
+use crate::{conversion, ParseError, Result};
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct CodeSection {
+ pub locals: Box<[ValType]>,
+ pub body: Box<[Instruction]>,
+}
#[derive(Default)]
-pub struct ModuleReader<'a> {
+pub struct ModuleReader {
pub version: Option<u16>,
pub start_func: Option<u32>,
- pub type_section: Option<TypeSectionReader<'a>>,
- pub function_section: Option<FunctionSectionReader<'a>>,
- pub export_section: Option<ExportSectionReader<'a>>,
- pub code_section: Option<CodeSection<'a>>,
+ pub type_section: Vec<FuncType>,
+ pub function_section: Vec<u32>,
+ pub export_section: Vec<Export>,
+ pub code_section: Vec<CodeSection>,
// pub table_section: Option<TableSectionReader<'a>>,
// pub memory_section: Option<MemorySectionReader<'a>>,
@@ -26,8 +31,8 @@ pub struct ModuleReader<'a> {
pub end_reached: bool,
}
-impl Debug for ModuleReader<'_> {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+impl Debug for ModuleReader {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ModuleReader")
.field("version", &self.version)
.field("type_section", &self.type_section)
@@ -44,16 +49,12 @@ impl Debug for ModuleReader<'_> {
}
}
-impl<'a> ModuleReader<'a> {
- pub fn new() -> ModuleReader<'a> {
+impl ModuleReader {
+ pub fn new() -> ModuleReader {
Self::default()
}
- pub fn process_payload(
- &mut self,
- payload: Payload<'a>,
- validator: &mut Validator,
- ) -> Result<()> {
+ pub fn process_payload(&mut self, payload: Payload, validator: &mut Validator) -> Result<()> {
use wasmparser::Payload::*;
match payload {
@@ -79,12 +80,18 @@ impl<'a> ModuleReader<'a> {
TypeSection(reader) => {
debug!("Found type section");
validator.type_section(&reader)?;
- self.type_section = Some(reader);
+ self.type_section = reader
+ .into_iter()
+ .map(|t| conversion::convert_module_type(t?))
+ .collect::<Result<Vec<FuncType>>>()?;
}
FunctionSection(reader) => {
debug!("Found function section");
validator.function_section(&reader)?;
- self.function_section = Some(reader);
+ self.function_section = reader
+ .into_iter()
+ .map(|f| Ok(f?))
+ .collect::<Result<Vec<_>>>()?;
}
TableSection(_reader) => {
return Err(ParseError::UnsupportedSection("Table section".into()));
@@ -118,22 +125,18 @@ impl<'a> ModuleReader<'a> {
}
CodeSectionStart { count, range, .. } => {
debug!("Found code section ({} functions)", count);
- if self.code_section.is_some() {
+ if !self.code_section.is_empty() {
return Err(ParseError::DuplicateSection("Code section".into()));
}
validator.code_section_start(count, &range)?;
- self.code_section = Some(CodeSection::new());
}
CodeSectionEntry(function) => {
debug!("Found code section entry");
validator.code_section_entry(&function)?;
- if let Some(code_section) = &mut self.code_section {
- code_section.functions.push(function);
- } else {
- return Err(ParseError::EmptySection("Code section".into()));
- }
+ self.code_section
+ .push(conversion::convert_module_code(function)?);
}
ImportSection(_reader) => {
return Err(ParseError::UnsupportedSection("Import section".into()));
@@ -145,7 +148,10 @@ impl<'a> ModuleReader<'a> {
ExportSection(reader) => {
debug!("Found export section");
validator.export_section(&reader)?;
- self.export_section = Some(reader);
+ self.export_section = reader
+ .into_iter()
+ .map(|e| conversion::convert_module_export(e?))
+ .collect::<Result<Vec<_>>>()?;
}
End(offset) => {
debug!("Reached end of module");
@@ -156,26 +162,17 @@ impl<'a> ModuleReader<'a> {
validator.end(offset)?;
self.end_reached = true;
}
- UnknownSection { .. } | _ => {
- return Err(ParseError::UnsupportedSection(format!("Unknown section")))
+ UnknownSection { .. } => {
+ return Err(ParseError::UnsupportedSection("Unknown section".into()))
+ }
+ section => {
+ return Err(ParseError::UnsupportedSection(format!(
+ "Unsupported section: {:?}",
+ section
+ )))
}
};
Ok(())
}
}
-
-/// A WebAssembly code section
-/// Can be cloned to read functions multiple times
-#[derive(Debug, Clone)]
-pub struct CodeSection<'a> {
- pub(crate) functions: Vec<FunctionBody<'a>>,
-}
-
-impl<'a> CodeSection<'a> {
- fn new() -> Self {
- Self {
- functions: Vec::new(),
- }
- }
-}