add functions

pull/2/head
vedhavyas 2019-08-19 21:13:53 -04:00
parent 771a2d95cf
commit fbfa1a0c93
No known key found for this signature in database
GPG Key ID: 317BF0923E3EB7E5
5 changed files with 244 additions and 73 deletions

176
bridge.go
View File

@ -3,8 +3,8 @@ package wasm
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"log"
"math" "math"
"reflect"
"sync" "sync"
"syscall" "syscall"
"unsafe" "unsafe"
@ -76,6 +76,13 @@ func BridgeFromFile(name, file string, imports *wasmer.Imports) (*Bridge, error)
} }
func (b *Bridge) addValues() { func (b *Bridge) addValues() {
var goObj *object
goObj = propObject("jsGo", map[string]interface{}{
"_makeFuncWrapper": wasmFunc(func(args []interface{}) (interface{}, error) {
return &funcWrapper{id: args[0]}, nil
}),
"_pendingEvent": nil,
})
b.values = []interface{}{ b.values = []interface{}{
math.NaN(), math.NaN(),
float64(0), float64(0),
@ -84,8 +91,8 @@ func (b *Bridge) addValues() {
false, false,
&object{ &object{
props: map[string]interface{}{ props: map[string]interface{}{
"Object": &object{name: "Object", props: map[string]interface{}{}}, "Object": propObject("Object", nil),
"Array": &object{name: "Array", props: map[string]interface{}{}}, "Array": propObject("Array", nil),
"Int8Array": typedArray("Int8Array"), "Int8Array": typedArray("Int8Array"),
"Int16Array": typedArray("Int16Array"), "Int16Array": typedArray("Int16Array"),
"Int32Array": typedArray("Int32Array"), "Int32Array": typedArray("Int32Array"),
@ -94,49 +101,67 @@ func (b *Bridge) addValues() {
"Uint32Array": typedArray("Uint32Array"), "Uint32Array": typedArray("Uint32Array"),
"Float32Array": typedArray("Float32Array"), "Float32Array": typedArray("Float32Array"),
"Float64Array": typedArray("Float64Array"), "Float64Array": typedArray("Float64Array"),
"process": &object{name: "process", props: map[string]interface{}{}}, "process": propObject("process", nil),
"fs": &object{name: "fs", props: map[string]interface{}{ "fs": propObject("fs", map[string]interface{}{
"constants": &object{name: "constants", props: map[string]interface{}{ "constants": propObject("constants", map[string]interface{}{
"O_WRONLY": syscall.O_WRONLY, "O_WRONLY": syscall.O_WRONLY,
"O_RDWR": syscall.O_RDWR, "O_RDWR": syscall.O_RDWR,
"O_CREAT": syscall.O_CREAT, "O_CREAT": syscall.O_CREAT,
"O_TRUNC": syscall.O_TRUNC, "O_TRUNC": syscall.O_TRUNC,
"O_APPEND": syscall.O_APPEND, "O_APPEND": syscall.O_APPEND,
"O_EXCL": syscall.O_EXCL, "O_EXCL": syscall.O_EXCL,
}}, }),
}},
"write": wasmFunc(func(args []interface{}) (interface{}, error) {
fd := int(args[0].(float64))
offset := int(args[2].(float64))
length := int(args[3].(float64))
buf := args[1].(*array).data()[offset : offset+length]
pos := args[4]
callback := args[5].(*funcWrapper)
var err error
var n int
if pos != nil {
position := int64(pos.(float64))
n, err = syscall.Pwrite(fd, buf, position)
} else {
n, err = syscall.Write(fd, buf)
}
if err != nil {
return nil, err
}
return b.makeFuncWrapper(callback.id, goObj, &[]interface{}{nil, n})
}),
}),
}, },
}, //global }, //global
&object{ propObject("mem", map[string]interface{}{
name: "mem", "buffer": &buffer{data: b.mem()}},
props: map[string]interface{}{ ),
"buffer": &arrayBuffer{data: b.mem()}, goObj, // jsGo
},
},
&object{name: "jsGo", props: map[string]interface{}{}}, // jsGo
} }
} }
// Run start the wasm instance. // Run start the wasm instance.
func (b *Bridge) Run() error { func (b *Bridge) Run(init chan bool, done chan error) {
defer b.instance.Close() defer b.instance.Close()
run := b.instance.Exports["run"] run := b.instance.Exports["run"]
resume := b.instance.Exports["resume"]
_, err := run(0, 0) _, err := run(0, 0)
if err != nil { if err != nil {
return err init <- false
done <- err
return
} }
init <- true
// use channel from wasm exit
for !b.vmExit { for !b.vmExit {
_, err = resume()
if err != nil {
return err
} }
}
fmt.Printf("WASM exited with code: %v\n", b.exitCode) fmt.Printf("WASM exited with code: %v\n", b.exitCode)
return nil done <- nil
} }
func (b *Bridge) mem() []byte { func (b *Bridge) mem() []byte {
@ -147,22 +172,37 @@ func (b *Bridge) getSP() int32 {
spFunc := b.instance.Exports["getsp"] spFunc := b.instance.Exports["getsp"]
val, err := spFunc() val, err := spFunc()
if err != nil { if err != nil {
log.Fatal("failed to get sp", err) panic("failed to get sp")
} }
return val.ToI32() return val.ToI32()
} }
func (b *Bridge) setUint8(offset int32, v uint8) {
mem := b.mem()
mem[offset] = byte(v)
}
func (b *Bridge) setInt64(offset int32, v int64) { 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) setInt32(offset int32, v int32) {
mem := b.mem()
binary.LittleEndian.PutUint32(mem[offset:], uint32(v))
}
func (b *Bridge) getInt64(offset int32) int64 { func (b *Bridge) getInt64(offset int32) int64 {
mem := b.mem() mem := b.mem()
return int64(binary.LittleEndian.Uint64(mem[offset:])) return int64(binary.LittleEndian.Uint64(mem[offset:]))
} }
func (b *Bridge) getInt32(offset int32) int32 {
mem := b.mem()
return int32(binary.LittleEndian.Uint32(mem[offset:]))
}
func (b *Bridge) setUint32(offset int32, v uint32) { func (b *Bridge) setUint32(offset int32, v uint32) {
mem := b.mem() mem := b.mem()
binary.LittleEndian.PutUint32(mem[offset:], v) binary.LittleEndian.PutUint32(mem[offset:], v)
@ -209,25 +249,23 @@ func (b *Bridge) loadSliceOfValues(addr int32) []interface{} {
arrLen := int(b.getInt64(addr + 8)) arrLen := int(b.getInt64(addr + 8))
vals := make([]interface{}, arrLen, arrLen) vals := make([]interface{}, arrLen, arrLen)
for i := 0; i < int(arrLen); i++ { for i := 0; i < int(arrLen); i++ {
_, vals[i] = b.loadValue(int32(arr + i*8)) vals[i] = b.loadValue(int32(arr + i*8))
} }
return vals return vals
} }
// TODO remove id once debugging is done func (b *Bridge) loadValue(addr int32) interface{} {
func (b *Bridge) loadValue(addr int32) (uint32, interface{}) {
f := b.getFloat64(addr) f := b.getFloat64(addr)
if f == 0 { if f == 0 {
return 0, undefined return undefined
} }
if !math.IsNaN(f) { if !math.IsNaN(f) {
return 0, f return f
} }
id := b.getUint32(addr) return b.values[b.getUint32(addr)]
return id, b.values[id]
} }
func (b *Bridge) storeValue(addr int32, v interface{}) { func (b *Bridge) storeValue(addr int32, v interface{}) {
@ -284,13 +322,17 @@ func (b *Bridge) storeValue(addr int32, v interface{}) {
} }
typeFlag := 0 typeFlag := 0
switch v.(type) { rv := reflect.TypeOf(v)
case string: if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
switch rv.Kind() {
case reflect.String:
typeFlag = 1 typeFlag = 1
case *object, *arrayBuffer: //TODO symbol maybe? case reflect.Struct, reflect.Slice: //TODO symbol maybe?
typeFlag = 2 typeFlag = 2
default: default:
log.Fatalf("unknown type: %T", v) panic(fmt.Sprintf("unknown type: %T", v))
// TODO function // TODO function
} }
b.setUint32(addr+4, uint32(nanHead|typeFlag)) b.setUint32(addr+4, uint32(nanHead|typeFlag))
@ -300,15 +342,73 @@ func (b *Bridge) storeValue(addr int32, v interface{}) {
type object struct { type object struct {
name string // TODO for debugging name string // TODO for debugging
props map[string]interface{} props map[string]interface{}
new func(args []interface{}) interface{}
} }
func propObject(name string, prop map[string]interface{}) *object {
return &object{name: name, props: prop}
}
type array struct {
buf *buffer
offset int
length int
}
func (a *array) data() []byte {
return a.buf.data[a.offset : a.offset+a.length]
}
func typedArray(name string) *object { func typedArray(name string) *object {
return &object{ return &object{
name: name, name: name,
props: map[string]interface{}{}, new: func(args []interface{}) interface{} {
return &array{
buf: args[0].(*buffer),
offset: int(args[1].(float64)),
length: int(args[2].(float64)),
}
},
} }
} }
type arrayBuffer struct { type buffer struct {
data []byte data []byte
} }
type wasmFunc func(args []interface{}) (interface{}, error)
func (b *Bridge) resume() error {
res := b.instance.Exports["resume"]
_, err := res()
return err
}
type funcWrapper struct {
id interface{}
}
func (b *Bridge) makeFuncWrapper(id, this interface{}, args *[]interface{}) (interface{}, error) {
goObj := b.values[7].(*object)
event := propObject("_pendingEvent", map[string]interface{}{
"id": id,
"this": nil,
"args": args,
})
goObj.props["_pendingEvent"] = event
err := b.resume()
if err != nil {
return nil, err
}
return event.props["result"], nil
}
func (b *Bridge) CallFunc(fn string, args *[]interface{}) (interface{}, error) {
fw, ok := b.values[5].(*object).props[fn]
if !ok {
return nil, fmt.Errorf("missing function: %v", fn)
}
return b.makeFuncWrapper(fw.(*funcWrapper).id, b.values[7], args)
}

View File

@ -29,7 +29,8 @@ import "C"
import ( import (
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"log" "reflect"
"syscall"
"time" "time"
"unsafe" "unsafe"
@ -38,7 +39,7 @@ import (
//export debug //export debug
func debug(ctx unsafe.Pointer, sp int32) { func debug(ctx unsafe.Pointer, sp int32) {
log.Println(sp) fmt.Println(sp)
} }
//export wexit //export wexit
@ -50,7 +51,12 @@ func wexit(ctx unsafe.Pointer, sp int32) {
//export wwrite //export wwrite
func wwrite(ctx unsafe.Pointer, sp int32) { func wwrite(ctx unsafe.Pointer, sp int32) {
log.Fatal("wasm write", sp) b := getBridge(ctx)
fd := int(b.getInt64(sp + 8))
p := int(b.getInt64(sp + 16))
l := int(b.getInt32(sp + 24))
syscall.Write(fd, b.mem()[p:p+l])
fmt.Println("wasm write", fd, p, l)
} }
//export nanotime //export nanotime
@ -61,17 +67,23 @@ 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) {
log.Fatal("wall time") b := getBridge(ctx)
t := time.Now().Unix()
sec := t / 1000
nanos := (t % 1000) * 1000000
b.setInt64(sp+8, sec)
b.setInt32(sp+16, int32(nanos))
} }
//export scheduleCallback //export scheduleCallback
func scheduleCallback(ctx unsafe.Pointer, sp int32) { func scheduleCallback(ctx unsafe.Pointer, sp int32) {
log.Fatal("schedule callback") panic("schedule callback")
} }
//export clearScheduledCallback //export clearScheduledCallback
func clearScheduledCallback(ctx unsafe.Pointer, sp int32) { func clearScheduledCallback(ctx unsafe.Pointer, sp int32) {
log.Fatal("clear scheduled callback") panic("clear scheduled callback")
} }
//export getRandomData //export getRandomData
@ -80,24 +92,23 @@ func getRandomData(ctx unsafe.Pointer, sp int32) {
_, err := rand.Read(s) _, err := rand.Read(s)
// TODO how to pass error? // TODO how to pass error?
if err != nil { if err != nil {
log.Fatal("failed: getRandomData", err) panic("failed: getRandomData")
} }
} }
//export stringVal //export stringVal
func stringVal(ctx unsafe.Pointer, sp int32) { func stringVal(ctx unsafe.Pointer, sp int32) {
log.Fatal("stringVal") panic("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)
id, val := b.loadValue(sp + 8) val := b.loadValue(sp + 8)
sp = b.getSP() sp = b.getSP()
obj, ok := val.(*object) obj, ok := val.(*object)
if !ok { if !ok {
fmt.Println("valueGet", str, id)
b.storeValue(sp+32, val) b.storeValue(sp+32, val)
return return
} }
@ -105,70 +116,123 @@ func valueGet(ctx unsafe.Pointer, sp int32) {
res, ok := obj.props[str] res, ok := obj.props[str]
if !ok { if !ok {
// TODO // TODO
log.Fatal("missing property", val, str) panic(fmt.Sprintln("missing property", str, val))
} }
fmt.Println("valueGet", str, id, obj.name) fmt.Println("valueGet", str, obj.name)
b.storeValue(sp+32, res) 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) b := getBridge(ctx)
log.Fatal("valueSet", str) val := b.loadValue(sp + 8)
obj := val.(*object)
prop := b.loadString(sp + 16)
propVal := b.loadValue(sp + 32)
obj.props[prop] = propVal
fmt.Println("valueSet", obj, prop, propVal)
} }
//export valueIndex //export valueIndex
func valueIndex(ctx unsafe.Pointer, sp int32) { func valueIndex(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueIndex") b := getBridge(ctx)
l := b.loadValue(sp + 8)
i := b.getInt64(sp + 16)
rv := reflect.ValueOf(l)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
iv := rv.Index(int(i))
b.storeValue(sp+24, iv.Interface())
fmt.Println("valueIndex:", iv)
} }
//export valueSetIndex //export valueSetIndex
func valueSetIndex(ctx unsafe.Pointer, sp int32) { func valueSetIndex(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueSetIndex") panic("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) b := getBridge(ctx)
log.Fatal("valueCall", str) v := b.loadValue(sp + 8)
str := b.loadString(sp + 16)
args := b.loadSliceOfValues(sp + 32)
fmt.Println("valueCall: ", v.(*object).name, str, args)
f, ok := v.(*object).props[str].(wasmFunc)
if !ok {
panic(fmt.Sprintln("valueCall: prop not found in ", v, str))
}
sp = b.getSP()
res, err := f(args)
if err != nil {
b.storeValue(sp+56, err.Error())
b.setUint8(sp+64, 0)
return
}
b.storeValue(sp+56, res)
b.setUint8(sp+64, 1)
} }
//export valueInvoke //export valueInvoke
func valueInvoke(ctx unsafe.Pointer, sp int32) { func valueInvoke(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueInvoke") panic("valueInvoke")
} }
//export valueNew //export valueNew
func valueNew(ctx unsafe.Pointer, sp int32) { func valueNew(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx) b := getBridge(ctx)
id, val := b.loadValue(sp + 8) val := b.loadValue(sp + 8)
args := b.loadSliceOfValues(sp + 16) args := b.loadSliceOfValues(sp + 16)
log.Fatal("valueNew ", id, val, args) res := val.(*object).new(args)
sp = b.getSP()
b.storeValue(sp+40, res)
b.setUint8(sp+48, 1)
fmt.Println("valueNew ", val, args)
} }
//export valueLength //export valueLength
func valueLength(ctx unsafe.Pointer, sp int32) { func valueLength(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueLength") b := getBridge(ctx)
val := b.loadValue(sp + 8)
rv := reflect.ValueOf(val)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
b.setInt64(sp+16, int64(rv.Len()))
fmt.Println("valueLength:", rv.Len())
} }
//export valuePrepareString //export valuePrepareString
func valuePrepareString(ctx unsafe.Pointer, sp int32) { func valuePrepareString(ctx unsafe.Pointer, sp int32) {
log.Fatal("valuePrepareString") b := getBridge(ctx)
val := b.loadValue(sp + 8)
var str string
if val != nil {
str = val.(string)
}
b.storeValue(sp+16, str)
b.setInt64(sp+24, int64(len(str)))
fmt.Println("valuePrepareString", val, str)
} }
//export valueLoadString //export valueLoadString
func valueLoadString(ctx unsafe.Pointer, sp int32) { func valueLoadString(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueLoadString") panic("valueLoadString")
} }
//export scheduleTimeoutEvent //export scheduleTimeoutEvent
func scheduleTimeoutEvent(ctx unsafe.Pointer, sp int32) { func scheduleTimeoutEvent(ctx unsafe.Pointer, sp int32) {
log.Fatal("scheduleTimeoutEvent") panic("scheduleTimeoutEvent")
} }
//export clearTimeoutEvent //export clearTimeoutEvent
func clearTimeoutEvent(ctx unsafe.Pointer, sp int32) { func clearTimeoutEvent(ctx unsafe.Pointer, sp int32) {
log.Fatal("clearTimeoutEvent") panic("clearTimeoutEvent")
} }
// addImports adds go Bridge imports in "go" namespace. // addImports adds go Bridge imports in "go" namespace.

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"log" "log"
wasmgo "github.com/vedhavyas/wasm" wasmgo "github.com/vedhavyas/wasm"
@ -12,7 +13,11 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
if err := b.Run(); err != nil { init, done := make(chan bool), make(chan error)
log.Fatal(err) go b.Run(init, done)
} <-init
res, err := b.CallFunc("printWasm", &[]interface{}{"success call"})
fmt.Println(res, err)
err = <-done
fmt.Println("wasm exited", err)
} }

View File

@ -7,17 +7,19 @@ import (
"syscall/js" "syscall/js"
) )
// TODO: log seems to cause an issue
func printWasm(this js.Value, v []js.Value) interface{} { func printWasm(this js.Value, v []js.Value) interface{} {
fmt.Println("Hello from WASM", v) fmt.Println("Hello from WASM", v)
return nil return nil
} }
func main() { func main() {
c := make(chan struct{}, 0) ch := make(chan bool)
fmt.Println("WASM Go Initialized") //fmt.Println("WASM Go Initialized")
// register functions // register functions
js.Global().Set("printWasm", js.FuncOf(printWasm)) fun := js.FuncOf(printWasm)
fmt.Println("Done...") js.Global().Set("printWasm", fun)
<-c //fmt.Println("Done...")
<-ch
} }

Binary file not shown.