summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/benchmarks/Cargo.toml24
-rw-r--r--crates/benchmarks/benches/argon2id.rs60
-rw-r--r--crates/benchmarks/benches/fibonacci.rs76
-rw-r--r--crates/benchmarks/benches/selfhosted.rs71
-rw-r--r--crates/benchmarks/benches/util/mod.rs42
-rw-r--r--crates/parser/src/conversion.rs13
-rw-r--r--crates/parser/src/error.rs26
-rw-r--r--crates/parser/src/lib.rs13
-rw-r--r--crates/parser/src/module.rs66
-rw-r--r--crates/parser/src/std.rs2
-rw-r--r--crates/tinywasm/src/error.rs17
-rw-r--r--crates/tinywasm/src/func.rs6
-rw-r--r--crates/tinywasm/src/imports.rs23
-rw-r--r--crates/tinywasm/src/instance.rs6
-rw-r--r--crates/tinywasm/src/lib.rs35
-rw-r--r--crates/tinywasm/src/module.rs3
-rw-r--r--crates/tinywasm/src/reference.rs17
-rw-r--r--crates/tinywasm/src/runtime/interpreter/macros.rs82
-rw-r--r--crates/tinywasm/src/runtime/interpreter/mod.rs60
-rw-r--r--crates/tinywasm/src/runtime/interpreter/no_std_floats.rs6
-rw-r--r--crates/tinywasm/src/runtime/interpreter/traits.rs18
-rw-r--r--crates/tinywasm/src/runtime/mod.rs3
-rw-r--r--crates/tinywasm/src/runtime/stack/blocks.rs2
-rw-r--r--crates/tinywasm/src/runtime/stack/call_stack.rs10
-rw-r--r--crates/tinywasm/src/runtime/stack/value_stack.rs1
-rw-r--r--crates/tinywasm/src/runtime/value.rs7
-rw-r--r--crates/tinywasm/src/store/function.rs7
-rw-r--r--crates/tinywasm/src/store/global.rs7
-rw-r--r--crates/tinywasm/src/store/memory.rs90
-rw-r--r--crates/tinywasm/src/store/mod.rs48
-rw-r--r--crates/tinywasm/src/store/table.rs1
-rw-r--r--crates/tinywasm/tests/generated/mvp.csv1
-rw-r--r--crates/tinywasm/tests/testsuite/indexmap.rs1
-rw-r--r--crates/tinywasm/tests/testsuite/mod.rs15
-rw-r--r--crates/tinywasm/tests/testsuite/run.rs36
-rw-r--r--crates/tinywasm/tests/testsuite/util.rs25
-rw-r--r--crates/types/src/instructions.rs15
-rw-r--r--crates/types/src/lib.rs73
-rw-r--r--crates/types/src/value.rs3
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,