test_webassembly_compile.html (13853B)
1 <!-- 2 Any copyright is dedicated to the Public Domain. 3 http://creativecommons.org/publicdomain/zero/1.0/ 4 --> 5 <html> 6 <head> 7 <title>WebAssembly.compile Test</title> 8 <script src="/tests/SimpleTest/SimpleTest.js"></script> 9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 10 </head> 11 <body> 12 <script> 13 const testingFunctions = SpecialPowers.Cu.getJSTestingFunctions(); 14 const wasmIsSupported = SpecialPowers.unwrap(testingFunctions.wasmIsSupported); 15 const wasmHasTier2CompilationCompleted = SpecialPowers.unwrap(testingFunctions.wasmHasTier2CompilationCompleted); 16 const wasmLoadedFromCache = SpecialPowers.unwrap(testingFunctions.wasmLoadedFromCache); 17 const wasmLazyTieringEnabled = SpecialPowers.unwrap(testingFunctions.wasmLazyTieringEnabled); 18 // Wasm caching is disabled when lazy tiering is enabled, hence the && here. 19 const isCachingEnabled = SpecialPowers.getBoolPref("javascript.options.wasm_caching") && !wasmLazyTieringEnabled(); 20 21 // The test_webassembly_compile_sample.wasm is a medium-sized module with 100 22 // functions that call each other recursively, returning a computed sum. 23 // Any other non-trivial module could be generated and used. 24 var sampleCode; 25 const sampleURL = "test_webassembly_compile_sample.wasm"; 26 const sampleFileSize = 16053; 27 const sampleURLWithRandomQuery = () => sampleURL + "?id=" + String(Math.ceil(Math.random()*100000)); 28 const sampleExportName = "run"; 29 const sampleResult = 1275; 30 31 function checkSampleModule(m) { 32 ok(m instanceof WebAssembly.Module, "got a module"); 33 var i = new WebAssembly.Instance(m); 34 ok(i instanceof WebAssembly.Instance, "got an instance"); 35 ok(i.exports[sampleExportName]() === sampleResult, "got result"); 36 } 37 38 function checkSampleInstance(i) { 39 ok(i instanceof WebAssembly.Instance, "got a module"); 40 ok(i.exports[sampleExportName]() === sampleResult, "got result"); 41 } 42 43 function fetchSampleModuleCode() { 44 fetch(sampleURL) 45 .then(response => response.arrayBuffer()) 46 .then(buffer => { sampleCode = buffer; runTest(); }) 47 .catch(err => ok(false, String(err))); 48 } 49 50 function propertiesExist() { 51 if (!wasmIsSupported()) { 52 ok(!this.WebAssembly, "If the device doesn't support, there will be no WebAssembly object"); 53 SimpleTest.finish(); 54 return; 55 } 56 57 ok(WebAssembly, "WebAssembly object should exist"); 58 ok(WebAssembly.compile, "WebAssembly.compile function should exist"); 59 runTest(); 60 } 61 62 function compileFail() { 63 WebAssembly.compile().then( 64 () => { ok(false, "should have failed"); runTest(); } 65 ).catch( 66 err => { ok(err instanceof TypeError, "empty compile failed"); runTest(); } 67 ); 68 } 69 70 function compileSuccess() { 71 WebAssembly.compile(sampleCode).then( 72 m => { checkSampleModule(m); runTest(); } 73 ).catch( 74 err => { ok(false, String(err)); runTest(); } 75 ); 76 } 77 78 function compileManySuccess() { 79 const N = 100; 80 81 var arr = []; 82 for (var i = 0; i < N; i++) 83 arr.push(WebAssembly.compile(sampleCode)); 84 85 SpecialPowers.gc(); 86 87 Promise.all(arr).then(ms => { 88 ok(ms.length === N, "got the right number"); 89 for (var j = 0; j < N; j++) 90 checkSampleModule(ms[j]); 91 runTest(); 92 }).catch( 93 err => { ok(false, String(err)); runTest(); } 94 ); 95 } 96 97 function terminateCompileInWorker() { 98 var w = new Worker(`data:text/plain, 99 var sampleCode; 100 function spawnWork() { 101 const N = 100; 102 var arr = []; 103 for (var i = 0; i < N; i++) 104 arr.push(WebAssembly.compile(sampleCode)); 105 Promise.all(arr).then(spawnWork); 106 } 107 onmessage = e => { 108 sampleCode = e.data; 109 spawnWork(); 110 postMessage("ok"); 111 } 112 `); 113 w.postMessage(sampleCode); 114 w.onmessage = e => { 115 ok(e.data === "ok", "worker finished first step"); 116 w.terminate(); 117 runTest(); 118 }; 119 } 120 121 function instantiateFail() { 122 WebAssembly.instantiate().then( 123 () => { ok(false, "should have failed"); runTest(); } 124 ).catch( 125 err => { ok(err instanceof TypeError, "empty compile failed"); runTest(); } 126 ); 127 } 128 129 function instantiateSuccess() { 130 WebAssembly.instantiate(sampleCode).then( 131 r => { checkSampleModule(r.module); checkSampleInstance(r.instance); runTest(); } 132 ).catch( 133 err => { ok(false, String(err)); runTest(); } 134 ); 135 } 136 137 function chainSuccess() { 138 WebAssembly.compile(sampleCode).then( 139 m => WebAssembly.instantiate(m) 140 ).then( 141 i => { checkSampleInstance(i); runTest(); } 142 ).catch( 143 err => { ok(false, String(err)); runTest(); } 144 ); 145 } 146 147 function compileStreamingNonResponse() { 148 WebAssembly.compileStreaming({}) 149 .then(() => { ok(false); }) 150 .catch(err => { ok(err instanceof TypeError, "rejected {}"); runTest(); }); 151 } 152 153 function compileStreamingNoMime() { 154 WebAssembly.compileStreaming(new Response(new ArrayBuffer())) 155 .then(() => { ok(false); }) 156 .catch(err => { ok(err instanceof TypeError, "rejected no MIME type"); runTest(); }); 157 } 158 159 function compileStreamingBadMime() { 160 var badMimes = [ 161 "", 162 "application/js", 163 "application/js;application/wasm", 164 "application/wasm;application/js", 165 "application/wasm;", 166 "application/wasm1", 167 ]; 168 var promises = []; 169 for (let mimeType of badMimes) { 170 var init = { headers: { "Content-Type": mimeType } }; 171 promises.push( 172 WebAssembly.compileStreaming(new Response(sampleCode, init)) 173 .then(() => Promise.reject(), err => { 174 is(err.message, 175 `WebAssembly: Response has unsupported MIME type '${mimeType}' expected 'application/wasm'`, 176 "correct MIME type error message"); 177 return Promise.resolve(); 178 }) 179 ); 180 } 181 Promise.all(promises) 182 .then(() => { ok(true, "all bad MIME types rejected"); runTest(); }); 183 } 184 185 function compileStreamingGoodMime() { 186 var badMimes = [ 187 "application/wasm", 188 " application/wasm ", 189 "application/wasm ", 190 ]; 191 var promises = []; 192 for (let mimeType of badMimes) { 193 var init = { headers: { "Content-Type": mimeType } }; 194 promises.push( 195 WebAssembly.compileStreaming(new Response(sampleCode, init)) 196 ); 197 } 198 Promise.all(promises) 199 .then(() => { ok(true, "all good MIME types accepted"); runTest(); }); 200 } 201 202 function compileStreamingDoubleUseFail() { 203 fetch(sampleURL) 204 .then(response => { 205 WebAssembly.compileStreaming(response) 206 .then(m => { 207 checkSampleModule(m); 208 return WebAssembly.compileStreaming(response); 209 }) 210 .then( 211 () => ok(false, "should have failed on second use"), 212 () => { ok(true, "failed on second use"); runTest(); } 213 ); 214 }); 215 } 216 217 function compileStreamingNullBody() { 218 var init = { headers: { "Content-Type": "application/wasm" } }; 219 WebAssembly.compileStreaming(new Response(undefined, init)) 220 .then(() => { ok(false); }) 221 .catch(err => { ok(err instanceof WebAssembly.CompileError, "null body"); runTest(); }); 222 } 223 224 function compileStreamingFetch() { 225 WebAssembly.compileStreaming(fetch(sampleURL)) 226 .then(m => { checkSampleModule(m); runTest(); }) 227 .catch(err => { ok(false, String(err)); }); 228 } 229 230 function compileCachedBasic() { 231 const url = sampleURLWithRandomQuery(); 232 WebAssembly.compileStreaming(fetch(url)) 233 .then(module => { 234 checkSampleModule(module); 235 ok(!wasmLoadedFromCache(module), "not cached yet"); 236 while(!wasmHasTier2CompilationCompleted(module)); 237 return WebAssembly.compileStreaming(fetch(url)); 238 }) 239 .then(module => { 240 checkSampleModule(module); 241 ok(wasmLoadedFromCache(module), "loaded from cache"); 242 }) 243 .then(() => runTest()) 244 .catch(err => { ok(false, String(err)) }); 245 } 246 247 function compileCachedCompressed() { 248 const url = sampleURLWithRandomQuery(); 249 250 // It is a rough estimate that compilation code is about 251 // 2-4 times of the wasm file size. After it compression 252 // it will be less (about 60% ?) 253 const EstimatedCompilationArtifactSize = 2 * sampleFileSize; 254 const EstimatedCompressedArtifactSize = 0.6 * EstimatedCompilationArtifactSize; 255 256 // Set limit on cache entry so it will fail if it is not 257 // compressed. 258 const cleanup = () => { 259 SpecialPowers.clearUserPref("browser.cache.disk.max_entry_size") 260 }; 261 Promise.resolve(SpecialPowers.setIntPref("browser.cache.disk.max_entry_size", 262 Math.round(EstimatedCompressedArtifactSize / 1024) /* kb */)) 263 .then(() => WebAssembly.compileStreaming(fetch(url))) 264 .then(module => { 265 checkSampleModule(module); 266 ok(!wasmLoadedFromCache(module), "not cached yet"); 267 while(!wasmHasTier2CompilationCompleted(module)); 268 return WebAssembly.compileStreaming(fetch(url)); 269 }) 270 .then(module => { 271 checkSampleModule(module); 272 ok(wasmLoadedFromCache(module), "loaded from cache"); 273 }) 274 .then(() => { cleanup(); runTest() }) 275 .catch(err => { cleanup(); ok(false, String(err)) }); 276 } 277 278 function compileCachedTooLargeForCache() { 279 const url = sampleURLWithRandomQuery(); 280 // Set unreasonable limit, caching will fail. 281 // Bug 1719508 can change name of pref, this and 282 // compileCachedCompressed tests will become invalid. 283 const cleanup = () => { 284 SpecialPowers.clearUserPref("browser.cache.disk.max_entry_size") 285 }; 286 Promise.resolve(SpecialPowers.setIntPref("browser.cache.disk.max_entry_size", 1 /* kb */)) 287 .then(() => WebAssembly.compileStreaming(fetch(url))) 288 .then(module => { 289 console.log(module) 290 checkSampleModule(module); 291 ok(!wasmLoadedFromCache(module), "not cached yet"); 292 while(!wasmHasTier2CompilationCompleted(module)); 293 return WebAssembly.compileStreaming(fetch(url)); 294 }) 295 .then(module => { 296 checkSampleModule(module); 297 ok(!wasmLoadedFromCache(module), "not cached (size limit)"); 298 }) 299 .then(() => { cleanup(); runTest() }) 300 .catch(err => { cleanup(); ok(false, String(err)) }); 301 } 302 303 const Original = "original"; 304 const Clone = "clone"; 305 306 function compileCachedBothClonesHitCache(which) { 307 const url = sampleURLWithRandomQuery(); 308 WebAssembly.compileStreaming(fetch(url)) 309 .then(module => { 310 checkSampleModule(module); 311 ok(!wasmLoadedFromCache(module), "not cached yet"); 312 while(!wasmHasTier2CompilationCompleted(module)); 313 return fetch(url); 314 }) 315 .then(original => { 316 let clone = original.clone(); 317 if (which === Clone) [clone, original] = [original, clone]; 318 return Promise.all([ 319 WebAssembly.compileStreaming(original), 320 WebAssembly.compileStreaming(clone) 321 ]); 322 }) 323 .then(([m1, m2]) => { 324 checkSampleModule(m1); 325 ok(wasmLoadedFromCache(m1), "clone loaded from cache"); 326 checkSampleModule(m2); 327 ok(wasmLoadedFromCache(m2), "original loaded from cache"); 328 }) 329 .then(() => runTest()) 330 .catch(err => { ok(false, String(err)) }); 331 } 332 333 function compileCachedCacheThroughClone(which) { 334 const url = sampleURLWithRandomQuery(); 335 fetch(url) 336 .then(original => { 337 ok(true, "fun time"); 338 let clone = original.clone(); 339 if (which === Clone) [clone, original] = [original, clone]; 340 return Promise.all([ 341 WebAssembly.compileStreaming(original), 342 clone.arrayBuffer() 343 ]); 344 }) 345 .then(([module, buffer]) => { 346 ok(!wasmLoadedFromCache(module), "not cached yet"); 347 ok(buffer instanceof ArrayBuffer); 348 while(!wasmHasTier2CompilationCompleted(module)); 349 return WebAssembly.compileStreaming(fetch(url)); 350 }) 351 .then(m => { 352 ok(wasmLoadedFromCache(m), "cache hit of " + which); 353 }) 354 .then(() => runTest()) 355 .catch(err => { ok(false, String(err)) }); 356 } 357 358 function instantiateStreamingFetch() { 359 WebAssembly.instantiateStreaming(fetch(sampleURL)) 360 .then(({module, instance}) => { checkSampleModule(module); checkSampleInstance(instance); runTest(); }) 361 .catch(err => { ok(false, String(err)); }); 362 } 363 364 function compileManyStreamingFetch() { 365 const N = 20; 366 367 var arr = []; 368 for (var i = 0; i < N; i++) 369 arr.push(WebAssembly.compileStreaming(fetch(sampleURL))); 370 371 SpecialPowers.gc(); 372 373 Promise.all(arr).then(ms => { 374 ok(ms.length === N, "got the right number"); 375 for (var j = 0; j < N; j++) 376 checkSampleModule(ms[j]); 377 runTest(); 378 }).catch( 379 err => { ok(false, String(err)); runTest(); } 380 ); 381 } 382 383 function runWorkerTests() { 384 var w = new Worker("test_webassembly_compile_worker.js"); 385 w.postMessage(sampleCode); 386 w.onmessage = e => { 387 ok(e.data === "ok", "worker test: " + e.data); 388 runTest(); 389 }; 390 } 391 392 function terminateCompileStreamingInWorker() { 393 var w = new Worker("test_webassembly_compile_worker_terminate.js"); 394 w.onmessage = e => { 395 ok(e.data === "ok", "worker streaming terminate test: " + e.data); 396 w.terminate(); 397 runTest(); 398 }; 399 } 400 401 var tests = [ propertiesExist, 402 compileFail, 403 compileSuccess, 404 compileManySuccess, 405 terminateCompileInWorker, 406 instantiateFail, 407 instantiateSuccess, 408 chainSuccess, 409 compileStreamingNonResponse, 410 compileStreamingNoMime, 411 compileStreamingBadMime, 412 compileStreamingGoodMime, 413 compileStreamingDoubleUseFail, 414 compileStreamingNullBody, 415 compileStreamingFetch, 416 ...(isCachingEnabled ? [ 417 compileCachedBasic, 418 compileCachedCompressed, 419 compileCachedTooLargeForCache, 420 compileCachedBothClonesHitCache.bind(Original), 421 compileCachedBothClonesHitCache.bind(Clone), 422 compileCachedCacheThroughClone.bind(Original), 423 compileCachedCacheThroughClone.bind(Clone), 424 ]: []), 425 instantiateStreamingFetch, 426 compileManyStreamingFetch, 427 runWorkerTests, 428 terminateCompileStreamingInWorker, 429 ]; 430 431 // This initialization must always run 432 tests.unshift(fetchSampleModuleCode); 433 434 function runTest() { 435 if (!tests.length) { 436 SimpleTest.finish(); 437 return; 438 } 439 440 var test = tests.shift(); 441 test(); 442 } 443 444 SimpleTest.waitForExplicitFinish(); 445 runTest(); 446 </script> 447 </body> 448 </html>