-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
wazero: adding initial support
- Loading branch information
Showing
5 changed files
with
204 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package wazero | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
|
||
"github.com/tetratelabs/wazero/api" | ||
) | ||
|
||
type Instance struct { | ||
module api.Module | ||
} | ||
|
||
func (i *Instance) Call(name string, args ...any) (any, error) { | ||
f := i.module.ExportedFunction(name) | ||
if f == nil { | ||
return nil, errors.New("Function not found") | ||
} | ||
ctx := context.Background() | ||
rawResults, err := f.Call(ctx, encodeArgs(args)...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
results := decodeResults(rawResults, f.Definition().ResultTypes()) | ||
if len(results) == 0 { | ||
return nil, nil | ||
} | ||
if len(results) == 1 { | ||
return results[0], nil | ||
} | ||
return results, nil | ||
} | ||
|
||
func encodeArgs(args []any) []uint64 { | ||
encoded := make([]uint64, len(args)) | ||
for _, arg := range args { | ||
encoded = append(encoded, encodeArg(arg)) | ||
} | ||
return encoded | ||
} | ||
|
||
func encodeArg(arg any) uint64 { | ||
switch val := arg.(type) { | ||
case int32: | ||
return api.EncodeI32(val) | ||
case int64: | ||
return api.EncodeI64(val) | ||
case float32: | ||
return api.EncodeF32(val) | ||
case float64: | ||
return api.EncodeF64(val) | ||
case uint32: | ||
return api.EncodeU32(val) | ||
} | ||
panic("bad arg type") | ||
} | ||
|
||
func decodeResults(results []uint64, vtypes []api.ValueType) []any { | ||
decoded := make([]any, len(results)) | ||
for i, result := range results { | ||
vtype := vtypes[i] | ||
decoded = append(decoded, decodeResult(result, vtype)) | ||
} | ||
return decoded | ||
} | ||
|
||
func decodeResult(result uint64, vtype api.ValueType) any { | ||
switch vtype { | ||
case api.ValueTypeF32: | ||
return api.DecodeF32(result) | ||
case api.ValueTypeF64: | ||
return api.DecodeF64(result) | ||
case api.ValueTypeI32: | ||
return api.DecodeI32(result) | ||
case api.ValueTypeI64: | ||
return api.DecodeI32(result) | ||
} | ||
panic("unreachable") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package wazero | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"io" | ||
|
||
"github.com/hybridgroup/mechanoid/engine" | ||
"github.com/tetratelabs/wazero" | ||
"github.com/tetratelabs/wazero/api" | ||
) | ||
|
||
type Interpreter struct { | ||
runtime wazero.Runtime | ||
defs map[string]map[string]any | ||
module api.Module | ||
} | ||
|
||
func (i *Interpreter) Name() string { | ||
return "wazero" | ||
} | ||
|
||
func (i *Interpreter) Init() error { | ||
ctx := context.Background() | ||
conf := wazero.NewRuntimeConfigInterpreter() | ||
conf = conf.WithDebugInfoEnabled(false) | ||
conf = conf.WithMemoryLimitPages(1) | ||
i.runtime = wazero.NewRuntimeWithConfig(ctx, conf) | ||
return nil | ||
} | ||
|
||
func (i *Interpreter) DefineFunc(moduleName, funcName string, f any) error { | ||
if i.defs == nil { | ||
i.defs = make(map[string]map[string]any) | ||
} | ||
if _, exists := i.defs[moduleName]; !exists { | ||
i.defs[moduleName] = make(map[string]any) | ||
} | ||
i.defs[moduleName][funcName] = f | ||
return nil | ||
} | ||
|
||
func (i *Interpreter) Load(code []byte) error { | ||
var err error | ||
ctx := context.Background() | ||
conf := wazero.NewModuleConfig() | ||
conf = conf.WithRandSource(cheapRand{}) | ||
for moduleName, funcs := range i.defs { | ||
b := i.runtime.NewHostModuleBuilder(moduleName) | ||
for funcName, f := range funcs { | ||
b = b.NewFunctionBuilder().WithFunc(f).Export(funcName) | ||
} | ||
compiled, err := b.Compile(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = i.runtime.InstantiateModule(ctx, compiled, conf) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
i.module, err = i.runtime.InstantiateWithConfig(ctx, code, conf) | ||
return err | ||
} | ||
|
||
func (i *Interpreter) Run() (engine.Instance, error) { | ||
var err error | ||
ctx := context.Background() | ||
init := i.module.ExportedFunction("_initialize") | ||
if init != nil { | ||
_, err = init.Call(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
return &Instance{i.module}, nil | ||
} | ||
|
||
func (i *Interpreter) Halt() error { | ||
ctx := context.Background() | ||
err := i.runtime.Close(ctx) | ||
i.runtime = nil | ||
return err | ||
} | ||
|
||
func (i *Interpreter) MemoryData(ptr, sz uint32) ([]byte, error) { | ||
memory := i.module.ExportedMemory("memory") | ||
if memory == nil { | ||
return nil, errors.New("memory not found") | ||
} | ||
data, inRange := memory.Read(ptr, sz) | ||
if !inRange { | ||
return nil, errors.New("out of range memory access") | ||
} | ||
return data, nil | ||
} | ||
|
||
// A fake RandSource for having fewer memory allocations. | ||
// | ||
// Should not be used with modules that do need an access to random functions. | ||
type cheapRand struct{} | ||
|
||
var _ io.Reader = cheapRand{} | ||
|
||
func (cheapRand) Read(b []byte) (int, error) { | ||
return len(b), nil | ||
} |