add store value

pull/2/head
vedhavyas 2019-08-15 20:12:20 +02:00
parent 4aefd0446f
commit 771a2d95cf
No known key found for this signature in database
GPG Key ID: 317BF0923E3EB7E5
3 changed files with 225 additions and 49 deletions

210
bridge.go
View File

@ -3,8 +3,10 @@ package wasm
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"log"
"math" "math"
"sync" "sync"
"syscall"
"unsafe" "unsafe"
"github.com/wasmerio/go-ext-wasm/wasmer" "github.com/wasmerio/go-ext-wasm/wasmer"
@ -13,11 +15,9 @@ import (
var undefined = &struct{}{} var undefined = &struct{}{}
var bridges = map[string]*Bridge{} var bridges = map[string]*Bridge{}
var mu sync.RWMutex // to protect bridges var mu sync.RWMutex // to protect bridges
type context struct{ n string }
type context struct { // TODO ensure it wont override the another context with same name.
n string
}
func setBridge(b *Bridge) unsafe.Pointer { func setBridge(b *Bridge) unsafe.Pointer {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
@ -38,9 +38,12 @@ type Bridge struct {
instance wasmer.Instance instance wasmer.Instance
vmExit bool vmExit bool
exitCode int exitCode int
values []interface{}
refs map[interface{}]int
} }
func (b *Bridge) InitWASMBytes(name string, bytes []byte, imports *wasmer.Imports) error { func BridgeFromBytes(name string, bytes []byte, imports *wasmer.Imports) (*Bridge, error) {
b := new(Bridge)
if imports == nil { if imports == nil {
imports = wasmer.NewImports() imports = wasmer.NewImports()
} }
@ -48,26 +51,70 @@ func (b *Bridge) InitWASMBytes(name string, bytes []byte, imports *wasmer.Import
b.name = name b.name = name
err := b.addImports(imports) err := b.addImports(imports)
if err != nil { if err != nil {
return err return nil, err
} }
inst, err := wasmer.NewInstanceWithImports(bytes, imports) inst, err := wasmer.NewInstanceWithImports(bytes, imports)
if err != nil { if err != nil {
return err return nil, err
} }
b.instance = inst b.instance = inst
inst.SetContextData(setBridge(b)) inst.SetContextData(setBridge(b))
return nil b.addValues()
b.refs = make(map[interface{}]int)
return b, nil
} }
func (b *Bridge) InitWASM(name, file string, imports *wasmer.Imports) (err error) { func BridgeFromFile(name, file string, imports *wasmer.Imports) (*Bridge, error) {
bytes, err := wasmer.ReadBytes(file) bytes, err := wasmer.ReadBytes(file)
if err != nil { if err != nil {
return err return nil, err
} }
return b.InitWASMBytes(name, bytes, imports) return BridgeFromBytes(name, bytes, imports)
}
func (b *Bridge) addValues() {
b.values = []interface{}{
math.NaN(),
float64(0),
nil,
true,
false,
&object{
props: map[string]interface{}{
"Object": &object{name: "Object", props: map[string]interface{}{}},
"Array": &object{name: "Array", props: map[string]interface{}{}},
"Int8Array": typedArray("Int8Array"),
"Int16Array": typedArray("Int16Array"),
"Int32Array": typedArray("Int32Array"),
"Uint8Array": typedArray("Uint8Array"),
"Uint16Array": typedArray("Uint16Array"),
"Uint32Array": typedArray("Uint32Array"),
"Float32Array": typedArray("Float32Array"),
"Float64Array": typedArray("Float64Array"),
"process": &object{name: "process", props: map[string]interface{}{}},
"fs": &object{name: "fs", props: map[string]interface{}{
"constants": &object{name: "constants", props: map[string]interface{}{
"O_WRONLY": syscall.O_WRONLY,
"O_RDWR": syscall.O_RDWR,
"O_CREAT": syscall.O_CREAT,
"O_TRUNC": syscall.O_TRUNC,
"O_APPEND": syscall.O_APPEND,
"O_EXCL": syscall.O_EXCL,
}},
}},
},
}, //global
&object{
name: "mem",
props: map[string]interface{}{
"buffer": &arrayBuffer{data: b.mem()},
},
},
&object{name: "jsGo", props: map[string]interface{}{}}, // jsGo
}
} }
// Run start the wasm instance. // Run start the wasm instance.
@ -96,57 +143,172 @@ func (b *Bridge) mem() []byte {
return b.instance.Memory.Data() return b.instance.Memory.Data()
} }
func (b Bridge) setInt64(offset int32, v int64) { func (b *Bridge) getSP() int32 {
spFunc := b.instance.Exports["getsp"]
val, err := spFunc()
if err != nil {
log.Fatal("failed to get sp", err)
}
return val.ToI32()
}
func (b *Bridge) setInt64(offset int32, v int64) {
mem := b.mem() mem := b.mem()
binary.LittleEndian.PutUint64(mem[offset:], uint64(v)) binary.LittleEndian.PutUint64(mem[offset:], uint64(v))
} }
func (b Bridge) setUint64(offset int32, v uint64) { func (b *Bridge) getInt64(offset int32) int64 {
mem := b.mem()
return int64(binary.LittleEndian.Uint64(mem[offset:]))
}
func (b *Bridge) setUint32(offset int32, v uint32) {
mem := b.mem()
binary.LittleEndian.PutUint32(mem[offset:], v)
}
func (b *Bridge) setUint64(offset int32, v uint64) {
mem := b.mem() mem := b.mem()
binary.LittleEndian.PutUint64(mem[offset:], v) binary.LittleEndian.PutUint64(mem[offset:], v)
} }
func (b Bridge) getUnit64(offset int32) uint64 { func (b *Bridge) getUnit64(offset int32) uint64 {
mem := b.mem() mem := b.mem()
return binary.LittleEndian.Uint64(mem[offset+0:]) return binary.LittleEndian.Uint64(mem[offset+0:])
} }
func (b Bridge) setFloat64(offset int32, v float64) { func (b *Bridge) setFloat64(offset int32, v float64) {
uf := math.Float64bits(v) uf := math.Float64bits(v)
b.setUint64(offset, uf) b.setUint64(offset, uf)
} }
func (b Bridge) getFloat64(offset int32) float64 { func (b *Bridge) getFloat64(offset int32) float64 {
uf := b.getUnit64(offset) uf := b.getUnit64(offset)
return math.Float64frombits(uf) return math.Float64frombits(uf)
} }
func (b Bridge) getUint32(offset int32) uint32 { func (b *Bridge) getUint32(offset int32) uint32 {
return binary.LittleEndian.Uint32(b.mem()[offset+0:]) return binary.LittleEndian.Uint32(b.mem()[offset+0:])
} }
func (b Bridge) loadSlice(addr int32) []byte { func (b *Bridge) loadSlice(addr int32) []byte {
mem := b.mem() mem := b.mem()
array := binary.LittleEndian.Uint64(mem[addr+0:]) array := binary.LittleEndian.Uint64(mem[addr+0:])
length := binary.LittleEndian.Uint64(mem[addr+8:]) length := binary.LittleEndian.Uint64(mem[addr+8:])
return mem[array : array+length] return mem[array : array+length]
} }
func (b Bridge) loadString(addr int32) string { func (b *Bridge) loadString(addr int32) string {
d := b.loadSlice(addr) d := b.loadSlice(addr)
return string(d) return string(d)
} }
func (b Bridge) loadValue(addr int32) interface{} { func (b *Bridge) loadSliceOfValues(addr int32) []interface{} {
arr := int(b.getInt64(addr + 0))
arrLen := int(b.getInt64(addr + 8))
vals := make([]interface{}, arrLen, arrLen)
for i := 0; i < int(arrLen); i++ {
_, vals[i] = b.loadValue(int32(arr + i*8))
}
return vals
}
// TODO remove id once debugging is done
func (b *Bridge) loadValue(addr int32) (uint32, interface{}) {
f := b.getFloat64(addr) f := b.getFloat64(addr)
if f == 0 { if f == 0 {
return undefined return 0, undefined
} }
if !math.IsNaN(f) { if !math.IsNaN(f) {
return f return 0, f
} }
// return value instead of uint32 id := b.getUint32(addr)
return b.getUint32(addr) return id, b.values[id]
}
func (b *Bridge) storeValue(addr int32, v interface{}) {
const nanHead = 0x7FF80000
if i, ok := v.(int); ok {
v = float64(i)
}
if i, ok := v.(uint); ok {
v = float64(i)
}
if v, ok := v.(float64); ok {
if math.IsNaN(v) {
b.setUint32(addr+4, nanHead)
b.setUint32(addr, 0)
return
}
if v == 0 {
b.setUint32(addr+4, nanHead)
b.setUint32(addr, 1)
return
}
b.setFloat64(addr, v)
return
}
switch v {
case undefined:
b.setFloat64(addr, 0)
return
case nil:
b.setUint32(addr+4, nanHead)
b.setUint32(addr, 2)
return
case true:
b.setUint32(addr+4, nanHead)
b.setUint32(addr, 3)
return
case false:
b.setUint32(addr+4, nanHead)
b.setUint32(addr, 4)
return
}
ref, ok := b.refs[v]
if !ok {
ref = len(b.values)
b.values = append(b.values, v)
b.refs[v] = ref
}
typeFlag := 0
switch v.(type) {
case string:
typeFlag = 1
case *object, *arrayBuffer: //TODO symbol maybe?
typeFlag = 2
default:
log.Fatalf("unknown type: %T", v)
// TODO function
}
b.setUint32(addr+4, uint32(nanHead|typeFlag))
b.setUint32(addr, uint32(ref))
}
type object struct {
name string // TODO for debugging
props map[string]interface{}
}
func typedArray(name string) *object {
return &object{
name: name,
props: map[string]interface{}{},
}
}
type arrayBuffer struct {
data []byte
} }

View File

@ -28,7 +28,6 @@ extern void clearTimeoutEvent(void *context, int32_t a);
import "C" import "C"
import ( import (
"crypto/rand" "crypto/rand"
"encoding/binary"
"fmt" "fmt"
"log" "log"
"time" "time"
@ -46,13 +45,12 @@ func debug(ctx unsafe.Pointer, sp int32) {
func wexit(ctx unsafe.Pointer, sp int32) { func wexit(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx) b := getBridge(ctx)
b.vmExit = true b.vmExit = true
mem := b.mem() b.exitCode = int(b.getUint32(sp + 8))
b.exitCode = int(binary.LittleEndian.Uint32(mem[sp+8:]))
} }
//export wwrite //export wwrite
func wwrite(ctx unsafe.Pointer, sp int32) { func wwrite(ctx unsafe.Pointer, sp int32) {
fmt.Println("wasm write", sp) log.Fatal("wasm write", sp)
} }
//export nanotime //export nanotime
@ -63,97 +61,114 @@ func nanotime(ctx unsafe.Pointer, sp int32) {
//export walltime //export walltime
func walltime(ctx unsafe.Pointer, sp int32) { func walltime(ctx unsafe.Pointer, sp int32) {
fmt.Println("wall time") log.Fatal("wall time")
} }
//export scheduleCallback //export scheduleCallback
func scheduleCallback(ctx unsafe.Pointer, sp int32) { func scheduleCallback(ctx unsafe.Pointer, sp int32) {
fmt.Println("schedule callback") log.Fatal("schedule callback")
} }
//export clearScheduledCallback //export clearScheduledCallback
func clearScheduledCallback(ctx unsafe.Pointer, sp int32) { func clearScheduledCallback(ctx unsafe.Pointer, sp int32) {
fmt.Println("clear scheduled callback") log.Fatal("clear scheduled callback")
} }
//export getRandomData //export getRandomData
func getRandomData(ctx unsafe.Pointer, sp int32) { func getRandomData(ctx unsafe.Pointer, sp int32) {
s := getBridge(ctx).loadSlice(sp + 8) s := getBridge(ctx).loadSlice(sp + 8)
_, err := rand.Read(s) _, err := rand.Read(s)
// TODO how to pass error?
if err != nil { if err != nil {
fmt.Println("failed: getRandomData", err) log.Fatal("failed: getRandomData", err)
} }
} }
//export stringVal //export stringVal
func stringVal(ctx unsafe.Pointer, sp int32) { func stringVal(ctx unsafe.Pointer, sp int32) {
fmt.Println("stringVal") log.Fatal("stringVal")
} }
//export valueGet //export valueGet
func valueGet(ctx unsafe.Pointer, sp int32) { func valueGet(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx) b := getBridge(ctx)
str := b.loadString(sp + 16) str := b.loadString(sp + 16)
val := b.loadValue(sp + 8) id, val := b.loadValue(sp + 8)
fmt.Println("valueGet", str, val) sp = b.getSP()
obj, ok := val.(*object)
if !ok {
fmt.Println("valueGet", str, id)
b.storeValue(sp+32, val)
return
}
res, ok := obj.props[str]
if !ok {
// TODO
log.Fatal("missing property", val, str)
}
fmt.Println("valueGet", str, id, obj.name)
b.storeValue(sp+32, res)
} }
//export valueSet //export valueSet
func valueSet(ctx unsafe.Pointer, sp int32) { func valueSet(ctx unsafe.Pointer, sp int32) {
str := getBridge(ctx).loadString(sp + 16) str := getBridge(ctx).loadString(sp + 16)
fmt.Println("valueSet", str) log.Fatal("valueSet", str)
} }
//export valueIndex //export valueIndex
func valueIndex(ctx unsafe.Pointer, sp int32) { func valueIndex(ctx unsafe.Pointer, sp int32) {
fmt.Println("valueIndex") log.Fatal("valueIndex")
} }
//export valueSetIndex //export valueSetIndex
func valueSetIndex(ctx unsafe.Pointer, sp int32) { func valueSetIndex(ctx unsafe.Pointer, sp int32) {
fmt.Println("valueSetIndex") log.Fatal("valueSetIndex")
} }
//export valueCall //export valueCall
func valueCall(ctx unsafe.Pointer, sp int32) { func valueCall(ctx unsafe.Pointer, sp int32) {
str := getBridge(ctx).loadString(sp + 16) str := getBridge(ctx).loadString(sp + 16)
fmt.Println("valueCall", str) log.Fatal("valueCall", str)
} }
//export valueInvoke //export valueInvoke
func valueInvoke(ctx unsafe.Pointer, sp int32) { func valueInvoke(ctx unsafe.Pointer, sp int32) {
fmt.Println("valueInvoke") log.Fatal("valueInvoke")
} }
//export valueNew //export valueNew
func valueNew(ctx unsafe.Pointer, sp int32) { func valueNew(ctx unsafe.Pointer, sp int32) {
str := getBridge(ctx).loadString(sp + 16) b := getBridge(ctx)
fmt.Println("valueNew", str) id, val := b.loadValue(sp + 8)
args := b.loadSliceOfValues(sp + 16)
log.Fatal("valueNew ", id, val, args)
} }
//export valueLength //export valueLength
func valueLength(ctx unsafe.Pointer, sp int32) { func valueLength(ctx unsafe.Pointer, sp int32) {
fmt.Println("valueLength") log.Fatal("valueLength")
} }
//export valuePrepareString //export valuePrepareString
func valuePrepareString(ctx unsafe.Pointer, sp int32) { func valuePrepareString(ctx unsafe.Pointer, sp int32) {
fmt.Println("valuePrepareString") log.Fatal("valuePrepareString")
} }
//export valueLoadString //export valueLoadString
func valueLoadString(ctx unsafe.Pointer, sp int32) { func valueLoadString(ctx unsafe.Pointer, sp int32) {
fmt.Println("valueLoadString") log.Fatal("valueLoadString")
} }
//export scheduleTimeoutEvent //export scheduleTimeoutEvent
func scheduleTimeoutEvent(ctx unsafe.Pointer, sp int32) { func scheduleTimeoutEvent(ctx unsafe.Pointer, sp int32) {
fmt.Println("scheduleTimeoutEvent") log.Fatal("scheduleTimeoutEvent")
} }
//export clearTimeoutEvent //export clearTimeoutEvent
func clearTimeoutEvent(ctx unsafe.Pointer, sp int32) { func clearTimeoutEvent(ctx unsafe.Pointer, sp int32) {
fmt.Println("clearTimeoutEvent") log.Fatal("clearTimeoutEvent")
} }
// addImports adds go Bridge imports in "go" namespace. // addImports adds go Bridge imports in "go" namespace.

View File

@ -7,8 +7,7 @@ import (
) )
func main() { func main() {
b := wasmgo.Bridge{} b, err := wasmgo.BridgeFromFile("test", "./simple/prog/main.wasm", nil)
err := b.InitWASM("test", "./simple/prog/main.wasm", nil)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }