Skip to content

Commit

Permalink
Merge #186
Browse files Browse the repository at this point in the history
186: feat(function) Implement host function with an environment r=jubianchi a=Hywan

Fix #183.

WIP

Need to double-check everything.

Co-authored-by: Ivan Enderlin <[email protected]>
  • Loading branch information
bors[bot] and Hywan authored Feb 5, 2021
2 parents a9dd8a9 + 444c311 commit 8e6bd90
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 3 deletions.
62 changes: 59 additions & 3 deletions wasmer/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ package wasmer
// wasm_val_vec_t* results
// );
//
// extern wasm_trap_t* function_with_environment_trampoline(
// void *environment,
// /* const */ wasm_val_vec_t* arguments,
// wasm_val_vec_t* results
// );
//
// typedef void (*wasm_func_callback_env_finalizer_t)(void* environment);
//
// extern void function_environment_finalizer(void *environment);
Expand Down Expand Up @@ -99,7 +105,56 @@ func function_trampoline(env unsafe.Pointer, args *C.wasm_val_vec_t, res *C.wasm
}

arguments := toValueList(args)
results, err := (hostFunction.function)(arguments)
function := (hostFunction.function).(func([]Value) ([]Value, error))
results, err := (function)(arguments)

if err != nil {
trap := NewTrap(hostFunction.store, err.Error())

runtime.KeepAlive(trap)

return trap.inner()
}

toValueVec(results, res)

return nil
}

func NewFunctionWithEnvironment(store *Store, ty *FunctionType, userEnvironment interface{}, functionWithEnv func(interface{}, []Value) ([]Value, error)) *Function {
hostFunction := &hostFunction{
store: store,
function: functionWithEnv,
userEnvironment: userEnvironment,
}
environment := &FunctionEnvironment{
hostFunctionStoreIndex: hostFunctionStore.store(hostFunction),
}
pointer := C.wasm_func_new_with_env(
store.inner(),
ty.inner(),
(C.wasm_func_callback_t)(C.function_with_environment_trampoline),
unsafe.Pointer(environment),
(C.wasm_func_callback_env_finalizer_t)(C.function_environment_finalizer),
)

runtime.KeepAlive(environment)

return newFunction(pointer, environment, nil)
}

//export function_with_environment_trampoline
func function_with_environment_trampoline(env unsafe.Pointer, args *C.wasm_val_vec_t, res *C.wasm_val_vec_t) *C.wasm_trap_t {
environment := (*FunctionEnvironment)(env)
hostFunction, err := hostFunctionStore.load(environment.hostFunctionStoreIndex)

if err != nil {
panic(err)
}

arguments := toValueList(args)
function := (hostFunction.function).(func(interface{}, []Value) ([]Value, error))
results, err := (function)(hostFunction.userEnvironment, arguments)

if err != nil {
trap := NewTrap(hostFunction.store, err.Error())
Expand Down Expand Up @@ -268,8 +323,9 @@ type FunctionEnvironment struct {
func function_environment_finalizer(_ unsafe.Pointer) {}

type hostFunction struct {
function func([]Value) ([]Value, error)
store *Store
store *Store
function interface{} // func([]Value) ([]Value, error) or func(interface{}, []Value) ([]Value, error)
userEnvironment interface{} // if the host function has an environment
}

type hostFunctions struct {
Expand Down
56 changes: 56 additions & 0 deletions wasmer/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,62 @@ func TestHostFunction(t *testing.T) {
assert.Equal(t, result, int32(42))
}

func TestHostFunctionWithEnv(t *testing.T) {
engine := NewEngine()
store := NewStore(engine)
module, err := NewModule(
store,
[]byte(`
(module
(import "math" "sum" (func $sum (param i32 i32) (result i32)))
(func (export "add_one") (param $x i32) (result i32)
local.get $x
i32.const 1
call $sum))
`),
)
assert.NoError(t, err)

type MyEnvironment struct {
theAnswer int32
}

environment := &MyEnvironment{
theAnswer: 42,
}

function := NewFunctionWithEnvironment(
store,
NewFunctionType(NewValueTypes(I32, I32), NewValueTypes(I32)),
environment,
func(environment interface{}, args []Value) ([]Value, error) {
env := environment.(*MyEnvironment)
x := args[0].I32()
y := args[1].I32()

return []Value{NewI32(env.theAnswer + x + y)}, nil
},
)

importObject := NewImportObject()
importObject.Register(
"math",
map[string]IntoExtern{
"sum": function,
},
)

instance, err := NewInstance(module, importObject)
assert.NoError(t, err)

addOne, err := instance.Exports.GetFunction("add_one")
assert.NoError(t, err)

result, err := addOne(7)
assert.NoError(t, err)
assert.Equal(t, result, int32(50))
}

func TestHostFunctionStore(t *testing.T) {
f := &hostFunction{
store: NewStore(NewEngine()),
Expand Down

0 comments on commit 8e6bd90

Please sign in to comment.