Skip to content

Commit b6a6cef

Browse files
feat: typed function references (#49)
Signed-off-by: Henry <mail@henrygressmann.de> --------- Signed-off-by: Henry <mail@henrygressmann.de>
1 parent d848efe commit b6a6cef

49 files changed

Lines changed: 1263 additions & 630 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Added support for the WebAssembly function-references proposal
13+
- Added `WasmValue::ty` and `WasmValue::matches_type`
14+
15+
### Changed
16+
17+
- Function types are now stored separately and resolved through `Function::ty(&Store)`.
18+
19+
### Fixed
20+
21+
- Tail calls to host functions now return directly to the caller frame.
22+
23+
### Breaking Changes
24+
25+
- Renamed `ModuleInstanceAddr` to `ModuleInstanceId` for consistency.
26+
- Removed `HostFunction::ty` and `WasmFunction::ty`. Use `Function::ty(&Store)` for runtime function types.
27+
- Changed `TableType::element_type` and `Element::ty` from `WasmType` to `RefType`, and replaced module `table_types` with `TableDefinition { ty, init }`.
28+
829
## [0.10.0] - 2026-07-24
930

1031
**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.9.1...v0.10.0

Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ members = ["crates/*"]
1111
default-members = [".", "crates/parser", "crates/tinywasm", "crates/types"]
1212

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

2222
[workspace.dependencies]
23-
tinywasm = { path = "crates/tinywasm", version = "0.10.0", default-features = false }
24-
tinywasm-cli = { path = "crates/cli", version = "0.10.0", default-features = false }
25-
tinywasm-parser = { path = "crates/parser", version = "0.10.0", default-features = false }
26-
tinywasm-types = { path = "crates/types", version = "0.10.0", default-features = false }
23+
tinywasm = { path = "crates/tinywasm", version = "0.11.0-pre.0", default-features = false }
24+
tinywasm-cli = { path = "crates/cli", version = "0.11.0-pre.0", default-features = false }
25+
tinywasm-parser = { path = "crates/parser", version = "0.11.0-pre.0", default-features = false }
26+
tinywasm-types = { path = "crates/types", version = "0.11.0-pre.0", default-features = false }
2727

2828
eyre = "0.6"
2929
indexmap = "2.14"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ The internal `twasm` bytecode format is not currently validated as an untrusted
8787
| [**Tail Call**](https://github.com/WebAssembly/tail-call/blob/main/proposals/tail-call/Overview.md) | 🟢 | 0.9.0 |
8888
| [**Relaxed SIMD**](https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md) | 🟢 | 0.9.0 |
8989
| [**Wide Arithmetic**](https://github.com/WebAssembly/wide-arithmetic/blob/main/proposals/wide-arithmetic/Overview.md) | 🟢 | 0.9.0 |
90+
| [**Typed Function References**](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md) | 🚧 | `next` |
9091
| [**Exception Handling**](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md) | 🌑 | - |
91-
| [**Typed Function References**](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md) | 🌑 | - |
9292
| [**Garbage Collection**](https://github.com/WebAssembly/gc/blob/main/proposals/gc/Overview.md) | 🌑 | - |
9393
| [**Stack Switching**](https://github.com/WebAssembly/stack-switching/blob/main/proposals/stack-switching/Explainer.md) | 🌑 | - |
9494
| [**Threads**](https://github.com/WebAssembly/threads/blob/main-legacy/proposals/threads/Overview.md) | 🌑 | - |

crates/cli/src/output.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ pub fn format_wasm_type(ty: WasmType) -> &'static str {
3030
WasmType::F32 => "f32",
3131
WasmType::F64 => "f64",
3232
WasmType::V128 => "v128",
33-
WasmType::RefFunc => "funcref",
34-
WasmType::RefExtern => "externref",
33+
WasmType::Ref(ty) if ty.is_func() => "funcref",
34+
WasmType::Ref(ty) if ty.is_extern() => "externref",
35+
WasmType::Ref(_) => "ref",
3536
}
3637
}
3738

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

6869
pub fn format_global_type(ty: &GlobalType) -> String {

crates/cli/src/value_parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn parse_arg(index: usize, ty: WasmType, value: &str) -> Result<WasmValue> {
2121
.parse::<i128>()
2222
.map(|v| WasmValue::V128(v.to_le_bytes()))
2323
.map_err(|e| format_error(index, ty, value, e))?,
24-
WasmType::RefFunc | WasmType::RefExtern => {
24+
WasmType::Ref(_) => {
2525
bail!(
2626
"unsupported CLI argument type at position {}: {}; use the embedding API for reference values",
2727
index + 1,

crates/cli/src/wast_runner.rs

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::time::Duration;
77

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

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

157-
let table = Table::new(
158-
store,
159-
TableType::new(WasmType::RefFunc, 10, Some(20)),
160-
WasmValue::default_for(WasmType::RefFunc),
161-
)?;
162-
let table64 = Table::new(
163-
store,
164-
TableType::new64(WasmType::RefFunc, 10, Some(20)),
165-
WasmValue::default_for(WasmType::RefFunc),
166-
)?;
157+
let table = Table::new(store, TableType::new(RefType::FUNCREF, 10, Some(20)), RefValue::Null.into())?;
158+
let table64 = Table::new(store, TableType::new64(RefType::FUNCREF, 10, Some(20)), RefValue::Null.into())?;
167159
let memory = Memory::new(store, MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)))?;
168160
let global_i32 =
169161
Global::new(store, tinywasm::types::GlobalType::new(WasmType::I32, false), WasmValue::I32(666))?;
@@ -467,7 +459,7 @@ impl WastRunner {
467459
let expected = expected_alternatives
468460
.iter()
469461
.filter_map(|alts| alts.first())
470-
.find(|exp| module_global.eq_loose(exp));
462+
.find(|exp| exp.matches(&module_global));
471463
if expected.is_none() {
472464
test_group.add_result(
473465
&format!("AssertReturn(unsupported-{i})"),
@@ -516,7 +508,7 @@ impl WastRunner {
516508
}
517509
if expected_alternatives.iter().any(|expected| {
518510
expected.len() == outcomes.len()
519-
&& outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| outcome.eq_loose(exp))
511+
&& outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| exp.matches(outcome))
520512
}) {
521513
Ok(())
522514
} else {
@@ -735,7 +727,7 @@ fn convert_wastargs(args: Vec<wast::WastArg>) -> Result<Vec<WasmValue>> {
735727
args.into_iter().map(wastarg2tinywasmvalue).collect()
736728
}
737729

738-
fn convert_wastret<'a>(args: impl Iterator<Item = wast::WastRet<'a>>) -> Result<Vec<Vec<WasmValue>>> {
730+
fn convert_wastret<'a>(args: impl Iterator<Item = wast::WastRet<'a>>) -> Result<Vec<Vec<ExpectedValue>>> {
739731
let mut alternatives = vec![Vec::new()];
740732
for arg in args {
741733
let choices = wastret2tinywasmvalues(arg)?;
@@ -763,14 +755,10 @@ fn wastarg2tinywasmvalue(arg: wast::WastArg) -> Result<WasmValue> {
763755
I32(i) => WasmValue::I32(i),
764756
I64(i) => WasmValue::I64(i),
765757
V128(i) => WasmValue::V128(i.to_le_bytes()),
766-
RefExtern(v) => WasmValue::RefExtern(ExternRef::new(Some(v))),
758+
RefExtern(v) => ExternRef::new(v).into(),
767759
RefNull(t) => match t {
768-
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func } => {
769-
WasmValue::RefFunc(FuncRef::null())
770-
}
771-
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern } => {
772-
WasmValue::RefExtern(ExternRef::null())
773-
}
760+
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func } => RefValue::Null.into(),
761+
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern } => RefValue::Null.into(),
774762
_ => {
775763
bail!("unsupported arg type: refnull: {:?}", t);
776764
}
@@ -797,7 +785,26 @@ fn wast_v128_to_bytes(i: wast::core::V128Pattern) -> [u8; 16] {
797785
res.try_into().unwrap()
798786
}
799787

800-
fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<WasmValue>> {
788+
#[derive(Clone, Copy)]
789+
enum ExpectedValue {
790+
Exact(WasmValue),
791+
RefNull,
792+
RefFunc,
793+
RefExtern,
794+
}
795+
796+
impl ExpectedValue {
797+
fn matches(&self, value: &WasmValue) -> bool {
798+
match self {
799+
Self::Exact(expected) => value.eq_loose(expected),
800+
Self::RefNull => matches!(value, WasmValue::Ref(RefValue::Null)),
801+
Self::RefFunc => matches!(value, WasmValue::Ref(RefValue::Func(_))),
802+
Self::RefExtern => matches!(value, WasmValue::Ref(RefValue::Extern(_))),
803+
}
804+
}
805+
}
806+
807+
fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<ExpectedValue>> {
801808
let wast::WastRet::Core(ret) = ret else {
802809
bail!("unsupported arg type");
803810
};
@@ -809,32 +816,22 @@ fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<WasmValue>> {
809816
}
810817
}
811818

812-
fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result<WasmValue> {
819+
fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result<ExpectedValue> {
813820
use wast::core::WastRetCore::{F32, F64, I32, I64, RefExtern, RefFunc, RefNull, V128};
814821
Ok(match ret {
815-
F32(f) => nanpattern2tinywasmvalue(f)?,
816-
F64(f) => nanpattern2tinywasmvalue(f)?,
817-
I32(i) => WasmValue::I32(i),
818-
I64(i) => WasmValue::I64(i),
819-
V128(i) => WasmValue::V128(wast_v128_to_bytes(i)),
820-
RefNull(t) => match t {
821-
Some(wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func }) => {
822-
WasmValue::RefFunc(FuncRef::null())
823-
}
824-
Some(wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern }) => {
825-
WasmValue::RefExtern(ExternRef::null())
826-
}
827-
_ => {
828-
bail!("unsupported arg type: refnull: {:?}", t);
829-
}
830-
},
831-
RefExtern(v) => WasmValue::RefExtern(ExternRef::new(v)),
832-
RefFunc(v) => WasmValue::RefFunc(FuncRef::new(match v {
833-
Some(wast::token::Index::Num(n, _)) => Some(n),
834-
_ => {
835-
bail!("unsupported arg type: reffunc: {:?}", v);
836-
}
837-
})),
822+
F32(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?),
823+
F64(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?),
824+
I32(i) => ExpectedValue::Exact(WasmValue::I32(i)),
825+
I64(i) => ExpectedValue::Exact(WasmValue::I64(i)),
826+
V128(i) => ExpectedValue::Exact(WasmValue::V128(wast_v128_to_bytes(i))),
827+
RefNull(_) => ExpectedValue::RefNull,
828+
RefExtern(Some(v)) => ExpectedValue::Exact(ExternRef::new(v).into()),
829+
RefExtern(None) => ExpectedValue::RefExtern,
830+
RefFunc(Some(wast::token::Index::Num(n, _))) => ExpectedValue::Exact(FuncRef::new(n).into()),
831+
RefFunc(None) => ExpectedValue::RefFunc,
832+
RefFunc(v) => {
833+
bail!("unsupported arg type: reffunc: {:?}", v);
834+
}
838835
a => {
839836
bail!("unsupported arg type {:?}", a);
840837
}

0 commit comments

Comments
 (0)