This prepares for exposing operations like Memory.Grow while keeping the ability to trace what did that, by adding a `context.Context` initial parameter. This adds this to all API methods that mutate or return mutated data. Before, we made a change to trace functions and general lifecycle commands, but we missed this part. Ex. We track functions, but can't track what closed the module, changed memory or a mutable constant. Changing to do this now is not only more consistent, but helps us optimize at least the interpreter to help users identify otherwise opaque code that can cause harm. This is critical before we add more functions that can cause harm, such as Memory.Grow. Signed-off-by: Adrian Cole <adrian@tetrate.io>
88 lines
2.1 KiB
Go
88 lines
2.1 KiB
Go
//go:build amd64 && cgo
|
|
|
|
package vs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/bytecodealliance/wasmtime-go"
|
|
)
|
|
|
|
func init() {
|
|
runtimes["wasmtime-go"] = newWasmtimeRuntime
|
|
}
|
|
|
|
func newWasmtimeRuntime() runtime {
|
|
return &wasmtimeRuntime{}
|
|
}
|
|
|
|
type wasmtimeRuntime struct {
|
|
engine *wasmtime.Engine
|
|
}
|
|
|
|
type wasmtimeModule struct {
|
|
store *wasmtime.Store
|
|
// instance is here because there's no close/destroy function. The only thing is garbage collection.
|
|
instance *wasmtime.Instance
|
|
funcs map[string]*wasmtime.Func
|
|
}
|
|
|
|
func (r *wasmtimeRuntime) Compile(_ context.Context, _ *runtimeConfig) (err error) {
|
|
r.engine = wasmtime.NewEngine()
|
|
// We can't reuse a store because even if we call close, re-instantiating too many times leads to:
|
|
// >> resource limit exceeded: instance count too high at 10001
|
|
return
|
|
}
|
|
|
|
func (r *wasmtimeRuntime) Instantiate(_ context.Context, cfg *runtimeConfig) (mod module, err error) {
|
|
wm := &wasmtimeModule{funcs: map[string]*wasmtime.Func{}}
|
|
wm.store = wasmtime.NewStore(r.engine)
|
|
var m *wasmtime.Module
|
|
if m, err = wasmtime.NewModule(wm.store.Engine, cfg.moduleWasm); err != nil {
|
|
return
|
|
}
|
|
|
|
// Set the module name
|
|
linker := wasmtime.NewLinker(wm.store.Engine)
|
|
if err = linker.DefineModule(wm.store, cfg.moduleName, m); err != nil {
|
|
return
|
|
}
|
|
instance, err := linker.Instantiate(wm.store, m)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, funcName := range cfg.funcNames {
|
|
if fn := instance.GetFunc(wm.store, funcName); fn == nil {
|
|
err = fmt.Errorf("%s is not an exported function", funcName)
|
|
return
|
|
} else {
|
|
wm.funcs[funcName] = fn
|
|
}
|
|
}
|
|
mod = wm
|
|
return
|
|
}
|
|
|
|
func (r *wasmtimeRuntime) Close(_ context.Context) error {
|
|
r.engine = nil
|
|
return nil // wasmtime only closes via finalizer
|
|
}
|
|
|
|
func (m *wasmtimeModule) CallI64_I64(_ context.Context, funcName string, param uint64) (uint64, error) {
|
|
fn := m.funcs[funcName]
|
|
if result, err := fn.Call(m.store, int64(param)); err != nil {
|
|
return 0, err
|
|
} else {
|
|
return uint64(result.(int64)), nil
|
|
}
|
|
}
|
|
|
|
func (m *wasmtimeModule) Close(_ context.Context) error {
|
|
m.store = nil
|
|
m.instance = nil
|
|
m.funcs = nil
|
|
return nil // wasmtime only closes via finalizer
|
|
}
|