tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>