go-wasm/bridge.go

153 lines
2.8 KiB
Go

package wasm
import (
"encoding/binary"
"fmt"
"math"
"sync"
"unsafe"
"github.com/wasmerio/go-ext-wasm/wasmer"
)
var undefined = &struct{}{}
var bridges = map[string]*Bridge{}
var mu sync.RWMutex // to protect bridges
type context struct {
n string
}
func setBridge(b *Bridge) unsafe.Pointer {
mu.Lock()
defer mu.Unlock()
bridges[b.name] = b
return unsafe.Pointer(&context{n: b.name})
}
func getBridge(ctx unsafe.Pointer) *Bridge {
ictx := wasmer.IntoInstanceContext(ctx)
c := (*context)(ictx.Data())
mu.RLock()
defer mu.RUnlock()
return bridges[c.n]
}
type Bridge struct {
name string
instance wasmer.Instance
vmExit bool
exitCode int
}
func (b *Bridge) InitWASMBytes(name string, bytes []byte, imports *wasmer.Imports) error {
if imports == nil {
imports = wasmer.NewImports()
}
b.name = name
err := b.addImports(imports)
if err != nil {
return err
}
inst, err := wasmer.NewInstanceWithImports(bytes, imports)
if err != nil {
return err
}
b.instance = inst
inst.SetContextData(setBridge(b))
return nil
}
func (b *Bridge) InitWASM(name, file string, imports *wasmer.Imports) (err error) {
bytes, err := wasmer.ReadBytes(file)
if err != nil {
return err
}
return b.InitWASMBytes(name, bytes, imports)
}
// Run start the wasm instance.
func (b *Bridge) Run() error {
defer b.instance.Close()
run := b.instance.Exports["run"]
resume := b.instance.Exports["resume"]
_, err := run(0, 0)
if err != nil {
return err
}
for !b.vmExit {
_, err = resume()
if err != nil {
return err
}
}
fmt.Printf("WASM exited with code: %v\n", b.exitCode)
return nil
}
func (b *Bridge) mem() []byte {
return b.instance.Memory.Data()
}
func (b Bridge) setInt64(offset int32, v int64) {
mem := b.mem()
binary.LittleEndian.PutUint64(mem[offset:], uint64(v))
}
func (b Bridge) setUint64(offset int32, v uint64) {
mem := b.mem()
binary.LittleEndian.PutUint64(mem[offset:], v)
}
func (b Bridge) getUnit64(offset int32) uint64 {
mem := b.mem()
return binary.LittleEndian.Uint64(mem[offset+0:])
}
func (b Bridge) setFloat64(offset int32, v float64) {
uf := math.Float64bits(v)
b.setUint64(offset, uf)
}
func (b Bridge) getFloat64(offset int32) float64 {
uf := b.getUnit64(offset)
return math.Float64frombits(uf)
}
func (b Bridge) getUint32(offset int32) uint32 {
return binary.LittleEndian.Uint32(b.mem()[offset+0:])
}
func (b Bridge) loadSlice(addr int32) []byte {
mem := b.mem()
array := binary.LittleEndian.Uint64(mem[addr+0:])
length := binary.LittleEndian.Uint64(mem[addr+8:])
return mem[array : array+length]
}
func (b Bridge) loadString(addr int32) string {
d := b.loadSlice(addr)
return string(d)
}
func (b Bridge) loadValue(addr int32) interface{} {
f := b.getFloat64(addr)
if f == 0 {
return undefined
}
if !math.IsNaN(f) {
return f
}
// return value instead of uint32
return b.getUint32(addr)
}