153 lines
2.8 KiB
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)
|
|
}
|