diff --git a/bridge.go b/bridge.go index 81a8400..7da6b40 100644 --- a/bridge.go +++ b/bridge.go @@ -18,9 +18,14 @@ import ( "github.com/wasmerio/go-ext-wasm/wasmer" ) -var undefined = &struct{}{} -var bridges = map[string]*Bridge{} -var mu sync.RWMutex // to protect bridges +const release_call = "_release_" + +var ( + undefined = &struct{}{} + bridges = map[string]*Bridge{} + mu sync.RWMutex // to protect bridges +) + type bctx struct{ n string } func getCtxData(b *Bridge) (unsafe.Pointer, error) { @@ -55,6 +60,30 @@ 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 { @@ -513,6 +542,21 @@ 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 { diff --git a/examples/wasm/main.wasm b/examples/wasm/main.wasm index 51d65fc..46875cc 100755 Binary files a/examples/wasm/main.wasm and b/examples/wasm/main.wasm differ diff --git a/go-converts/wasm.go b/go-converts/wasm.go index 94ac596..73697fc 100644 --- a/go-converts/wasm.go +++ b/go-converts/wasm.go @@ -7,8 +7,14 @@ import "syscall/js" func ToBytes(v js.Value) []byte { buf := make([]byte, v.Length(), v.Length()) for i := 0; i < v.Length(); i++ { - buf[i] = byte(v.Index(i).Int()) + 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_") +} diff --git a/imports.go b/imports.go index bbac0ad..9b77c70 100644 --- a/imports.go +++ b/imports.go @@ -156,13 +156,19 @@ 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) - args := b.loadSliceOfValues(sp + 32) - f, ok := v.(*object).props[str].(Func) - if !ok { - panic(fmt.Sprintln("valueCall: prop not found in ", v, str)) + 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)) + } } - sp = b.getSP() res, err := f(args) if err != nil {