Files
wazero/internal/integration_test/vs/wasmtime/wasmtime.go
Crypt Keeper e31856d360 updates external tool versions (#1498)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
2023-06-02 15:53:29 +08:00

206 lines
5.1 KiB
Go

//go:build cgo
package wasmtime
import (
"context"
"fmt"
wt "github.com/bytecodealliance/wasmtime-go/v9"
"github.com/tetratelabs/wazero/internal/integration_test/vs"
)
func newWasmtimeRuntime() vs.Runtime {
return &wasmtimeRuntime{}
}
type wasmtimeRuntime struct {
engine *wt.Engine
module *wt.Module
}
type wasmtimeModule struct {
store *wt.Store
// instance is here because there's no close/destroy function. The only thing is garbage collection.
instance *wt.Instance
funcs map[string]*wt.Func
logFn func([]byte) error
mem *wt.Memory
}
func (r *wasmtimeRuntime) Name() string {
return "wasmtime"
}
func (m *wasmtimeModule) log(_ *wt.Caller, args []wt.Val) ([]wt.Val, *wt.Trap) {
unsafeSlice := m.mem.UnsafeData(m.store)
offset := args[0].I32()
byteCount := args[1].I32()
if err := m.logFn(unsafeSlice[offset : offset+byteCount]); err != nil {
return nil, wt.NewTrap(err.Error())
}
return []wt.Val{}, nil
}
func (r *wasmtimeRuntime) Compile(_ context.Context, cfg *vs.RuntimeConfig) (err error) {
r.engine = wt.NewEngine()
if r.module, err = wt.NewModule(r.engine, cfg.ModuleWasm); err != nil {
return
}
return
}
func (r *wasmtimeRuntime) Instantiate(_ context.Context, cfg *vs.RuntimeConfig) (mod vs.Module, err error) {
wm := &wasmtimeModule{funcs: map[string]*wt.Func{}}
// 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
wm.store = wt.NewStore(r.engine)
linker := wt.NewLinker(wm.store.Engine)
// Instantiate WASI, if configured.
if cfg.NeedsWASI {
if err = linker.DefineWasi(); err != nil {
return
}
config := wt.NewWasiConfig() // defaults to toss stdout
config.InheritStderr() // see errors
wm.store.SetWasi(config)
}
// Instantiate the host module, "env", if configured.
if cfg.LogFn != nil {
wm.logFn = cfg.LogFn
if err = linker.Define(wm.store, "env", "log", wt.NewFunc(
wm.store,
wt.NewFuncType(
[]*wt.ValType{
wt.NewValType(wt.KindI32),
wt.NewValType(wt.KindI32),
},
[]*wt.ValType{},
),
wm.log,
)); err != nil {
return
}
} else if cfg.EnvFReturnValue != 0 {
ret := []wt.Val{wt.ValI64(int64(cfg.EnvFReturnValue))}
if err = linker.Define(wm.store, "env", "f", wt.NewFunc(
wm.store,
wt.NewFuncType(
[]*wt.ValType{
wt.NewValType(wt.KindI64),
},
[]*wt.ValType{wt.NewValType(wt.KindI64)},
),
func(_ *wt.Caller, args []wt.Val) ([]wt.Val, *wt.Trap) {
return ret, nil
},
)); err != nil {
return
}
}
// Set the module name.
if err = linker.DefineModule(wm.store, cfg.ModuleName, r.module); err != nil {
return
}
// Instantiate the module.
instance, instantiateErr := linker.Instantiate(wm.store, r.module)
if instantiateErr != nil {
err = instantiateErr
return
}
if cfg.LogFn != nil || cfg.NeedsMemoryExport {
// Wasmtime does not allow a host function parameter for memory, so you have to manually propagate it.
if wm.mem = instance.GetExport(wm.store, "memory").Memory(); wm.mem == nil {
err = fmt.Errorf(`"memory" not exported`)
return
}
}
// If WASI is needed, we have to go back and invoke the _start function.
if cfg.NeedsWASI {
start := instance.GetFunc(wm.store, "_start")
if _, err = start.Call(wm.store); err != nil {
return
}
}
// Ensure function exports exist.
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.module = nil
r.engine = nil
return nil // wt only closes via finalizer
}
func (m *wasmtimeModule) Memory() []byte {
return m.mem.UnsafeData(m.store)
}
func (m *wasmtimeModule) CallI32_I32(_ context.Context, funcName string, param uint32) (uint32, error) {
fn := m.funcs[funcName]
if result, err := fn.Call(m.store, int32(param)); err != nil {
return 0, err
} else {
return uint32(result.(int32)), nil
}
}
func (m *wasmtimeModule) CallI32I32_V(_ context.Context, funcName string, x, y uint32) (err error) {
fn := m.funcs[funcName]
_, err = fn.Call(m.store, int32(x), int32(y))
return
}
func (m *wasmtimeModule) CallV_V(_ context.Context, funcName string) (err error) {
fn := m.funcs[funcName]
_, err = fn.Call(m.store)
return
}
func (m *wasmtimeModule) CallI32_V(_ context.Context, funcName string, param uint32) (err error) {
fn := m.funcs[funcName]
_, err = fn.Call(m.store, int32(param))
return
}
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) WriteMemory(offset uint32, bytes []byte) error {
unsafeSlice := m.mem.UnsafeData(m.store)
copy(unsafeSlice[offset:], bytes)
return nil
}
func (m *wasmtimeModule) Close(context.Context) error {
m.store = nil
m.instance = nil
m.funcs = nil
return nil // wt only closes via finalizer
}