summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/parser/src/conversion.rs77
-rw-r--r--crates/parser/src/lib.rs81
-rw-r--r--crates/parser/src/module.rs89
-rw-r--r--crates/parser/src/visit.rs32
-rw-r--r--crates/tinywasm/Cargo.toml4
-rw-r--r--crates/tinywasm/src/imports.rs27
-rw-r--r--crates/tinywasm/src/instance.rs46
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs10
-rw-r--r--crates/tinywasm/src/interpreter/stack/call_stack.rs6
-rw-r--r--crates/tinywasm/src/interpreter/stack/value_stack.rs8
-rw-r--r--crates/tinywasm/src/module.rs9
-rw-r--r--crates/types/src/instructions.rs4
-rw-r--r--crates/types/src/lib.rs36
13 files changed, 142 insertions, 287 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index b62e670..570d0a1 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -70,38 +70,33 @@ pub(crate) fn convert_module_imports<'a, T: IntoIterator<Item = wasmparser::Resu
}
pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result<Import> {
- Ok(Import {
- module: import.module.to_string().into_boxed_str(),
- name: import.name.to_string().into_boxed_str(),
- kind: match import.ty {
- wasmparser::TypeRef::Func(ty) => ImportKind::Function(ty),
- wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType {
- element_type: convert_reftype(ty.element_type),
- size_initial: ty.initial.try_into().map_err(|_| {
- crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", ty.initial))
- })?,
- size_max: match ty.maximum {
- Some(max) => Some(max.try_into().map_err(|_| {
- crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}"))
- })?),
- None => None,
- },
- }),
- wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)),
- wasmparser::TypeRef::Global(ty) => {
- ImportKind::Global(GlobalType { mutable: ty.mutable, ty: convert_valtype(&ty.content_type) })
- }
- wasmparser::TypeRef::Tag(ty) => {
- return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {ty:?}")));
- }
- _ => {
- return Err(crate::ParseError::UnsupportedOperator(format!(
- "Unsupported import kind: {:?}",
- import.ty
- )));
- }
- },
- })
+ let kind = match import.ty {
+ wasmparser::TypeRef::Func(ty) => ImportKind::Function(ty),
+ wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType {
+ element_type: convert_reftype(ty.element_type),
+ size_initial: ty.initial.try_into().map_err(|_| {
+ crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", ty.initial))
+ })?,
+ size_max: match ty.maximum {
+ Some(max) => Some(max.try_into().map_err(|_| {
+ crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}"))
+ })?),
+ None => None,
+ },
+ }),
+ wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)),
+ wasmparser::TypeRef::Global(ty) => {
+ ImportKind::Global(GlobalType { mutable: ty.mutable, ty: convert_valtype(&ty.content_type) })
+ }
+ wasmparser::TypeRef::Tag(ty) => {
+ return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {ty:?}")));
+ }
+ _ => {
+ return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {:?}", import.ty)));
+ }
+ };
+
+ Ok(Import { module: import.module.into(), name: import.name.into(), kind })
}
pub(crate) fn convert_module_memories<T: IntoIterator<Item = wasmparser::Result<wasmparser::MemoryType>>>(
@@ -130,21 +125,16 @@ pub(crate) fn convert_module_table(table: wasmparser::Table<'_>) -> Result<Table
crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", table.ty.initial))
})?;
- let size_max = match table.ty.maximum {
- Some(max) => Some(
- max.try_into()
- .map_err(|_| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}")))?,
- ),
- None => None,
- };
-
+ let size_max = table.ty.maximum.map(|max| max.try_into()).transpose();
+ let size_max =
+ size_max.map_err(|e| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {e}")))?;
Ok(TableType { element_type: convert_reftype(table.ty.element_type), size_initial, size_max })
}
pub(crate) fn convert_module_globals(
globals: wasmparser::SectionLimited<'_, wasmparser::Global<'_>>,
) -> Result<Vec<Global>> {
- let globals = globals
+ globals
.into_iter()
.map(|global| {
let global = global?;
@@ -152,8 +142,7 @@ pub(crate) fn convert_module_globals(
let ops = global.init_expr.get_operators_reader();
Ok(Global { init: process_const_operators(ops)?, ty: GlobalType { mutable: global.ty.mutable, ty } })
})
- .collect::<Result<Vec<_>>>()?;
- Ok(globals)
+ .collect::<Result<Vec<_>>>()
}
pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result<Export> {
@@ -215,7 +204,6 @@ pub(crate) fn convert_module_code(
pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType> {
let mut types = ty.types();
-
if types.len() != 1 {
return Err(crate::ParseError::UnsupportedOperator(
"Expected exactly one type in the type section".to_string(),
@@ -225,7 +213,6 @@ pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType>
let ty = types.next().unwrap().unwrap_func();
let params = ty.params().iter().map(convert_valtype).collect::<Vec<ValType>>().into_boxed_slice();
let results = ty.results().iter().map(convert_valtype).collect::<Vec<ValType>>().into_boxed_slice();
-
Ok(FuncType { params, results })
}
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 2d46857..a0c0c7e 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -36,7 +36,7 @@ mod optimize;
mod visit;
pub use error::*;
use module::ModuleReader;
-use wasmparser::{Validator, WasmFeaturesInflated};
+use wasmparser::{Validator, WasmFeatures};
pub use tinywasm_types::TinyWasmModule;
@@ -79,56 +79,32 @@ impl Parser {
}
fn create_validator(_options: ParserOptions) -> Validator {
- let features = WasmFeaturesInflated {
- bulk_memory: true,
- floats: true,
- multi_value: true,
- mutable_global: true,
- reference_types: true,
- sign_extension: true,
- saturating_float_to_int: true,
- function_references: true,
- tail_call: true,
- multi_memory: true,
- simd: true,
- memory64: true,
- custom_page_sizes: true,
- bulk_memory_opt: true,
- call_indirect_overlong: true,
- wide_arithmetic: true,
- relaxed_simd: true,
-
- compact_imports: false,
- cm_map: false,
- custom_descriptors: false,
- cm_threading: false,
- extended_const: false,
- gc_types: true,
- stack_switching: false,
- component_model: false,
- exceptions: false,
- gc: false,
- memory_control: false,
- threads: false,
- shared_everything_threads: false,
- legacy_exceptions: false,
- cm_async: false,
- cm_async_builtins: false,
- cm_async_stackful: false,
- cm_nested_names: false,
- cm_values: false,
- cm_error_context: false,
- cm_fixed_length_lists: false,
- cm_gc: false,
- };
- Validator::new_with_features(features.into())
+ let features = WasmFeatures::CALL_INDIRECT_OVERLONG
+ | WasmFeatures::BULK_MEMORY_OPT
+ | WasmFeatures::RELAXED_SIMD
+ | WasmFeatures::GC_TYPES
+ | WasmFeatures::REFERENCE_TYPES
+ | WasmFeatures::MUTABLE_GLOBAL
+ | WasmFeatures::MULTI_VALUE
+ | WasmFeatures::FLOATS
+ | WasmFeatures::BULK_MEMORY
+ | WasmFeatures::SATURATING_FLOAT_TO_INT
+ | WasmFeatures::SIGN_EXTENSION
+ | WasmFeatures::FUNCTION_REFERENCES
+ | WasmFeatures::TAIL_CALL
+ | WasmFeatures::MULTI_MEMORY
+ | WasmFeatures::SIMD
+ | WasmFeatures::MEMORY64
+ | WasmFeatures::CUSTOM_PAGE_SIZES
+ | WasmFeatures::WIDE_ARITHMETIC;
+ Validator::new_with_features(features)
}
/// Parse a [`TinyWasmModule`] from bytes
pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result<TinyWasmModule> {
let wasm = wasm.as_ref();
let mut validator = Self::create_validator(self.options.clone());
- let mut reader = ModuleReader::new();
+ let mut reader = ModuleReader::default();
for payload in wasmparser::Parser::new(0).parse_all(wasm) {
reader.process_payload(payload?, &mut validator)?;
@@ -144,21 +120,16 @@ impl Parser {
#[cfg(feature = "std")]
/// Parse a [`TinyWasmModule`] from a file. Requires `std` feature.
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)
- .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)
+ let file = crate::std::fs::File::open(&path)
+ .map_err(|e| ParseError::Other(alloc::format!("Error opening file {:?}: {}", path.as_ref(), e)))?;
+ self.parse_module_stream(&mut crate::std::io::BufReader::new(file))
}
#[cfg(feature = "std")]
/// Parse a [`TinyWasmModule`] from a stream. Requires `std` feature.
pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result<TinyWasmModule> {
- use alloc::format;
-
let mut validator = Self::create_validator(self.options.clone());
- let mut reader = ModuleReader::new();
+ let mut reader = ModuleReader::default();
let mut buffer = alloc::vec::Vec::new();
let mut parser = wasmparser::Parser::new(0);
let mut eof = false;
@@ -170,7 +141,7 @@ impl Parser {
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}")))?;
+ .map_err(|e| ParseError::Other(alloc::format!("Error reading from stream: {e}")))?;
buffer.truncate(len + read_bytes);
eof = read_bytes == 0;
}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index 29e556c..cb29e21 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -2,10 +2,7 @@ use crate::log::debug;
use crate::{ParseError, ParserOptions, Result, conversion, optimize};
use alloc::string::ToString;
use alloc::{format, vec::Vec};
-use tinywasm_types::{
- ArcSlice, Data, Element, Export, FuncType, Global, Import, ImportKind, Instruction, MemoryType, TableType,
- TinyWasmModule, ValueCounts, ValueCountsSmall, WasmFunction, WasmFunctionData,
-};
+use tinywasm_types::*;
use wasmparser::{FuncValidatorAllocations, Payload, Validator};
pub(crate) type Code = (Vec<Instruction>, WasmFunctionData, ValueCounts);
@@ -30,23 +27,16 @@ pub(crate) struct ModuleReader {
}
impl ModuleReader {
- pub(crate) fn new() -> Self {
- Self::default()
- }
-
pub(crate) fn process_payload(&mut self, payload: Payload<'_>, validator: &mut Validator) -> Result<()> {
- use wasmparser::Payload::*;
-
match payload {
- Version { num, encoding, range } => {
+ Payload::Version { num, encoding, range } => {
validator.version(num, encoding, &range)?;
self.version = Some(num);
- match encoding {
- wasmparser::Encoding::Module => {}
- wasmparser::Encoding::Component => return Err(ParseError::InvalidEncoding(encoding)),
+ if let wasmparser::Encoding::Component = encoding {
+ return Err(ParseError::InvalidEncoding(encoding));
}
}
- StartSection { func, range } => {
+ Payload::StartSection { func, range } => {
if self.start_func.is_some() {
return Err(ParseError::DuplicateSection("Start section".into()));
}
@@ -55,7 +45,7 @@ impl ModuleReader {
validator.start_section(func, &range)?;
self.start_func = Some(func);
}
- TypeSection(reader) => {
+ Payload::TypeSection(reader) => {
if !self.func_types.is_empty() {
return Err(ParseError::DuplicateSection("Type section".into()));
}
@@ -68,7 +58,7 @@ impl ModuleReader {
.collect::<Result<Vec<FuncType>>>()?;
}
- GlobalSection(reader) => {
+ Payload::GlobalSection(reader) => {
if !self.globals.is_empty() {
return Err(ParseError::DuplicateSection("Global section".into()));
}
@@ -77,7 +67,7 @@ impl ModuleReader {
validator.global_section(&reader)?;
self.globals = conversion::convert_module_globals(reader)?;
}
- TableSection(reader) => {
+ Payload::TableSection(reader) => {
if !self.table_types.is_empty() {
return Err(ParseError::DuplicateSection("Table section".into()));
}
@@ -85,7 +75,7 @@ impl ModuleReader {
validator.table_section(&reader)?;
self.table_types = conversion::convert_module_tables(reader)?;
}
- MemorySection(reader) => {
+ Payload::MemorySection(reader) => {
if !self.memory_types.is_empty() {
return Err(ParseError::DuplicateSection("Memory section".into()));
}
@@ -94,12 +84,12 @@ impl ModuleReader {
validator.memory_section(&reader)?;
self.memory_types = conversion::convert_module_memories(reader)?;
}
- ElementSection(reader) => {
+ Payload::ElementSection(reader) => {
debug!("Found element section");
validator.element_section(&reader)?;
self.elements = conversion::convert_module_elements(reader)?;
}
- DataSection(reader) => {
+ Payload::DataSection(reader) => {
if !self.data.is_empty() {
return Err(ParseError::DuplicateSection("Data section".into()));
}
@@ -108,14 +98,14 @@ impl ModuleReader {
validator.data_section(&reader)?;
self.data = conversion::convert_module_data_sections(reader)?;
}
- DataCountSection { count, range } => {
+ Payload::DataCountSection { count, range } => {
debug!("Found data count section");
if !self.data.is_empty() {
return Err(ParseError::DuplicateSection("Data count section".into()));
}
validator.data_count_section(count, &range)?;
}
- FunctionSection(reader) => {
+ Payload::FunctionSection(reader) => {
if !self.code_type_addrs.is_empty() {
return Err(ParseError::DuplicateSection("Function section".into()));
}
@@ -124,7 +114,7 @@ impl ModuleReader {
validator.function_section(&reader)?;
self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::<Result<Vec<_>>>()?;
}
- CodeSectionStart { count, range, .. } => {
+ Payload::CodeSectionStart { count, range, .. } => {
debug!("Found code section ({count} functions)");
if !self.code.is_empty() {
return Err(ParseError::DuplicateSection("Code section".into()));
@@ -132,7 +122,7 @@ impl ModuleReader {
self.code.reserve(count as usize);
validator.code_section_start(&range)?;
}
- CodeSectionEntry(function) => {
+ Payload::CodeSectionEntry(function) => {
debug!("Found code section entry");
let v = validator.code_section_entry(&function)?;
let func_validator = v.into_validator(self.func_validator_allocations.take().unwrap_or_default());
@@ -140,7 +130,7 @@ impl ModuleReader {
self.code.push(code);
self.func_validator_allocations = Some(allocations);
}
- ImportSection(reader) => {
+ Payload::ImportSection(reader) => {
if !self.imports.is_empty() {
return Err(ParseError::DuplicateSection("Import section".into()));
}
@@ -149,7 +139,7 @@ impl ModuleReader {
validator.import_section(&reader)?;
self.imports = conversion::convert_module_imports(reader.into_imports())?;
}
- ExportSection(reader) => {
+ Payload::ExportSection(reader) => {
if !self.exports.is_empty() {
return Err(ParseError::DuplicateSection("Export section".into()));
}
@@ -159,7 +149,7 @@ impl ModuleReader {
self.exports =
reader.into_iter().map(|e| conversion::convert_module_export(e?)).collect::<Result<Vec<_>>>()?;
}
- End(offset) => {
+ Payload::End(offset) => {
debug!("Reached end of module");
if self.end_reached {
return Err(ParseError::DuplicateSection("End section".into()));
@@ -168,14 +158,13 @@ impl ModuleReader {
validator.end(offset)?;
self.end_reached = true;
}
- CustomSection(_reader) => {
+ Payload::CustomSection(_reader) => {
debug!("Found custom section");
debug!("Skipping custom section: {:?}", _reader.name());
}
- UnknownSection { .. } => return Err(ParseError::UnsupportedSection("Unknown section".into())),
+ Payload::UnknownSection { .. } => return Err(ParseError::UnsupportedSection("Unknown section".into())),
section => return Err(ParseError::UnsupportedSection(format!("Unsupported section: {section:?}"))),
};
-
Ok(())
}
@@ -188,38 +177,22 @@ impl ModuleReader {
return Err(ParseError::Other("Code and code type address count mismatch".to_string()));
}
- let imported_func_count =
- self.imports.iter().filter(|i| matches!(&i.kind, ImportKind::Function(_))).count() as u32;
-
- let funcs = self
- .code
- .into_iter()
- .zip(self.code_type_addrs)
- .enumerate()
- .map(|(func_idx, ((instructions, mut data, locals), ty_idx))| {
+ let imported_func_count = self.imports.iter().filter(|i| matches!(&i.kind, ImportKind::Function(_))).count();
+ let funcs = self.code.into_iter().zip(self.code_type_addrs).enumerate().map(
+ |(func_idx, ((instructions, mut data, locals), ty_idx))| {
let ty = self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone();
- let params = ValueCountsSmall::from(&ty.params);
- let locals = ValueCountsSmall {
- c32: u16::try_from(locals.c32).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- c64: u16::try_from(locals.c64).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- c128: u16::try_from(locals.c128).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- cref: u16::try_from(locals.cref).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- };
- let self_func_addr = imported_func_count + func_idx as u32;
- let instructions = optimize::optimize_instructions(instructions, &mut data, self_func_addr, options);
-
+ let params = ValueCounts::from(&ty.params);
+ let self_func = (imported_func_count + func_idx) as u32;
+ let instructions = optimize::optimize_instructions(instructions, &mut data, self_func, options);
WasmFunction { instructions: ArcSlice::from(instructions), data, locals, params, ty }
- })
- .collect::<Vec<_>>();
-
- let globals = self.globals;
- let table_types = self.table_types;
+ },
+ );
Ok(TinyWasmModule {
- funcs: funcs.into(),
+ funcs: funcs.collect(),
func_types: self.func_types.into(),
- globals: globals.into(),
- table_types: table_types.into(),
+ globals: self.globals.into(),
+ table_types: self.table_types.into(),
imports: self.imports.into(),
start_func: self.start_func,
data: self.data.into(),
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index 076f7c1..8313d9f 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -75,7 +75,7 @@ impl<R: WasmModuleResources> VisitSimdOperator<'_> for ValidateThenVisit<'_, R>
pub(crate) fn process_operators_and_validate<R: WasmModuleResources>(
validator: FuncValidator<R>,
body: FunctionBody<'_>,
- local_addr_map: Vec<u32>,
+ local_addr_map: Vec<u16>,
) -> Result<(Vec<Instruction>, WasmFunctionData, FuncValidatorAllocations)> {
let mut reader = body.get_operators_reader()?;
let remaining = reader.get_binary_reader().bytes_remaining();
@@ -148,7 +148,7 @@ pub(crate) struct FunctionBuilder<R: WasmModuleResources> {
instructions: Vec<Instruction>,
data: FunctionDataBuilder,
ctx_stack: Vec<LoweringCtx>,
- local_addr_map: Vec<u32>,
+ local_addr_map: Vec<u16>,
errors: Vec<crate::ParseError>,
}
@@ -244,13 +244,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_local_get(&mut self, idx: u32) -> Self::Output {
- let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else {
- self.errors.push(crate::ParseError::UnsupportedOperator(
- "Local index is too large, tinywasm does not support local indexes that large".to_string(),
- ));
- return;
- };
-
+ let resolved_idx = self.local_addr_map[idx as usize];
if let Some(t) = self.validator.get_local_type(idx) {
match t {
wasmparser::ValType::I32 | wasmparser::ValType::F32 => {
@@ -270,13 +264,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_local_set(&mut self, idx: u32) -> Self::Output {
- let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else {
- self.errors.push(crate::ParseError::UnsupportedOperator(
- "Local index is too large, tinywasm does not support local indexes that large".to_string(),
- ));
- return;
- };
-
+ let resolved_idx = self.local_addr_map[idx as usize];
if let Some(Some(t)) = self.validator.get_operand_type(0) {
self.instructions.push(match t {
wasmparser::ValType::I32 => Instruction::LocalSet32(resolved_idx),
@@ -290,13 +278,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_local_tee(&mut self, idx: u32) -> Self::Output {
- let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else {
- self.errors.push(crate::ParseError::UnsupportedOperator(
- "Local index is too large, tinywasm does not support local indexes that large".to_string(),
- ));
- return;
- };
-
+ let resolved_idx = self.local_addr_map[idx as usize];
if let Some(Some(t)) = self.validator.get_operand_type(0) {
self.instructions.push(match t {
wasmparser::ValType::I32 => Instruction::LocalTee32(resolved_idx),
@@ -472,7 +454,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
fn visit_typed_select_multi(&mut self, tys: Vec<wasmparser::ValType>) -> Self::Output {
let (c32, c64, c128, cref) = Self::label_keep_counts(&tys);
- self.instructions.push(Instruction::SelectMulti(tinywasm_types::ValueCountsSmall { c32, c64, c128, cref }));
+ self.instructions.push(Instruction::SelectMulti(tinywasm_types::ValueCounts { c32, c64, c128, cref }));
}
fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output {
@@ -588,7 +570,7 @@ impl<R: WasmModuleResources> FunctionBuilder<R> {
self.validator.simd_visitor(offset)
}
- pub(crate) fn new(instr_capacity: usize, validator: FuncValidator<R>, local_addr_map: Vec<u32>) -> Self {
+ pub(crate) fn new(instr_capacity: usize, validator: FuncValidator<R>, local_addr_map: Vec<u16>) -> Self {
Self {
validator,
local_addr_map,
diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml
index f11b3ad..00a47cf 100644
--- a/crates/tinywasm/Cargo.toml
+++ b/crates/tinywasm/Cargo.toml
@@ -90,7 +90,6 @@ harness=false
[[test]]
name="test-wasm-memory64"
harness=false
-test=false
[[test]]
name="test-wasm-extended-const"
@@ -100,17 +99,14 @@ test=false
[[test]]
name="test-wasm-relaxed-simd"
harness=false
-test=false
[[test]]
name="test-wasm-simd"
harness=false
-test=false
[[test]]
name="test-wasm-wide-arithmetic"
harness=false
-test=false
[[test]]
name="test-wast"
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index d55536f..9e8de16 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -159,18 +159,18 @@ impl Extern {
ty: &tinywasm_types::FuncType,
func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>> + 'static,
) -> Self {
- let _ty = ty.clone();
+ let ty_inner = ty.clone();
let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
- let _ty = _ty.clone();
+ let ty = ty_inner.clone();
let result = func(ctx, args)?;
- if result.len() != _ty.results.len() {
- return Err(crate::Error::InvalidHostFnReturn { expected: _ty.clone(), actual: result });
+ if result.len() != ty.results.len() {
+ return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result });
};
- result.iter().zip(_ty.results.iter()).try_for_each(|(val, ty)| {
- if val.val_type() != *ty {
- return Err(crate::Error::InvalidHostFnReturn { expected: _ty.clone(), actual: result.clone() });
+ result.iter().zip(ty.results.iter()).try_for_each(|(val, res_ty)| {
+ if val.val_type() != *res_ty {
+ return Err(crate::Error::InvalidHostFnReturn { expected: ty.clone(), actual: result.clone() });
}
Ok(())
})?;
@@ -344,20 +344,17 @@ impl Imports {
fn compare_table_types(import: &Import, expected: &TableType, actual: &TableType) -> Result<()> {
Self::compare_types(import, &actual.element_type, &expected.element_type)?;
-
if actual.size_initial > expected.size_initial {
return Err(LinkingError::incompatible_import_type(import).into());
}
match (expected.size_max, actual.size_max) {
- (None, Some(_)) => return Err(LinkingError::incompatible_import_type(import).into()),
+ (None, Some(_)) => Err(LinkingError::incompatible_import_type(import).into()),
(Some(expected_max), Some(actual_max)) if actual_max < expected_max => {
- return Err(LinkingError::incompatible_import_type(import).into());
+ Err(LinkingError::incompatible_import_type(import).into())
}
- _ => {}
+ _ => Ok(()),
}
-
- Ok(())
}
fn compare_memory_types(
@@ -394,9 +391,7 @@ impl Imports {
let mut imports = ResolvedImports::new();
for import in &*module.0.imports {
- let val = self.take(store, import).ok_or_else(|| LinkingError::unknown_import(import))?;
-
- match val {
+ match self.take(store, import).ok_or_else(|| LinkingError::unknown_import(import))? {
// A link to something that needs to be added to the store
ResolvedExtern::Extern(ex) => match (ex, &import.kind) {
(Extern::Global { ty, val }, ImportKind::Global(import_ty)) => {
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index 2782402..b95a94d 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -14,25 +14,18 @@ use crate::{Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct ModuleInstance(pub(crate) Rc<ModuleInstanceInner>);
-#[expect(dead_code)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct ModuleInstanceInner {
- pub(crate) failed_to_instantiate: bool,
-
pub(crate) store_id: usize,
pub(crate) idx: ModuleInstanceAddr,
-
pub(crate) types: ArcSlice<FuncType>,
-
pub(crate) func_addrs: Box<[FuncAddr]>,
pub(crate) table_addrs: Box<[TableAddr]>,
pub(crate) mem_addrs: Box<[MemAddr]>,
pub(crate) global_addrs: Box<[GlobalAddr]>,
pub(crate) elem_addrs: Box<[ElemAddr]>,
pub(crate) data_addrs: Box<[DataAddr]>,
-
pub(crate) func_start: Option<FuncAddr>,
- pub(crate) imports: ArcSlice<Import>,
pub(crate) exports: ArcSlice<Export>,
}
@@ -107,7 +100,6 @@ impl ModuleInstanceInner {
impl ModuleInstance {
/// Get the module instance's address
- #[inline]
pub fn id(&self) -> ModuleInstanceAddr {
self.0.idx
}
@@ -116,24 +108,18 @@ impl ModuleInstance {
///
/// See <https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation>
pub fn instantiate(store: &mut Store, module: Module, imports: Option<Imports>) -> Result<Self> {
- // This doesn't completely follow the steps in the spec, but the end result is the same
- // Constant expressions are evaluated directly where they are used, so we
- // don't need to create a auxiliary frame etc.
-
let idx = store.next_module_instance_idx();
let mut addrs = imports.unwrap_or_default().link(store, &module, idx)?;
addrs.funcs.extend(store.init_funcs(&module.0.funcs, idx)?);
addrs.tables.extend(store.init_tables(&module.0.table_types, idx)?);
addrs.memories.extend(store.init_memories(&module.0.memory_types, idx)?);
-
let global_addrs = store.init_globals(addrs.globals, &module.0.globals, &addrs.funcs, idx)?;
let (elem_addrs, elem_trapped) =
store.init_elements(&addrs.tables, &addrs.funcs, &global_addrs, &module.0.elements, idx)?;
let (data_addrs, data_trapped) = store.init_data(&addrs.memories, &module.0.data, idx)?;
let instance = ModuleInstanceInner {
- failed_to_instantiate: elem_trapped.is_some() || data_trapped.is_some(),
store_id: store.id(),
idx,
types: module.0.func_types.clone(),
@@ -144,7 +130,6 @@ impl ModuleInstance {
elem_addrs,
data_addrs,
func_start: module.0.start_func,
- imports: module.0.imports.clone(),
exports: module.0.exports.clone(),
};
@@ -166,7 +151,6 @@ impl ModuleInstance {
ExternalKind::Memory => self.0.mem_addrs.get(exports.index as usize)?,
ExternalKind::Global => self.0.global_addrs.get(exports.index as usize)?,
};
-
Some(ExternVal::new(exports.kind, *addr))
}
@@ -186,11 +170,11 @@ impl ModuleInstance {
}
/// Get a typed exported function by name
- pub fn exported_func<P, R>(&self, store: &Store, name: &str) -> Result<FuncHandleTyped<P, R>>
- where
- P: IntoWasmValueTuple,
- R: FromWasmValueTuple,
- {
+ pub fn exported_func<P: IntoWasmValueTuple, R: FromWasmValueTuple>(
+ &self,
+ store: &Store,
+ name: &str,
+ ) -> Result<FuncHandleTyped<P, R>> {
let func = self.exported_func_untyped(store, name)?;
Ok(FuncHandleTyped { func, marker: core::marker::PhantomData })
}
@@ -201,7 +185,6 @@ impl ModuleInstance {
let ExternVal::Memory(mem_addr) = export else {
return Err(Error::Other(format!("Export is not a memory: {name}")));
};
-
self.memory(store, mem_addr)
}
@@ -211,26 +194,23 @@ impl ModuleInstance {
let ExternVal::Memory(mem_addr) = export else {
return Err(Error::Other(format!("Export is not a memory: {name}")));
};
-
self.memory_mut(store, mem_addr)
}
/// Get a memory by address
pub fn memory<'a>(&self, store: &'a Store, addr: MemAddr) -> Result<MemoryRef<'a>> {
- let mem = store.state.get_mem(self.0.resolve_mem_addr(addr));
- Ok(MemoryRef(mem))
+ Ok(MemoryRef(store.state.get_mem(self.0.resolve_mem_addr(addr))))
}
/// Get a memory by address (mutable)
pub fn memory_mut<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result<MemoryRefMut<'a>> {
- let mem = store.state.get_mem_mut(self.0.resolve_mem_addr(addr));
- Ok(MemoryRefMut(mem))
+ Ok(MemoryRefMut(store.state.get_mem_mut(self.0.resolve_mem_addr(addr))))
}
/// Get the start function of the module
///
/// Returns None if the module has no start function
- /// If no start function is specified, also checks for a _start function in the exports
+ /// If no start function is specified, also checks for a `_start` function in the exports
///
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#start-function>
pub fn start_func(&self, store: &Store) -> Result<Option<FuncHandle>> {
@@ -251,23 +231,19 @@ impl ModuleInstance {
};
let func_addr = self.0.resolve_func_addr(func_index);
- let func_inst = store.state.get_func(func_addr);
- let ty = func_inst.func.ty();
-
+ let ty = store.state.get_func(func_addr).func.ty();
Ok(Some(FuncHandle { module_addr: self.id(), addr: func_addr, ty: ty.clone() }))
}
/// Invoke the start function of the module
///
- /// Returns None if the module has no start function
+ /// Returns `None` if the module has no start function
///
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#syntax-start>
pub fn start(&self, store: &mut Store) -> Result<Option<()>> {
let Some(func) = self.start_func(store)? else {
return Ok(None);
};
-
- let _ = func.call(store, &[])?;
- Ok(Some(()))
+ func.call(store, &[]).map(|_| Some(()))
}
}
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index 8316421..f739906 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -818,7 +818,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
fn exec_return(&mut self) -> bool {
- let result_counts = ValueCountsSmall::from(self.func.ty.results.iter());
+ let result_counts = ValueCounts::from(self.func.ty.results.iter());
self.store.stack.values.truncate_keep_counts(self.cf.locals_base, result_counts);
let Some(cf) = self.store.stack.call_stack.pop() else { return true };
@@ -1188,12 +1188,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
impl<'store> Executor<'store, false> {
#[inline(always)]
pub(crate) fn run_to_completion(&mut self) -> Result<()> {
- loop {
- // for some reason, using a iteration count of 4096 here seems to be a sweet spot for performance
- if self.exec::<1024>()?.is_some() {
- return Ok(());
- }
+ if self.exec::<{ usize::MAX }>()?.is_some() {
+ return Ok(());
}
+ unreachable!();
}
#[cfg(feature = "std")]
diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs
index a91f938..dfc71bc 100644
--- a/crates/tinywasm/src/interpreter/stack/call_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs
@@ -1,7 +1,7 @@
use crate::{Result, Trap, unlikely};
use alloc::vec::Vec;
-use tinywasm_types::{FuncAddr, ModuleInstanceAddr, ValueCountsSmall};
+use tinywasm_types::{FuncAddr, ModuleInstanceAddr, ValueCounts};
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct CallStack {
@@ -39,7 +39,7 @@ pub(crate) struct CallFrame {
pub(crate) module_addr: ModuleInstanceAddr,
pub(crate) func_addr: FuncAddr,
pub(crate) locals_base: StackBase,
- pub(crate) stack_offset: ValueCountsSmall,
+ pub(crate) stack_offset: ValueCounts,
}
#[derive(Clone, Copy, Default)]
@@ -56,7 +56,7 @@ impl CallFrame {
func_addr: FuncAddr,
module_addr: ModuleInstanceAddr,
locals_base: StackBase,
- stack_offset: ValueCountsSmall,
+ stack_offset: ValueCounts,
) -> Self {
Self { instr_ptr: 0, func_addr, module_addr, locals_base, stack_offset }
}
diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs
index 87d8452..b67ab09 100644
--- a/crates/tinywasm/src/interpreter/stack/value_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs
@@ -1,6 +1,6 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
-use tinywasm_types::{ExternRef, FuncRef, LocalAddr, ValType, ValueCountsSmall, WasmValue};
+use tinywasm_types::{ExternRef, FuncRef, LocalAddr, ValType, ValueCounts, WasmValue};
use crate::{Result, Trap, engine::Config, interpreter::*, unlikely};
@@ -195,7 +195,7 @@ impl ValueStack {
}
#[inline]
- pub(crate) fn select_multi(&mut self, counts: ValueCountsSmall) {
+ pub(crate) fn select_multi(&mut self, counts: ValueCounts) {
let condition = self.pop::<i32>() != 0;
self.stack_32.select_many(counts.c32 as usize, condition);
self.stack_64.select_many(counts.c64 as usize, condition);
@@ -257,7 +257,7 @@ impl ValueStack {
val_types.into_iter().map(|val_type| self.pop_wasmvalue(*val_type))
}
- pub(crate) fn enter_locals(&mut self, params: &ValueCountsSmall, locals: &ValueCountsSmall) -> Result<StackBase> {
+ pub(crate) fn enter_locals(&mut self, params: &ValueCounts, locals: &ValueCounts) -> Result<StackBase> {
let locals_base32 = if params.c32 == 0 && locals.c32 == 0 {
self.stack_32.len as u32
} else {
@@ -282,7 +282,7 @@ impl ValueStack {
Ok(StackBase { s32: locals_base32, s64: locals_base64, s128: locals_base128, sref: locals_baseref })
}
- pub(crate) fn truncate_keep_counts(&mut self, base: StackBase, keep: ValueCountsSmall) {
+ pub(crate) fn truncate_keep_counts(&mut self, base: StackBase, keep: ValueCounts) {
if keep.c32 == 0 && keep.c64 == 0 && keep.c128 == 0 && keep.cref == 0 {
self.stack_32.len = base.s32 as usize;
self.stack_64.len = base.s64 as usize;
diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs
index d6c5210..1ce3ff0 100644
--- a/crates/tinywasm/src/module.rs
+++ b/crates/tinywasm/src/module.rs
@@ -24,24 +24,21 @@ impl Module {
#[cfg(feature = "parser")]
/// Parse a module from bytes. Requires `parser` feature.
pub fn parse_bytes(wasm: &[u8]) -> Result<Self> {
- let parser = tinywasm_parser::Parser::new();
- let data = parser.parse_module_bytes(wasm)?;
+ let data = tinywasm_parser::Parser::new().parse_module_bytes(wasm)?;
Ok(data.into())
}
#[cfg(all(feature = "parser", feature = "std"))]
/// Parse a module from a file. Requires `parser` and `std` features.
pub fn parse_file(path: impl AsRef<crate::std::path::Path> + Clone) -> Result<Self> {
- let parser = tinywasm_parser::Parser::new();
- let data = parser.parse_module_file(path)?;
+ let data = tinywasm_parser::Parser::new().parse_module_file(path)?;
Ok(data.into())
}
#[cfg(all(feature = "parser", feature = "std"))]
/// Parse a module from a stream. Requires `parser` and `std` features.
pub fn parse_stream(stream: impl crate::std::io::Read) -> Result<Self> {
- let parser = tinywasm_parser::Parser::new();
- let data = parser.parse_module_stream(stream)?;
+ let data = tinywasm_parser::Parser::new().parse_module_stream(stream)?;
Ok(data.into())
}
diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs
index 78b52b8..0cbcad7 100644
--- a/crates/types/src/instructions.rs
+++ b/crates/types/src/instructions.rs
@@ -1,4 +1,4 @@
-use super::{FuncAddr, GlobalAddr, LocalAddr, TableAddr, TypeAddr, ValType, ValueCountsSmall};
+use super::{FuncAddr, GlobalAddr, LocalAddr, TableAddr, TypeAddr, ValType, ValueCounts};
use crate::{ConstIdx, DataAddr, ElemAddr, ExternAddr, MemAddr};
/// Represents a memory immediate in a WebAssembly memory instruction.
@@ -92,7 +92,7 @@ pub enum Instruction {
Drop64, Select64,
Drop128, Select128,
DropRef, SelectRef,
- SelectMulti(ValueCountsSmall),
+ SelectMulti(ValueCounts),
// > Variable Instructions
// See <https://webassembly.github.io/spec/core/binary/instructions.html#variable-instructions>
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index a56dc9f..e9d2729 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -208,16 +208,6 @@ pub struct FuncType {
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
pub struct ValueCounts {
- pub c32: u32,
- pub c64: u32,
- pub c128: u32,
- pub cref: u32,
-}
-
-#[derive(Default, Clone, Copy, PartialEq, Eq)]
-#[cfg_attr(feature = "debug", derive(Debug))]
-#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
-pub struct ValueCountsSmall {
pub c32: u16,
pub c64: u16,
pub c128: u16,
@@ -240,30 +230,14 @@ impl<'a, T: IntoIterator<Item = &'a ValType>> From<T> for ValueCounts {
}
}
-impl<'a, T: IntoIterator<Item = &'a ValType>> From<T> for ValueCountsSmall {
- #[inline]
- fn from(types: T) -> Self {
- let mut counts = Self::default();
- for ty in types {
- match ty {
- ValType::I32 | ValType::F32 => counts.c32 += 1,
- ValType::I64 | ValType::F64 => counts.c64 += 1,
- ValType::V128 => counts.c128 += 1,
- ValType::RefExtern | ValType::RefFunc => counts.cref += 1,
- }
- }
- counts
- }
-}
-
#[derive(Clone, PartialEq, Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
pub struct WasmFunction {
pub instructions: ArcSlice<Instruction>,
pub data: WasmFunctionData,
- pub locals: ValueCountsSmall,
- pub params: ValueCountsSmall,
+ pub locals: ValueCounts,
+ pub params: ValueCounts,
pub ty: FuncType,
}
@@ -298,6 +272,12 @@ impl<T> Deref for ArcSlice<T> {
}
}
+impl<T> FromIterator<T> for ArcSlice<T> {
+ fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
+ Self(Arc::from_iter(iter))
+ }
+}
+
#[cfg(feature = "archive")]
impl<T: serde::Serialize> serde::Serialize for ArcSlice<T> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {