Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Added support for the WebAssembly function-references proposal
- Added `WasmValue::ty` and `WasmValue::matches_type`

### Changed

- Function types are now stored separately and resolved through `Function::ty(&Store)`.

### Fixed

- Tail calls to host functions now return directly to the caller frame.

### Breaking Changes

- Renamed `ModuleInstanceAddr` to `ModuleInstanceId` for consistency.
- Removed `HostFunction::ty` and `WasmFunction::ty`. Use `Function::ty(&Store)` for runtime function types.
- Changed `TableType::element_type` and `Element::ty` from `WasmType` to `RefType`, and replaced module `table_types` with `TableDefinition { ty, init }`.

## [0.10.0] - 2026-07-24

**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.9.1...v0.10.0
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ members = ["crates/*"]
default-members = [".", "crates/parser", "crates/tinywasm", "crates/types"]

[workspace.package]
version = "0.10.0"
version = "0.11.0-pre.0"
edition = "2024"
rust-version = "1.95"
repository = "https://github.com/explodingcamera/tinywasm"
Expand All @@ -20,10 +20,10 @@ keywords = ["interpreter", "no-std", "tinywasm", "wasm", "webassembly"]
categories = ["compilers", "embedded", "no-std", "virtualization", "wasm"]

[workspace.dependencies]
tinywasm = { path = "crates/tinywasm", version = "0.10.0", default-features = false }
tinywasm-cli = { path = "crates/cli", version = "0.10.0", default-features = false }
tinywasm-parser = { path = "crates/parser", version = "0.10.0", default-features = false }
tinywasm-types = { path = "crates/types", version = "0.10.0", default-features = false }
tinywasm = { path = "crates/tinywasm", version = "0.11.0-pre.0", default-features = false }
tinywasm-cli = { path = "crates/cli", version = "0.11.0-pre.0", default-features = false }
tinywasm-parser = { path = "crates/parser", version = "0.11.0-pre.0", default-features = false }
tinywasm-types = { path = "crates/types", version = "0.11.0-pre.0", default-features = false }

eyre = "0.6"
indexmap = "2.14"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ The internal `twasm` bytecode format is not currently validated as an untrusted
| [**Tail Call**](https://github.com/WebAssembly/tail-call/blob/main/proposals/tail-call/Overview.md) | 🟢 | 0.9.0 |
| [**Relaxed SIMD**](https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md) | 🟢 | 0.9.0 |
| [**Wide Arithmetic**](https://github.com/WebAssembly/wide-arithmetic/blob/main/proposals/wide-arithmetic/Overview.md) | 🟢 | 0.9.0 |
| [**Typed Function References**](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md) | 🚧 | `next` |
| [**Exception Handling**](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md) | 🌑 | - |
| [**Typed Function References**](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md) | 🌑 | - |
| [**Garbage Collection**](https://github.com/WebAssembly/gc/blob/main/proposals/gc/Overview.md) | 🌑 | - |
| [**Stack Switching**](https://github.com/WebAssembly/stack-switching/blob/main/proposals/stack-switching/Explainer.md) | 🌑 | - |
| [**Threads**](https://github.com/WebAssembly/threads/blob/main-legacy/proposals/threads/Overview.md) | 🌑 | - |
Expand Down
7 changes: 4 additions & 3 deletions crates/cli/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ pub fn format_wasm_type(ty: WasmType) -> &'static str {
WasmType::F32 => "f32",
WasmType::F64 => "f64",
WasmType::V128 => "v128",
WasmType::RefFunc => "funcref",
WasmType::RefExtern => "externref",
WasmType::Ref(ty) if ty.is_func() => "funcref",
WasmType::Ref(ty) if ty.is_extern() => "externref",
WasmType::Ref(_) => "ref",
}
}

Expand Down Expand Up @@ -62,7 +63,7 @@ pub fn format_table_type(ty: &TableType) -> String {
MemoryArch::I64 => "i64",
};
let max = ty.size_max.map(|v| v.to_string()).unwrap_or_else(|| "unbounded".to_string());
format!("table[{arch} {}] initial={} max={max}", format_wasm_type(ty.element_type), ty.size_initial)
format!("table[{arch} {}] initial={} max={max}", format_wasm_type(WasmType::Ref(ty.element_type)), ty.size_initial)
}

pub fn format_global_type(ty: &GlobalType) -> String {
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/value_parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn parse_arg(index: usize, ty: WasmType, value: &str) -> Result<WasmValue> {
.parse::<i128>()
.map(|v| WasmValue::V128(v.to_le_bytes()))
.map_err(|e| format_error(index, ty, value, e))?,
WasmType::RefFunc | WasmType::RefExtern => {
WasmType::Ref(_) => {
bail!(
"unsupported CLI argument type at position {}: {}; use the embedding API for reference values",
index + 1,
Expand Down
89 changes: 43 additions & 46 deletions crates/cli/src/wast_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::time::Duration;

use eyre::{Context, Result, bail, eyre};
use log::{debug, error};
use tinywasm::types::{ExternRef, FuncRef, MemoryType, TableType, WasmType, WasmValue};
use tinywasm::types::{ExternRef, FuncRef, MemoryType, RefType, RefValue, TableType, WasmType, WasmValue};
use tinywasm::{ExecProgress, Global, HostFunction, Imports, Memory, Module, ModuleInstance, Store, Table};
use wast::{QuoteWat, core::AbstractHeapType};

Expand Down Expand Up @@ -154,16 +154,8 @@ impl WastRunner {
fn imports(store: &mut Store, modules: &HashMap<String, ModuleInstance>) -> Result<Imports> {
let mut imports = Imports::new();

let table = Table::new(
store,
TableType::new(WasmType::RefFunc, 10, Some(20)),
WasmValue::default_for(WasmType::RefFunc),
)?;
let table64 = Table::new(
store,
TableType::new64(WasmType::RefFunc, 10, Some(20)),
WasmValue::default_for(WasmType::RefFunc),
)?;
let table = Table::new(store, TableType::new(RefType::FUNCREF, 10, Some(20)), RefValue::Null.into())?;
let table64 = Table::new(store, TableType::new64(RefType::FUNCREF, 10, Some(20)), RefValue::Null.into())?;
let memory = Memory::new(store, MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)))?;
let global_i32 =
Global::new(store, tinywasm::types::GlobalType::new(WasmType::I32, false), WasmValue::I32(666))?;
Expand Down Expand Up @@ -467,7 +459,7 @@ impl WastRunner {
let expected = expected_alternatives
.iter()
.filter_map(|alts| alts.first())
.find(|exp| module_global.eq_loose(exp));
.find(|exp| exp.matches(&module_global));
if expected.is_none() {
test_group.add_result(
&format!("AssertReturn(unsupported-{i})"),
Expand Down Expand Up @@ -516,7 +508,7 @@ impl WastRunner {
}
if expected_alternatives.iter().any(|expected| {
expected.len() == outcomes.len()
&& outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| outcome.eq_loose(exp))
&& outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| exp.matches(outcome))
}) {
Ok(())
} else {
Expand Down Expand Up @@ -735,7 +727,7 @@ fn convert_wastargs(args: Vec<wast::WastArg>) -> Result<Vec<WasmValue>> {
args.into_iter().map(wastarg2tinywasmvalue).collect()
}

fn convert_wastret<'a>(args: impl Iterator<Item = wast::WastRet<'a>>) -> Result<Vec<Vec<WasmValue>>> {
fn convert_wastret<'a>(args: impl Iterator<Item = wast::WastRet<'a>>) -> Result<Vec<Vec<ExpectedValue>>> {
let mut alternatives = vec![Vec::new()];
for arg in args {
let choices = wastret2tinywasmvalues(arg)?;
Expand Down Expand Up @@ -763,14 +755,10 @@ fn wastarg2tinywasmvalue(arg: wast::WastArg) -> Result<WasmValue> {
I32(i) => WasmValue::I32(i),
I64(i) => WasmValue::I64(i),
V128(i) => WasmValue::V128(i.to_le_bytes()),
RefExtern(v) => WasmValue::RefExtern(ExternRef::new(Some(v))),
RefExtern(v) => ExternRef::new(v).into(),
RefNull(t) => match t {
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func } => {
WasmValue::RefFunc(FuncRef::null())
}
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern } => {
WasmValue::RefExtern(ExternRef::null())
}
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func } => RefValue::Null.into(),
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern } => RefValue::Null.into(),
_ => {
bail!("unsupported arg type: refnull: {:?}", t);
}
Expand All @@ -797,7 +785,26 @@ fn wast_v128_to_bytes(i: wast::core::V128Pattern) -> [u8; 16] {
res.try_into().unwrap()
}

fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<WasmValue>> {
#[derive(Clone, Copy)]
enum ExpectedValue {
Exact(WasmValue),
RefNull,
RefFunc,
RefExtern,
}

impl ExpectedValue {
fn matches(&self, value: &WasmValue) -> bool {
match self {
Self::Exact(expected) => value.eq_loose(expected),
Self::RefNull => matches!(value, WasmValue::Ref(RefValue::Null)),
Self::RefFunc => matches!(value, WasmValue::Ref(RefValue::Func(_))),
Self::RefExtern => matches!(value, WasmValue::Ref(RefValue::Extern(_))),
}
}
}

fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<ExpectedValue>> {
let wast::WastRet::Core(ret) = ret else {
bail!("unsupported arg type");
};
Expand All @@ -809,32 +816,22 @@ fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<WasmValue>> {
}
}

fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result<WasmValue> {
fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result<ExpectedValue> {
use wast::core::WastRetCore::{F32, F64, I32, I64, RefExtern, RefFunc, RefNull, V128};
Ok(match ret {
F32(f) => nanpattern2tinywasmvalue(f)?,
F64(f) => nanpattern2tinywasmvalue(f)?,
I32(i) => WasmValue::I32(i),
I64(i) => WasmValue::I64(i),
V128(i) => WasmValue::V128(wast_v128_to_bytes(i)),
RefNull(t) => match t {
Some(wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func }) => {
WasmValue::RefFunc(FuncRef::null())
}
Some(wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern }) => {
WasmValue::RefExtern(ExternRef::null())
}
_ => {
bail!("unsupported arg type: refnull: {:?}", t);
}
},
RefExtern(v) => WasmValue::RefExtern(ExternRef::new(v)),
RefFunc(v) => WasmValue::RefFunc(FuncRef::new(match v {
Some(wast::token::Index::Num(n, _)) => Some(n),
_ => {
bail!("unsupported arg type: reffunc: {:?}", v);
}
})),
F32(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?),
F64(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?),
I32(i) => ExpectedValue::Exact(WasmValue::I32(i)),
I64(i) => ExpectedValue::Exact(WasmValue::I64(i)),
V128(i) => ExpectedValue::Exact(WasmValue::V128(wast_v128_to_bytes(i))),
RefNull(_) => ExpectedValue::RefNull,
RefExtern(Some(v)) => ExpectedValue::Exact(ExternRef::new(v).into()),
RefExtern(None) => ExpectedValue::RefExtern,
RefFunc(Some(wast::token::Index::Num(n, _))) => ExpectedValue::Exact(FuncRef::new(n).into()),
RefFunc(None) => ExpectedValue::RefFunc,
RefFunc(v) => {
bail!("unsupported arg type: reffunc: {:?}", v);
}
a => {
bail!("unsupported arg type {:?}", a);
}
Expand Down
Loading
Loading