Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1a64f40

Browse files
authored
docs: reference types for typed functions (#28)
1 parent 9f1bc8d commit 1a64f40

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

examples/funcref_callbacks.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
use eyre::Result;
2+
use tinywasm::{types::FuncRef, Extern, FuncContext, Imports, Module, Store};
3+
use wat;
4+
5+
fn main() -> Result<()> {
6+
by_func_ref_passed()?;
7+
by_func_ref_returned()?;
8+
Ok(())
9+
}
10+
11+
/// example of passing wasm functions (as funcref) to imported host function
12+
/// and imported host function calling them
13+
fn by_func_ref_passed() -> Result<()> {
14+
// a module with:
15+
// imported function "host.call_this" that accepts a callback
16+
// exported wasm function "tell_host_to_call" that calls "host.call_this" with wasm functions $add and $sub
17+
// wasm functions $add and $sub and imported function $mul used as callbacks
18+
// (just to show that imported functions can be referenced too)
19+
// exported wasm function "call_binop_by_ref" is a proxy used by host to call func-references of type (i32, i32)->i32
20+
const WASM: &str = r#"
21+
(module
22+
(import "host" "call_this" (func $host_callback_caller (param funcref)))
23+
(import "host" "mul" (func $host_mul (param $x i32) (param $y i32) (result i32)))
24+
25+
(func $tell_host_to_call (export "tell_host_to_call")
26+
(call $host_callback_caller (ref.func $add))
27+
(call $host_callback_caller (ref.func $sub))
28+
(call $host_callback_caller (ref.func $host_mul))
29+
)
30+
31+
(type $binop (func (param i32 i32) (result i32)))
32+
33+
(table 3 funcref)
34+
(elem (i32.const 0) $add $sub $host_mul) ;; function can only be taken reference of if it's added to a table
35+
(func $add (param $x i32) (param $y i32) (result i32)
36+
local.get $x
37+
local.get $y
38+
i32.add
39+
)
40+
(func $sub (param $x i32) (param $y i32) (result i32)
41+
local.get $x
42+
local.get $y
43+
i32.sub
44+
)
45+
46+
(table $callback_register 1 funcref)
47+
(func (export "call_binop_by_ref") (param funcref i32 i32) (result i32)
48+
(table.set $callback_register (i32.const 0) (local.get 0))
49+
(call_indirect $callback_register (type $binop) (local.get 1)(local.get 2)(i32.const 0))
50+
)
51+
)
52+
"#;
53+
54+
let wasm = wat::parse_str(WASM).expect("failed to parse wat");
55+
let module = Module::parse_bytes(&wasm)?;
56+
let mut store = Store::default();
57+
let mut imports = Imports::new();
58+
// import host function that takes callbacks and calls them
59+
imports.define(
60+
"host",
61+
"call_this",
62+
Extern::typed_func(|mut ctx: FuncContext<'_>, fn_ref: FuncRef| -> tinywasm::Result<()> {
63+
let proxy_caller =
64+
ctx.module().exported_func::<(FuncRef, i32, i32), i32>(ctx.store(), "call_binop_by_ref")?;
65+
// call callback we got as argument using call_binop_by_ref
66+
let res = proxy_caller.call(ctx.store_mut(), (fn_ref, 5, 3))?;
67+
println!("(funcref {fn_ref:?})(5,3) results in {res}");
68+
69+
Ok(())
70+
}),
71+
)?;
72+
// import host.mul function (one of the functions whose references are taken)
73+
imports.define(
74+
"host",
75+
"mul",
76+
Extern::typed_func(|_, args: (i32, i32)| -> tinywasm::Result<i32> { Ok(args.0 * args.1) }),
77+
)?;
78+
let instance = module.instantiate(&mut store, Some(imports))?;
79+
let caller = instance.exported_func::<(), ()>(&mut store, "tell_host_to_call")?;
80+
// call the tell_host_to_call
81+
caller.call(&mut store, ())?;
82+
// interesting detail is that neither $add $sub $mul were exported,
83+
// but with a little help from proxy "call_binop_by_ref" references to them host was able to call them
84+
Ok(())
85+
}
86+
87+
/// example of returning wasm function as callback to host function
88+
/// and host function calling it
89+
fn by_func_ref_returned() -> Result<()> {
90+
// a module with:
91+
// an exported function "what_should_host_call" that returns 3 funcrefs
92+
// wasm functions $add and $sub and imported function $mul used as callbacks
93+
// (just to show that imported functions can be referenced too)
94+
// exported wasm function "call_binop_by_ref" is a proxy used by host to call func-references of type (i32, i32)->i32
95+
const WASM: &str = r#"
96+
(module
97+
(import "host" "mul" (func $host_mul (param $x i32) (param $y i32) (result i32)))
98+
(type $binop (func (param i32 i32) (result i32)))
99+
(table 3 funcref)
100+
(elem (i32.const 0) $add $sub $host_mul)
101+
(func $add (param $x i32) (param $y i32) (result i32)
102+
local.get $x
103+
local.get $y
104+
i32.add
105+
)
106+
(func $sub (param $x i32) (param $y i32) (result i32)
107+
local.get $x
108+
local.get $y
109+
i32.sub
110+
)
111+
(func $ref_to_funcs (export "what_should_host_call") (result funcref funcref funcref)
112+
(ref.func $add)
113+
(ref.func $sub)
114+
(ref.func $host_mul)
115+
)
116+
117+
(table $callback_register 1 funcref)
118+
(func $call (export "call_binop_by_ref") (param funcref i32 i32) (result i32)
119+
(table.set $callback_register (i32.const 0) (local.get 0))
120+
(call_indirect $callback_register (type $binop) (local.get 1)(local.get 2)(i32.const 0))
121+
)
122+
)
123+
"#;
124+
125+
let wasm = wat::parse_str(WASM).expect("failed to parse wat");
126+
let module = Module::parse_bytes(&wasm)?;
127+
let mut store = Store::default();
128+
let mut imports = Imports::new();
129+
// import host.mul function (one of the possible operations)
130+
imports.define(
131+
"host",
132+
"mul",
133+
Extern::typed_func(|_, args: (i32, i32)| -> tinywasm::Result<i32> { Ok(args.0 * args.1) }),
134+
)?;
135+
136+
let instance = module.instantiate(&mut store, Some(imports))?;
137+
{
138+
// ask module what should we call
139+
let funcrefs = {
140+
let address_getter =
141+
instance.exported_func::<(), (FuncRef, FuncRef, FuncRef)>(&mut store, "what_should_host_call")?;
142+
address_getter.call(&mut store, ())?
143+
};
144+
let proxy_caller = instance.exported_func::<(FuncRef, i32, i32), i32>(&mut store, "call_binop_by_ref")?;
145+
for (idx, func_ref) in [funcrefs.0, funcrefs.1, funcrefs.2].iter().enumerate() {
146+
// call those funcrefs via "call_binop_by_ref"
147+
let res = proxy_caller.call(&mut store, (*func_ref, 5, 3))?;
148+
println!("at idx: {idx} funcref {func_ref:?}(5,3) results in {res}");
149+
}
150+
}
151+
Ok(())
152+
}

0 commit comments

Comments
 (0)