support go 1.13
This commit is contained in:
		
							parent
							
								
									a3327b9b07
								
							
						
					
					
						commit
						90853f742d
					
				
							
								
								
									
										134
									
								
								bridge.go
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								bridge.go
									
									
									
									
									
								
							@ -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}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -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.
										
									
								
							@ -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_")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								imports.go
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								imports.go
									
									
									
									
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user