call_indirect


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.