diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-01-31 00:03:42 +0100 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-01-31 00:03:42 +0100 |
| commit | 6850e8bc669171c658696655f4f31996d6e934af (patch) | |
| tree | 6efb61f6dc9790dd219a8bf7e39ab2434af3140a /crates | |
| parent | 712b7da4f6b20a9754dd78be632988bbb66f2cbe (diff) | |
chore: overall code cleanup
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
Diffstat (limited to 'crates')
39 files changed, 588 insertions, 423 deletions
diff --git a/crates/benchmarks/Cargo.toml b/crates/benchmarks/Cargo.toml new file mode 100644 index 0000000..b9225c1 --- /dev/null +++ b/crates/benchmarks/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name="benchmarks" +publish=false +edition.workspace=true + +[dependencies] +criterion={version="0.5", features=["html_reports"]} +tinywasm={path="../../crates/tinywasm", features=["unsafe"]} +wat={version="1.0"} +wasmi={version="0.31", features=["std"]} +wasmer={version="4.2", features=["cranelift", "singlepass"]} +argon2={version="0.5"} + +[[bench]] +name="selfhosted" +harness=false + +[[bench]] +name="fibonacci" +harness=false + +[[bench]] +name="argon2id" +harness=false diff --git a/crates/benchmarks/benches/argon2id.rs b/crates/benchmarks/benches/argon2id.rs new file mode 100644 index 0000000..7c1ffc5 --- /dev/null +++ b/crates/benchmarks/benches/argon2id.rs @@ -0,0 +1,60 @@ +mod util; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use util::wasm_to_twasm; + +fn run_tinywasm(twasm: &[u8], params: (i32, i32, i32), name: &str) { + let (mut store, instance) = util::tinywasm(twasm); + let argon2 = instance.exported_func::<(i32, i32, i32), i32>(&store, name).expect("exported_func"); + argon2.call(&mut store, params).expect("call"); +} + +fn run_wasmi(wasm: &[u8], params: (i32, i32, i32), name: &str) { + let (module, mut store, linker) = util::wasmi(wasm); + let instance = linker.instantiate(&mut store, &module).expect("instantiate").start(&mut store).expect("start"); + let argon2 = instance.get_typed_func::<(i32, i32, i32), i32>(&mut store, name).expect("get_typed_func"); + argon2.call(&mut store, params).expect("call"); +} + +fn run_wasmer(wasm: &[u8], params: (i32, i32, i32), name: &str) { + use wasmer::Value; + let (mut store, instance) = util::wasmer(wasm); + let argon2 = instance.exports.get_function(name).expect("get_function"); + argon2.call(&mut store, &[Value::I32(params.0), Value::I32(params.1), Value::I32(params.2)]).expect("call"); +} + +fn run_native(params: (i32, i32, i32)) { + fn run_native(m_cost: i32, t_cost: i32, p_cost: i32) { + let password = b"password"; + let salt = b"some random salt"; + + let params = argon2::Params::new(m_cost as u32, t_cost as u32, p_cost as u32, None).unwrap(); + let argon = argon2::Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params); + + let mut hash = [0u8; 32]; + argon.hash_password_into(password, salt, &mut hash).unwrap(); + } + run_native(params.0, params.1, params.2) +} + +const ARGON2ID: &[u8] = include_bytes!("../../../examples/rust/out/argon2id.wasm"); +fn criterion_benchmark(c: &mut Criterion) { + let twasm = wasm_to_twasm(ARGON2ID); + let params = (1000, 2, 1); + + let mut group = c.benchmark_group("argon2id"); + group.measurement_time(std::time::Duration::from_secs(7)); + group.sample_size(10); + + group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); + group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(params), "argon2id"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); +} + +criterion_group!( + name = benches; + config = Criterion::default().significance_level(0.1); + targets = criterion_benchmark +); + +criterion_main!(benches); diff --git a/crates/benchmarks/benches/fibonacci.rs b/crates/benchmarks/benches/fibonacci.rs new file mode 100644 index 0000000..38bbde9 --- /dev/null +++ b/crates/benchmarks/benches/fibonacci.rs @@ -0,0 +1,76 @@ +mod util; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use util::wasm_to_twasm; + +fn run_tinywasm(twasm: &[u8], iterations: i32, name: &str) { + let (mut store, instance) = util::tinywasm(twasm); + let fib = instance.exported_func::<i32, i32>(&store, name).expect("exported_func"); + fib.call(&mut store, iterations).expect("call"); +} + +fn run_wasmi(wasm: &[u8], iterations: i32, name: &str) { + let (module, mut store, linker) = util::wasmi(wasm); + let instance = linker.instantiate(&mut store, &module).expect("instantiate").start(&mut store).expect("start"); + let fib = instance.get_typed_func::<i32, i32>(&mut store, name).expect("get_typed_func"); + fib.call(&mut store, iterations).expect("call"); +} + +fn run_wasmer(wasm: &[u8], iterations: i32, name: &str) { + use wasmer::*; + let engine: Engine = wasmer::Singlepass::default().into(); + let mut store = Store::default(); + let import_object = imports! {}; + let module = wasmer::Module::from_binary(&engine, wasm).expect("wasmer::Module::from_binary"); + let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); + let fib = instance.exports.get_typed_function::<i32, i32>(&store, name).expect("get_function"); + fib.call(&mut store, iterations).expect("call"); +} + +fn run_native(n: i32) -> i32 { + let mut sum = 0; + let mut last = 0; + let mut curr = 1; + for _i in 1..n { + sum = last + curr; + last = curr; + curr = sum; + } + sum +} + +fn run_native_recursive(n: i32) -> i32 { + if n <= 1 { + return n; + } + run_native_recursive(n - 1) + run_native_recursive(n - 2) +} + +const FIBONACCI: &[u8] = include_bytes!("../../../examples/rust/out/fibonacci.wasm"); +fn criterion_benchmark(c: &mut Criterion) { + let twasm = wasm_to_twasm(FIBONACCI); + + { + let mut group = c.benchmark_group("fibonacci"); + group.bench_function("native", |b| b.iter(|| run_native(black_box(60)))); + group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(60), "fibonacci"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(60), "fibonacci"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(60), "fibonacci"))); + } + + { + let mut group = c.benchmark_group("fibonacci-recursive"); + group.measurement_time(std::time::Duration::from_secs(5)); + group.bench_function("native", |b| b.iter(|| run_native_recursive(black_box(26)))); + group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(26), "fibonacci_recursive"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(26), "fibonacci_recursive"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(26), "fibonacci_recursive"))); + } +} + +criterion_group!( + name = benches; + config = Criterion::default().significance_level(0.1); + targets = criterion_benchmark +); + +criterion_main!(benches); diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs new file mode 100644 index 0000000..b022fd1 --- /dev/null +++ b/crates/benchmarks/benches/selfhosted.rs @@ -0,0 +1,71 @@ +mod util; +use crate::util::twasm_to_module; +use criterion::{criterion_group, criterion_main, Criterion}; + +fn run_native() { + use tinywasm::*; + let module = tinywasm::Module::parse_bytes(include_bytes!("../../../examples/rust/out/print.wasm")).expect("parse"); + let mut store = Store::default(); + let mut imports = Imports::default(); + imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(()))).expect("define"); + let instance = ModuleInstance::instantiate(&mut store, module, Some(imports)).expect("instantiate"); + let hello = instance.exported_func::<(i32, i32), ()>(&store, "add_and_print").expect("exported_func"); + hello.call(&mut store, (2, 3)).expect("call"); +} + +fn run_tinywasm(twasm: &[u8]) { + use tinywasm::*; + let module = twasm_to_module(twasm); + let mut store = Store::default(); + let mut imports = Imports::default(); + imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(()))).expect("define"); + let instance = ModuleInstance::instantiate(&mut store, module, Some(imports)).expect("instantiate"); + let hello = instance.exported_func::<(), ()>(&store, "hello").expect("exported_func"); + hello.call(&mut store, ()).expect("call"); +} + +fn run_wasmi(wasm: &[u8]) { + use wasmi::*; + let engine = Engine::default(); + let module = wasmi::Module::new(&engine, wasm).expect("wasmi::Module::new"); + let mut store = Store::new(&engine, ()); + let mut linker = <Linker<()>>::new(&engine); + linker.define("env", "printi32", Func::wrap(&mut store, |_: Caller<'_, ()>, _: i32| {})).expect("define"); + let instance = linker.instantiate(&mut store, &module).expect("instantiate").start(&mut store).expect("start"); + let hello = instance.get_typed_func::<(), ()>(&mut store, "hello").expect("get_typed_func"); + hello.call(&mut store, ()).expect("call"); +} + +fn run_wasmer(wasm: &[u8]) { + use wasmer::*; + let engine = wasmer::Engine::default(); + let mut store = Store::default(); + let import_object = imports! { + "env" => { + "printi32" => Function::new_typed(&mut store, |_: i32| {}), + }, + }; + let module = wasmer::Module::from_binary(&engine, wasm).expect("wasmer::Module::from_binary"); + let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); + let hello = instance.exports.get_function("hello").expect("get_function"); + hello.call(&mut store, &[]).expect("call"); +} + +const TINYWASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm"); +fn criterion_benchmark(c: &mut Criterion) { + let twasm = util::wasm_to_twasm(TINYWASM); + + let mut group = c.benchmark_group("selfhosted"); + group.bench_function("native", |b| b.iter(run_native)); + group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); +} + +criterion_group!( + name = benches; + config = Criterion::default().sample_size(100).measurement_time(std::time::Duration::from_secs(5)).significance_level(0.1); + targets = criterion_benchmark +); + +criterion_main!(benches); diff --git a/crates/benchmarks/benches/util/mod.rs b/crates/benchmarks/benches/util/mod.rs new file mode 100644 index 0000000..d6594b9 --- /dev/null +++ b/crates/benchmarks/benches/util/mod.rs @@ -0,0 +1,42 @@ +#![allow(dead_code)] + +use tinywasm::{self, parser::Parser, types::TinyWasmModule}; + +pub fn wasm_to_twasm(wasm: &[u8]) -> Vec<u8> { + let parser = Parser::new(); + let res = parser.parse_module_bytes(wasm).expect("parse_module_bytes"); + res.serialize_twasm().to_vec() +} + +#[inline] +pub fn twasm_to_module(twasm: &[u8]) -> tinywasm::Module { + unsafe { TinyWasmModule::from_twasm_unchecked(twasm) }.into() +} + +pub fn tinywasm(twasm: &[u8]) -> (tinywasm::Store, tinywasm::ModuleInstance) { + use tinywasm::*; + let module = twasm_to_module(twasm); + let mut store = Store::default(); + let imports = Imports::default(); + let instance = ModuleInstance::instantiate(&mut store, module, Some(imports)).expect("instantiate"); + (store, instance) +} + +pub fn wasmi(wasm: &[u8]) -> (wasmi::Module, wasmi::Store<()>, wasmi::Linker<()>) { + use wasmi::*; + let engine = Engine::default(); + let module = wasmi::Module::new(&engine, wasm).expect("wasmi::Module::new"); + let store = Store::new(&engine, ()); + let linker = <Linker<()>>::new(&engine); + (module, store, linker) +} + +pub fn wasmer(wasm: &[u8]) -> (wasmer::Store, wasmer::Instance) { + use wasmer::*; + let compiler = Singlepass::default(); + let mut store = Store::new(compiler); + let import_object = imports! {}; + let module = Module::new(&store, wasm).expect("wasmer::Module::new"); + let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); + (store, instance) +} diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 7c10d62..38a2ada 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -131,7 +131,7 @@ pub(crate) fn convert_module_globals<'a, T: IntoIterator<Item = wasmparser::Resu Ok(globals) } -pub(crate) fn convert_module_export(export: wasmparser::Export) -> Result<Export> { +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, @@ -146,7 +146,7 @@ pub(crate) fn convert_module_export(export: wasmparser::Export) -> Result<Export } pub(crate) fn convert_module_code( - func: wasmparser::FunctionBody, + func: wasmparser::FunctionBody<'_>, mut validator: FuncValidator<ValidatorResources>, ) -> Result<CodeSection> { let locals_reader = func.get_locals_reader()?; @@ -205,18 +205,17 @@ pub(crate) fn convert_memarg(memarg: wasmparser::MemArg) -> MemoryArg { MemoryArg { offset: memarg.offset, align: memarg.align, align_max: memarg.max_align, mem_addr: memarg.memory } } -pub(crate) fn process_const_operators(ops: OperatorsReader) -> Result<ConstInstruction> { +pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result<ConstInstruction> { let ops = ops.into_iter().collect::<wasmparser::Result<Vec<_>>>()?; // In practice, the len can never be something other than 2, // but we'll keep this here since it's part of the spec // Invalid modules will be rejected by the validator anyway (there are also tests for this in the testsuite) assert!(ops.len() >= 2); assert!(matches!(ops[ops.len() - 1], wasmparser::Operator::End)); - process_const_operator(ops[ops.len() - 2].clone()) } -pub fn process_const_operator(op: wasmparser::Operator) -> Result<ConstInstruction> { +pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result<ConstInstruction> { match op { wasmparser::Operator::RefNull { ty } => Ok(ConstInstruction::RefNull(convert_valtype(&ty))), wasmparser::Operator::RefFunc { function_index } => Ok(ConstInstruction::RefFunc(function_index)), @@ -229,7 +228,7 @@ pub fn process_const_operator(op: wasmparser::Operator) -> Result<ConstInstructi } } -pub fn process_operators<'a>( +pub(crate) fn process_operators<'a>( mut offset: usize, ops: impl Iterator<Item = Result<wasmparser::Operator<'a>, wasmparser::BinaryReaderError>>, mut validator: FuncValidator<ValidatorResources>, @@ -515,7 +514,6 @@ pub fn process_operators<'a>( return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", op))); } }; - instructions.push(res); } @@ -524,6 +522,5 @@ pub fn process_operators<'a>( } validator.finish(offset)?; - Ok(instructions.into_boxed_slice()) } diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs index 35bad28..76d806d 100644 --- a/crates/parser/src/error.rs +++ b/crates/parser/src/error.rs @@ -6,15 +6,35 @@ use wasmparser::Encoding; #[derive(Debug)] /// Errors that can occur when parsing a WebAssembly module pub enum ParseError { + /// An invalid type was encountered InvalidType, + /// An unsupported section was encountered UnsupportedSection(String), + /// A duplicate section was encountered DuplicateSection(String), + /// An empty section was encountered EmptySection(String), + /// An unsupported operator was encountered UnsupportedOperator(String), - ParseError { message: String, offset: usize }, + /// An error occurred while parsing the module + ParseError { + /// The error message + message: String, + /// The offset in the module where the error occurred + offset: usize, + }, + /// An invalid encoding was encountered InvalidEncoding(Encoding), - InvalidLocalCount { expected: u32, actual: u32 }, + /// An invalid local count was encountered + InvalidLocalCount { + /// The expected local count + expected: u32, + /// The actual local count + actual: u32, + }, + /// The end of the module was not reached EndNotReached, + /// An unknown error occurred Other(String), } @@ -48,4 +68,4 @@ impl From<wasmparser::BinaryReaderError> for ParseError { } } -pub type Result<T, E = ParseError> = core::result::Result<T, E>; +pub(crate) type Result<T, E = ParseError> = core::result::Result<T, E>; diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index c608232..8cc34db 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,6 +1,12 @@ #![no_std] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables)) +))] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] #![forbid(unsafe_code)] #![cfg_attr(not(feature = "std"), feature(error_in_core))] +//! See [`tinywasm`](https://docs.rs/tinywasm) for documentation. mod std; extern crate alloc; @@ -30,14 +36,17 @@ use wasmparser::Validator; pub use tinywasm_types::TinyWasmModule; -#[derive(Default)] +/// A WebAssembly parser +#[derive(Default, Debug)] pub struct Parser {} impl Parser { + /// Create a new parser instance pub fn new() -> Self { Self {} } + /// 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 = Validator::new(); @@ -55,6 +64,7 @@ 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.clone()) @@ -65,6 +75,7 @@ impl Parser { } #[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; diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index f5c01ac..a18d343 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -6,58 +6,34 @@ use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instructio use wasmparser::{Payload, Validator}; #[derive(Debug, Clone)] -pub struct CodeSection { - pub locals: Box<[ValType]>, - pub body: Box<[Instruction]>, +pub(crate) struct CodeSection { + pub(crate) locals: Box<[ValType]>, + pub(crate) body: Box<[Instruction]>, } #[derive(Default)] -pub struct ModuleReader { - pub version: Option<u16>, - pub start_func: Option<u32>, - - pub func_types: Vec<FuncType>, - - // map from local function index to type index - pub code_type_addrs: Vec<u32>, - - pub exports: Vec<Export>, - pub code: Vec<CodeSection>, - pub globals: Vec<Global>, - pub table_types: Vec<TableType>, - pub memory_types: Vec<MemoryType>, - pub imports: Vec<Import>, - pub data: Vec<Data>, - pub elements: Vec<Element>, - - // pub element_section: Option<ElementSectionReader<'a>>, - pub end_reached: bool, -} - -impl Debug for ModuleReader { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("ModuleReader") - .field("version", &self.version) - .field("func_types", &self.func_types) - .field("func_addrs", &self.code_type_addrs) - .field("code", &self.code) - .field("exports", &self.exports) - .field("globals", &self.globals) - .field("table_types", &self.table_types) - .field("memory_types", &self.memory_types) - .field("import_section", &self.imports) - // .field("element_section", &self.element_section) - // .field("data_section", &self.data_section) - .finish() - } +pub(crate) struct ModuleReader { + pub(crate) version: Option<u16>, + pub(crate) start_func: Option<u32>, + pub(crate) func_types: Vec<FuncType>, + pub(crate) code_type_addrs: Vec<u32>, + pub(crate) exports: Vec<Export>, + pub(crate) code: Vec<CodeSection>, + pub(crate) globals: Vec<Global>, + pub(crate) table_types: Vec<TableType>, + pub(crate) memory_types: Vec<MemoryType>, + pub(crate) imports: Vec<Import>, + pub(crate) data: Vec<Data>, + pub(crate) elements: Vec<Element>, + pub(crate) end_reached: bool, } impl ModuleReader { - pub fn new() -> ModuleReader { + pub(crate) fn new() -> ModuleReader { Self::default() } - pub fn process_payload(&mut self, payload: Payload, validator: &mut Validator) -> Result<()> { + pub(crate) fn process_payload(&mut self, payload: Payload<'_>, validator: &mut Validator) -> Result<()> { use wasmparser::Payload::*; match payload { @@ -191,10 +167,6 @@ impl ModuleReader { debug!("Found custom section"); debug!("Skipping custom section: {:?}", _reader.name()); } - // TagSection(tag) => { - // debug!("Found tag section"); - // validator.tag_section(&tag)?; - // } UnknownSection { .. } => return Err(ParseError::UnsupportedSection("Unknown section".into())), section => return Err(ParseError::UnsupportedSection(format!("Unsupported section: {:?}", section))), }; diff --git a/crates/parser/src/std.rs b/crates/parser/src/std.rs index 67152be..16a7058 100644 --- a/crates/parser/src/std.rs +++ b/crates/parser/src/std.rs @@ -2,4 +2,4 @@ extern crate std; #[cfg(feature = "std")] -pub use std::*; +pub(crate) use std::*; diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 4f68eaa..6b8119f 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -8,14 +8,6 @@ pub use tinywasm_parser::ParseError; /// Errors that can occur for TinyWasm operations #[derive(Debug)] pub enum Error { - #[cfg(feature = "std")] - /// An I/O error occurred - Io(crate::std::io::Error), - - #[cfg(feature = "parser")] - /// A parsing error occurred - ParseError(ParseError), - /// A WebAssembly trap occurred Trap(Trap), @@ -45,6 +37,14 @@ pub enum Error { /// The store is not the one that the module instance was instantiated in InvalidStore, + + #[cfg(feature = "std")] + /// An I/O error occurred + Io(crate::std::io::Error), + + #[cfg(feature = "parser")] + /// A parsing error occurred + ParseError(ParseError), } #[derive(Debug)] @@ -57,6 +57,7 @@ pub enum LinkingError { /// The import name name: String, }, + /// A mismatched import type was encountered IncompatibleImportType { /// The module name diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 2088494..faf7931 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -2,10 +2,8 @@ use crate::{log, runtime::RawWasmValue, unlikely, Function}; use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; use tinywasm_types::{FuncType, ModuleInstanceAddr, ValType, WasmValue}; -use crate::{ - runtime::{CallFrame, Stack}, - Error, FuncContext, Result, Store, -}; +use crate::runtime::{CallFrame, Stack}; +use crate::{Error, FuncContext, Result, Store}; #[derive(Debug)] /// A function handle diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index e273838..522a82d 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -1,18 +1,12 @@ -#![allow(dead_code)] - +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use core::fmt::Debug; -use crate::{ - func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple}, - log, LinkingError, Result, -}; -use alloc::{ - boxed::Box, - collections::BTreeMap, - rc::Rc, - string::{String, ToString}, - vec::Vec, -}; +use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple}; +use crate::{log, LinkingError, Result}; use tinywasm_types::*; /// The internal representation of a function @@ -167,7 +161,8 @@ impl Extern { Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty }))) } - pub(crate) fn kind(&self) -> ExternalKind { + /// Get the kind of the external value + pub fn kind(&self) -> ExternalKind { match self { Self::Global { .. } => ExternalKind::Global, Self::Table { .. } => ExternalKind::Table, diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index cb12f1b..75bd20d 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -1,10 +1,8 @@ use alloc::{boxed::Box, format, rc::Rc, string::ToString}; use tinywasm_types::*; -use crate::{ - func::{FromWasmValueTuple, IntoWasmValueTuple}, - log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store, -}; +use crate::func::{FromWasmValueTuple, IntoWasmValueTuple}; +use crate::{log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store}; /// An instanciated WebAssembly module /// diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 79b111c..77ecb1e 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -81,34 +81,29 @@ use log; pub(crate) mod log { macro_rules! debug ( ($($tt:tt)*) => {{}} ); macro_rules! info ( ($($tt:tt)*) => {{}} ); - macro_rules! trace ( ($($tt:tt)*) => {{}} ); macro_rules! error ( ($($tt:tt)*) => {{}} ); pub(crate) use debug; pub(crate) use error; pub(crate) use info; - pub(crate) use trace; } mod error; -pub use error::*; - -mod store; -pub use store::*; - -mod module; -pub use module::Module; - -mod instance; -pub use instance::ModuleInstance; - -mod reference; -pub use reference::*; +pub use { + error::*, + func::{FuncHandle, FuncHandleTyped}, + imports::*, + instance::ModuleInstance, + module::Module, + reference::*, + store::*, +}; mod func; -pub use func::{FuncHandle, FuncHandleTyped}; - mod imports; -pub use imports::*; +mod instance; +mod module; +mod reference; +mod store; /// Runtime for executing WebAssembly modules. pub mod runtime; @@ -130,7 +125,7 @@ pub(crate) fn cold() {} pub(crate) fn unlikely(b: bool) -> bool { if b { - cold(); - } + cold() + }; b } diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs index 5a2c4f7..08e76da 100644 --- a/crates/tinywasm/src/module.rs +++ b/crates/tinywasm/src/module.rs @@ -1,6 +1,5 @@ -use tinywasm_types::TinyWasmModule; - use crate::{Imports, ModuleInstance, Result, Store}; +use tinywasm_types::TinyWasmModule; #[derive(Debug)] /// A WebAssembly Module diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index a34e30b..4c6d703 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -1,15 +1,12 @@ -use core::{ - cell::{Ref, RefCell, RefMut}, - ffi::CStr, -}; +use core::cell::{Ref, RefCell, RefMut}; +use core::ffi::CStr; + +use alloc::ffi::CString; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use crate::{GlobalInstance, MemoryInstance, Result}; -use alloc::{ - ffi::CString, - rc::Rc, - string::{String, ToString}, - vec::Vec, -}; use tinywasm_types::WasmValue; // This module essentially contains the public APIs to interact with the data stored in the store diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index a769b12..9333a4c 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -4,6 +4,23 @@ //! In some basic tests this generated better assembly than using generic functions, even when inlined. //! (Something to revisit in the future) +// Break to a block at the given index (relative to the current frame) +// If there is no block at the given index, return or call the parent function +// +// This is a bit hard to see from the spec, but it's vaild to use breaks to return +// from a function, so we need to check if the label stack is empty +macro_rules! break_to { + ($cf:ident, $stack:ident, $break_to_relative:ident) => {{ + if $cf.break_to(*$break_to_relative, &mut $stack.values).is_none() { + if $stack.call_stack.is_empty() { + return Ok(ExecResult::Return); + } else { + return Ok(ExecResult::Call); + } + } + }}; +} + /// Load a value from memory macro_rules! mem_load { ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ @@ -69,45 +86,28 @@ macro_rules! mem_store { /// Rust sadly doesn't have wrapping casts for floats yet, maybe never. /// Alternatively, https://crates.io/crates/az could be used for this but /// it's not worth the dependency. +#[rustfmt::skip] macro_rules! float_min_max { - (f32, i32) => { - (-2147483904.0_f32, 2147483648.0_f32) - }; - (f64, i32) => { - (-2147483649.0_f64, 2147483648.0_f64) - }; - (f32, u32) => { - (-1.0_f32, 4294967296.0_f32) // 2^32 - }; - (f64, u32) => { - (-1.0_f64, 4294967296.0_f64) // 2^32 - }; - (f32, i64) => { - (-9223373136366403584.0_f32, 9223372036854775808.0_f32) // 2^63 + 2^40 | 2^63 - }; - (f64, i64) => { - (-9223372036854777856.0_f64, 9223372036854775808.0_f64) // 2^63 + 2^40 | 2^63 - }; - (f32, u64) => { - (-1.0_f32, 18446744073709551616.0_f32) // 2^64 - }; - (f64, u64) => { - (-1.0_f64, 18446744073709551616.0_f64) // 2^64 - }; + (f32, i32) => {(-2147483904.0_f32, 2147483648.0_f32)}; + (f64, i32) => {(-2147483649.0_f64, 2147483648.0_f64)}; + (f32, u32) => {(-1.0_f32, 4294967296.0_f32)}; // 2^32 + (f64, u32) => {(-1.0_f64, 4294967296.0_f64)}; // 2^32 + (f32, i64) => {(-9223373136366403584.0_f32, 9223372036854775808.0_f32)}; // 2^63 + 2^40 | 2^63 + (f64, i64) => {(-9223372036854777856.0_f64, 9223372036854775808.0_f64)}; // 2^63 + 2^40 | 2^63 + (f32, u64) => {(-1.0_f32, 18446744073709551616.0_f32)}; // 2^64 + (f64, u64) => {(-1.0_f64, 18446744073709551616.0_f64)}; // 2^64 // other conversions are not allowed - ($from:ty, $to:ty) => { - compile_error!("invalid float conversion"); - }; + ($from:ty, $to:ty) => {compile_error!("invalid float conversion")}; } /// Convert a value on the stack macro_rules! conv { ($from:ty, $intermediate:ty, $to:ty, $stack:ident) => {{ - let a: $from = $stack.values.pop()?.into(); - $stack.values.push((a as $intermediate as $to).into()); + let a = $stack.values.pop_t::<$from>()? as $intermediate; + $stack.values.push((a as $to).into()); }}; ($from:ty, $to:ty, $stack:ident) => {{ - let a: $from = $stack.values.pop()?.into(); + let a = $stack.values.pop_t::<$from>()?; $stack.values.push((a as $to).into()); }}; } @@ -123,11 +123,11 @@ macro_rules! checked_conv_float { let (min, max) = float_min_max!($from, $intermediate); let a: $from = $stack.values.pop()?.into(); - if a.is_nan() { + if unlikely(a.is_nan()) { return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); } - if a <= min || a >= max { + if unlikely(a <= min || a >= max) { return Err(Error::Trap(crate::Trap::IntegerOverflow)); } @@ -158,9 +158,9 @@ macro_rules! comp_zero { /// Apply an arithmetic method to two values on the stack macro_rules! arithmetic { - ($op:ident, $ty:ty, $stack:ident) => {{ + ($op:ident, $ty:ty, $stack:ident) => { arithmetic!($op, $ty, $ty, $stack) - }}; + }; // also allow operators such as +, - ($op:tt, $ty:ty, $stack:ident) => {{ @@ -172,23 +172,20 @@ macro_rules! arithmetic { ($op:ident, $intermediate:ty, $to:ty, $stack:ident) => {{ let b = $stack.values.pop_t::<$to>()? as $intermediate; let a = $stack.values.pop_t::<$to>()? as $intermediate; - let result = a.$op(b); - $stack.values.push((result as $to).into()); + $stack.values.push((a.$op(b) as $to).into()); }}; } /// Apply an arithmetic method to a single value on the stack macro_rules! arithmetic_single { ($op:ident, $ty:ty, $stack:ident) => {{ - let a: $ty = $stack.values.pop()?.into(); - let result = a.$op(); - $stack.values.push((result as $ty).into()); + let a = $stack.values.pop_t::<$ty>()?; + $stack.values.push((a.$op() as $ty).into()); }}; ($op:ident, $from:ty, $to:ty, $stack:ident) => {{ - let a: $from = $stack.values.pop()?.into(); - let result = a.$op(); - $stack.values.push((result as $to).into()); + let a = $stack.values.pop_t::<$from>()?; + $stack.values.push((a.$op() as $to).into()); }}; } @@ -215,6 +212,7 @@ macro_rules! checked_int_arithmetic { pub(super) use arithmetic; pub(super) use arithmetic_single; +pub(super) use break_to; pub(super) use checked_conv_float; pub(super) use checked_int_arithmetic; pub(super) use comp; diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 79abbf3..c8d8791 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -1,26 +1,23 @@ -use super::{InterpreterRuntime, Stack}; -use crate::{cold, log, unlikely}; -use crate::{ - runtime::{BlockType, CallFrame, LabelFrame}, - Error, FuncContext, ModuleInstance, Result, Store, Trap, -}; use alloc::format; use alloc::{string::ToString, vec::Vec}; use core::ops::{BitAnd, BitOr, BitXor, Neg}; use tinywasm_types::{ElementKind, ValType}; +use super::{InterpreterRuntime, Stack}; +use crate::runtime::{BlockType, CallFrame, LabelFrame}; +use crate::{cold, log, unlikely}; +use crate::{Error, FuncContext, ModuleInstance, Result, Store, Trap}; + +mod macros; +mod traits; +use {macros::*, traits::*}; + #[cfg(not(feature = "std"))] mod no_std_floats; #[cfg(not(feature = "std"))] #[allow(unused_imports)] -use no_std_floats::FExt; - -mod macros; -mod traits; - -use macros::*; -use traits::*; +use no_std_floats::NoStdFloatExt; impl InterpreterRuntime { // #[inline(always)] // a small 2-3% performance improvement in some cases @@ -67,23 +64,6 @@ enum ExecResult { Trap(crate::Trap), } -// Break to a block at the given index (relative to the current frame) -// If there is no block at the given index, return or call the parent function -// -// This is a bit hard to see from the spec, but it's vaild to use breaks to return -// from a function, so we need to check if the label stack is empty -macro_rules! break_to { - ($cf:ident, $stack:ident, $break_to_relative:ident) => {{ - if $cf.break_to(*$break_to_relative, &mut $stack.values).is_none() { - if $stack.call_stack.is_empty() { - return Ok(ExecResult::Return); - } else { - return Ok(ExecResult::Call); - } - } - }}; -} - /// Run a single step of the interpreter /// A seperate function is used so later, we can more easily implement /// a step-by-step debugger (using generators once they're stable?) @@ -96,6 +76,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M return Err(Error::Other(format!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len()))); } + // A match statement is probably the fastest way to do this without + // unreasonable complexity + // See https://pliniker.github.io/post/dispatchers/ use tinywasm_types::Instruction::*; match &instrs[cf.instr_ptr] { Nop => { /* do nothing */ } @@ -600,22 +583,19 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M F64Floor => arithmetic_single!(floor, f64, stack), F32Trunc => arithmetic_single!(trunc, f32, stack), F64Trunc => arithmetic_single!(trunc, f64, stack), - F32Nearest => arithmetic_single!(wasm_nearest, f32, stack), - F64Nearest => arithmetic_single!(wasm_nearest, f64, stack), + F32Nearest => arithmetic_single!(tw_nearest, f32, stack), + F64Nearest => arithmetic_single!(tw_nearest, f64, stack), F32Sqrt => arithmetic_single!(sqrt, f32, stack), F64Sqrt => arithmetic_single!(sqrt, f64, stack), - F32Min => arithmetic!(wasm_min, f32, stack), - F64Min => arithmetic!(wasm_min, f64, stack), - F32Max => arithmetic!(wasm_max, f32, stack), - F64Max => arithmetic!(wasm_max, f64, stack), + F32Min => arithmetic!(tw_minimum, f32, stack), + F64Min => arithmetic!(tw_minimum, f64, stack), + F32Max => arithmetic!(tw_maximum, f32, stack), + F64Max => arithmetic!(tw_maximum, f64, stack), F32Copysign => arithmetic!(copysign, f32, stack), F64Copysign => arithmetic!(copysign, f64, stack), // no-op instructions since types are erased at runtime - I32ReinterpretF32 => {} - I64ReinterpretF64 => {} - F32ReinterpretI32 => {} - F64ReinterpretI64 => {} + I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {} // unsigned versions of these are a bit broken atm I32TruncF32S => checked_conv_float!(f32, i32, stack), diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs index 5620249..91c74b5 100644 --- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs +++ b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs @@ -1,4 +1,4 @@ -pub(super) trait FExt { +pub(super) trait NoStdFloatExt { fn round(self) -> Self; fn abs(self) -> Self; fn signum(self) -> Self; @@ -9,7 +9,7 @@ pub(super) trait FExt { fn copysign(self, other: Self) -> Self; } -impl FExt for f64 { +impl NoStdFloatExt for f64 { #[inline] fn round(self) -> Self { libm::round(self) @@ -50,7 +50,7 @@ impl FExt for f64 { libm::copysign(self, other) } } -impl FExt for f32 { +impl NoStdFloatExt for f32 { #[inline] fn round(self) -> Self { libm::roundf(self) diff --git a/crates/tinywasm/src/runtime/interpreter/traits.rs b/crates/tinywasm/src/runtime/interpreter/traits.rs index 06a97e3..523265b 100644 --- a/crates/tinywasm/src/runtime/interpreter/traits.rs +++ b/crates/tinywasm/src/runtime/interpreter/traits.rs @@ -5,20 +5,20 @@ where fn checked_wrapping_rem(self, rhs: Self) -> Option<Self>; } -pub(crate) trait WasmFloatOps { - fn wasm_min(self, other: Self) -> Self; - fn wasm_max(self, other: Self) -> Self; - fn wasm_nearest(self) -> Self; +pub(crate) trait TinywasmFloatExt { + fn tw_minimum(self, other: Self) -> Self; + fn tw_maximum(self, other: Self) -> Self; + fn tw_nearest(self) -> Self; } #[cfg(not(feature = "std"))] -use super::no_std_floats::FExt; +use super::no_std_floats::NoStdFloatExt; macro_rules! impl_wasm_float_ops { ($($t:ty)*) => ($( - impl WasmFloatOps for $t { + impl TinywasmFloatExt for $t { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fnearest - fn wasm_nearest(self) -> Self { + fn tw_nearest(self) -> Self { match self { x if x.is_nan() => x, // preserve NaN x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros @@ -48,7 +48,7 @@ macro_rules! impl_wasm_float_ops { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmin // Based on f32::minimum (which is not yet stable) #[inline] - fn wasm_min(self, other: Self) -> Self { + fn tw_minimum(self, other: Self) -> Self { if self < other { self } else if other < self { @@ -64,7 +64,7 @@ macro_rules! impl_wasm_float_ops { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmax // Based on f32::maximum (which is not yet stable) #[inline] - fn wasm_max(self, other: Self) -> Self { + fn tw_maximum(self, other: Self) -> Self { if self > other { self } else if other > self { diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/runtime/mod.rs index 3b9a57c..8c22ce0 100644 --- a/crates/tinywasm/src/runtime/mod.rs +++ b/crates/tinywasm/src/runtime/mod.rs @@ -2,11 +2,10 @@ mod interpreter; mod stack; mod value; +use crate::Result; pub use stack::*; pub(crate) use value::RawWasmValue; -use crate::Result; - #[allow(rustdoc::private_intra_doc_links)] /// A WebAssembly runtime. /// diff --git a/crates/tinywasm/src/runtime/stack/blocks.rs b/crates/tinywasm/src/runtime/stack/blocks.rs index f302e59..c04632c 100644 --- a/crates/tinywasm/src/runtime/stack/blocks.rs +++ b/crates/tinywasm/src/runtime/stack/blocks.rs @@ -7,11 +7,13 @@ use crate::{unlikely, ModuleInstance}; pub(crate) struct Labels(Vec<LabelFrame>); // TODO: maybe Box<[LabelFrame]> by analyzing the lable count when parsing the module? impl Labels { + #[inline] pub(crate) fn new() -> Self { // this is somehow a lot faster than Vec::with_capacity(128) or even using Default::default() in the benchmarks Self(Vec::new()) } + #[inline] pub(crate) fn len(&self) -> usize { self.0.len() } diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index dcbfcac..a902833 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -1,14 +1,11 @@ -use crate::unlikely; -use crate::{ - runtime::{BlockType, RawWasmValue}, - Error, Result, Trap, -}; use alloc::{boxed::Box, rc::Rc, vec::Vec}; use tinywasm_types::{ModuleInstanceAddr, WasmFunction}; use super::{blocks::Labels, LabelFrame}; +use crate::runtime::{BlockType, RawWasmValue}; +use crate::unlikely; +use crate::{Error, Result, Trap}; -// minimum call stack size const CALL_STACK_SIZE: usize = 128; const CALL_STACK_MAX_SIZE: usize = 1024; @@ -51,7 +48,6 @@ impl CallStack { #[derive(Debug, Clone)] pub(crate) struct CallFrame { pub(crate) instr_ptr: usize, - // pub(crate) module: ModuleInstanceAddr, pub(crate) func_instance: (Rc<WasmFunction>, ModuleInstanceAddr), pub(crate) labels: Labels, pub(crate) locals: Box<[RawWasmValue]>, diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index 9b8f82d..cc35bc2 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -5,7 +5,6 @@ use alloc::vec::Vec; use tinywasm_types::{ValType, WasmValue}; pub(crate) const MIN_VALUE_STACK_SIZE: usize = 1024; -// pub(crate) const MAX_VALUE_STACK_SIZE: usize = 1024 * 1024; #[derive(Debug)] pub(crate) struct ValueStack { diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs index 5341361..bc78adb 100644 --- a/crates/tinywasm/src/runtime/value.rs +++ b/crates/tinywasm/src/runtime/value.rs @@ -1,5 +1,4 @@ use core::fmt::Debug; - use tinywasm_types::{ValType, WasmValue}; /// A raw wasm value. @@ -23,6 +22,7 @@ impl RawWasmValue { self.0 } + #[inline] pub fn attach_type(self, ty: ValType) -> WasmValue { match ty { ValType::I32 => WasmValue::I32(self.0 as i32), @@ -48,6 +48,7 @@ impl RawWasmValue { } impl From<WasmValue> for RawWasmValue { + #[inline] fn from(v: WasmValue) -> Self { match v { WasmValue::I32(i) => Self(i as u64), @@ -65,6 +66,7 @@ macro_rules! impl_from_raw_wasm_value { ($type:ty, $to_raw:expr, $from_raw:expr) => { // Implement From<$type> for RawWasmValue impl From<$type> for RawWasmValue { + #[inline] fn from(value: $type) -> Self { #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :) Self($to_raw(value)) @@ -73,6 +75,7 @@ macro_rules! impl_from_raw_wasm_value { // Implement From<RawWasmValue> for $type impl From<RawWasmValue> for $type { + #[inline] fn from(value: RawWasmValue) -> Self { #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :) $from_raw(value.0) @@ -86,7 +89,7 @@ impl_from_raw_wasm_value!(i64, |x| x as u64, |x| x as i64); impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as u64, |x| f32::from_bits(x as u32)); impl_from_raw_wasm_value!(f64, f64::to_bits, f64::from_bits); -// convenience impls (not actually part of the spec) +// used for memory load/store impl_from_raw_wasm_value!(i8, |x| x as u64, |x| x as i8); impl_from_raw_wasm_value!(i16, |x| x as u64, |x| x as i16); impl_from_raw_wasm_value!(u32, |x| x as u64, |x| x as u32); diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs index 7508d00..ef370c2 100644 --- a/crates/tinywasm/src/store/function.rs +++ b/crates/tinywasm/src/store/function.rs @@ -1,4 +1,5 @@ use crate::Function; +use alloc::rc::Rc; use tinywasm_types::*; #[derive(Debug, Clone)] @@ -9,3 +10,9 @@ pub(crate) struct FunctionInstance { pub(crate) func: Function, pub(crate) owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions } + +impl FunctionInstance { + pub(crate) fn new_wasm(func: WasmFunction, owner: ModuleInstanceAddr) -> Self { + Self { func: Function::Wasm(Rc::new(func)), owner } + } +} diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs index 298a31e..1bcbad7 100644 --- a/crates/tinywasm/src/store/global.rs +++ b/crates/tinywasm/src/store/global.rs @@ -1,7 +1,7 @@ use alloc::{format, string::ToString}; use tinywasm_types::*; -use crate::{runtime::RawWasmValue, Error, Result}; +use crate::{runtime::RawWasmValue, unlikely, Error, Result}; /// A WebAssembly Global Instance /// @@ -18,12 +18,13 @@ impl GlobalInstance { Self { ty, value, _owner: owner } } + #[inline] pub(crate) fn get(&self) -> WasmValue { self.value.attach_type(self.ty.ty) } pub(crate) fn set(&mut self, val: WasmValue) -> Result<()> { - if val.val_type() != self.ty.ty { + if unlikely(val.val_type() != self.ty.ty) { return Err(Error::Other(format!( "global type mismatch: expected {:?}, got {:?}", self.ty.ty, @@ -31,7 +32,7 @@ impl GlobalInstance { ))); } - if !self.ty.mutable { + if unlikely(!self.ty.mutable) { return Err(Error::Other("global is immutable".to_string())); } diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 9b527d3..64e16c6 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -2,11 +2,11 @@ use alloc::vec; use alloc::vec::Vec; use tinywasm_types::{MemoryType, ModuleInstanceAddr}; -use crate::{cold, unlikely, Error, Result}; +use crate::{Error, Result}; -pub(crate) const PAGE_SIZE: usize = 65536; -pub(crate) const MAX_PAGES: usize = 65536; -pub(crate) const MAX_SIZE: u64 = PAGE_SIZE as u64 * MAX_PAGES as u64; +const PAGE_SIZE: usize = 65536; +const MAX_PAGES: usize = 65536; +const MAX_SIZE: u64 = PAGE_SIZE as u64 * MAX_PAGES as u64; /// A WebAssembly Memory Instance /// @@ -32,22 +32,18 @@ impl MemoryInstance { } } + #[cold] + fn trap_oob(&self, addr: usize, len: usize) -> Error { + Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() }) + } + pub(crate) fn store(&mut self, addr: usize, _align: usize, data: &[u8], len: usize) -> Result<()> { let Some(end) = addr.checked_add(len) else { - cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: addr, - len: data.len(), - max: self.data.len(), - })); + return Err(self.trap_oob(addr, data.len())); }; - if unlikely(end > self.data.len() || end < addr) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: addr, - len: data.len(), - max: self.data.len(), - })); + if end > self.data.len() || end < addr { + return Err(self.trap_oob(addr, data.len())); } // WebAssembly doesn't require alignment for stores @@ -73,12 +69,11 @@ impl MemoryInstance { pub(crate) fn load(&self, addr: usize, _align: usize, len: usize) -> Result<&[u8]> { let Some(end) = addr.checked_add(len) else { - cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() })); + return Err(self.trap_oob(addr, len)); }; - if unlikely(end > self.data.len() || end < addr) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() })); + if end > self.data.len() || end < addr { + return Err(self.trap_oob(addr, len)); } Ok(&self.data[addr..end]) @@ -87,23 +82,21 @@ impl MemoryInstance { // this is a workaround since we can't use generic const expressions yet (https://github.com/rust-lang/rust/issues/76560) pub(crate) fn load_as<const SIZE: usize, T: MemLoadable<SIZE>>(&self, addr: usize, _align: usize) -> Result<T> { let Some(end) = addr.checked_add(SIZE) else { - cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len: SIZE, max: self.max_pages() })); + return Err(self.trap_oob(addr, SIZE)); }; - if unlikely(end > self.data.len()) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len: SIZE, max: self.data.len() })); + if end > self.data.len() { + return Err(self.trap_oob(addr, SIZE)); } + #[cfg(not(feature = "unsafe"))] + let val = T::from_le_bytes(self.data[addr..end].try_into().expect("slice size mismatch")); + #[cfg(feature = "unsafe")] - // WebAssembly doesn't require alignment for loads // SAFETY: we checked that `end` is in bounds above. All types that implement `Into<RawWasmValue>` are valid // to load from unaligned addresses. let val = unsafe { core::ptr::read_unaligned(self.data[addr..end].as_ptr() as *const T) }; - #[cfg(not(feature = "unsafe"))] - let val = T::from_le_bytes(self.data[addr..end].try_into().expect("slice size mismatch")); - Ok(val) } @@ -112,11 +105,9 @@ impl MemoryInstance { } pub(crate) fn fill(&mut self, addr: usize, len: usize, val: u8) -> Result<()> { - let end = addr - .checked_add(len) - .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() }))?; - if unlikely(end > self.data.len()) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() })); + let end = addr.checked_add(len).ok_or_else(|| self.trap_oob(addr, len))?; + if end > self.data.len() { + return Err(self.trap_oob(addr, len)); } self.data[addr..end].fill(val); @@ -124,15 +115,9 @@ impl MemoryInstance { } pub(crate) fn copy_from_slice(&mut self, dst: usize, src: &[u8]) -> Result<()> { - let end = dst.checked_add(src.len()).ok_or_else(|| { - Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len: src.len(), max: self.data.len() }) - })?; - if unlikely(end > self.data.len()) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: dst, - len: src.len(), - max: self.data.len(), - })); + let end = dst.checked_add(src.len()).ok_or_else(|| self.trap_oob(dst, src.len()))?; + if end > self.data.len() { + return Err(self.trap_oob(dst, src.len())); } self.data[dst..end].copy_from_slice(src); @@ -141,19 +126,15 @@ impl MemoryInstance { pub(crate) fn copy_within(&mut self, dst: usize, src: usize, len: usize) -> Result<()> { // Calculate the end of the source slice - let src_end = src - .checked_add(len) - .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: src, len, max: self.data.len() }))?; + let src_end = src.checked_add(len).ok_or_else(|| self.trap_oob(src, len))?; if src_end > self.data.len() { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: src, len, max: self.data.len() })); + return Err(self.trap_oob(src, len)); } // Calculate the end of the destination slice - let dst_end = dst - .checked_add(len) - .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len, max: self.data.len() }))?; + let dst_end = dst.checked_add(len).ok_or_else(|| self.trap_oob(dst, len))?; if dst_end > self.data.len() { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len, max: self.data.len() })); + return Err(self.trap_oob(dst, len)); } // Perform the copy @@ -170,7 +151,6 @@ impl MemoryInstance { } if new_pages as usize > self.max_pages() { - log::info!("memory size out of bounds: {}", new_pages); return None; } @@ -182,12 +162,8 @@ impl MemoryInstance { // Zero initialize the new pages self.data.resize(new_size, 0); self.page_count = new_pages as usize; - - log::debug!("memory was {} pages", current_pages); - log::debug!("memory grown by {} pages", pages_delta); - log::debug!("memory grown to {} pages", self.page_count); - - Some(current_pages.try_into().expect("memory size out of bounds, this should have been caught earlier")) + debug_assert!(current_pages <= i32::MAX as usize, "page count should never be greater than i32::MAX"); + Some(current_pages as i32) } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index c8c5d8a..ad24480 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -1,15 +1,10 @@ -use crate::log; use alloc::{boxed::Box, format, rc::Rc, string::ToString, vec::Vec}; -use core::{ - cell::RefCell, - sync::atomic::{AtomicUsize, Ordering}, -}; +use core::cell::RefCell; +use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; -use crate::{ - runtime::{self, InterpreterRuntime, RawWasmValue}, - Error, Function, ModuleInstance, Result, Trap, -}; +use crate::runtime::{self, InterpreterRuntime, RawWasmValue}; +use crate::{Error, Function, ModuleInstance, Result, Trap}; mod data; mod element; @@ -17,6 +12,7 @@ mod function; mod global; mod memory; mod table; + pub(crate) use {data::*, element::*, function::*, global::*, memory::*, table::*}; // global store id counter @@ -130,7 +126,7 @@ impl Store { let mut func_addrs = Vec::with_capacity(func_count); for (i, func) in funcs.into_iter().enumerate() { - self.data.funcs.push(FunctionInstance { func: Function::Wasm(Rc::new(func.wasm_function)), owner: idx }); + self.data.funcs.push(FunctionInstance::new_wasm(func.wasm_function, idx)); func_addrs.push((i + func_count) as FuncAddr); } @@ -156,9 +152,7 @@ impl Store { if let MemoryArch::I64 = mem.arch { return Err(Error::UnsupportedFeature("64-bit memories".to_string())); } - log::info!("adding memory: {:?}", mem); self.data.memories.push(Rc::new(RefCell::new(MemoryInstance::new(mem, idx)))); - mem_addrs.push((i + mem_count) as MemAddr); } Ok(mem_addrs) @@ -235,8 +229,6 @@ impl Store { .map(|item| Ok(TableElement::from(self.elem_addr(item, global_addrs, func_addrs)?))) .collect::<Result<Vec<_>>>()?; - log::error!("element kind: {:?}", element.kind); - let items = match element.kind { // doesn't need to be initialized, can be initialized lazily using the `table.init` instruction ElementKind::Passive => Some(init), @@ -400,55 +392,57 @@ impl Store { Ok(val) } + #[cold] + fn not_found_error(name: &str) -> Error { + Error::Other(format!("{} not found", name)) + } + /// Get the function at the actual index in the store pub(crate) fn get_func(&self, addr: usize) -> Result<&FunctionInstance> { - self.data.funcs.get(addr).ok_or_else(|| Error::Other(format!("function {} not found", addr))) + self.data.funcs.get(addr).ok_or_else(|| Self::not_found_error("function")) } /// Get the memory at the actual index in the store pub(crate) fn get_mem(&self, addr: usize) -> Result<&Rc<RefCell<MemoryInstance>>> { - self.data.memories.get(addr).ok_or_else(|| Error::Other(format!("memory {} not found", addr))) + self.data.memories.get(addr).ok_or_else(|| Self::not_found_error("memory")) } /// Get the table at the actual index in the store pub(crate) fn get_table(&self, addr: usize) -> Result<&Rc<RefCell<TableInstance>>> { - self.data.tables.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + self.data.tables.get(addr).ok_or_else(|| Self::not_found_error("table")) } /// Get the data at the actual index in the store pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> { - self.data.datas.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + self.data.datas.get(addr).ok_or_else(|| Self::not_found_error("data")) } /// Get the data at the actual index in the store pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> { - self.data.datas.get_mut(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + self.data.datas.get_mut(addr).ok_or_else(|| Self::not_found_error("data")) } /// Get the element at the actual index in the store pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> { - self.data.elements.get(addr).ok_or_else(|| Error::Other(format!("element {} not found", addr))) + self.data.elements.get(addr).ok_or_else(|| Self::not_found_error("element")) } /// Get the global at the actual index in the store pub(crate) fn get_global(&self, addr: usize) -> Result<&Rc<RefCell<GlobalInstance>>> { - self.data.globals.get(addr).ok_or_else(|| Error::Other(format!("global {} not found", addr))) + self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")) } /// Get the global at the actual index in the store pub fn get_global_val(&self, addr: usize) -> Result<RawWasmValue> { - self.data - .globals - .get(addr) - .ok_or_else(|| Error::Other(format!("global {} not found", addr))) - .map(|global| global.borrow().value) + self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")).map(|global| global.borrow().value) } + /// Set the global at the actual index in the store pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> { self.data .globals .get(addr) - .ok_or_else(|| Error::Other(format!("global {} not found", addr))) + .ok_or_else(|| Self::not_found_error("global")) .map(|global| global.borrow_mut().value = value) } } diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index ea520b8..1b31999 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -82,7 +82,6 @@ impl TableInstance { // Initialize the table with the given elements (resolves function references) pub(crate) fn init(&mut self, func_addrs: &[u32], offset: i32, init: &[TableElement]) -> Result<()> { let init = init.iter().map(|item| item.map(|addr| self.resolve_func_ref(func_addrs, addr))).collect::<Vec<_>>(); - self.init_raw(offset, &init) } } diff --git a/crates/tinywasm/tests/generated/mvp.csv b/crates/tinywasm/tests/generated/mvp.csv index 23c080d..f9fda83 100644 --- a/crates/tinywasm/tests/generated/mvp.csv +++ b/crates/tinywasm/tests/generated/mvp.csv @@ -3,3 +3,4 @@ 0.1.0,17630,2598,[{"name":"address.wast","passed":5,"failed":255},{"name":"align.wast","passed":108,"failed":48},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":110,"failed":2},{"name":"block.wast","passed":193,"failed":30},{"name":"br.wast","passed":84,"failed":13},{"name":"br_if.wast","passed":90,"failed":28},{"name":"br_table.wast","passed":25,"failed":149},{"name":"call.wast","passed":29,"failed":62},{"name":"call_indirect.wast","passed":36,"failed":134},{"name":"comments.wast","passed":7,"failed":1},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":371,"failed":248},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":47,"failed":14},{"name":"elem.wast","passed":50,"failed":49},{"name":"endianness.wast","passed":1,"failed":68},{"name":"exports.wast","passed":92,"failed":4},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":2,"failed":6},{"name":"float_exprs.wast","passed":761,"failed":139},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":6,"failed":84},{"name":"float_misc.wast","passed":437,"failed":4},{"name":"forward.wast","passed":1,"failed":4},{"name":"func.wast","passed":124,"failed":48},{"name":"func_ptrs.wast","passed":10,"failed":26},{"name":"global.wast","passed":51,"failed":59},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":120,"failed":121},{"name":"imports.wast","passed":74,"failed":109},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":14,"failed":15},{"name":"left-to-right.wast","passed":1,"failed":95},{"name":"linking.wast","passed":21,"failed":111},{"name":"load.wast","passed":60,"failed":37},{"name":"local_get.wast","passed":32,"failed":4},{"name":"local_set.wast","passed":50,"failed":3},{"name":"local_tee.wast","passed":68,"failed":29},{"name":"loop.wast","passed":93,"failed":27},{"name":"memory.wast","passed":34,"failed":45},{"name":"memory_grow.wast","passed":12,"failed":84},{"name":"memory_redundancy.wast","passed":1,"failed":7},{"name":"memory_size.wast","passed":6,"failed":36},{"name":"memory_trap.wast","passed":2,"failed":180},{"name":"names.wast","passed":485,"failed":1},{"name":"nop.wast","passed":46,"failed":42},{"name":"return.wast","passed":73,"failed":11},{"name":"select.wast","passed":86,"failed":62},{"name":"skip-stack-guard-page.wast","passed":1,"failed":10},{"name":"stack.wast","passed":2,"failed":5},{"name":"start.wast","passed":9,"failed":11},{"name":"store.wast","passed":59,"failed":9},{"name":"switch.wast","passed":2,"failed":26},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":22,"failed":14},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":50,"failed":14},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unwind.wast","passed":35,"failed":15},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.2.0,19344,884,[{"name":"address.wast","passed":181,"failed":79},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":220,"failed":3},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":171,"failed":3},{"name":"call.wast","passed":73,"failed":18},{"name":"call_indirect.wast","passed":50,"failed":120},{"name":"comments.wast","passed":7,"failed":1},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":439,"failed":180},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":47,"failed":14},{"name":"elem.wast","passed":56,"failed":43},{"name":"endianness.wast","passed":29,"failed":40},{"name":"exports.wast","passed":92,"failed":4},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":6,"failed":2},{"name":"float_exprs.wast","passed":890,"failed":10},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":78,"failed":12},{"name":"float_misc.wast","passed":437,"failed":4},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":168,"failed":4},{"name":"func_ptrs.wast","passed":10,"failed":26},{"name":"global.wast","passed":103,"failed":7},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":231,"failed":10},{"name":"imports.wast","passed":80,"failed":103},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":26,"failed":3},{"name":"left-to-right.wast","passed":92,"failed":4},{"name":"linking.wast","passed":29,"failed":103},{"name":"load.wast","passed":93,"failed":4},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":93,"failed":4},{"name":"loop.wast","passed":116,"failed":4},{"name":"memory.wast","passed":78,"failed":1},{"name":"memory_grow.wast","passed":91,"failed":5},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":35,"failed":7},{"name":"memory_trap.wast","passed":180,"failed":2},{"name":"names.wast","passed":485,"failed":1},{"name":"nop.wast","passed":78,"failed":10},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":114,"failed":34},{"name":"skip-stack-guard-page.wast","passed":1,"failed":10},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":11,"failed":9},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.3.0,20254,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":183,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.4.0,20254,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":183,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/testsuite/indexmap.rs b/crates/tinywasm/tests/testsuite/indexmap.rs index 1642ce2..3e751c4 100644 --- a/crates/tinywasm/tests/testsuite/indexmap.rs +++ b/crates/tinywasm/tests/testsuite/indexmap.rs @@ -1,3 +1,4 @@ +/// A naive implementation of an index map for use in the test suite pub struct IndexMap<K, V> { map: std::collections::HashMap<K, V>, keys: Vec<K>, diff --git a/crates/tinywasm/tests/testsuite/mod.rs b/crates/tinywasm/tests/testsuite/mod.rs index 2019c04..35f0607 100644 --- a/crates/tinywasm/tests/testsuite/mod.rs +++ b/crates/tinywasm/tests/testsuite/mod.rs @@ -128,23 +128,10 @@ impl Debug for TestSuite { writeln!(f, "{}", link(group_name, &group.file, None).bold().underline())?; writeln!(f, " Tests Passed: {}", group_passed.to_string().green())?; + if group_failed != 0 { writeln!(f, " Tests Failed: {}", group_failed.to_string().red())?; } - - // for (test_name, test) in &group.tests { - // write!(f, " {}: ", test_name.bold())?; - // match &test.result { - // Ok(()) => { - // writeln!(f, "{}", "Passed".green())?; - // } - // Err(e) => { - // writeln!(f, "{}", "Failed".red())?; - // // writeln!(f, "Error: {:?}", e)?; - // } - // } - // writeln!(f, " Span: {:?}", test.span)?; - // } } writeln!(f, "\n{}", "Total Test Summary:".bold().underline())?; diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index 125a9d6..d4dafb1 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -1,3 +1,4 @@ +/// Here be dragons (this file is in need of a big refactor) use crate::testsuite::util::*; use std::{borrow::Cow, collections::HashMap}; @@ -6,7 +7,7 @@ use eyre::{eyre, Result}; use log::{debug, error, info}; use tinywasm::{Extern, Imports, ModuleInstance}; use tinywasm_types::{ExternVal, MemoryType, ModuleInstanceAddr, TableType, ValType, WasmValue}; -use wast::{lexer::Lexer, parser::ParseBuffer, QuoteWat, Wast}; +use wast::{lexer::Lexer, parser::ParseBuffer, Wast}; #[derive(Default)] struct RegisteredModules { @@ -195,31 +196,7 @@ impl TestSuite { Wat(module) => { debug!("got wat module"); let result = catch_unwind_silent(|| { - let (name, bytes) = match module { - QuoteWat::QuoteModule(_, quoted_wat) => { - let wat = quoted_wat - .iter() - .map(|(_, s)| std::str::from_utf8(s).expect("failed to convert wast to utf8")) - .collect::<Vec<_>>() - .join("\n"); - - let lexer = Lexer::new(&wat); - let buf = ParseBuffer::new_with_lexer(lexer).expect("failed to create parse buffer"); - let mut wat_data = wast::parser::parse::<wast::Wat>(&buf).expect("failed to parse wat"); - (None, wat_data.encode().expect("failed to encode module")) - } - QuoteWat::Wat(mut wat) => { - let wast::Wat::Module(ref module) = wat else { - unimplemented!("Not supported"); - }; - ( - module.id.map(|id| id.name().to_string()), - wat.encode().expect("failed to encode module"), - ) - } - _ => unimplemented!("Not supported"), - }; - + let (name, bytes) = encode_quote_wat(module); let m = parse_module_bytes(&bytes).expect("failed to parse module bytes"); let module_instance = tinywasm::Module::from(m) @@ -488,10 +465,6 @@ impl TestSuite { e })?; - debug!("outcomes: {:?}", outcomes); - - debug!("expected: {:?}", expected); - if outcomes.len() != expected.len() { return Err(eyre!( "span: {:?} expected {} results, got {}", @@ -501,8 +474,6 @@ impl TestSuite { )); } - log::debug!("outcomes: {:?}", outcomes); - outcomes.iter().zip(expected).enumerate().try_for_each(|(i, (outcome, exp))| { (outcome.eq_loose(&exp)) .then_some(()) @@ -511,7 +482,6 @@ impl TestSuite { }); let res = res.map_err(|e| eyre!("test panicked: {:?}", try_downcast_panic(e))).and_then(|r| r); - test_group.add_result(&format!("AssertReturn({}-{})", invoke_name, i), span.linecol_in(wast), res); } _ => test_group.add_result( diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index 1b91e1e..a45c59f 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -2,6 +2,7 @@ use std::panic::{self, AssertUnwindSafe}; use eyre::{eyre, Result}; use tinywasm_types::{ModuleInstanceAddr, TinyWasmModule, ValType, WasmValue}; +use wast::QuoteWat; pub fn try_downcast_panic(panic: Box<dyn std::any::Any + Send>) -> String { let info = panic.downcast_ref::<panic::PanicInfo>().or(None).map(|p| p.to_string()).clone(); @@ -53,6 +54,30 @@ pub fn catch_unwind_silent<F: FnOnce() -> R, R>(f: F) -> std::thread::Result<R> result } +pub fn encode_quote_wat(module: QuoteWat) -> (Option<String>, Vec<u8>) { + match module { + QuoteWat::QuoteModule(_, quoted_wat) => { + let wat = quoted_wat + .iter() + .map(|(_, s)| std::str::from_utf8(s).expect("failed to convert wast to utf8")) + .collect::<Vec<_>>() + .join("\n"); + + let lexer = wast::lexer::Lexer::new(&wat); + let buf = wast::parser::ParseBuffer::new_with_lexer(lexer).expect("failed to create parse buffer"); + let mut wat_data = wast::parser::parse::<wast::Wat>(&buf).expect("failed to parse wat"); + (None, wat_data.encode().expect("failed to encode module")) + } + QuoteWat::Wat(mut wat) => { + let wast::Wat::Module(ref module) = wat else { + unimplemented!("Not supported"); + }; + (module.id.map(|id| id.name().to_string()), wat.encode().expect("failed to encode module")) + } + _ => unimplemented!("Not supported"), + } +} + pub fn parse_module_bytes(bytes: &[u8]) -> Result<TinyWasmModule> { let parser = tinywasm_parser::Parser::new(); Ok(parser.parse_module_bytes(bytes)?) diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 0e2eafe..b5a68a4 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -1,10 +1,8 @@ -use crate::{DataAddr, ElemAddr, MemAddr}; - use super::{FuncAddr, GlobalAddr, LabelAddr, LocalAddr, TableAddr, TypeAddr, ValType}; +use crate::{DataAddr, ElemAddr, MemAddr}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum BlockArgs { Empty, Type(ValType), @@ -13,8 +11,7 @@ pub enum BlockArgs { /// Represents a memory immediate in a WebAssembly memory instruction. #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct MemoryArg { pub mem_addr: MemAddr, pub align: u8, @@ -28,8 +25,7 @@ type EndOffset = usize; type ElseOffset = usize; #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ConstInstruction { I32Const(i32), I64Const(i64), @@ -53,8 +49,7 @@ pub enum ConstInstruction { /// /// See <https://webassembly.github.io/spec/core/binary/instructions.html> #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum Instruction { // Custom Instructions BrLabel(LabelAddr), diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 205ec5a..d2da7d7 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -39,16 +39,17 @@ pub mod archive; /// TinyWasmModules are validated before being created, so they are guaranteed to be valid (as long as they were created by TinyWasm). /// This means you should not trust a TinyWasmModule created by a third party to be valid. #[derive(Debug, Clone, Default, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct TinyWasmModule { /// The version of the WebAssembly module. pub version: Option<u16>, + /// The start function of the WebAssembly module. pub start_func: Option<FuncAddr>, /// The functions of the WebAssembly module. pub funcs: Box<[TypedWasmFunction]>, + /// The types of the WebAssembly module. pub func_types: Box<[FuncType]>, @@ -78,8 +79,7 @@ pub struct TinyWasmModule { /// /// See <https://webassembly.github.io/spec/core/syntax/types.html#external-types> #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ExternalKind { /// A WebAssembly Function. Func, @@ -97,6 +97,8 @@ pub enum ExternalKind { /// /// See <https://webassembly.github.io/spec/core/exec/runtime.html#addresses> pub type Addr = u32; + +// aliases for clarity pub type FuncAddr = Addr; pub type TableAddr = Addr; pub type MemAddr = Addr; @@ -104,6 +106,7 @@ pub type GlobalAddr = Addr; pub type ElemAddr = Addr; pub type DataAddr = Addr; pub type ExternAddr = Addr; + // additional internal addresses pub type TypeAddr = Addr; pub type LocalAddr = Addr; @@ -146,25 +149,15 @@ impl ExternVal { /// The type of a WebAssembly Function. /// /// See <https://webassembly.github.io/spec/core/syntax/types.html#function-types> -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[derive(Debug, Clone, PartialEq, Default)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct FuncType { pub params: Box<[ValType]>, pub results: Box<[ValType]>, } -impl FuncType { - /// Get the number of parameters of a function type. - #[inline] - pub fn empty() -> Self { - Self { params: Box::new([]), results: Box::new([]) } - } -} - #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct WasmFunction { pub instructions: Box<[Instruction]>, pub locals: Box<[ValType]>, @@ -172,8 +165,7 @@ pub struct WasmFunction { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct TypedWasmFunction { pub type_addr: u32, pub wasm_function: WasmFunction, @@ -181,8 +173,7 @@ pub struct TypedWasmFunction { /// A WebAssembly Module Export #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Export { /// The name of the export. pub name: Box<str>, @@ -193,24 +184,21 @@ pub struct Export { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Global { pub ty: GlobalType, pub init: ConstInstruction, } #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct GlobalType { pub mutable: bool, pub ty: ValType, } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct TableType { pub element_type: ValType, pub size_initial: u32, @@ -227,12 +215,9 @@ impl TableType { } } -#[derive(Debug, Clone, PartialEq)] - /// Represents a memory's type. -#[derive(Copy)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct MemoryType { pub arch: MemoryArch, pub page_count_initial: u64, @@ -246,16 +231,14 @@ impl MemoryType { } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum MemoryArch { I32, I64, } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Import { pub module: Box<str>, pub name: Box<str>, @@ -263,8 +246,7 @@ pub struct Import { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ImportKind { Function(TypeAddr), Table(TableType), @@ -285,8 +267,7 @@ impl From<&ImportKind> for ExternalKind { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Data { pub data: Box<[u8]>, pub range: Range<usize>, @@ -294,16 +275,14 @@ pub struct Data { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum DataKind { Active { mem: MemAddr, offset: ConstInstruction }, Passive, } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Element { pub kind: ElementKind, pub items: Box<[ElementItem]>, @@ -312,8 +291,7 @@ pub struct Element { } #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ElementKind { Passive, Active { table: TableAddr, offset: ConstInstruction }, @@ -321,8 +299,7 @@ pub enum ElementKind { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ElementItem { Func(FuncAddr), Expr(ConstInstruction), diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index e46092b..24fed3b 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -114,8 +114,7 @@ impl WasmValue { /// Type of a WebAssembly value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ValType { /// A 32-bit integer. I32, |
