aboutsummaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorRichard Musiol <mail@richard-musiol.de>2019-10-26 21:01:32 +0200
committerBrad Fitzpatrick <bradfitz@golang.org>2019-11-04 22:50:43 +0000
commit54e6ba6724dfde355070238f9abc16362cac2e3d (patch)
tree545a12103881cfa87f536865a8cf0bfd9e85b731 /misc
parent063d0f11e535edf61d1e0b4ba16cfeae0f312bcf (diff)
downloadgo-54e6ba6724dfde355070238f9abc16362cac2e3d.tar.gz
go-54e6ba6724dfde355070238f9abc16362cac2e3d.zip
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.js49
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;