summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry Gressmann <mail@henrygressmann.de>2024-01-26 15:16:05 +0100
committerHenry Gressmann <mail@henrygressmann.de>2024-01-26 15:16:05 +0100
commitd8d439e6401607fab18feb5025f3b91f135e3a50 (patch)
treef20f1d5961a5b0810cceca575a206ad45d2a3a9c
parent67f0fd68f1f60f0629d997d5181e5e06440258d7 (diff)
feat: add more examples, work on new public api
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
-rw-r--r--.cargo/config.toml1
-rw-r--r--.github/workflows/test.yaml8
-rw-r--r--.gitignore2
-rw-r--r--.vscode/settings.json6
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml6
-rw-r--r--crates/cli/src/bin.rs2
-rw-r--r--crates/tinywasm/src/export.rs1
-rw-r--r--crates/tinywasm/src/imports.rs9
-rw-r--r--crates/tinywasm/src/instance.rs36
-rw-r--r--crates/tinywasm/src/lib.rs5
-rw-r--r--crates/tinywasm/src/reference.rs17
-rw-r--r--crates/tinywasm/tests/testsuite/run.rs2
-rw-r--r--crates/tinywasm/tests/testsuite/util.rs4
-rw-r--r--crates/types/src/lib.rs6
-rw-r--r--examples/README.md7
-rw-r--r--examples/rust/Cargo.toml14
-rw-r--r--examples/rust/README.md5
-rwxr-xr-xexamples/rust/build.sh7
-rw-r--r--examples/rust/src/fibonacci.rs17
-rw-r--r--examples/rust/src/tinywasm.rs2
-rw-r--r--examples/wasm-rust.rs19
-rw-r--r--examples/wasm/add.wasmbin0 -> 65 bytes
23 files changed, 138 insertions, 45 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml
index 19d47a2..807bc56 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,7 +1,6 @@
[alias]
version-dev="workspaces version --no-git-commit --force tinywasm*"
dev="run -- -l debug run"
-
test-mvp="test --package tinywasm --test test-mvp --release -- --enable "
test-2="test --package tinywasm --test test-two --release -- --enable "
test-wast="test --package tinywasm --test test-wast -- --enable "
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 92d56f4..05f7397 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -20,10 +20,10 @@ jobs:
run: rustup update stable
- name: Build (stable)
- run: cargo +stable build --workspace --exclude wasm-testsuite
+ run: cargo +stable build --workspace
- name: Run tests (stable)
- run: cargo +stable test --workspace --exclude wasm-testsuite
+ run: cargo +stable test --workspace
- name: Run MVP testsuite
run: cargo +stable test-mvp
@@ -41,10 +41,10 @@ jobs:
run: rustup update nightly
- name: Build (nightly, no default features)
- run: cargo +nightly build --workspace --exclude wasm-testsuite --exclude rust-wasm-examples --no-default-features
+ run: cargo +nightly build --workspace --no-default-features
- name: Run tests (nightly, no default features)
- run: cargo +nightly test --workspace --exclude wasm-testsuite --exclude rust-wasm-examples --no-default-features
+ run: cargo +nightly test --workspace --no-default-features
- name: Run MVP testsuite (nightly)
run: cargo +nightly test-mvp
diff --git a/.gitignore b/.gitignore
index f5dcc83..a41fff2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
/target
notes.md
examples/rust/out/*
+examples/rust/target
+examples/rust/Cargo.lock
examples/wast/*
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4f9768f..51f36b4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,5 +1,9 @@
{
"search.exclude": {
"**/wasm-testsuite/data": true
- }
+ },
+ "rust-analyzer.linkedProjects": [
+ "./Cargo.toml",
+ "./examples/rust/Cargo.toml"
+ ]
} \ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index b145eff..ff4d494 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1046,13 +1046,6 @@ dependencies = [
]
[[package]]
-name = "rust-wasm-examples"
-version = "0.0.0"
-dependencies = [
- "tinywasm",
-]
-
-[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index ee9a9df..13a9397 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
[workspace]
-members=["crates/*", "examples/rust"]
+members=["crates/*"]
resolver="2"
[profile.wasm]
@@ -21,6 +21,10 @@ name="tinywasm-root"
publish=false
edition="2021"
+[[example]]
+name="wasm-rust"
+test=false
+
[dev-dependencies]
color-eyre="0.6"
tinywasm={path="crates/tinywasm"}
diff --git a/crates/cli/src/bin.rs b/crates/cli/src/bin.rs
index 1c166cf..34d4bcd 100644
--- a/crates/cli/src/bin.rs
+++ b/crates/cli/src/bin.rs
@@ -112,7 +112,7 @@ fn run(module: Module, func: Option<String>, args: Vec<WasmValue>) -> Result<()>
let instance = module.instantiate(&mut store, None)?;
if let Some(func) = func {
- let func = instance.exported_func_by_name(&store, &func)?;
+ let func = instance.exported_func_untyped(&store, &func)?;
let res = func.call(&mut store, &args)?;
info!("{res:?}");
}
diff --git a/crates/tinywasm/src/export.rs b/crates/tinywasm/src/export.rs
deleted file mode 100644
index 8b13789..0000000
--- a/crates/tinywasm/src/export.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index a640081..9c5086a 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -77,6 +77,11 @@ impl FuncContext<'_> {
pub fn module(&self) -> &crate::ModuleInstance {
self.module
}
+
+ /// Get a reference to an exported memory
+ pub fn memory(&mut self, name: &str) -> Result<crate::MemoryRef> {
+ self.module.exported_memory(self.store, name)
+ }
}
impl Debug for HostFunction {
@@ -276,7 +281,7 @@ impl Imports {
if let Some(addr) = self.modules.get(&name.module) {
let instance = store.get_module_instance(*addr)?;
- return Some(ResolvedExtern::Store(instance.export(&import.name)?));
+ return Some(ResolvedExtern::Store(instance.export_addr(&import.name)?));
}
None
@@ -398,7 +403,7 @@ impl Imports {
Self::compare_table_types(import, &table.borrow().kind, ty)?;
imports.tables.push(table_addr);
}
- (ExternVal::Mem(memory_addr), ImportKind::Memory(ty)) => {
+ (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => {
let mem = store.get_mem(memory_addr as usize)?;
let (size, kind) = {
let mem = mem.borrow();
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index d750cd5..dfee2ca 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -1,12 +1,9 @@
use alloc::{boxed::Box, format, string::ToString, sync::Arc};
-use tinywasm_types::{
- DataAddr, ElemAddr, Export, ExternVal, ExternalKind, FuncAddr, FuncType, GlobalAddr, Import, MemAddr,
- ModuleInstanceAddr, TableAddr,
-};
+use tinywasm_types::*;
use crate::{
func::{FromWasmValueTuple, IntoWasmValueTuple},
- log, Error, FuncHandle, FuncHandleTyped, Imports, Module, Result, Store,
+ log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, Module, Result, Store,
};
/// An instanciated WebAssembly module
@@ -106,7 +103,7 @@ impl ModuleInstance {
}
/// Get a export by name
- pub fn export(&self, name: &str) -> Option<ExternVal> {
+ pub fn export_addr(&self, name: &str) -> Option<ExternVal> {
let exports = self.0.exports.iter().find(|e| e.name == name.into())?;
let kind = exports.kind.clone();
let addr = match kind {
@@ -162,12 +159,12 @@ impl ModuleInstance {
}
/// Get an exported function by name
- pub fn exported_func_by_name(&self, store: &Store, name: &str) -> Result<FuncHandle> {
+ pub fn exported_func_untyped(&self, store: &Store, name: &str) -> Result<FuncHandle> {
if self.0.store_id != store.id() {
return Err(Error::InvalidStore);
}
- let export = self.export(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?;
+ let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?;
let ExternVal::Func(func_addr) = export else {
return Err(Error::Other(format!("Export is not a function: {}", name)));
};
@@ -179,15 +176,32 @@ impl ModuleInstance {
}
/// Get a typed exported function by name
- pub fn typed_func<P, R>(&self, store: &Store, name: &str) -> Result<FuncHandleTyped<P, R>>
+ pub fn exported_func<P, R>(&self, store: &Store, name: &str) -> Result<FuncHandleTyped<P, R>>
where
P: IntoWasmValueTuple,
R: FromWasmValueTuple,
{
- let func = self.exported_func_by_name(store, name)?;
+ let func = self.exported_func_untyped(store, name)?;
Ok(FuncHandleTyped { func, marker: core::marker::PhantomData })
}
+ /// Get an exported memory by name
+ pub fn exported_memory(&self, store: &mut Store, name: &str) -> Result<MemoryRef> {
+ let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?;
+ let ExternVal::Memory(mem_addr) = export else {
+ return Err(Error::Other(format!("Export is not a memory: {}", name)));
+ };
+ let mem = self.memory(store, mem_addr)?;
+ Ok(mem)
+ }
+
+ /// Get a memory by address
+ pub fn memory(&self, store: &Store, addr: MemAddr) -> Result<MemoryRef> {
+ let addr = self.resolve_mem_addr(addr);
+ let mem = store.get_mem(addr as usize)?;
+ Ok(MemoryRef { instance: mem.clone() })
+ }
+
/// Get the start function of the module
///
/// Returns None if the module has no start function
@@ -204,7 +218,7 @@ impl ModuleInstance {
Some(func_index) => func_index,
None => {
// alternatively, check for a _start function in the exports
- let Some(ExternVal::Func(func_addr)) = self.export("_start") else {
+ let Some(ExternVal::Func(func_addr)) = self.export_addr("_start") else {
return Ok(None);
};
diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs
index 36270a7..f2951b6 100644
--- a/crates/tinywasm/src/lib.rs
+++ b/crates/tinywasm/src/lib.rs
@@ -51,7 +51,7 @@
//! // Get a typed handle to the exported "add" function
//! // Alternatively, you can use `instance.get_func` to get an untyped handle
//! // that takes and returns [`WasmValue`]s
-//! let func = instance.typed_func::<(i32, i32), i32>(&mut store, "add")?;
+//! let func = instance.exported_func::<(i32, i32), i32>(&mut store, "add")?;
//! let res = func.call(&mut store, (1, 2))?;
//!
//! assert_eq!(res, 3);
@@ -99,6 +99,9 @@ pub use module::Module;
mod instance;
pub use instance::ModuleInstance;
+mod reference;
+pub use reference::*;
+
mod func;
pub use func::{FuncHandle, FuncHandleTyped};
diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs
new file mode 100644
index 0000000..21471f6
--- /dev/null
+++ b/crates/tinywasm/src/reference.rs
@@ -0,0 +1,17 @@
+use core::cell::RefCell;
+
+use alloc::rc::Rc;
+
+use crate::{GlobalInstance, MemoryInstance};
+
+/// A reference to a memory instance
+#[derive(Debug, Clone)]
+pub struct MemoryRef {
+ pub(crate) instance: Rc<RefCell<MemoryInstance>>,
+}
+
+/// A reference to a global instance
+#[derive(Debug, Clone)]
+pub struct GlobalRef {
+ pub(crate) instance: Rc<RefCell<GlobalInstance>>,
+}
diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs
index 79d9acc..c44c3fb 100644
--- a/crates/tinywasm/tests/testsuite/run.rs
+++ b/crates/tinywasm/tests/testsuite/run.rs
@@ -428,7 +428,7 @@ impl TestSuite {
continue;
};
- let module_global = match match module.export(global) {
+ let module_global = match match module.export_addr(global) {
Some(ExternVal::Global(addr)) => {
store.get_global_val(addr as usize).map_err(|_| eyre!("failed to get global"))
}
diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs
index b74eec5..09a4769 100644
--- a/crates/tinywasm/tests/testsuite/util.rs
+++ b/crates/tinywasm/tests/testsuite/util.rs
@@ -25,7 +25,7 @@ pub fn exec_fn_instance(
return Err(tinywasm::Error::Other("no instance found".to_string()));
};
- let func = instance.exported_func_by_name(store, name)?;
+ let func = instance.exported_func_untyped(store, name)?;
func.call(store, args)
}
@@ -42,7 +42,7 @@ pub fn exec_fn(
let mut store = tinywasm::Store::new();
let module = tinywasm::Module::from(module);
let instance = module.instantiate(&mut store, imports)?;
- instance.exported_func_by_name(&store, name)?.call(&mut store, args)
+ instance.exported_func_untyped(&store, name)?.call(&mut store, args)
}
pub fn catch_unwind_silent<F: FnOnce() -> R, R>(f: F) -> std::thread::Result<R> {
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index d0d854c..365ead7 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -316,7 +316,7 @@ pub type ModuleInstanceAddr = Addr;
pub enum ExternVal {
Func(FuncAddr),
Table(TableAddr),
- Mem(MemAddr),
+ Memory(MemAddr),
Global(GlobalAddr),
}
@@ -325,7 +325,7 @@ impl ExternVal {
match self {
Self::Func(_) => ExternalKind::Func,
Self::Table(_) => ExternalKind::Table,
- Self::Mem(_) => ExternalKind::Memory,
+ Self::Memory(_) => ExternalKind::Memory,
Self::Global(_) => ExternalKind::Global,
}
}
@@ -334,7 +334,7 @@ impl ExternVal {
match kind {
ExternalKind::Func => Self::Func(addr),
ExternalKind::Table => Self::Table(addr),
- ExternalKind::Memory => Self::Mem(addr),
+ ExternalKind::Memory => Self::Memory(addr),
ExternalKind::Global => Self::Global(addr),
}
}
diff --git a/examples/README.md b/examples/README.md
index ce47073..94f974b 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,10 +1,10 @@
# Examples
-## WasmRust
+## Wasm-Rust
These are examples using WebAssembly generated from Rust code.
-To run these, you first need to build the Rust code into WebAssembly, since the wasm files are not included in the repository to keep it small.
-This requires the `wasm32-unknown-unknown` target and `wasm-opt` to be installed (available via Binaryen).
+To run these, you first need to build the Rust code, since the resulting wasm files are not included in the repository to keep it small.
+This requires the `wasm32-unknown-unknown` target and `wasm-opt` to be installed (available via [Binaryen](https://github.com/WebAssembly/binaryen)).
```bash
$ ./examples/rust/build.sh
@@ -20,3 +20,4 @@ Where `<example>` is one of the following:
- `hello`: A simple example that prints a number to the console.
- `tinywasm`: Runs `hello` using TinyWasm - inside of TinyWasm itself!
+- `fibonacci`: Calculates the x-th Fibonacci number.
diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml
index 9ff80dc..d557392 100644
--- a/examples/rust/Cargo.toml
+++ b/examples/rust/Cargo.toml
@@ -1,5 +1,8 @@
cargo-features=["per-package-target"]
+# treat this as an independent package
+[workspace]
+
[package]
publish=false
name="rust-wasm-examples"
@@ -16,3 +19,14 @@ path="src/hello.rs"
[[bin]]
name="tinywasm"
path="src/tinywasm.rs"
+
+[[bin]]
+name="fibonacci"
+path="src/fibonacci.rs"
+
+[profile.wasm]
+opt-level="s"
+lto="thin"
+codegen-units=1
+panic="abort"
+inherits="release"
diff --git a/examples/rust/README.md b/examples/rust/README.md
index 8ecac52..1b6be2f 100644
--- a/examples/rust/README.md
+++ b/examples/rust/README.md
@@ -1 +1,4 @@
-# Examples using Rust compiled to WebAssembly
+# WebAssembly Rust Examples
+
+This is a seperate crate that generates WebAssembly from Rust code.
+It is used by the `wasm-rust` example.
diff --git a/examples/rust/build.sh b/examples/rust/build.sh
index 2c8069a..a8c587a 100755
--- a/examples/rust/build.sh
+++ b/examples/rust/build.sh
@@ -1,11 +1,14 @@
#!/usr/bin/env bash
cd "$(dirname "$0")"
-bins=("hello" "tinywasm")
+bins=("hello" "tinywasm" "fibonacci")
exclude_wat=("tinywasm")
-out_dir="../../target/wasm32-unknown-unknown/wasm"
+out_dir="./target/wasm32-unknown-unknown/wasm"
dest_dir="out"
+# ensure out dir exists
+mkdir -p "$dest_dir"
+
for bin in "${bins[@]}"; do
cargo build --target wasm32-unknown-unknown --package rust-wasm-examples --profile=wasm --bin "$bin"
diff --git a/examples/rust/src/fibonacci.rs b/examples/rust/src/fibonacci.rs
new file mode 100644
index 0000000..7493132
--- /dev/null
+++ b/examples/rust/src/fibonacci.rs
@@ -0,0 +1,17 @@
+#![no_std]
+#![no_main]
+
+#[cfg(not(test))]
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+ core::arch::wasm32::unreachable()
+}
+
+#[no_mangle]
+// The rust compiler will convert this to an iterative algorithm.
+pub extern "C" fn fibonacci(n: i32) -> i32 {
+ if n <= 1 {
+ return n;
+ }
+ fibonacci(n - 1) + fibonacci(n - 2)
+}
diff --git a/examples/rust/src/tinywasm.rs b/examples/rust/src/tinywasm.rs
index 0f18ab9..0fb9261 100644
--- a/examples/rust/src/tinywasm.rs
+++ b/examples/rust/src/tinywasm.rs
@@ -26,7 +26,7 @@ fn run() -> tinywasm::Result<()> {
)?;
let instance = module.instantiate(&mut store, Some(imports))?;
- let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
+ let add_and_print = instance.exported_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
add_and_print.call(&mut store, (1, 2))?;
Ok(())
}
diff --git a/examples/wasm-rust.rs b/examples/wasm-rust.rs
index 3b8877e..3b26863 100644
--- a/examples/wasm-rust.rs
+++ b/examples/wasm-rust.rs
@@ -13,6 +13,7 @@ fn main() -> Result<()> {
match args[1].as_str() {
"hello" => hello()?,
+ "fibonacci" => fibonacci()?,
"tinywasm" => tinywasm()?,
_ => {}
}
@@ -36,7 +37,7 @@ fn tinywasm() -> Result<()> {
)?;
let instance = module.instantiate(&mut store, Some(imports))?;
- let hello = instance.typed_func::<(), ()>(&mut store, "hello")?;
+ let hello = instance.exported_func::<(), ()>(&mut store, "hello")?;
hello.call(&mut store, ())?;
Ok(())
@@ -58,8 +59,22 @@ fn hello() -> Result<()> {
)?;
let instance = module.instantiate(&mut store, Some(imports))?;
- let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
+ let add_and_print = instance.exported_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
add_and_print.call(&mut store, (1, 2))?;
Ok(())
}
+
+fn fibonacci() -> Result<()> {
+ const FIBONACCI_WASM: &[u8] = include_bytes!("./rust/out/fibonacci.wasm");
+ let module = Module::parse_bytes(&FIBONACCI_WASM)?;
+ let mut store = Store::default();
+
+ let instance = module.instantiate(&mut store, None)?;
+ let fibonacci = instance.exported_func::<i32, i32>(&mut store, "fibonacci")?;
+ let n = 30;
+ let result = fibonacci.call(&mut store, n)?;
+ println!("fibonacci({}) = {}", n, result);
+
+ Ok(())
+}
diff --git a/examples/wasm/add.wasm b/examples/wasm/add.wasm
new file mode 100644
index 0000000..92e3432
--- /dev/null
+++ b/examples/wasm/add.wasm
Binary files differ