tor-browser

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

js-promise-integration.any.js (11754B)


      1 // META: global=window,dedicatedworker,jsshell
      2 // META: script=/wasm/jsapi/wasm-module-builder.js
      3 
      4 // Test for invalid wrappers
      5 test(() => {
      6  assert_throws_js(TypeError, () => WebAssembly.promising({}),
      7      "Argument 0 must be a function");
      8  assert_throws_js(TypeError, () => WebAssembly.promising(() => {}),
      9      "Argument 0 must be a WebAssembly exported function");
     10  assert_throws_js(TypeError, () => WebAssembly.Suspending(() => {}),
     11      "WebAssembly.Suspending must be invoked with 'new'");
     12  assert_throws_js(TypeError, () => new WebAssembly.Suspending({}),
     13      "Argument 0 must be a function");
     14 
     15  function asmModule() {
     16      "use asm";
     17 
     18      function x(v) {
     19          v = v | 0;
     20      }
     21      return x;
     22  }
     23  assert_throws_js(TypeError, () => WebAssembly.promising(asmModule()),
     24      "Argument 0 must be a WebAssembly exported function");
     25 },"Valid use of API");
     26 
     27 test(() => {
     28  let builder = new WasmModuleBuilder();
     29  builder.addGlobal(kWasmI32, true).exportAs('g');
     30  builder.addFunction("test", kSig_i_v)
     31      .addBody([
     32          kExprI32Const, 42,
     33          kExprGlobalSet, 0,
     34          kExprI32Const, 0
     35      ]).exportFunc();
     36  let instance = builder.instantiate();
     37  let wrapper = WebAssembly.promising(instance.exports.test);
     38  wrapper();
     39  assert_equals(42, instance.exports.g.value);
     40 },"Promising function always entered");
     41 
     42 promise_test(async () => {
     43  let builder = new WasmModuleBuilder();
     44  let import_index = builder.addImport('m', 'import', kSig_i_v);
     45  builder.addFunction("test", kSig_i_i)
     46      .addBody([
     47          kExprCallFunction, import_index, // suspend
     48      ]).exportFunc();
     49  let js_import = () => 42;
     50  let instance = builder.instantiate({
     51      m: {
     52          import: js_import
     53      }
     54  });
     55  let wrapped_export = WebAssembly.promising(instance.exports.test);
     56  let export_promise = wrapped_export();
     57  assert_true(export_promise instanceof Promise);
     58  assert_equals(await export_promise, 42);
     59 }, "Always get a Promise");
     60 
     61 promise_test(async () => {
     62  let builder = new WasmModuleBuilder();
     63  let import_index = builder.addImport('m', 'import', kSig_i_i);
     64  builder.addFunction("test", kSig_i_i)
     65      .addBody([
     66          kExprLocalGet, 0,
     67          kExprCallFunction, import_index, // suspend
     68      ]).exportFunc();
     69  let js_import = new WebAssembly.Suspending(() => Promise.resolve(42));
     70  let instance = builder.instantiate({
     71      m: {
     72          import: js_import
     73      }
     74  });
     75  let wrapped_export = WebAssembly.promising(instance.exports.test);
     76  let export_promise = wrapped_export();
     77  assert_true(export_promise instanceof Promise);
     78  assert_equals(await export_promise, 42);
     79 }, "Suspend once");
     80 
     81 promise_test(async () => {
     82  let builder = new WasmModuleBuilder();
     83  builder.addGlobal(kWasmI32, true).exportAs('g');
     84  let import_index = builder.addImport('m', 'import', kSig_i_v);
     85  // void test() {
     86  //   for (i = 0; i < 5; ++i) {
     87  //     g = g + await import();
     88  //   }
     89  // }
     90  builder.addFunction("test", kSig_v_i)
     91      .addLocals({
     92          i32_count: 1
     93      })
     94      .addBody([
     95          kExprI32Const, 5,
     96          kExprLocalSet, 1,
     97          kExprLoop, kWasmStmt,
     98          kExprCallFunction, import_index, // suspend
     99          kExprGlobalGet, 0,
    100          kExprI32Add,
    101          kExprGlobalSet, 0,
    102          kExprLocalGet, 1,
    103          kExprI32Const, 1,
    104          kExprI32Sub,
    105          kExprLocalTee, 1,
    106          kExprBrIf, 0,
    107          kExprEnd,
    108      ]).exportFunc();
    109  let i = 0;
    110 
    111  function js_import() {
    112      return Promise.resolve(++i);
    113  };
    114  let wasm_js_import = new WebAssembly.Suspending(js_import);
    115  let instance = builder.instantiate({
    116      m: {
    117          import: wasm_js_import
    118      }
    119  });
    120  let wrapped_export = WebAssembly.promising(instance.exports.test);
    121  let export_promise = wrapped_export();
    122  assert_equals(instance.exports.g.value, 0);
    123  assert_true(export_promise instanceof Promise);
    124  await export_promise;
    125  assert_equals(instance.exports.g.value, 15);
    126 }, "Suspend/resume in a loop");
    127 
    128 promise_test(async () => {
    129  let builder = new WasmModuleBuilder();
    130  let import_index = builder.addImport('m', 'import', kSig_i_v);
    131  builder.addFunction("test", kSig_i_v)
    132      .addBody([
    133          kExprCallFunction, import_index, // suspend
    134      ]).exportFunc();
    135  let js_import = new WebAssembly.Suspending(() => Promise.resolve(42));
    136  let instance = builder.instantiate({
    137      m: {
    138          import: js_import
    139      }
    140  });
    141  let wrapped_export = WebAssembly.promising(instance.exports.test);
    142  assert_equals(await wrapped_export(), 42);
    143 
    144  // Also try with a JS function with a mismatching arity.
    145  js_import = new WebAssembly.Suspending((unused) => Promise.resolve(42));
    146  instance = builder.instantiate({
    147      m: {
    148          import: js_import
    149      }
    150  });
    151  wrapped_export = WebAssembly.promising(instance.exports.test);
    152  assert_equals(await wrapped_export(), 42);
    153 
    154  // Also try with a proxy.
    155  js_import = new WebAssembly.Suspending(new Proxy(() => Promise.resolve(42), {}));
    156  instance = builder.instantiate({
    157      m: {
    158          import: js_import
    159      }
    160  });
    161  wrapped_export = WebAssembly.promising(instance.exports.test);
    162  assert_equals(await wrapped_export(), 42);
    163 },"Suspending with mismatched args and via Proxy");
    164 
    165 function recordAbeforeB() {
    166  let AbeforeB = [];
    167  let setA = () => {
    168      AbeforeB.push("A")
    169  }
    170  let setB = () => {
    171      AbeforeB.push("B")
    172  }
    173  let isAbeforeB = () =>
    174      AbeforeB[0] == "A" && AbeforeB[1] == "B";
    175 
    176  let isBbeforeA = () =>
    177    AbeforeB[0] == "B" && AbeforeB[1] == "A";
    178 
    179  return {
    180      setA: setA,
    181      setB: setB,
    182      isAbeforeB: isAbeforeB,
    183      isBbeforeA: isBbeforeA,
    184  }
    185 }
    186 
    187 promise_test(async () => {
    188  let builder = new WasmModuleBuilder();
    189  let AbeforeB = recordAbeforeB();
    190  let import42_index = builder.addImport('m', 'import42', kSig_i_i);
    191  let importSetA_index = builder.addImport('m', 'setA', kSig_v_v);
    192  builder.addFunction("test", kSig_i_i)
    193      .addBody([
    194          kExprLocalGet, 0,
    195          kExprCallFunction, import42_index, // suspend?
    196          kExprCallFunction, importSetA_index
    197      ]).exportFunc();
    198  let import42 = new WebAssembly.Suspending(() => Promise.resolve(42));
    199  let instance = builder.instantiate({
    200      m: {
    201          import42: import42,
    202          setA: AbeforeB.setA
    203      }
    204  });
    205 
    206  let wrapped_export = WebAssembly.promising(instance.exports.test);
    207 
    208  let exported_promise = wrapped_export();
    209 
    210  AbeforeB.setB();
    211 
    212  assert_equals(await exported_promise, 42);
    213 
    214  assert_true(AbeforeB.isBbeforeA());
    215 }, "Make sure we actually suspend");
    216 
    217 promise_test(async () => {
    218  let builder = new WasmModuleBuilder();
    219  let AbeforeB = recordAbeforeB();
    220  let import42_index = builder.addImport('m', 'import42', kSig_i_i);
    221  let importSetA_index = builder.addImport('m', 'setA', kSig_v_v);
    222  builder.addFunction("test", kSig_i_i)
    223      .addBody([
    224          kExprLocalGet, 0,
    225          kExprCallFunction, import42_index, // suspend?
    226          kExprCallFunction, importSetA_index
    227      ]).exportFunc();
    228  let import42 = new WebAssembly.Suspending(() => 42);
    229  let instance = builder.instantiate({
    230      m: {
    231          import42: import42,
    232          setA: AbeforeB.setA
    233      }
    234  });
    235 
    236  let wrapped_export = WebAssembly.promising(instance.exports.test);
    237 
    238  let exported_promise = wrapped_export();
    239  AbeforeB.setB();
    240 
    241  assert_equals(await exported_promise, 42);
    242  assert_true(AbeforeB.isBbeforeA());
    243 }, "Do suspend even if the import's return value is not a Promise by wrapping it with Promise.resolve");
    244 
    245 promise_test(async (t) => {
    246  let tag = new WebAssembly.Tag({
    247      parameters: ['i32']
    248  });
    249  let builder = new WasmModuleBuilder();
    250  let import_index = builder.addImport('m', 'import', kSig_i_i);
    251  let tag_index = builder.addImportedTag('m', 'tag', kSig_v_i);
    252  builder.addFunction("test", kSig_i_i)
    253      .addBody([
    254          kExprTry, kWasmI32,
    255          kExprLocalGet, 0,
    256          kExprCallFunction, import_index,
    257          kExprCatch, tag_index,
    258          kExprEnd
    259      ]).exportFunc();
    260 
    261  function js_import(unused) {
    262      return Promise.reject(new WebAssembly.Exception(tag, [42]));
    263  };
    264  let wasm_js_import = new WebAssembly.Suspending(js_import);
    265 
    266  let instance = builder.instantiate({
    267      m: {
    268          import: wasm_js_import,
    269          tag: tag
    270      }
    271  });
    272  let wrapped_export = WebAssembly.promising(instance.exports.test);
    273  let export_promise = wrapped_export();
    274  assert_true(export_promise instanceof Promise);
    275  assert_equals(await export_promise, 42);
    276 }, "Catch rejected promise");
    277 
    278 async function TestSandwich(suspend) {
    279  // Set up a 'sandwich' scenario. The call chain looks like:
    280  // top (wasm) -> outer (js) -> bottom (wasm) -> inner (js)
    281  // If 'suspend' is true, the inner JS function returns a Promise, which
    282  // suspends the bottom wasm function, which returns a Promise, which suspends
    283  // the top wasm function, which returns a Promise. The inner Promise
    284  // resolves first, which resumes the bottom continuation. Then the outer
    285  // promise resolves which resumes the top continuation.
    286  // If 'suspend' is false, the bottom JS function returns a regular value and
    287  // no computation is suspended.
    288  let builder = new WasmModuleBuilder();
    289  let inner_index = builder.addImport('m', 'inner', kSig_i_i);
    290  let outer_index = builder.addImport('m', 'outer', kSig_i_i);
    291  builder.addFunction("top", kSig_i_i)
    292      .addBody([
    293          kExprLocalGet, 0,
    294          kExprCallFunction, outer_index
    295      ]).exportFunc();
    296  builder.addFunction("bottom", kSig_i_i)
    297      .addBody([
    298          kExprLocalGet, 0,
    299          kExprCallFunction, inner_index
    300      ]).exportFunc();
    301 
    302  let inner = new WebAssembly.Suspending(() => suspend ? Promise.resolve(42) : 43);
    303 
    304  let export_inner;
    305  let outer = new WebAssembly.Suspending(() => export_inner());
    306 
    307  let instance = builder.instantiate({
    308      m: {
    309          inner,
    310          outer
    311      }
    312  });
    313  export_inner = WebAssembly.promising(instance.exports.bottom);
    314  let export_top = WebAssembly.promising(instance.exports.top);
    315  let result = export_top();
    316  assert_true(result instanceof Promise);
    317  if (suspend)
    318      assert_equals(await result, 42);
    319  else
    320      assert_equals(await result, 43);
    321 }
    322 
    323 promise_test(async () => {
    324  TestSandwich(true);
    325 }, "Test sandwich with suspension");
    326 
    327 promise_test(async () => {
    328  TestSandwich(false);
    329 }, "Test sandwich with no suspension");
    330 
    331 test(() => {
    332  // Check that a promising function with no return is allowed.
    333  let builder = new WasmModuleBuilder();
    334  builder.addFunction("export", kSig_v_v).addBody([]).exportFunc();
    335  let instance = builder.instantiate();
    336  let export_wrapper = WebAssembly.promising(instance.exports.export);
    337  assert_true(export_wrapper instanceof Function);
    338 },"Promising with no return");
    339 
    340 promise_test(async () => {
    341  let builder1 = new WasmModuleBuilder();
    342  let import_index = builder1.addImport('m', 'import', kSig_i_v);
    343  builder1.addFunction("f", kSig_i_v)
    344      .addBody([
    345          kExprCallFunction, import_index, // suspend
    346          kExprI32Const, 1,
    347          kExprI32Add,
    348      ]).exportFunc();
    349  let js_import = new WebAssembly.Suspending(() => Promise.resolve(1));
    350  let instance1 = builder1.instantiate({
    351      m: {
    352          import: js_import
    353      }
    354  });
    355  let builder2 = new WasmModuleBuilder();
    356  import_index = builder2.addImport('m', 'import', kSig_i_v);
    357  builder2.addFunction("main", kSig_i_v)
    358      .addBody([
    359          kExprCallFunction, import_index,
    360          kExprI32Const, 1,
    361          kExprI32Add,
    362      ]).exportFunc();
    363  let instance2 = builder2.instantiate({
    364      m: {
    365          import: instance1.exports.f
    366      }
    367  });
    368  let wrapped_export = WebAssembly.promising(instance2.exports.main);
    369  assert_equals(await wrapped_export(), 3);
    370 },"Suspend two modules");