support go 1.13

pull/4/head
vedhavyas 2020-01-05 17:56:36 +01:00
parent a3327b9b07
commit 90853f742d
No known key found for this signature in database
GPG Key ID: 317BF0923E3EB7E5
7 changed files with 87 additions and 142 deletions

134
bridge.go
View File

@ -18,8 +18,6 @@ import (
"github.com/wasmerio/go-ext-wasm/wasmer" "github.com/wasmerio/go-ext-wasm/wasmer"
) )
const release_call = "_release_"
var ( var (
undefined = &struct{}{} undefined = &struct{}{}
bridges = map[string]*Bridge{} bridges = map[string]*Bridge{}
@ -60,30 +58,6 @@ type Bridge struct {
cancF context.CancelFunc cancF context.CancelFunc
} }
// releaseRef removes the ref from the refs.
// Returns the idx and true if remove was successful.
func (b *Bridge) releaseRef(v interface{}) (int, bool) {
idx, ok := b.refs[v]
if !ok {
return 0, false
}
delete(b.refs, v)
return idx, true
}
// releaseVal removes val from the valueMap
// Returns the value and true if remove was successful
func (b *Bridge) releaseVal(idx int) (interface{}, bool) {
v, ok := b.valueMap[idx]
if !ok {
return nil, false
}
delete(b.valueMap, idx)
return v, true
}
func BridgeFromBytes(name string, bytes []byte, imports *wasmer.Imports) (*Bridge, error) { func BridgeFromBytes(name string, bytes []byte, imports *wasmer.Imports) (*Bridge, error) {
b := new(Bridge) b := new(Bridge)
if imports == nil { if imports == nil {
@ -142,16 +116,9 @@ func (b *Bridge) addValues() {
"Object": &object{name: "Object", new: func(args []interface{}) interface{} { "Object": &object{name: "Object", new: func(args []interface{}) interface{} {
return &object{name: "ObjectInner", props: map[string]interface{}{}} return &object{name: "ObjectInner", props: map[string]interface{}{}}
}}, }},
"Array": propObject("Array", nil), "Array": propObject("Array", nil),
"Int8Array": typedArray, "Uint8Array": typedArray,
"Int16Array": typedArray, "process": propObject("process", nil),
"Int32Array": typedArray,
"Uint8Array": typedArray,
"Uint16Array": typedArray,
"Uint32Array": typedArray,
"Float32Array": typedArray,
"Float64Array": typedArray,
"process": propObject("process", nil),
"Date": &object{name: "Date", new: func(args []interface{}) interface{} { "Date": &object{name: "Date", new: func(args []interface{}) interface{} {
t := time.Now() t := time.Now()
return &object{name: "DateInner", props: map[string]interface{}{ return &object{name: "DateInner", props: map[string]interface{}{
@ -168,7 +135,7 @@ func (b *Bridge) addValues() {
"crypto": propObject("crypto", map[string]interface{}{ "crypto": propObject("crypto", map[string]interface{}{
"getRandomValues": Func(func(args []interface{}) (interface{}, error) { "getRandomValues": Func(func(args []interface{}) (interface{}, error) {
arr := args[0].(*array) arr := args[0].(*array)
return rand.Read(arr.data()) return rand.Read(arr.buf)
}), }),
}), }),
"AbortController": &object{name: "AbortController", new: func(args []interface{}) interface{} { "AbortController": &object{name: "AbortController", new: func(args []interface{}) interface{} {
@ -189,7 +156,7 @@ func (b *Bridge) addValues() {
return obj return obj
}}, }},
"fetch": Func(func(args []interface{}) (interface{}, error) { "fetch": Func(func(args []interface{}) (interface{}, error) {
// TODO(ved): implement fetch // Fixme(ved): implement fetch
log.Fatalln(args) log.Fatalln(args)
return nil, nil return nil, nil
}), }),
@ -207,7 +174,7 @@ func (b *Bridge) addValues() {
fd := int(args[0].(float64)) fd := int(args[0].(float64))
offset := int(args[2].(float64)) offset := int(args[2].(float64))
length := int(args[3].(float64)) length := int(args[3].(float64))
buf := args[1].(*array).data()[offset : offset+length] buf := args[1].(*array).buf[offset : offset+length]
pos := args[4] pos := args[4]
callback := args[5].(*funcWrapper) callback := args[5].(*funcWrapper)
var err error var err error
@ -228,10 +195,7 @@ func (b *Bridge) addValues() {
}), }),
}, },
}, //global }, //global
6: propObject("mem", map[string]interface{}{ 6: goObj, // jsGo
"buffer": &buffer{data: b.mem()}},
),
7: goObj, // jsGo
} }
} }
@ -421,35 +385,34 @@ func (b *Bridge) storeValue(addr int32, v interface{}) {
return return
} }
rv := reflect.TypeOf(v) rt := reflect.TypeOf(v)
if !rv.Comparable() { if rt.Kind() == reflect.Ptr {
panic(fmt.Sprintf("%T is not comparable", v)) rt = rt.Elem()
} }
if rv.Kind() == reflect.Ptr { rv := v
rv = rv.Elem() if !rt.Comparable() {
// since some types like Func cant be set as key, we will use their reflect value
// as key to insert for refs[key] so that we can avoid any duplicates
rv = reflect.ValueOf(v)
} }
ref, ok := b.refs[v] ref, ok := b.refs[rv]
if !ok { if !ok {
b.valuesMu.RLock() b.valuesMu.RLock()
b.valueMap[b.valueIDX] = v b.valueMap[b.valueIDX] = v
ref = b.valueIDX ref = b.valueIDX
b.refs[v] = ref b.refs[rv] = ref
b.valueIDX++ b.valueIDX++
b.valuesMu.RUnlock() b.valuesMu.RUnlock()
} }
typeFlag := 0 typeFlag := 0
switch rv.Kind() { switch rt.Kind() {
case reflect.String: case reflect.String:
typeFlag = 1 typeFlag = 1
case reflect.Struct, reflect.Slice:
typeFlag = 2
case reflect.Func: case reflect.Func:
typeFlag = 3 typeFlag = 3
default:
panic(fmt.Sprintf("unknown type: %T", v))
} }
b.setUint32(addr+4, uint32(nanHead|typeFlag)) b.setUint32(addr+4, uint32(nanHead|typeFlag))
b.setUint32(addr, uint32(ref)) b.setUint32(addr, uint32(ref))
@ -466,30 +429,20 @@ func propObject(name string, prop map[string]interface{}) *object {
} }
type array struct { type array struct {
buf *buffer buf []byte
offset int
length int
}
func (a *array) data() []byte {
return a.buf.data[a.offset : a.offset+a.length]
} }
var typedArray = &object{ var typedArray = &object{
name: "TypedArray", name: "TypedArray",
new: func(args []interface{}) interface{} { new: func(args []interface{}) interface{} {
l := int(args[0].(float64))
return &array{ return &array{
buf: args[0].(*buffer), buf: make([]byte, l, l),
offset: int(args[1].(float64)),
length: int(args[2].(float64)),
} }
}, },
} }
type buffer struct { // TODO make this a wrapper that takes an inner `this` js object
data []byte
}
type Func func(args []interface{}) (interface{}, error) type Func func(args []interface{}) (interface{}, error)
func (b *Bridge) resume() error { func (b *Bridge) resume() error {
@ -503,9 +456,7 @@ type funcWrapper struct {
} }
func (b *Bridge) makeFuncWrapper(id, this interface{}, args *[]interface{}) (interface{}, error) { func (b *Bridge) makeFuncWrapper(id, this interface{}, args *[]interface{}) (interface{}, error) {
b.valuesMu.RLock() goObj := this.(*object)
goObj := b.valueMap[7].(*object)
b.valuesMu.RUnlock()
event := propObject("_pendingEvent", map[string]interface{}{ event := propObject("_pendingEvent", map[string]interface{}{
"id": id, "id": id,
"this": goObj, "this": goObj,
@ -528,10 +479,8 @@ func (b *Bridge) CallFunc(fn string, args []interface{}) (interface{}, error) {
if !ok { if !ok {
return nil, fmt.Errorf("missing function: %v", fn) return nil, fmt.Errorf("missing function: %v", fn)
} }
this := b.valueMap[6]
this := b.valueMap[7]
b.valuesMu.RUnlock() b.valuesMu.RUnlock()
return b.makeFuncWrapper(fw.(*funcWrapper).id, this, &args) return b.makeFuncWrapper(fw.(*funcWrapper).id, this, &args)
} }
@ -542,28 +491,13 @@ func (b *Bridge) SetFunc(fname string, fn Func) error {
return nil return nil
} }
func (b *Bridge) releaseFunc(v interface{}) Func {
return Func(func(args []interface{}) (interface{}, error) {
b.valuesMu.Lock()
defer b.valuesMu.Unlock()
idx, ok := b.releaseRef(v)
if !ok {
return nil, nil
}
b.releaseVal(idx)
return nil, nil
})
}
func Bytes(v interface{}) ([]byte, error) { func Bytes(v interface{}) ([]byte, error) {
arr, ok := v.(*array) arr, ok := v.(*array)
if !ok { if !ok {
return nil, fmt.Errorf("got %T instead of bytes", v) return nil, fmt.Errorf("got %T instead of bytes", v)
} }
return arr.data(), nil return arr.buf, nil
} }
func String(v interface{}) (string, error) { func String(v interface{}) (string, error) {
@ -575,7 +509,7 @@ func String(v interface{}) (string, error) {
return str, nil return str, nil
} }
func Error(v interface{}) (errVal error, err error) { func Error(v interface{}) (errVal, err error) {
str, ok := v.(string) str, ok := v.(string)
if !ok { if !ok {
return nil, fmt.Errorf("got %T instead of error", v) return nil, fmt.Errorf("got %T instead of error", v)
@ -584,16 +518,8 @@ func Error(v interface{}) (errVal error, err error) {
return errors.New(str), nil return errors.New(str), nil
} }
func UintArray(v interface{}) *[]uint { func FromBytes(v []byte) interface{} {
rv := reflect.ValueOf(v) buf := make([]byte, len(v), len(v))
if rv.Kind() != reflect.Slice { copy(buf, v)
panic("not a slice") return &array{buf: buf}
}
buf := make([]uint, rv.Len(), rv.Len())
for i := 0; i < rv.Len(); i++ {
buf[i] = uint(rv.Index(i).Uint())
}
return &buf
} }

View File

@ -60,7 +60,7 @@ func main() {
} }
log.Println(bytes) log.Println(bytes)
res, err = b.CallFunc("bytes", []interface{}{wasm.UintArray(bytes)}) res, err = b.CallFunc("bytes", []interface{}{wasm.FromBytes(bytes)})
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -7,8 +7,6 @@ import (
"errors" "errors"
"log" "log"
"syscall/js" "syscall/js"
"github.com/vedhavyas/go-wasm/go-converts"
) )
func addition(this js.Value, args []js.Value) interface{} { func addition(this js.Value, args []js.Value) interface{} {
@ -27,7 +25,10 @@ func getBytes(this js.Value, args []js.Value) interface{} {
if err != nil { if err != nil {
panic(err) panic(err)
} }
return js.TypedArrayOf(r)
v := js.Global().Get("Uint8Array").New(len(r))
js.CopyBytesToJS(v, r)
return v
} }
func getError(this js.Value, args []js.Value) interface{} { func getError(this js.Value, args []js.Value) interface{} {
@ -37,8 +38,11 @@ func getError(this js.Value, args []js.Value) interface{} {
func receiveSendBytes(this js.Value, args []js.Value) interface{} { func receiveSendBytes(this js.Value, args []js.Value) interface{} {
b := args[0] b := args[0]
buf := converts.ToBytes(b) buf := make([]byte, b.Length(), b.Length())
return js.TypedArrayOf(buf) js.CopyBytesToGo(buf, b)
v := js.Global().Get("Uint8Array").New(len(buf))
js.CopyBytesToJS(v, buf)
return v
} }
func main() { func main() {

Binary file not shown.

Binary file not shown.

View File

@ -1,20 +0,0 @@
// +build js,wasm
package converts
import "syscall/js"
func ToBytes(v js.Value) []byte {
buf := make([]byte, v.Length(), v.Length())
for i := 0; i < v.Length(); i++ {
sv := v.Index(i)
buf[i] = byte(sv.Int())
}
return buf
}
// Free frees the value for GC
func free(v js.Value) {
v.Call("_release_")
}

View File

@ -24,6 +24,8 @@ extern void valuePrepareString(void *context, int32_t a);
extern void valueLoadString(void *context, int32_t a); extern void valueLoadString(void *context, int32_t a);
extern void scheduleTimeoutEvent(void *context, int32_t a); extern void scheduleTimeoutEvent(void *context, int32_t a);
extern void clearTimeoutEvent(void *context, int32_t a); extern void clearTimeoutEvent(void *context, int32_t a);
extern void copyBytesToGo (void *context, int32_t a);
extern void copyBytesToJS (void *context, int32_t a);
*/ */
import "C" import "C"
import ( import (
@ -156,18 +158,11 @@ func valueSetIndex(_ unsafe.Pointer, _ int32) {
func valueCall(ctx unsafe.Pointer, sp int32) { func valueCall(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx) b := getBridge(ctx)
v := b.loadValue(sp + 8) v := b.loadValue(sp + 8)
var f Func
var args []interface{}
str := b.loadString(sp + 16) str := b.loadString(sp + 16)
if str == release_call { args := b.loadSliceOfValues(sp + 32)
f = b.releaseFunc(v) f, ok := v.(*object).props[str].(Func)
} else { if !ok {
args = b.loadSliceOfValues(sp + 32) panic(fmt.Sprintf("valueCall: prop not found in %v, %s", v.(*object).name, str))
var ok bool
f, ok = v.(*object).props[str].(Func)
if !ok {
panic(fmt.Sprintln("valueCall: prop not found in ", v, str))
}
} }
sp = b.getSP() sp = b.getSP()
res, err := f(args) res, err := f(args)
@ -217,7 +212,17 @@ func valueLength(ctx unsafe.Pointer, sp int32) {
if rv.Kind() == reflect.Ptr { if rv.Kind() == reflect.Ptr {
rv = rv.Elem() rv = rv.Elem()
} }
b.setInt64(sp+16, int64(rv.Len())) var l int
switch {
case rv.Kind() == reflect.Slice:
l = rv.Len()
case rv.Type() == reflect.TypeOf(array{}):
l = len(val.(*array).buf)
default:
panic(fmt.Sprintf("valueLength on %T", val))
}
b.setInt64(sp+16, int64(l))
} }
//export valuePrepareString //export valuePrepareString
@ -251,6 +256,34 @@ func clearTimeoutEvent(_ unsafe.Pointer, _ int32) {
panic("clearTimeoutEvent") panic("clearTimeoutEvent")
} }
//export copyBytesToJS
func copyBytesToJS(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx)
dst, ok := b.loadValue(sp + 8).(*array)
if !ok {
b.setUint8(sp+48, 0)
return
}
src := b.loadSlice(sp + 16)
n := copy(dst.buf, src[:len(dst.buf)])
b.setInt64(sp+40, int64(n))
b.setUint8(sp+48, 1)
}
//export copyBytesToGo
func copyBytesToGo(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx)
dst := b.loadSlice(sp + 8)
src, ok := b.loadValue(sp + 32).(*array)
if !ok {
b.setUint8(sp+48, 0)
return
}
n := copy(dst, src.buf[:len(dst)])
b.setInt64(sp+40, int64(n))
b.setUint8(sp+48, 1)
}
// addImports adds go Bridge imports in "go" namespace. // addImports adds go Bridge imports in "go" namespace.
func (b *Bridge) addImports(imps *wasmer.Imports) error { func (b *Bridge) addImports(imps *wasmer.Imports) error {
imps = imps.Namespace("go") imps = imps.Namespace("go")
@ -280,6 +313,8 @@ func (b *Bridge) addImports(imps *wasmer.Imports) error {
{"syscall/js.valueLength", valueLength, C.valueLength}, {"syscall/js.valueLength", valueLength, C.valueLength},
{"syscall/js.valuePrepareString", valuePrepareString, C.valuePrepareString}, {"syscall/js.valuePrepareString", valuePrepareString, C.valuePrepareString},
{"syscall/js.valueLoadString", valueLoadString, C.valueLoadString}, {"syscall/js.valueLoadString", valueLoadString, C.valueLoadString},
{"syscall/js.copyBytesToGo", copyBytesToGo, C.copyBytesToGo},
{"syscall/js.copyBytesToJS", copyBytesToJS, C.copyBytesToJS},
} }
var err error var err error