summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/table-gc.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/table-gc.js')
-rw-r--r--js/src/jit-test/tests/wasm/table-gc.js221
1 files changed, 221 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/table-gc.js b/js/src/jit-test/tests/wasm/table-gc.js
new file mode 100644
index 000000000..0cce3edf8
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/table-gc.js
@@ -0,0 +1,221 @@
+// |jit-test| --no-baseline
+// Turn off baseline and since it messes up the GC finalization assertions by
+// adding spurious edges to the GC graph.
+
+load(libdir + 'wasm.js');
+
+const Module = WebAssembly.Module;
+const Instance = WebAssembly.Instance;
+const Table = WebAssembly.Table;
+const RuntimeError = WebAssembly.RuntimeError;
+
+var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
+var callee = i => `(func $f${i} (type $v2i) (i32.const ${i}))`;
+
+// A table should not hold exported functions alive and exported functions
+// should not hold their originating table alive. Live exported functions should
+// hold instances alive and instances hold imported tables alive. Nothing
+// should hold the export object alive.
+resetFinalizeCount();
+var i = wasmEvalText(`(module (table 2 anyfunc) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
+var e = i.exports;
+var t = e.tbl;
+var f = t.get(0);
+assertEq(f(), e.call(0));
+assertErrorMessage(() => e.call(1), RuntimeError, /indirect call to null/);
+assertErrorMessage(() => e.call(2), RuntimeError, /index out of bounds/);
+assertEq(finalizeCount(), 0);
+i.edge = makeFinalizeObserver();
+t.edge = makeFinalizeObserver();
+f.edge = makeFinalizeObserver();
+gc();
+assertEq(finalizeCount(), 0);
+f = null;
+gc();
+assertEq(finalizeCount(), 1);
+f = t.get(0);
+f.edge = makeFinalizeObserver();
+gc();
+assertEq(finalizeCount(), 1);
+i.exports = null;
+e = null;
+gc();
+assertEq(finalizeCount(), 1);
+t = null;
+gc();
+assertEq(finalizeCount(), 1);
+i = null;
+gc();
+assertEq(finalizeCount(), 1);
+assertEq(f(), 0);
+f = null;
+gc();
+assertEq(finalizeCount(), 4);
+
+// A table should hold the instance of any of its elements alive.
+resetFinalizeCount();
+var i = wasmEvalText(`(module (table 1 anyfunc) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
+var e = i.exports;
+var t = e.tbl;
+var f = t.get(0);
+i.edge = makeFinalizeObserver();
+t.edge = makeFinalizeObserver();
+f.edge = makeFinalizeObserver();
+gc();
+assertEq(finalizeCount(), 0);
+i.exports = null;
+e = null;
+gc();
+assertEq(finalizeCount(), 0);
+f = null;
+gc();
+assertEq(finalizeCount(), 1);
+i = null;
+gc();
+assertEq(finalizeCount(), 1);
+t = null;
+gc();
+assertEq(finalizeCount(), 3);
+
+// Null elements shouldn't keep anything alive.
+resetFinalizeCount();
+var i = wasmEvalText(`(module (table 2 anyfunc) (export "tbl" table) ${caller})`);
+var e = i.exports;
+var t = e.tbl;
+i.edge = makeFinalizeObserver();
+t.edge = makeFinalizeObserver();
+gc();
+assertEq(finalizeCount(), 0);
+i.exports = null;
+e = null;
+gc();
+assertEq(finalizeCount(), 0);
+i = null;
+gc();
+assertEq(finalizeCount(), 1);
+t = null;
+gc();
+assertEq(finalizeCount(), 2);
+
+// Before initialization, a table is not bound to any instance.
+resetFinalizeCount();
+var i = wasmEvalText(`(module (func $f0 (result i32) (i32.const 0)) (export "f0" $f0))`);
+var t = new Table({initial:4, element:"anyfunc"});
+i.edge = makeFinalizeObserver();
+t.edge = makeFinalizeObserver();
+gc();
+assertEq(finalizeCount(), 0);
+i = null;
+gc();
+assertEq(finalizeCount(), 1);
+t = null;
+gc();
+assertEq(finalizeCount(), 2);
+
+// When a Table is created (uninitialized) and then first assigned, it keeps the
+// first element's Instance alive (as above).
+resetFinalizeCount();
+var i = wasmEvalText(`(module (func $f (result i32) (i32.const 42)) (export "f" $f))`);
+var f = i.exports.f;
+var t = new Table({initial:1, element:"anyfunc"});
+i.edge = makeFinalizeObserver();
+f.edge = makeFinalizeObserver();
+t.edge = makeFinalizeObserver();
+t.set(0, f);
+assertEq(t.get(0), f);
+assertEq(t.get(0)(), 42);
+gc();
+assertEq(finalizeCount(), 0);
+f = null;
+i.exports = null;
+gc();
+assertEq(finalizeCount(), 1);
+assertEq(t.get(0)(), 42);
+t.get(0).edge = makeFinalizeObserver();
+gc();
+assertEq(finalizeCount(), 2);
+i = null;
+gc();
+assertEq(finalizeCount(), 2);
+t.set(0, null);
+assertEq(t.get(0), null);
+gc();
+assertEq(finalizeCount(), 3);
+t = null;
+gc();
+assertEq(finalizeCount(), 4);
+
+// Once all of an instance's elements in a Table have been clobbered, the
+// Instance should not be reachable.
+resetFinalizeCount();
+var i1 = wasmEvalText(`(module (func $f1 (result i32) (i32.const 13)) (export "f1" $f1))`);
+var i2 = wasmEvalText(`(module (func $f2 (result i32) (i32.const 42)) (export "f2" $f2))`);
+var f1 = i1.exports.f1;
+var f2 = i2.exports.f2;
+var t = new Table({initial:2, element:"anyfunc"});
+i1.edge = makeFinalizeObserver();
+i2.edge = makeFinalizeObserver();
+f1.edge = makeFinalizeObserver();
+f2.edge = makeFinalizeObserver();
+t.edge = makeFinalizeObserver();
+t.set(0, f1);
+t.set(1, f2);
+gc();
+assertEq(finalizeCount(), 0);
+f1 = f2 = null;
+i1.exports = null;
+i2.exports = null;
+gc();
+assertEq(finalizeCount(), 2);
+i1 = null;
+i2 = null;
+gc();
+assertEq(finalizeCount(), 2);
+t.set(0, t.get(1));
+gc();
+assertEq(finalizeCount(), 3);
+t = null;
+gc();
+assertEq(finalizeCount(), 5);
+
+// Ensure that an instance that is only live on the stack cannot be GC even if
+// there are no outstanding references.
+resetFinalizeCount();
+const N = 10;
+var tbl = new Table({initial:N, element:"anyfunc"});
+tbl.edge = makeFinalizeObserver();
+function runTest() {
+ tbl = null;
+ gc();
+ assertEq(finalizeCount(), 0);
+ return 100;
+}
+var i = wasmEvalText(
+ `(module
+ (import $imp "a" "b" (result i32))
+ (func $f (param i32) (result i32) (call $imp))
+ (export "f" $f)
+ )`,
+ {a:{b:runTest}}
+);
+i.edge = makeFinalizeObserver();
+tbl.set(0, i.exports.f);
+var m = new Module(wasmTextToBinary(`(module
+ (import "a" "b" (table ${N} anyfunc))
+ (type $i2i (func (param i32) (result i32)))
+ (func $f (param $i i32) (result i32)
+ (set_local $i (i32.sub (get_local $i) (i32.const 1)))
+ (i32.add
+ (i32.const 1)
+ (call_indirect $i2i (get_local $i) (get_local $i))))
+ (export "f" $f)
+)`));
+for (var i = 1; i < N; i++) {
+ var inst = new Instance(m, {a:{b:tbl}});
+ inst.edge = makeFinalizeObserver();
+ tbl.set(i, inst.exports.f);
+}
+inst = null;
+assertEq(tbl.get(N - 1)(N - 1), 109);
+gc();
+assertEq(finalizeCount(), N + 1);