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"
)
const release_call = "_release_"
var (
undefined = &struct{}{}
bridges = map[string]*Bridge{}
@ -60,30 +58,6 @@ type Bridge struct {
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) {
b := new(Bridge)
if imports == nil {
@ -142,16 +116,9 @@ func (b *Bridge) addValues() {
"Object": &object{name: "Object", new: func(args []interface{}) interface{} {
return &object{name: "ObjectInner", props: map[string]interface{}{}}
}},
"Array": propObject("Array", nil),
"Int8Array": typedArray,
"Int16Array": typedArray,
"Int32Array": typedArray,
"Uint8Array": typedArray,
"Uint16Array": typedArray,
"Uint32Array": typedArray,
"Float32Array": typedArray,
"Float64Array": typedArray,
"process": propObject("process", nil),
"Array": propObject("Array", nil),
"Uint8Array": typedArray,
"process": propObject("process", nil),
"Date": &object{name: "Date", new: func(args []interface{}) interface{} {
t := time.Now()
return &object{name: "DateInner", props: map[string]interface{}{
@ -168,7 +135,7 @@ func (b *Bridge) addValues() {
"crypto": propObject("crypto", map[string]interface{}{
"getRandomValues": Func(func(args []interface{}) (interface{}, error) {
arr := args[0].(*array)
return rand.Read(arr.data())
return rand.Read(arr.buf)
}),
}),
"AbortController": &object{name: "AbortController", new: func(args []interface{}) interface{} {
@ -189,7 +156,7 @@ func (b *Bridge) addValues() {
return obj
}},
"fetch": Func(func(args []interface{}) (interface{}, error) {
// TODO(ved): implement fetch
// Fixme(ved): implement fetch
log.Fatalln(args)
return nil, nil
}),
@ -207,7 +174,7 @@ func (b *Bridge) addValues() {
fd := int(args[0].(float64))
offset := int(args[2].(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]
callback := args[5].(*funcWrapper)
var err error
@ -228,10 +195,7 @@ func (b *Bridge) addValues() {
}),
},
}, //global
6: propObject("mem", map[string]interface{}{
"buffer": &buffer{data: b.mem()}},
),
7: goObj, // jsGo
6: goObj, // jsGo
}
}
@ -421,35 +385,34 @@ func (b *Bridge) storeValue(addr int32, v interface{}) {
return
}
rv := reflect.TypeOf(v)
if !rv.Comparable() {
panic(fmt.Sprintf("%T is not comparable", v))
rt := reflect.TypeOf(v)
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
rv := v
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 {
b.valuesMu.RLock()
b.valueMap[b.valueIDX] = v
ref = b.valueIDX
b.refs[v] = ref
b.refs[rv] = ref
b.valueIDX++
b.valuesMu.RUnlock()
}
typeFlag := 0
switch rv.Kind() {
switch rt.Kind() {
case reflect.String:
typeFlag = 1
case reflect.Struct, reflect.Slice:
typeFlag = 2
case reflect.Func:
typeFlag = 3
default:
panic(fmt.Sprintf("unknown type: %T", v))
}
b.setUint32(addr+4, uint32(nanHead|typeFlag))
b.setUint32(addr, uint32(ref))
@ -466,30 +429,20 @@ func propObject(name string, prop map[string]interface{}) *object {
}
type array struct {
buf *buffer
offset int
length int
}
func (a *array) data() []byte {
return a.buf.data[a.offset : a.offset+a.length]
buf []byte
}
var typedArray = &object{
name: "TypedArray",
new: func(args []interface{}) interface{} {
l := int(args[0].(float64))
return &array{
buf: args[0].(*buffer),
offset: int(args[1].(float64)),
length: int(args[2].(float64)),
buf: make([]byte, l, l),
}
},
}
type buffer struct {
data []byte
}
// TODO make this a wrapper that takes an inner `this` js object
type Func func(args []interface{}) (interface{}, error)
func (b *Bridge) resume() error {
@ -503,9 +456,7 @@ type funcWrapper struct {
}
func (b *Bridge) makeFuncWrapper(id, this interface{}, args *[]interface{}) (interface{}, error) {
b.valuesMu.RLock()
goObj := b.valueMap[7].(*object)
b.valuesMu.RUnlock()
goObj := this.(*object)
event := propObject("_pendingEvent", map[string]interface{}{
"id": id,
"this": goObj,
@ -528,10 +479,8 @@ func (b *Bridge) CallFunc(fn string, args []interface{}) (interface{}, error) {
if !ok {
return nil, fmt.Errorf("missing function: %v", fn)
}
this := b.valueMap[7]
this := b.valueMap[6]
b.valuesMu.RUnlock()
return b.makeFuncWrapper(fw.(*funcWrapper).id, this, &args)
}
@ -542,28 +491,13 @@ func (b *Bridge) SetFunc(fname string, fn Func) error {
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) {
arr, ok := v.(*array)
if !ok {
return nil, fmt.Errorf("got %T instead of bytes", v)
}
return arr.data(), nil
return arr.buf, nil
}
func String(v interface{}) (string, error) {
@ -575,7 +509,7 @@ func String(v interface{}) (string, error) {
return str, nil
}
func Error(v interface{}) (errVal error, err error) {
func Error(v interface{}) (errVal, err error) {
str, ok := v.(string)
if !ok {
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
}
func UintArray(v interface{}) *[]uint {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Slice {
panic("not a slice")
}
buf := make([]uint, rv.Len(), rv.Len())
for i := 0; i < rv.Len(); i++ {
buf[i] = uint(rv.Index(i).Uint())
}
return &buf
func FromBytes(v []byte) interface{} {
buf := make([]byte, len(v), len(v))
copy(buf, v)
return &array{buf: buf}
}

View File

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

View File

@ -7,8 +7,6 @@ import (
"errors"
"log"
"syscall/js"
"github.com/vedhavyas/go-wasm/go-converts"
)
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 {
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{} {
@ -37,8 +38,11 @@ func getError(this js.Value, args []js.Value) interface{} {
func receiveSendBytes(this js.Value, args []js.Value) interface{} {
b := args[0]
buf := converts.ToBytes(b)
return js.TypedArrayOf(buf)
buf := make([]byte, b.Length(), b.Length())
js.CopyBytesToGo(buf, b)
v := js.Global().Get("Uint8Array").New(len(buf))
js.CopyBytesToJS(v, buf)
return v
}
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 scheduleTimeoutEvent(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 (
@ -156,18 +158,11 @@ func valueSetIndex(_ unsafe.Pointer, _ int32) {
func valueCall(ctx unsafe.Pointer, sp int32) {
b := getBridge(ctx)
v := b.loadValue(sp + 8)
var f Func
var args []interface{}
str := b.loadString(sp + 16)
if str == release_call {
f = b.releaseFunc(v)
} else {
args = b.loadSliceOfValues(sp + 32)
var ok bool
f, ok = v.(*object).props[str].(Func)
if !ok {
panic(fmt.Sprintln("valueCall: prop not found in ", v, str))
}
args := b.loadSliceOfValues(sp + 32)
f, ok := v.(*object).props[str].(Func)
if !ok {
panic(fmt.Sprintf("valueCall: prop not found in %v, %s", v.(*object).name, str))
}
sp = b.getSP()
res, err := f(args)
@ -217,7 +212,17 @@ func valueLength(ctx unsafe.Pointer, sp int32) {
if rv.Kind() == reflect.Ptr {
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
@ -251,6 +256,34 @@ func clearTimeoutEvent(_ unsafe.Pointer, _ int32) {
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.
func (b *Bridge) addImports(imps *wasmer.Imports) error {
imps = imps.Namespace("go")
@ -280,6 +313,8 @@ func (b *Bridge) addImports(imps *wasmer.Imports) error {
{"syscall/js.valueLength", valueLength, C.valueLength},
{"syscall/js.valuePrepareString", valuePrepareString, C.valuePrepareString},
{"syscall/js.valueLoadString", valueLoadString, C.valueLoadString},
{"syscall/js.copyBytesToGo", copyBytesToGo, C.copyBytesToGo},
{"syscall/js.copyBytesToJS", copyBytesToJS, C.copyBytesToJS},
}
var err error