throw-to-js.js (7061B)
1 // Tests for throwing exceptions to JS from Wasm. 2 3 function assertWasmThrowsExn(thunk) { 4 let thrown = false; 5 6 try { 7 thunk(); 8 } catch (exn) { 9 thrown = true; 10 assertEq(exn instanceof WebAssembly.Exception, true); 11 } 12 13 assertEq(thrown, true, "missing exception"); 14 } 15 16 // Test that handler-less trys don't catch anything. 17 assertWasmThrowsExn(() => 18 wasmEvalText( 19 `(module 20 (type (func (param))) 21 (tag $exn (type 0)) 22 (func (export "f") 23 try (throw $exn) end))` 24 ).exports.f() 25 ); 26 27 assertWasmThrowsExn(() => 28 wasmEvalText( 29 `(module 30 (type (func (param))) 31 (tag $exn (type 0)) 32 (func $g (throw $exn)) 33 (func (export "f") 34 try (call $g) end) 35 )` 36 ).exports.f() 37 ); 38 39 assertWasmThrowsExn(() => 40 wasmEvalText( 41 `(module 42 (type (func (param))) 43 (tag $exn (type 0)) 44 (func (export "f") 45 try try (throw $exn) end end))` 46 ).exports.f() 47 ); 48 49 assertWasmThrowsExn(() => 50 wasmEvalText( 51 `(module 52 (tag $exn (param)) 53 (func (export "f") 54 try 55 try 56 throw $exn 57 delegate 0 58 end))` 59 ).exports.f() 60 ); 61 62 assertWasmThrowsExn(() => 63 wasmEvalText( 64 `(module 65 (tag $exn (param)) 66 (func (export "f") 67 try 68 try 69 throw $exn 70 delegate 1 71 end))` 72 ).exports.f() 73 ); 74 75 assertWasmThrowsExn(() => 76 wasmEvalText( 77 `(module 78 (tag $exn (param)) 79 (func (export "f") 80 block 81 try 82 throw $exn 83 delegate 0 84 end))` 85 ).exports.f() 86 ); 87 88 assertWasmThrowsExn(() => 89 wasmEvalText( 90 `(module 91 (tag $exn (param)) 92 (func (export "f") 93 loop 94 try 95 throw $exn 96 delegate 0 97 end))` 98 ).exports.f() 99 ); 100 101 assertWasmThrowsExn(() => 102 wasmEvalText( 103 `(module 104 (tag $exn (param)) 105 (func (export "f") 106 (i32.const 1) 107 if 108 try 109 throw $exn 110 delegate 0 111 end))` 112 ).exports.f() 113 ); 114 115 // Test throwing simple empty exceptions to JS. 116 assertWasmThrowsExn(() => 117 wasmEvalText( 118 `(module 119 (type (func (param))) 120 (tag $exn (type 0)) 121 (func (export "f") 122 (throw $exn)))` 123 ).exports.f() 124 ); 125 126 // Test that wasm preserves the values of non-object exceptions that pass 127 // through it back to JS. 128 assertThrowsValue( 129 () => 130 wasmEvalText( 131 `(module 132 (tag $exn) 133 (import "m" "import" (func $import)) 134 (func (export "f") 135 try 136 (call $import) 137 catch $exn 138 ;; this block shouldn't be reached 139 end))`, 140 { 141 m: { 142 import: () => { 143 throw 42; 144 }, 145 }, 146 } 147 ).exports.f(), 148 42 149 ); 150 151 // Like previous test, but using a rethrow instruction instead. 152 assertThrowsValue( 153 () => 154 wasmEvalText( 155 `(module 156 (import "m" "import" (func $import)) 157 (func (export "f") 158 try 159 (call $import) 160 catch_all 161 (rethrow 0) 162 end))`, 163 { 164 m: { 165 import: () => { 166 throw 42; 167 }, 168 }, 169 } 170 ).exports.f(), 171 42 172 ); 173 174 // Test for throwing to JS and then back to Wasm. 175 { 176 var wasmThrower; 177 let exports = wasmEvalText( 178 `(module 179 (type (func (param i32))) 180 (tag $exn (type 0)) 181 (import "m" "import" (func $import (result i32))) 182 (func (export "thrower") 183 (i32.const 42) 184 (throw $exn)) 185 (func (export "catcher") (result i32) 186 try (result i32) 187 (call $import) 188 catch $exn 189 end))`, 190 { 191 m: { 192 import: () => { 193 return wasmThrower(); 194 }, 195 }, 196 } 197 ).exports; 198 199 wasmThrower = exports.thrower; 200 assertEq(exports.catcher(), 42); 201 } 202 203 // Tests for checking the tags of exceptions. 204 { 205 let exports = wasmEvalText( 206 `(module 207 (type (func (param i32))) 208 (tag $exn (export "exn") (type 0)) 209 (func (export "thrower") 210 (i32.const 42) 211 (throw $exn)))` 212 ).exports; 213 214 let imports = { 215 store: { 216 throws: () => { 217 return exports.thrower(); 218 }, 219 exn: exports.exn, 220 }, 221 }; 222 223 // This passes the exception tag check and the exception is caught. 224 assertEq( 225 wasmEvalText( 226 `(module 227 (type (func (param i32))) 228 (import "store" "throws" (func $thrower (result i32))) 229 (import "store" "exn" (tag $exn (type 0))) 230 (func (export "catches") (result i32) 231 try (result i32) 232 (call $thrower) 233 catch $exn 234 (i32.const 15) 235 (i32.sub) 236 end))`, 237 imports 238 ).exports.catches(), 239 27 240 ); 241 242 // This fails the exception tag check, despite the local exception having 243 // a matching signature. 244 assertWasmThrowsExn(() => 245 wasmEvalText( 246 `(module 247 (type (func (param i32))) 248 (import "store" "throws" (func $thrower (result i32))) 249 (tag $exn (type 0)) 250 (func (export "catchesFail") (result i32) 251 try (result i32) 252 (call $thrower) 253 catch $exn ;; This should not recognise $exn, thus not unpack 42. 254 end))`, 255 imports 256 ).exports.catchesFail() 257 ); 258 } 259 260 // Test that JS finally block executes after a Wasm throw. 261 assertEq( 262 (() => { 263 try { 264 wasmEvalText( 265 `(module 266 (type (func (param))) 267 (tag $exn (type 0)) 268 (func (export "f") 269 (throw $exn)))` 270 ).exports.f(); 271 } finally { 272 return true; 273 } 274 return false; 275 })(), 276 true 277 ); 278 279 // Test that a wasm trap that passes through JS cannot be caught in Wasm. 280 { 281 let throwTrap = wasmEvalText(`(module (func (export "f") unreachable))`) 282 .exports.f; 283 let catcher = wasmEvalText( 284 `(module 285 (type (func)) 286 (tag $exn (type 0)) 287 (import "m" "f" (func $foreign (param) (result))) 288 (func (export "f") 289 try 290 call $foreign 291 catch $exn 292 catch_all 293 end))`, 294 { 295 m: { 296 // JS frame that goes between the two wasm frames and just rethrows. 297 f: () => { 298 try { 299 throwTrap(); 300 } catch (e) { 301 throw e; 302 } 303 }, 304 }, 305 } 306 ).exports.f; 307 308 assertErrorMessage( 309 () => catcher(), 310 WebAssembly.RuntimeError, 311 "unreachable executed" 312 ); 313 } 314 315 // Test delegate throwing out of function. 316 assertWasmThrowsExn(() => 317 wasmEvalText( 318 `(module 319 (tag $exn (param)) 320 (func (export "f") (result i32) 321 try (result i32) 322 throw $exn 323 delegate 0))` 324 ).exports.f() 325 ); 326 327 assertWasmThrowsExn(() => 328 wasmEvalText( 329 `(module 330 (tag $exn (param)) 331 (func (export "f") (result i32) 332 try (result i32) 333 i32.const 0 334 if 335 i32.const 1 336 return 337 else 338 throw $exn 339 end 340 i32.const 0 341 delegate 0))` 342 ).exports.f() 343 );