try-table.js (11064B)
1 // A try_table acts like a block label, with results 2 { 3 let maxResults1 = Array.from(Array(1000).keys()); 4 let maxResults2 = maxResults1.map((x) => x - 1); 5 const TESTS = [ 6 ['i32', 'i32.const', [0], [1]], 7 ['i32 '.repeat(1000), 'i32.const', maxResults1, maxResults2], 8 ['i64', 'i64.const', [0], [1]], 9 ['i64 '.repeat(1000), 'i64.const', maxResults1, maxResults2], 10 ['f32', 'f32.const', [0], [1]], 11 ['f32 '.repeat(1000), 'f32.const', maxResults1, maxResults2], 12 ['f64', 'f64.const', [0], [1]], 13 ['f64 '.repeat(1000), 'f64.const', maxResults1, maxResults2], 14 ]; 15 for (let [types, constructor, values1, values2] of TESTS) { 16 let {test} = wasmEvalText(`(module 17 (func (export "test") (param $shouldBranch i32) (result ${types}) 18 try_table (result ${types}) 19 ${values2.map((x) => `${constructor} ${x}`).join(" ")} 20 (br_if 1 local.get $shouldBranch) 21 ${values1.map((x) => `${constructor} ${x}`).join(" ")} 22 br 0 23 end 24 ) 25 )`).exports; 26 assertEqResults(test(0), values1); 27 assertEqResults(test(1), values2); 28 } 29 } 30 31 // A try_table can have params 32 { 33 let maxParams1 = Array.from(Array(1000).keys()); 34 const TESTS = [ 35 ['i32', 'i32.const', [0]], 36 ['i32 '.repeat(1000), 'i32.const', maxParams1], 37 ['i64', 'i64.const', [0]], 38 ['i64 '.repeat(1000), 'i64.const', maxParams1], 39 ['f32', 'f32.const', [0]], 40 ['f32 '.repeat(1000), 'f32.const', maxParams1], 41 ['f64', 'f64.const', [0]], 42 ['f64 '.repeat(1000), 'f64.const', maxParams1], 43 ]; 44 for (let [types, constructor, params] of TESTS) { 45 let {test} = wasmEvalText(`(module 46 (func (export "test") (result ${types}) 47 ${params.map((x) => `${constructor} ${x}`).join(" ")} 48 try_table (param ${types}) 49 return 50 end 51 unreachable 52 ) 53 )`).exports; 54 assertEqResults(test(), params); 55 } 56 } 57 58 // Test try_table catching exceptions 59 { 60 let {test} = wasmEvalText(`(module 61 (tag $A (param i32)) 62 (tag $B (param i32)) 63 (tag $C) 64 65 (table funcref (elem $throwA $throwB $throwC $doNothing)) 66 67 (type $empty (func)) 68 (func $throwA 69 i32.const 1 70 throw $A 71 ) 72 (func $throwB 73 i32.const 2 74 throw $B 75 ) 76 (func $throwC 77 throw $C 78 ) 79 (func $doNothing) 80 81 (func (export "test") (param i32) (result i32) 82 block $handleA (result i32 (ref exn)) 83 block $handleB (result i32 (ref exn)) 84 block $handleUnknown (result (ref exn)) 85 try_table 86 (catch_ref $A $handleA) 87 (catch_ref $B $handleB) 88 (catch_all_ref $handleUnknown) 89 90 (call_indirect (type $empty) 91 local.get 0) 92 end 93 (; nothing threw ;) 94 i32.const -1 95 return 96 end 97 (; $handleUnknown ;) 98 drop 99 i32.const 3 100 return 101 end 102 (; $handleB ;) 103 drop 104 return 105 end 106 (; $handleA ;) 107 drop 108 return 109 ) 110 )`).exports; 111 // Throwing A results in 1 from the payload from the catch 112 assertEq(test(0), 1); 113 // Throwing B results in 2 from the payload from the catch 114 assertEq(test(1), 2); 115 // Throwing C results in 3 from the constant in the catch_all 116 assertEq(test(2), 3); 117 // Not throwing anything gets -1 from the fallthrough 118 assertEq(test(3), -1); 119 } 120 121 // Test try_table catching exceptions without capturing the exnref 122 { 123 let {test} = wasmEvalText(`(module 124 (tag $A (param i32)) 125 (tag $B (param i32)) 126 (tag $C) 127 128 (table funcref (elem $throwA $throwB $throwC $doNothing)) 129 130 (type $empty (func)) 131 (func $throwA 132 i32.const 1 133 throw $A 134 ) 135 (func $throwB 136 i32.const 2 137 throw $B 138 ) 139 (func $throwC 140 throw $C 141 ) 142 (func $doNothing) 143 144 (func (export "test") (param i32) (result i32) 145 block $handleA (result i32) 146 block $handleB (result i32) 147 block $handleUnknown 148 try_table 149 (catch $A $handleA) 150 (catch $B $handleB) 151 (catch_all $handleUnknown) 152 153 (call_indirect (type $empty) 154 local.get 0) 155 end 156 (; nothing threw ;) 157 i32.const -1 158 return 159 end 160 (; $handleUnknown ;) 161 i32.const 3 162 return 163 end 164 (; $handleB ;) 165 return 166 end 167 (; $handleA ;) 168 return 169 ) 170 )`).exports; 171 // Throwing A results in 1 from the payload from the catch 172 assertEq(test(0), 1); 173 // Throwing B results in 2 from the payload from the catch 174 assertEq(test(1), 2); 175 // Throwing C results in 3 from the constant in the catch_all 176 assertEq(test(2), 3); 177 // Not throwing anything gets -1 from the fallthrough 178 assertEq(test(3), -1); 179 } 180 181 // Test try_table catching exceptions with various payloads 182 { 183 let maxResults1 = Array.from(Array(999).keys()); 184 const TESTS = [ 185 ['i32', 'i32.const', [0]], 186 ['i32 '.repeat(999), 'i32.const', maxResults1], 187 ['i64', 'i64.const', [0]], 188 ['i64 '.repeat(999), 'i64.const', maxResults1], 189 ['f32', 'f32.const', [0]], 190 ['f32 '.repeat(999), 'f32.const', maxResults1], 191 ['f64', 'f64.const', [0]], 192 ['f64 '.repeat(999), 'f64.const', maxResults1], 193 ]; 194 for (let [types, constructor, params] of TESTS) { 195 let {testCatch, testCatchRef} = wasmEvalText(`(module 196 (tag $E (param ${types})) 197 198 (func (export "testCatch") (result ${types}) 199 try_table (catch $E 0) 200 ${params.map((x) => `${constructor} ${x}`).join(" ")} 201 throw $E 202 end 203 unreachable 204 ) 205 (func (export "testCatchRef") (result ${types}) 206 (block (result ${types} exnref) 207 try_table (catch_ref $E 0) 208 ${params.map((x) => `${constructor} ${x}`).join(" ")} 209 throw $E 210 end 211 unreachable 212 ) 213 drop 214 return 215 ) 216 217 )`).exports; 218 assertEqResults(testCatch(), params); 219 assertEqResults(testCatchRef(), params); 220 } 221 } 222 223 // Test setting locals in conditional control flow 224 { 225 let {test} = wasmEvalText(`(module 226 (tag $E) 227 228 (func (export "test") (param $shouldThrow i32) (result i32) 229 (local $result i32) 230 231 (block $join 232 (block $catch 233 try_table (catch $E $catch) 234 local.get $shouldThrow 235 if 236 throw $E 237 end 238 (local.set $result i32.const 0) 239 br $join 240 end 241 ) 242 (local.set $result i32.const 1) 243 br $join 244 ) 245 246 local.get $result 247 ) 248 249 )`).exports; 250 assertEq(test(0), 0); 251 assertEq(test(1), 1); 252 } 253 254 // Matching catch clauses is done in order 255 { 256 let {testCatch, testCatchRef, testCatchAll, testCatchAllRef} = wasmEvalText(`(module 257 (tag $E) 258 259 (func (export "testCatch") 260 (block $good 261 (block $bad 262 try_table (catch $E $good) (catch $E $bad) (catch_all $bad) 263 throw $E 264 end 265 ) 266 unreachable 267 ) 268 ) 269 (func (export "testCatchAll") 270 (block $good 271 (block $bad 272 try_table (catch_all $good) (catch $E $bad) (catch $E $bad) 273 throw $E 274 end 275 ) 276 unreachable 277 ) 278 ) 279 (func (export "testCatchRef") 280 (block $good (result (ref exn)) 281 (block $bad (result (ref exn)) 282 try_table (catch_ref $E $good) (catch_ref $E $bad) (catch_all_ref $bad) 283 throw $E 284 end 285 unreachable 286 ) 287 unreachable 288 ) 289 drop 290 ) 291 (func (export "testCatchAllRef") 292 (block $good (result (ref exn)) 293 (block $bad (result (ref exn)) 294 try_table (catch_all_ref $good) (catch_ref $E $bad) (catch_ref $E $bad) 295 throw $E 296 end 297 unreachable 298 ) 299 unreachable 300 ) 301 drop 302 ) 303 )`).exports; 304 testCatch(); 305 testCatchAll(); 306 testCatchRef(); 307 testCatchAllRef(); 308 } 309 310 // Test try_table as target of a delegate 311 { 312 let {test} = wasmEvalText(`(module 313 (tag $E) 314 (func (export "test") 315 block $good 316 block $bad 317 try_table $a (catch_all $good) 318 try 319 try 320 throw $E 321 delegate $a 322 catch $E 323 br $bad 324 end 325 end 326 end 327 unreachable 328 end 329 ) 330 )`).exports; 331 test(); 332 } 333 334 // Try table cannot be target of rethrow 335 { 336 wasmFailValidateText(`(module 337 (func 338 try_table (catch_all 0) rethrow 0 end 339 ) 340 )`, /rethrow target/); 341 } 342 343 // Test try_table catching and rethrowing JS exceptions 344 { 345 let tag = new WebAssembly.Tag({parameters: []}); 346 let exn = new WebAssembly.Exception(tag, []); 347 let values = [...WasmExternrefValues, exn]; 348 function throwJS(value) { 349 throw value; 350 } 351 let {test} = wasmEvalText(`(module 352 (import "" "tag" (tag $tag)) 353 (import "" "throwJS" (func $throwJS (param externref))) 354 (func $innerRethrow (param externref) 355 (block (result exnref) 356 try_table (catch_ref $tag 0) (catch_all_ref 0) 357 local.get 0 358 call $throwJS 359 end 360 return 361 ) 362 throw_ref 363 ) 364 (func (export "test") (param externref) 365 (block (result exnref) 366 try_table (catch_ref $tag 0) (catch_all_ref 0) 367 local.get 0 368 call $innerRethrow 369 end 370 return 371 ) 372 throw_ref 373 ) 374 )`, {"": {tag, throwJS}}).exports; 375 376 for (let value of values) { 377 try { 378 test(value); 379 assertEq(true, false); 380 } catch (thrownValue) { 381 assertEq(thrownValue, value); 382 } 383 } 384 } 385 386 // WebAssembly.JSTag property is read-only and enumerable 387 WebAssembly.JSTag = null; 388 assertEq(WebAssembly.JSTag !== null, true); 389 assertEq(WebAssembly.propertyIsEnumerable('JSTag'), true); 390 391 // Test try_table catching JS exceptions and unpacking them using JSTag 392 { 393 let tag = WebAssembly.JSTag; 394 let values = [...WasmExternrefValues]; 395 function throwJS(value) { 396 throw value; 397 } 398 let {test} = wasmEvalText(`(module 399 (import "" "tag" (tag $tag (param externref))) 400 (import "" "throwJS" (func $throwJS (param externref))) 401 (func (export "test") (param externref) (result externref) 402 try_table (catch $tag 0) 403 local.get 0 404 call $throwJS 405 end 406 unreachable 407 ) 408 )`, {"": {tag, throwJS}}).exports; 409 410 for (let value of values) { 411 assertEq(value, test(value)); 412 } 413 } 414 415 // Test try_table catching JS exceptions using JSTag and unpacking them using JSTag 416 { 417 let tag = WebAssembly.JSTag; 418 let values = [...WasmExternrefValues]; 419 function throwJS(value) { 420 throw new WebAssembly.Exception(tag, [value]); 421 } 422 let {test} = wasmEvalText(`(module 423 (import "" "tag" (tag $tag (param externref))) 424 (import "" "throwJS" (func $throwJS (param externref))) 425 (func (export "test") (param externref) (result externref) 426 try_table (catch $tag 0) 427 local.get 0 428 call $throwJS 429 end 430 unreachable 431 ) 432 )`, {"": {tag, throwJS}}).exports; 433 434 for (let value of values) { 435 assertEq(value, test(value)); 436 } 437 }