From 771a2d95cf0f69b201c9442e468c6a58a423a775 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 15 Aug 2019 20:12:20 +0200 Subject: [PATCH] add store value --- bridge.go | 210 +++++++++++++++++++++++++++++++++++++----- imports.go | 61 +++++++----- simple/caller/main.go | 3 +- 3 files changed, 225 insertions(+), 49 deletions(-) diff --git a/bridge.go b/bridge.go index 011b403..16e86d7 100644 --- a/bridge.go +++ b/bridge.go @@ -3,8 +3,10 @@ package wasm import ( "encoding/binary" "fmt" + "log" "math" "sync" + "syscall" "unsafe" "github.com/wasmerio/go-ext-wasm/wasmer" @@ -13,11 +15,9 @@ import ( var undefined = &struct{}{} var bridges = map[string]*Bridge{} var mu sync.RWMutex // to protect bridges +type context struct{ n string } -type context struct { - n string -} - +// TODO ensure it wont override the another context with same name. func setBridge(b *Bridge) unsafe.Pointer { mu.Lock() defer mu.Unlock() @@ -38,9 +38,12 @@ type Bridge struct { instance wasmer.Instance vmExit bool 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 { imports = wasmer.NewImports() } @@ -48,26 +51,70 @@ func (b *Bridge) InitWASMBytes(name string, bytes []byte, imports *wasmer.Import b.name = name err := b.addImports(imports) if err != nil { - return err + return nil, err } inst, err := wasmer.NewInstanceWithImports(bytes, imports) if err != nil { - return err + return nil, err } b.instance = inst 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) 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. @@ -96,57 +143,172 @@ func (b *Bridge) mem() []byte { 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() 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() binary.LittleEndian.PutUint64(mem[offset:], v) } -func (b Bridge) getUnit64(offset int32) uint64 { +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) { +func (b *Bridge) setFloat64(offset int32, v float64) { uf := math.Float64bits(v) b.setUint64(offset, uf) } -func (b Bridge) getFloat64(offset int32) float64 { +func (b *Bridge) getFloat64(offset int32) float64 { uf := b.getUnit64(offset) 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:]) } -func (b Bridge) loadSlice(addr int32) []byte { +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 { +func (b *Bridge) loadString(addr int32) string { d := b.loadSlice(addr) 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) if f == 0 { - return undefined + return 0, undefined } if !math.IsNaN(f) { - return f + return 0, f } - // return value instead of uint32 - return b.getUint32(addr) + id := 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 } diff --git a/imports.go b/imports.go index a6143ae..e12a2df 100644 --- a/imports.go +++ b/imports.go @@ -28,7 +28,6 @@ extern void clearTimeoutEvent(void *context, int32_t a); import "C" import ( "crypto/rand" - "encoding/binary" "fmt" "log" "time" @@ -46,13 +45,12 @@ func debug(ctx unsafe.Pointer, sp int32) { func wexit(ctx unsafe.Pointer, sp int32) { b := getBridge(ctx) b.vmExit = true - mem := b.mem() - b.exitCode = int(binary.LittleEndian.Uint32(mem[sp+8:])) + b.exitCode = int(b.getUint32(sp + 8)) } //export wwrite func wwrite(ctx unsafe.Pointer, sp int32) { - fmt.Println("wasm write", sp) + log.Fatal("wasm write", sp) } //export nanotime @@ -63,97 +61,114 @@ func nanotime(ctx unsafe.Pointer, sp int32) { //export walltime func walltime(ctx unsafe.Pointer, sp int32) { - fmt.Println("wall time") + log.Fatal("wall time") } //export scheduleCallback func scheduleCallback(ctx unsafe.Pointer, sp int32) { - fmt.Println("schedule callback") + log.Fatal("schedule callback") } //export clearScheduledCallback func clearScheduledCallback(ctx unsafe.Pointer, sp int32) { - fmt.Println("clear scheduled callback") + log.Fatal("clear scheduled callback") } //export getRandomData func getRandomData(ctx unsafe.Pointer, sp int32) { s := getBridge(ctx).loadSlice(sp + 8) _, err := rand.Read(s) + // TODO how to pass error? if err != nil { - fmt.Println("failed: getRandomData", err) + log.Fatal("failed: getRandomData", err) } } //export stringVal func stringVal(ctx unsafe.Pointer, sp int32) { - fmt.Println("stringVal") + log.Fatal("stringVal") } //export valueGet func valueGet(ctx unsafe.Pointer, sp int32) { b := getBridge(ctx) str := b.loadString(sp + 16) - val := b.loadValue(sp + 8) - fmt.Println("valueGet", str, val) + id, val := b.loadValue(sp + 8) + 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 func valueSet(ctx unsafe.Pointer, sp int32) { str := getBridge(ctx).loadString(sp + 16) - fmt.Println("valueSet", str) + log.Fatal("valueSet", str) } //export valueIndex func valueIndex(ctx unsafe.Pointer, sp int32) { - fmt.Println("valueIndex") + log.Fatal("valueIndex") } //export valueSetIndex func valueSetIndex(ctx unsafe.Pointer, sp int32) { - fmt.Println("valueSetIndex") + log.Fatal("valueSetIndex") } //export valueCall func valueCall(ctx unsafe.Pointer, sp int32) { str := getBridge(ctx).loadString(sp + 16) - fmt.Println("valueCall", str) + log.Fatal("valueCall", str) } //export valueInvoke func valueInvoke(ctx unsafe.Pointer, sp int32) { - fmt.Println("valueInvoke") + log.Fatal("valueInvoke") } //export valueNew func valueNew(ctx unsafe.Pointer, sp int32) { - str := getBridge(ctx).loadString(sp + 16) - fmt.Println("valueNew", str) + b := getBridge(ctx) + id, val := b.loadValue(sp + 8) + args := b.loadSliceOfValues(sp + 16) + log.Fatal("valueNew ", id, val, args) } //export valueLength func valueLength(ctx unsafe.Pointer, sp int32) { - fmt.Println("valueLength") + log.Fatal("valueLength") } //export valuePrepareString func valuePrepareString(ctx unsafe.Pointer, sp int32) { - fmt.Println("valuePrepareString") + log.Fatal("valuePrepareString") } //export valueLoadString func valueLoadString(ctx unsafe.Pointer, sp int32) { - fmt.Println("valueLoadString") + log.Fatal("valueLoadString") } //export scheduleTimeoutEvent func scheduleTimeoutEvent(ctx unsafe.Pointer, sp int32) { - fmt.Println("scheduleTimeoutEvent") + log.Fatal("scheduleTimeoutEvent") } //export clearTimeoutEvent func clearTimeoutEvent(ctx unsafe.Pointer, sp int32) { - fmt.Println("clearTimeoutEvent") + log.Fatal("clearTimeoutEvent") } // addImports adds go Bridge imports in "go" namespace. diff --git a/simple/caller/main.go b/simple/caller/main.go index 61070aa..621d786 100644 --- a/simple/caller/main.go +++ b/simple/caller/main.go @@ -7,8 +7,7 @@ import ( ) func main() { - b := wasmgo.Bridge{} - err := b.InitWASM("test", "./simple/prog/main.wasm", nil) + b, err := wasmgo.BridgeFromFile("test", "./simple/prog/main.wasm", nil) if err != nil { log.Fatal(err) }