table-gc.js (6166B)
1 // |jit-test| --no-baseline; --no-blinterp 2 // Turn off baseline and since it messes up the GC finalization assertions by 3 // adding spurious edges to the GC graph. 4 5 const Module = WebAssembly.Module; 6 const Instance = WebAssembly.Instance; 7 const Table = WebAssembly.Table; 8 const RuntimeError = WebAssembly.RuntimeError; 9 10 var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect (type $v2i) (local.get $i))) (export "call" (func $call))` 11 var callee = i => `(func $f${i} (type $v2i) (i32.const ${i}))`; 12 13 // A table should not hold exported functions alive and exported functions 14 // should not hold their originating table alive. Live exported functions should 15 // hold instances alive and instances hold imported tables alive. Nothing 16 // should hold the export object alive. 17 resetFinalizeCount(); 18 var i = wasmEvalText(`(module (table 2 funcref) (export "tbl" (table 0)) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`); 19 var e = i.exports; 20 var t = e.tbl; 21 var f = t.get(0); 22 assertEq(f(), e.call(0)); 23 assertErrorMessage(() => e.call(1), RuntimeError, /indirect call to null/); 24 assertErrorMessage(() => e.call(2), RuntimeError, /index out of bounds/); 25 assertEq(finalizeCount(), 0); 26 i.edge = makeFinalizeObserver(); 27 t.edge = makeFinalizeObserver(); 28 f.edge = makeFinalizeObserver(); 29 gc(); 30 assertEq(finalizeCount(), 0); 31 f.x = 42; 32 f = null; 33 gc(); 34 assertEq(finalizeCount(), 0); 35 f = t.get(0); 36 assertEq(f.x, 42); 37 gc(); 38 assertEq(finalizeCount(), 0); 39 i.exports = null; 40 e = null; 41 gc(); 42 assertEq(finalizeCount(), 0); 43 t = null; 44 gc(); 45 assertEq(finalizeCount(), 0); 46 i = null; 47 gc(); 48 assertEq(finalizeCount(), 0); 49 assertEq(f(), 0); 50 f = null; 51 gc(); 52 assertEq(finalizeCount(), 3); 53 54 // A table should hold the instance of any of its elements alive. 55 resetFinalizeCount(); 56 var i = wasmEvalText(`(module (table 1 funcref) (export "tbl" (table 0)) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`); 57 var e = i.exports; 58 var t = e.tbl; 59 var f = t.get(0); 60 i.edge = makeFinalizeObserver(); 61 t.edge = makeFinalizeObserver(); 62 f.edge = makeFinalizeObserver(); 63 gc(); 64 assertEq(finalizeCount(), 0); 65 i.exports = null; 66 e = null; 67 gc(); 68 assertEq(finalizeCount(), 0); 69 f = null; 70 gc(); 71 assertEq(finalizeCount(), 0); 72 i = null; 73 gc(); 74 assertEq(finalizeCount(), 0); 75 t = null; 76 gc(); 77 assertEq(finalizeCount(), 3); 78 79 // Null elements shouldn't keep anything alive. 80 resetFinalizeCount(); 81 var i = wasmEvalText(`(module (table 2 funcref) (export "tbl" (table 0)) ${caller})`); 82 var e = i.exports; 83 var t = e.tbl; 84 i.edge = makeFinalizeObserver(); 85 t.edge = makeFinalizeObserver(); 86 gc(); 87 assertEq(finalizeCount(), 0); 88 i.exports = null; 89 e = null; 90 gc(); 91 assertEq(finalizeCount(), 0); 92 i = null; 93 gc(); 94 assertEq(finalizeCount(), 1); 95 t = null; 96 gc(); 97 assertEq(finalizeCount(), 2); 98 99 // Before initialization, a table is not bound to any instance. 100 resetFinalizeCount(); 101 var i = wasmEvalText(`(module (func $f0 (result i32) (i32.const 0)) (export "f0" (func $f0)))`); 102 var t = new Table({initial:4, element:"anyfunc"}); 103 i.edge = makeFinalizeObserver(); 104 t.edge = makeFinalizeObserver(); 105 gc(); 106 assertEq(finalizeCount(), 0); 107 i = null; 108 gc(); 109 assertEq(finalizeCount(), 1); 110 t = null; 111 gc(); 112 assertEq(finalizeCount(), 2); 113 114 // When a Table is created (uninitialized) and then first assigned, it keeps the 115 // first element's Instance alive (as above). 116 resetFinalizeCount(); 117 var i = wasmEvalText(`(module (func $f (result i32) (i32.const 42)) (export "f" (func $f)))`); 118 var f = i.exports.f; 119 var t = new Table({initial:1, element:"anyfunc"}); 120 i.edge = makeFinalizeObserver(); 121 f.edge = makeFinalizeObserver(); 122 t.edge = makeFinalizeObserver(); 123 t.set(0, f); 124 assertEq(t.get(0), f); 125 assertEq(t.get(0)(), 42); 126 gc(); 127 assertEq(finalizeCount(), 0); 128 f = null; 129 i.exports = null; 130 gc(); 131 assertEq(finalizeCount(), 0); 132 assertEq(t.get(0)(), 42); 133 i = null; 134 gc(); 135 assertEq(finalizeCount(), 0); 136 t.set(0, null); 137 assertEq(t.get(0), null); 138 gc(); 139 assertEq(finalizeCount(), 2); 140 t = null; 141 gc(); 142 assertEq(finalizeCount(), 3); 143 144 // Once all of an instance's elements in a Table have been clobbered, the 145 // Instance should not be reachable. 146 resetFinalizeCount(); 147 var i1 = wasmEvalText(`(module (func $f1 (result i32) (i32.const 13)) (export "f1" (func $f1)))`); 148 var i2 = wasmEvalText(`(module (func $f2 (result i32) (i32.const 42)) (export "f2" (func $f2)))`); 149 var f1 = i1.exports.f1; 150 var f2 = i2.exports.f2; 151 var t = new Table({initial:2, element:"anyfunc"}); 152 i1.edge = makeFinalizeObserver(); 153 i2.edge = makeFinalizeObserver(); 154 f1.edge = makeFinalizeObserver(); 155 f2.edge = makeFinalizeObserver(); 156 t.edge = makeFinalizeObserver(); 157 t.set(0, f1); 158 t.set(1, f2); 159 gc(); 160 assertEq(finalizeCount(), 0); 161 f1 = f2 = null; 162 i1.exports = null; 163 i2.exports = null; 164 gc(); 165 assertEq(finalizeCount(), 0); 166 i1 = null; 167 i2 = null; 168 gc(); 169 assertEq(finalizeCount(), 0); 170 t.set(0, t.get(1)); 171 gc(); 172 assertEq(finalizeCount(), 2); 173 t.set(0, null); 174 t.set(1, null); 175 gc(); 176 assertEq(finalizeCount(), 4); 177 t = null; 178 gc(); 179 assertEq(finalizeCount(), 5); 180 181 // Ensure that an instance that is only live on the stack cannot be GC even if 182 // there are no outstanding references. 183 resetFinalizeCount(); 184 const N = 10; 185 var tbl = new Table({initial:N, element:"anyfunc"}); 186 tbl.edge = makeFinalizeObserver(); 187 function runTest() { 188 tbl = null; 189 gc(); 190 assertEq(finalizeCount(), 0); 191 return 100; 192 } 193 var i = wasmEvalText( 194 `(module 195 (import "a" "b" (func $imp (result i32))) 196 (func $f (param i32) (result i32) (call $imp)) 197 (export "f" (func $f)) 198 )`, 199 {a:{b:runTest}} 200 ); 201 i.edge = makeFinalizeObserver(); 202 tbl.set(0, i.exports.f); 203 var m = new Module(wasmTextToBinary(`(module 204 (import "a" "b" (table ${N} funcref)) 205 (type $i2i (func (param i32) (result i32))) 206 (func $f (param $i i32) (result i32) 207 (local.set $i (i32.sub (local.get $i) (i32.const 1))) 208 (i32.add 209 (i32.const 1) 210 (call_indirect (type $i2i) (local.get $i) (local.get $i)))) 211 (export "f" (func $f)) 212 )`)); 213 for (var i = 1; i < N; i++) { 214 var inst = new Instance(m, {a:{b:tbl}}); 215 inst.edge = makeFinalizeObserver(); 216 tbl.set(i, inst.exports.f); 217 } 218 inst = null; 219 assertEq(tbl.get(N - 1)(N - 1), 109); 220 gc(); 221 assertEq(finalizeCount(), N + 1);