ref-struct.js (11876B)
1 // |jit-test| test-also=--gc-zeal=2 2 3 // We'll be running some binary-format tests shortly. 4 5 load(libdir + "wasm-binary.js"); 6 7 const v2vSigSection = sigSection([{args:[], ret:VoidCode}]); 8 9 function checkInvalid(body, errorMessage) { 10 assertErrorMessage(() => new WebAssembly.Module( 11 moduleWithSections([v2vSigSection, 12 declSection([0]), 13 bodySection([body])])), 14 WebAssembly.CompileError, 15 errorMessage); 16 } 17 18 // General test case for struct.new, struct.get, and struct.set: binary tree 19 // manipulation. 20 21 { 22 let bin = wasmTextToBinary( 23 `(module 24 (import "" "print_lp" (func $print_lp)) 25 (import "" "print_rp" (func $print_rp)) 26 (import "" "print_int" (func $print_int (param i32))) 27 28 (type $wabbit (struct 29 (field $x (mut i32)) 30 (field $left (mut (ref null $wabbit))) 31 (field $right (mut (ref null $wabbit))))) 32 33 (global $g (mut (ref null $wabbit)) (ref.null $wabbit)) 34 35 (global $k (mut i32) (i32.const 0)) 36 37 (func (export "init") (param $n i32) 38 (global.set $g (call $make (local.get $n)))) 39 40 (func $make (param $n i32) (result (ref null $wabbit)) 41 (local $tmp i32) 42 (local.set $tmp (global.get $k)) 43 (global.set $k (i32.add (local.get $tmp) (i32.const 1))) 44 (if (result (ref null $wabbit)) (i32.le_s (local.get $n) (i32.const 2)) 45 (then (struct.new $wabbit (local.get $tmp) (ref.null $wabbit) (ref.null $wabbit))) 46 (else 47 (block (result (ref null $wabbit)) 48 (struct.new $wabbit 49 (local.get $tmp) 50 (call $make (i32.sub (local.get $n) (i32.const 1))) 51 (call $make (i32.sub (local.get $n) (i32.const 2)))))))) 52 53 (func (export "accumulate") (result i32) 54 (call $accum (global.get $g))) 55 56 (func $accum (param $w (ref null $wabbit)) (result i32) 57 (if (result i32) (ref.is_null (local.get $w)) 58 (then (i32.const 0)) 59 (else 60 (i32.add (struct.get $wabbit 0 (local.get $w)) 61 (i32.sub (call $accum (struct.get $wabbit 1 (local.get $w))) 62 (call $accum (struct.get $wabbit 2 (local.get $w)))))))) 63 64 (func (export "reverse") 65 (call $reverse (global.get $g))) 66 67 (func $reverse (param $w (ref null $wabbit)) 68 (local $tmp (ref null $wabbit)) 69 (if (i32.eqz (ref.is_null (local.get $w))) 70 (then 71 (block 72 (struct.set $wabbit 0 (local.get $w) (i32.mul (i32.const 2) (struct.get $wabbit 0 (local.get $w)))) 73 (local.set $tmp (struct.get $wabbit 1 (local.get $w))) 74 (struct.set $wabbit 1 (local.get $w) (struct.get $wabbit 2 (local.get $w))) 75 (struct.set $wabbit 2 (local.get $w) (local.get $tmp)) 76 (call $reverse (struct.get $wabbit 1 (local.get $w))) 77 (call $reverse (struct.get $wabbit 2 (local.get $w))))))) 78 79 (func (export "print") 80 (call $pr (global.get $g))) 81 82 (func $pr (param $w (ref null $wabbit)) 83 (if (i32.eqz (ref.is_null (local.get $w))) 84 (then 85 (block 86 (call $print_lp) 87 (call $print_int (struct.get $wabbit 0 (local.get $w))) 88 (call $pr (struct.get $wabbit 1 (local.get $w))) 89 (call $pr (struct.get $wabbit 2 (local.get $w))) 90 (call $print_rp))))) 91 )`); 92 93 let s = ""; 94 function pr_int(k) { s += k + " "; } 95 function pr_lp() { s += "(" }; 96 function pr_rp() { s += ")" } 97 98 let mod = new WebAssembly.Module(bin); 99 let ins = new WebAssembly.Instance(mod, {"":{print_int:pr_int,print_lp:pr_lp,print_rp:pr_rp}}).exports; 100 101 ins.init(6); 102 s = ""; ins.print(); assertEq(s, "(0 (1 (2 (3 (4 )(5 ))(6 ))(7 (8 )(9 )))(10 (11 (12 )(13 ))(14 )))"); 103 assertEq(ins.accumulate(), -13); 104 105 ins.reverse(); 106 s = ""; ins.print(); assertEq(s, "(0 (20 (28 )(22 (26 )(24 )))(2 (14 (18 )(16 ))(4 (12 )(6 (10 )(8 )))))"); 107 assertEq(ins.accumulate(), 14); 108 109 for (let i=10; i < 22; i++ ) { 110 ins.init(i); 111 ins.reverse(); 112 gc(); 113 ins.reverse(); 114 } 115 } 116 117 // Sanity check for struct.set: we /can/ store a (ref null T) into a (ref null U) field 118 // with struct.set if T <: U; this should fall out of normal coercion but good 119 // to test. 120 121 wasmEvalText( 122 `(module 123 (type $node (sub (struct (field (mut (ref null $node)))))) 124 (type $nix (sub $node (struct (field (mut (ref null $node))) (field i32)))) 125 (func $f (param $p (ref null $node)) (param $q (ref null $nix)) 126 (struct.set $node 0 (local.get $p) (local.get $q))))`); 127 128 // ref.cast: if the downcast succeeds we get the original pointer 129 130 assertEq(wasmEvalText( 131 `(module 132 (type $node (sub (struct (field i32)))) 133 (type $node2 (sub $node (struct (field i32) (field f32)))) 134 (func $f (param $p (ref null $node)) (result (ref null $node2)) 135 (ref.cast (ref null $node2) (local.get $p))) 136 (func (export "test") (result i32) 137 (local $n (ref null $node)) 138 (local.set $n (struct.new $node2 (i32.const 0) (f32.const 12))) 139 (ref.eq (call $f (local.get $n)) (local.get $n))))`).exports.test(), 140 1); 141 142 // ref.cast: if the pointer is null we trap 143 144 assertErrorMessage(() => wasmEvalText( 145 `(module 146 (type $node (struct (field i32))) 147 (type $node2 (struct (field i32) (field f32))) 148 (func $f (param $p (ref null $node)) (result (ref null $node2)) 149 (ref.cast (ref $node2) (local.get $p))) 150 (func (export "test") (result eqref) 151 (call $f (ref.null $node))))`).exports.test(), 152 WebAssembly.RuntimeError, 153 /bad cast/, 154 ); 155 156 // ref.cast null: if the pointer is null we do not trap 157 158 wasmEvalText( 159 `(module 160 (type $node (struct (field i32))) 161 (type $node2 (struct (field i32) (field f32))) 162 (func $f (param $p (ref null $node)) (result (ref null $node2)) 163 (ref.cast (ref null $node2) (local.get $p))) 164 (func (export "test") (result eqref) 165 (call $f (ref.null $node))))`).exports.test(); 166 167 168 // And once more with mutable fields 169 170 assertEq(wasmEvalText( 171 `(module 172 (type $node (sub (struct (field (mut i32))))) 173 (type $node2 (sub $node (struct (field (mut i32)) (field f32)))) 174 (func $f (param $p (ref null $node)) (result (ref null $node2)) 175 (ref.cast (ref null $node2) (local.get $p))) 176 (func (export "test") (result i32) 177 (local $n (ref null $node)) 178 (local.set $n (struct.new $node2 (i32.const 0) (f32.const 12))) 179 (ref.eq (call $f (local.get $n)) (local.get $n))))`).exports.test(), 180 1); 181 182 // ref.cast: eqref -> struct when the eqref is the right struct; 183 // special case since eqref requires unboxing 184 185 assertEq(wasmEvalText( 186 `(module 187 (type $node (struct (field i32))) 188 (func $f (param $p eqref) (result (ref null $node)) 189 (ref.cast (ref null $node) (local.get $p))) 190 (func (export "test") (result i32) 191 (local $n (ref null $node)) 192 (local.set $n (struct.new $node (i32.const 0))) 193 (ref.eq (call $f (local.get $n)) (local.get $n))))`).exports.test(), 194 1); 195 196 // Can default initialize a struct which zero initializes 197 198 { 199 let {makeA, makeB, makeC} = wasmEvalText(` 200 (module 201 (type $a (struct)) 202 (type $b (struct (field i32) (field f32))) 203 (type $c (struct (field eqref))) 204 205 (func (export "makeA") (result eqref) 206 struct.new_default $a 207 ) 208 (func (export "makeB") (result eqref) 209 struct.new_default $b 210 ) 211 (func (export "makeC") (result eqref) 212 struct.new_default $c 213 ) 214 )`).exports; 215 let a = makeA(); 216 217 let b = makeB(); 218 assertEq(wasmGcReadField(b, 0), 0); 219 assertEq(wasmGcReadField(b, 1), 0); 220 221 let c = makeC(); 222 assertEq(wasmGcReadField(c, 0), null); 223 } 224 225 // struct.new_default: valid if all struct fields are defaultable 226 227 wasmFailValidateText(`(module 228 (type $a (struct (field (ref $a)))) 229 (func 230 struct.new_default $a 231 ) 232 )`, /defaultable/); 233 234 wasmFailValidateText(`(module 235 (type $a (struct (field i32) (field i32) (field (ref $a)))) 236 (func 237 struct.new_default $a 238 ) 239 )`, /defaultable/); 240 241 // Negative tests 242 243 // Attempting to mutate immutable field with struct.set 244 245 assertErrorMessage(() => wasmEvalText( 246 `(module 247 (type $node (struct (field i32))) 248 (func $f (param $p (ref null $node)) 249 (struct.set $node 0 (local.get $p) (i32.const 37))))`), 250 WebAssembly.CompileError, 251 /field is not mutable/); 252 253 // Attempting to store incompatible value in mutable field with struct.set 254 255 assertErrorMessage(() => wasmEvalText( 256 `(module 257 (type $node (struct (field (mut i32)))) 258 (func $f (param $p (ref null $node)) 259 (struct.set $node 0 (local.get $p) (f32.const 37))))`), 260 WebAssembly.CompileError, 261 /expression has type f32 but expected i32/); 262 263 // Out-of-bounds reference for struct.get 264 265 assertErrorMessage(() => wasmEvalText( 266 `(module 267 (type $node (struct (field i32))) 268 (func $f (param $p (ref null $node)) (result i32) 269 (struct.get $node 1 (local.get $p))))`), 270 WebAssembly.CompileError, 271 /field index out of range/); 272 273 // Out-of-bounds reference for struct.set 274 275 assertErrorMessage(() => wasmEvalText( 276 `(module 277 (type $node (struct (field (mut i32)))) 278 (func $f (param $p (ref null $node)) 279 (struct.set $node 1 (local.get $p) (i32.const 37))))`), 280 WebAssembly.CompileError, 281 /field index out of range/); 282 283 // Base pointer is of unrelated type to stated type in struct.get 284 285 assertErrorMessage(() => wasmEvalText( 286 `(module 287 (type $node (struct (field i32))) 288 (type $snort (struct (field f64))) 289 (func $f (param $p (ref null $snort)) (result i32) 290 (struct.get $node 0 (local.get $p))))`), 291 WebAssembly.CompileError, 292 /expression has type.*but expected.*/); 293 294 // Base pointer is of unrelated type to stated type in struct.set 295 296 assertErrorMessage(() => wasmEvalText( 297 `(module 298 (type $node (struct (field (mut i32)))) 299 (type $snort (struct (field f64))) 300 (func $f (param $p (ref null $snort)) (result i32) 301 (struct.set $node 0 (local.get $p) (i32.const 0))))`), 302 WebAssembly.CompileError, 303 /expression has type.*but expected.*/); 304 305 // Null pointer dereference in struct.get 306 307 assertErrorMessage(function() { 308 let ins = wasmEvalText( 309 `(module 310 (type $node (struct (field i32))) 311 (func (export "test") 312 (drop (call $f (ref.null $node)))) 313 (func $f (param $p (ref null $node)) (result i32) 314 (struct.get $node 0 (local.get $p))))`); 315 ins.exports.test(); 316 }, 317 WebAssembly.RuntimeError, 318 /dereferencing null pointer/); 319 320 // Null pointer dereference in struct.set 321 322 assertErrorMessage(function() { 323 let ins = wasmEvalText( 324 `(module 325 (type $node (struct (field (mut i32)))) 326 (func (export "test") 327 (call $f (ref.null $node))) 328 (func $f (param $p (ref null $node)) 329 (struct.set $node 0 (local.get $p) (i32.const 0))))`); 330 ins.exports.test(); 331 }, 332 WebAssembly.RuntimeError, 333 /dereferencing null pointer/);