(module (import "env" "greet" (func $greet (param i32))) (memory 1) (type $func (func (param $callstack i32) (result i32))) (func $do_greet (param $callstack i32) (result i32) (local $greet_number i32) local.get $callstack i32.const 4 i32.sub local.set $callstack local.get $callstack i32.load local.set $greet_number local.get $greet_number call $greet local.get $callstack i32.const -1 ;; return to caller i32.store local.get $callstack i32.const 4 i32.add local.set $callstack local.get $callstack return ) (func $on_module_loaded (param $callstack i32) (result i32) ;; Param 1: number to send to $greet local.get $callstack i32.const 29 i32.store local.get $callstack i32.const 4 i32.add local.set $callstack local.get $callstack i32.const 17 ;; goto do_greet i32.store local.get $callstack i32.const 4 i32.add local.set $callstack local.get $callstack return ) ;; You could have a $on_module_loaded.0 that processes ;; the result from greet (table 32 funcref) (elem (i32.const 16) $on_module_loaded) (elem (i32.const 17) $do_greet) (func $runtime (param $func i32) (param $callstack i32) loop $call_methods local.get $callstack local.get $func call_indirect (type $func) local.set $callstack ;; Pop the func number of the callback local.get $callstack i32.const 4 i32.sub local.set $callstack local.get $callstack i32.load local.set $func ;; See if we still have a function to jump to local.get $func i32.const 0 i32.ge_s br_if $call_methods end ) (func $start i32.const 16 i32.const 0 call $runtime ) (start $start) ) ;; A function that takes 3 params and returns 1 ;; stack param0 param1 param2 func_id ;; => $runtime pops func_id and calls its function ;; stack param0 param1 param2 ;; => function$func_id pops param2, param1 and param0 ;; stack ;; => function$func_id stores its return value ;; stack $result ;; => function$func_id returns to $runtime ;; $runtime now has the $result on top of the stack, rather the func_id to call -.-