diff options
author | Richard Musiol <mail@richard-musiol.de> | 2019-10-26 21:01:32 +0200 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@golang.org> | 2019-11-04 22:50:43 +0000 |
commit | 54e6ba6724dfde355070238f9abc16362cac2e3d (patch) | |
tree | 545a12103881cfa87f536865a8cf0bfd9e85b731 /misc | |
parent | 063d0f11e535edf61d1e0b4ba16cfeae0f312bcf (diff) | |
download | go-git-54e6ba6724dfde355070238f9abc16362cac2e3d.tar.gz |
syscall/js: garbage collect references to JavaScript values
The js.Value struct now contains a pointer, so a finalizer can
determine if the value is not referenced by Go any more.
Unfortunately this breaks Go's == operator with js.Value. This change
adds a new Equal method to check for the equality of two Values.
This is a breaking change. The == operator is now disallowed to
not silently break code.
Additionally the helper methods IsUndefined, IsNull and IsNaN got added.
Fixes #35111
Change-Id: I58a50ca18f477bf51a259c668a8ba15bfa76c955
Reviewed-on: https://go-review.googlesource.com/c/go/+/203600
Run-TryBot: Richard Musiol <neelance@gmail.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'misc')
-rw-r--r-- | misc/wasm/wasm_exec.js | 49 |
1 files changed, 35 insertions, 14 deletions
diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js index 3c2c186867..bb66cf254d 100644 --- a/misc/wasm/wasm_exec.js +++ b/misc/wasm/wasm_exec.js @@ -205,26 +205,31 @@ return; } - let ref = this._refs.get(v); - if (ref === undefined) { - ref = this._values.length; - this._values.push(v); - this._refs.set(v, ref); + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); } - let typeFlag = 0; + this._goRefCounts[id]++; + let typeFlag = 1; switch (typeof v) { case "string": - typeFlag = 1; + typeFlag = 2; break; case "symbol": - typeFlag = 2; + typeFlag = 3; break; case "function": - typeFlag = 3; + typeFlag = 4; break; } this.mem.setUint32(addr + 4, nanHead | typeFlag, true); - this.mem.setUint32(addr, ref, true); + this.mem.setUint32(addr, id, true); } const loadSlice = (addr) => { @@ -263,7 +268,9 @@ this.exited = true; delete this._inst; delete this._values; - delete this._refs; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; this.exit(code); }, @@ -323,6 +330,18 @@ crypto.getRandomValues(loadSlice(sp + 8)); }, + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + // func stringVal(value string) ref "syscall/js.stringVal": (sp) => { storeValue(sp + 24, loadString(sp + 8)); @@ -462,7 +481,7 @@ async run(instance) { this._inst = instance; this.mem = new DataView(this._inst.exports.mem.buffer); - this._values = [ // TODO: garbage collection + this._values = [ // JS values that Go currently has references to, indexed by reference id NaN, 0, null, @@ -471,8 +490,10 @@ global, this, ]; - this._refs = new Map(); - this.exited = false; + this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map(); // mapping from JS values to reference ids + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. let offset = 4096; |