Skip to content

Commit 5674c35

Browse files
aykevldeadprogram
authored andcommitted
wasm: backport "garbage collect references to JavaScript values"
See commit: golang/go@54e6ba6 Warning: this will drop support for Go 1.13 for WebAssembly targets! I have modified the integration tests to specifically blacklist Go 1.13 instead of whitelisting any other version, to avoid accidentally not testing WebAssembly.
1 parent 0f9038a commit 5674c35

File tree

2 files changed

+43
-16
lines changed

2 files changed

+43
-16
lines changed

main_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import (
1313
"path/filepath"
1414
"runtime"
1515
"sort"
16+
"strings"
1617
"sync"
1718
"testing"
1819
"time"
1920

2021
"github.com/tinygo-org/tinygo/builder"
2122
"github.com/tinygo-org/tinygo/compileopts"
23+
"github.com/tinygo-org/tinygo/goenv"
2224
)
2325

2426
const TESTDATA = "testdata"
@@ -71,9 +73,20 @@ func TestCompiler(t *testing.T) {
7173
t.Run("ARM64Linux", func(t *testing.T) {
7274
runPlatTests("aarch64--linux-gnu", matches, t)
7375
})
74-
t.Run("WebAssembly", func(t *testing.T) {
75-
runPlatTests("wasm", matches, t)
76-
})
76+
goVersion, err := builder.GorootVersionString(goenv.Get("GOROOT"))
77+
if err != nil {
78+
t.Error("could not get Go version:", err)
79+
return
80+
}
81+
minorVersion := strings.Split(goVersion, ".")[1]
82+
if minorVersion != "13" {
83+
// WebAssembly tests fail on Go 1.13, so skip them there. Versions
84+
// below that are also not supported but still seem to pass, so
85+
// include them in the tests for now.
86+
t.Run("WebAssembly", func(t *testing.T) {
87+
runPlatTests("wasm", matches, t)
88+
})
89+
}
7790
}
7891
}
7992

targets/wasm_exec.js

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -202,26 +202,31 @@
202202
return;
203203
}
204204

205-
let ref = this._refs.get(v);
206-
if (ref === undefined) {
207-
ref = this._values.length;
208-
this._values.push(v);
209-
this._refs.set(v, ref);
205+
let id = this._ids.get(v);
206+
if (id === undefined) {
207+
id = this._idPool.pop();
208+
if (id === undefined) {
209+
id = this._values.length;
210+
}
211+
this._values[id] = v;
212+
this._goRefCounts[id] = 0;
213+
this._ids.set(v, id);
210214
}
211-
let typeFlag = 0;
215+
this._goRefCounts[id]++;
216+
let typeFlag = 1;
212217
switch (typeof v) {
213218
case "string":
214-
typeFlag = 1;
219+
typeFlag = 2;
215220
break;
216221
case "symbol":
217-
typeFlag = 2;
222+
typeFlag = 3;
218223
break;
219224
case "function":
220-
typeFlag = 3;
225+
typeFlag = 4;
221226
break;
222227
}
223228
mem().setUint32(addr + 4, nanHead | typeFlag, true);
224-
mem().setUint32(addr, ref, true);
229+
mem().setUint32(addr, id, true);
225230
}
226231

227232
const loadSlice = (array, len, cap) => {
@@ -284,6 +289,13 @@
284289
setTimeout(this._inst.exports.go_scheduler, timeout);
285290
},
286291

292+
// func finalizeRef(v ref)
293+
"syscall/js.finalizeRef": (sp) => {
294+
// Note: TinyGo does not support finalizers so this should never be
295+
// called.
296+
console.error('syscall/js.finalizeRef not implemented');
297+
},
298+
287299
// func stringVal(value string) ref
288300
"syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => {
289301
const s = loadString(value_ptr, value_len);
@@ -405,7 +417,7 @@
405417

406418
async run(instance) {
407419
this._inst = instance;
408-
this._values = [ // TODO: garbage collection
420+
this._values = [ // JS values that Go currently has references to, indexed by reference id
409421
NaN,
410422
0,
411423
null,
@@ -414,8 +426,10 @@
414426
global,
415427
this,
416428
];
417-
this._refs = new Map();
418-
this.exited = false;
429+
this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
430+
this._ids = new Map(); // mapping from JS values to reference ids
431+
this._idPool = []; // unused ids that have been garbage collected
432+
this.exited = false; // whether the Go program has exited
419433

420434
const mem = new DataView(this._inst.exports.memory.buffer)
421435

0 commit comments

Comments
 (0)