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

178
bridge.go
View File

@ -3,8 +3,8 @@ package wasm
import (
"encoding/binary"
"fmt"
"log"
"math"
"reflect"
"sync"
"syscall"
"unsafe"
@ -76,6 +76,13 @@ func BridgeFromFile(name, file string, imports *wasmer.Imports) (*Bridge, error)
}
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{}{
math.NaN(),
float64(0),
@ -84,8 +91,8 @@ func (b *Bridge) addValues() {
false,
&object{
props: map[string]interface{}{
"Object": &object{name: "Object", props: map[string]interface{}{}},
"Array": &object{name: "Array", props: map[string]interface{}{}},
"Object": propObject("Object", nil),
"Array": propObject("Array", nil),
"Int8Array": typedArray("Int8Array"),
"Int16Array": typedArray("Int16Array"),
"Int32Array": typedArray("Int32Array"),
@ -94,49 +101,67 @@ func (b *Bridge) addValues() {
"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{}{
"process": propObject("process", nil),
"fs": propObject("fs", map[string]interface{}{
"constants": propObject("constants", 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,
}},
}},
}),
"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
&object{
name: "mem",
props: map[string]interface{}{
"buffer": &arrayBuffer{data: b.mem()},
},
},
&object{name: "jsGo", props: map[string]interface{}{}}, // jsGo
propObject("mem", map[string]interface{}{
"buffer": &buffer{data: b.mem()}},
),
goObj, // jsGo
}
}
// Run start the wasm instance.
func (b *Bridge) Run() error {
func (b *Bridge) Run(init chan bool, done chan error) {
defer b.instance.Close()
run := b.instance.Exports["run"]
resume := b.instance.Exports["resume"]
_, err := run(0, 0)
if err != nil {
return err
init <- false
done <- err
return
}
init <- true
// use channel from wasm exit
for !b.vmExit {
_, err = resume()
if err != nil {
return err
}
}
fmt.Printf("WASM exited with code: %v\n", b.exitCode)
return nil
done <- nil
}
func (b *Bridge) mem() []byte {
@ -147,22 +172,37 @@ func (b *Bridge) getSP() int32 {
spFunc := b.instance.Exports["getsp"]
val, err := spFunc()
if err != nil {
log.Fatal("failed to get sp", err)
panic("failed to get sp")
}
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) {
mem := b.mem()
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 {
mem := b.mem()
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) {
mem := b.mem()
binary.LittleEndian.PutUint32(mem[offset:], v)
@ -209,25 +249,23 @@ func (b *Bridge) loadSliceOfValues(addr int32) []interface{} {
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))
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{}) {
func (b *Bridge) loadValue(addr int32) interface{} {
f := b.getFloat64(addr)
if f == 0 {
return 0, undefined
return undefined
}
if !math.IsNaN(f) {
return 0, f
return f
}
id := b.getUint32(addr)
return id, b.values[id]
return b.values[b.getUint32(addr)]
}
func (b *Bridge) storeValue(addr int32, v interface{}) {
@ -284,13 +322,17 @@ func (b *Bridge) storeValue(addr int32, v interface{}) {
}
typeFlag := 0
switch v.(type) {
case string:
rv := reflect.TypeOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
switch rv.Kind() {
case reflect.String:
typeFlag = 1
case *object, *arrayBuffer: //TODO symbol maybe?
case reflect.Struct, reflect.Slice: //TODO symbol maybe?
typeFlag = 2
default:
log.Fatalf("unknown type: %T", v)
panic(fmt.Sprintf("unknown type: %T", v))
// TODO function
}
b.setUint32(addr+4, uint32(nanHead|typeFlag))
@ -300,15 +342,73 @@ func (b *Bridge) storeValue(addr int32, v interface{}) {
type object struct {
name string // TODO for debugging
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 {
return &object{
name: name,
props: map[string]interface{}{},
name: name,
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
}
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 (
"crypto/rand"
"fmt"
"log"
"reflect"
"syscall"
"time"
"unsafe"
@ -38,7 +39,7 @@ import (
//export debug
func debug(ctx unsafe.Pointer, sp int32) {
log.Println(sp)
fmt.Println(sp)
}
//export wexit
@ -50,7 +51,12 @@ func wexit(ctx unsafe.Pointer, sp int32) {
//export wwrite
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
@ -61,17 +67,23 @@ func nanotime(ctx unsafe.Pointer, sp int32) {
//export walltime
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
func scheduleCallback(ctx unsafe.Pointer, sp int32) {
log.Fatal("schedule callback")
panic("schedule callback")
}
//export clearScheduledCallback
func clearScheduledCallback(ctx unsafe.Pointer, sp int32) {
log.Fatal("clear scheduled callback")
panic("clear scheduled callback")
}
//export getRandomData
@ -80,24 +92,23 @@ func getRandomData(ctx unsafe.Pointer, sp int32) {
_, err := rand.Read(s)
// TODO how to pass error?
if err != nil {
log.Fatal("failed: getRandomData", err)
panic("failed: getRandomData")
}
}
//export stringVal
func stringVal(ctx unsafe.Pointer, sp int32) {
log.Fatal("stringVal")
panic("stringVal")
}
//export valueGet
func valueGet(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx)
str := b.loadString(sp + 16)
id, val := b.loadValue(sp + 8)
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
}
@ -105,70 +116,123 @@ func valueGet(ctx unsafe.Pointer, sp int32) {
res, ok := obj.props[str]
if !ok {
// 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)
}
//export valueSet
func valueSet(ctx unsafe.Pointer, sp int32) {
str := getBridge(ctx).loadString(sp + 16)
log.Fatal("valueSet", str)
b := getBridge(ctx)
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
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
func valueSetIndex(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueSetIndex")
panic("valueSetIndex")
}
//export valueCall
func valueCall(ctx unsafe.Pointer, sp int32) {
str := getBridge(ctx).loadString(sp + 16)
log.Fatal("valueCall", str)
b := getBridge(ctx)
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
func valueInvoke(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueInvoke")
panic("valueInvoke")
}
//export valueNew
func valueNew(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx)
id, val := b.loadValue(sp + 8)
val := b.loadValue(sp + 8)
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
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
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
func valueLoadString(ctx unsafe.Pointer, sp int32) {
log.Fatal("valueLoadString")
panic("valueLoadString")
}
//export scheduleTimeoutEvent
func scheduleTimeoutEvent(ctx unsafe.Pointer, sp int32) {
log.Fatal("scheduleTimeoutEvent")
panic("scheduleTimeoutEvent")
}
//export clearTimeoutEvent
func clearTimeoutEvent(ctx unsafe.Pointer, sp int32) {
log.Fatal("clearTimeoutEvent")
panic("clearTimeoutEvent")
}
// addImports adds go Bridge imports in "go" namespace.

View File

@ -1,6 +1,7 @@
package main
import (
"fmt"
"log"
wasmgo "github.com/vedhavyas/wasm"
@ -12,7 +13,11 @@ func main() {
log.Fatal(err)
}
if err := b.Run(); err != nil {
log.Fatal(err)
}
init, done := make(chan bool), make(chan error)
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"
)
// TODO: log seems to cause an issue
func printWasm(this js.Value, v []js.Value) interface{} {
fmt.Println("Hello from WASM", v)
return nil
}
func main() {
c := make(chan struct{}, 0)
fmt.Println("WASM Go Initialized")
ch := make(chan bool)
//fmt.Println("WASM Go Initialized")
// register functions
js.Global().Set("printWasm", js.FuncOf(printWasm))
fmt.Println("Done...")
<-c
fun := js.FuncOf(printWasm)
js.Global().Set("printWasm", fun)
//fmt.Println("Done...")
<-ch
}

Binary file not shown.