In the MVP, You cannot get functions stored in a table directly. You can only call_indirect
to call a function from the table. For example:
(module
(type $ft (func (param i32)))
(import "env" "log" (func $log (param i32)))
(table $tb 1 anyfunc)
(func $f1 (param $p i32)
(i32.add (get_local $p) (i32.const 10))
call $log
)
(elem (i32.const 0) $f1)
(func $main
i32.const 10
i32.const 0
call_indirect (type $ft)
)
(start $main)
)
call_indirect
checks for signature match dynamically. Using type
to define the type and name is a convenient way when specifing a type. call_indirect
implicitly pops a number n
off the stack for invoking the nth function in the table. Then, the required arguments of the function will be popped off the stack.
For example, you may define a rate module containing two rate functions.
(module
(func $rate1 (result f32)
f32.const 0.015
)
(func $rate2 (result f32)
f32.const 0.025
)
(export "rate1" (func $rate1))
(export "rate2" (func $rate2))
)
After that, define an interest module using call_indirect
to invoke a specified function.
(module
(type $rate (func (result f32)))
(import "env" "tb" (table $tb 1 anyfunc))
(func $interest (param $money f32) (result f32)
(f32.mul
(call_indirect (type $rate) (i32.const 0))
(get_local $money)
)
)
(export "interest" (func $interest))
)
In JavaScript, create a WebAssembly.Table
instance and specify rate functions dynamically.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const rtb = new WebAssembly.Table({initial:1, element:"anyfunc"});
const importObj = {
env: {rtb}
};
Promise.all([
WebAssembly.instantiateStreaming(fetch('rate.wasm')),
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
])
.then(wasms => {
const rate = wasms[0].instance;
const prog = wasms[1].instance;
rtb.set(0, rate.exports.rate1);
console.log(prog.exports.interest(10000)); // print 150
rtb.set(0, rate.exports.rate2);
console.log(prog.exports.interest(10000)); // print 250
});
</script>
</body>
</html>
A WebAssembly table can implement the concept of function pointers in C/C++. For example:
(module
(import "env" "log" (func $log (param f32)))
(type $rate (func (result f32)))
(table $rtb 2 anyfunc)
(func $rate1 (result f32)
f32.const 0.015
)
(func $rate2 (result f32)
f32.const 0.025
)
(elem (i32.const 0) $rate1 $rate2)
(func $interest (param $money f32) (param $rf i32) (result f32)
(f32.mul
(call_indirect (type $rate) (get_local $rf))
(get_local $money)
)
)
(func $main
(local $rf i32)
(call $interest (f32.const 10000) (get_local $rf))
call $log ;; print 150
(set_local $rf (i32.const 1))
(call $interest (f32.const 10000) (get_local $rf))
call $log ;; print 250
)
(start $main)
)
You store two functions in the table. The second parameter of $interest
accepts an index for indirectly calling a function in the table. It's like the concept of passing a function into a function.