tor-browser

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

js-promise-integration.new.js (16975B)


      1 // JS promise integration API tests
      2 // Modified https://github.com/WebAssembly/js-promise-integration/tree/main/test/js-api/js-promise-integration
      3 
      4 var tests = Promise.resolve();
      5 function test(fn, n) {
      6  if (n == null) n = (new Error()).stack.split('\n')[1];
      7  tests = tests.then(() => {
      8    let t = {res: null};
      9    print("# " + n);
     10    fn(t);
     11    return t.res;
     12  }, (e) => {
     13    throw e;
     14  });
     15 }
     16 function promise_test(fn, n) {
     17  if (n == null) n = (new Error()).stack.split('\n')[1];
     18  tests = tests.then(() => {
     19    let t = {res: null};
     20    print("# " + n);
     21    return fn(t);
     22  });
     23 }
     24 function assert_true(f) { assertEq(f, true); }
     25 function assert_false(f) { assertEq(f, false); }
     26 function assert_equals(a, b) { assertEq(a, b); }
     27 function assert_array_equals(a, b) {
     28  assert_equals(a.length, a.length);
     29  for (let i = 0; i < a.length; i++) {
     30    assert_equals(a[i], b[i]);
     31  }
     32 }
     33 function assert_throws(ex, fn, msg) {
     34  try {
     35    fn();
     36  } catch(e) {
     37    assertEq(e instanceof ex, true, `Type of e: ${e.constructor.name}`);
     38    return;
     39  }
     40  assertEq(false, true, "fail expected");
     41 }
     42 function promise_rejects(t, obj, p) {
     43  t.res = p.then(() => {
     44    assertEq(true, false);
     45  }, (e) => {
     46    assertEq(e instanceof obj.constructor, true);
     47  });
     48 }
     49 
     50 function Promising(wasm_export) {
     51  return WebAssembly.promising(wasm_export);
     52 }
     53 
     54 function Suspending(jsFun){
     55  return new WebAssembly.Suspending(jsFun);
     56 }
     57 
     58 // Test for invalid wrappers
     59 test(() => {
     60  assert_throws(TypeError, () => WebAssembly.promising({}),
     61      /Argument 0 must be a function/);
     62  assert_throws(TypeError, () => WebAssembly.promising(() => {}),
     63      /Argument 0 must be a WebAssembly exported function/);
     64  assert_throws(TypeError, () => WebAssembly.Suspending(() => {}),
     65      /WebAssembly.Suspending must be invoked with 'new'/);
     66  assert_throws(TypeError, () => new WebAssembly.Suspending({}),
     67      /Argument 0 must be a function/);
     68  function asmModule() {
     69    "use asm";
     70    function x(v) {
     71      v = v | 0;
     72    }
     73    return x;
     74  }
     75  assert_throws(TypeError, () => WebAssembly.promising(asmModule()),
     76      /Argument 0 must be a WebAssembly exported function/);
     77 });
     78 
     79 test(() => {
     80  let instance = wasmEvalText(`(module
     81  (type (func (result i32)))
     82  (func $test (type 0) (result i32)
     83    i32.const 42
     84    global.set 0
     85    i32.const 0
     86  )
     87  (global (mut i32) i32.const 0)
     88  (export "g" (global 0))
     89  (export "test" (func $test))
     90 )`);
     91  let wrapper = WebAssembly.promising(instance.exports.test);
     92  wrapper();
     93  assert_equals(42, instance.exports.g.value);
     94 });
     95 
     96 promise_test(async () => {
     97  let js_import = Suspending(() => Promise.resolve(42));
     98  let instance = wasmEvalText(`(module
     99  (type (func (param i32) (result i32)))
    100  (type (func (param i32) (result i32)))
    101  (import "m" "import" (func (type 0)))
    102  (func $test (type 1) (param i32) (result i32)
    103    local.get 0
    104    call 0
    105  )
    106  (export "test" (func $test))
    107 )`, {m: {import: js_import}});
    108  let wrapped_export = Promising(instance.exports.test);
    109  let export_promise = wrapped_export();
    110  assert_true(export_promise instanceof Promise);
    111  assert_equals(await export_promise, 42);
    112 }, "Suspend once");
    113 
    114 promise_test(async () => {
    115  let i = 0;
    116  function js_import() {
    117    return Promise.resolve(++i);
    118  };
    119  let wasm_js_import = Suspending(js_import);
    120  let instance = wasmEvalText(`(module
    121  (type (func (param i32) (result i32)))
    122  (type (func (param i32)))
    123  (import "m" "import" (func (type 0)))
    124  (func $test (type 1) (param i32)
    125    (local i32)
    126    i32.const 5
    127    local.set 1
    128    loop ;; label = @1
    129      local.get 0
    130      call 0
    131      global.get 0
    132      i32.add
    133      global.set 0
    134      local.get 1
    135      i32.const 1
    136      i32.sub
    137      local.tee 1
    138      br_if 0 (;@1;)
    139    end
    140  )
    141  (global (mut i32) i32.const 0)
    142  (export "g" (global 0))
    143  (export "test" (func $test))
    144 )`, {m: {import: wasm_js_import}});
    145  let wrapped_export = Promising(instance.exports.test);
    146  let export_promise = wrapped_export();
    147  assert_equals(instance.exports.g.value, 0);
    148  assert_true(export_promise instanceof Promise);
    149  await export_promise;
    150  assert_equals(instance.exports.g.value, 15);
    151 }, "Suspend/resume in a loop");
    152 
    153 promise_test(async ()=>{
    154  let js_import = new WebAssembly.Suspending(() => Promise.resolve(42));
    155  let instance = wasmEvalText(`(module
    156  (type (func (result i32)))
    157  (type (func (result i32)))
    158  (import "m" "import" (func (type 0)))
    159  (func $test (type 1) (result i32)
    160    call 0
    161  )
    162  (export "test" (func $test))
    163 )`, {m: {import: js_import}});
    164  let wrapped_export = WebAssembly.promising(instance.exports.test);
    165  assert_equals(await wrapped_export(), 42);
    166 
    167  // Also try with a JS function with a mismatching arity.
    168  js_import = new WebAssembly.Suspending((unused) => Promise.resolve(42));
    169  instance = wasmEvalText(`(module
    170  (type (func (result i32)))
    171  (type (func (result i32)))
    172  (import "m" "import" (func (type 0)))
    173  (func $test (type 1) (result i32)
    174    call 0
    175  )
    176  (export "test" (func $test))
    177 )`, {m: {import: js_import}});
    178  wrapped_export = WebAssembly.promising(instance.exports.test);
    179  assert_equals(await wrapped_export(), 42);
    180 
    181  // Also try with a proxy.
    182  js_import = new WebAssembly.Suspending(new Proxy(() => Promise.resolve(42), {}));
    183  instance = wasmEvalText(`(module
    184  (type (func (result i32)))
    185  (type (func (result i32)))
    186  (import "m" "import" (func (type 0)))
    187  (func $test (type 1) (result i32)
    188    call 0
    189  )
    190  (export "test" (func $test))
    191 )`, {m: {import: js_import}});
    192  wrapped_export = WebAssembly.promising(instance.exports.test);
    193  assert_equals(await wrapped_export(), 42);
    194 });
    195 
    196 function recordAbeforeB(){
    197  let AbeforeB = [];
    198  let setA = ()=>{
    199    AbeforeB.push("A")
    200  }
    201  let setB = ()=>{
    202    AbeforeB.push("B")
    203  }
    204  let isAbeforeB = ()=>
    205    AbeforeB[0]=="A" && AbeforeB[1]=="B";
    206 
    207  let showAbeforeB = ()=>{
    208    console.log(AbeforeB)
    209  }
    210  return {setA : setA, setB : setB, isAbeforeB :isAbeforeB,showAbeforeB:showAbeforeB}
    211 }
    212 
    213 promise_test(async () => {
    214  let AbeforeB = recordAbeforeB();
    215  let import42 = Suspending(()=>Promise.resolve(42));
    216  let instance = wasmEvalText(`(module
    217  (type (func (param i32) (result i32)))
    218  (type (func))
    219  (type (func (param i32) (result i32)))
    220  (import "m" "import42" (func (type 0)))
    221  (import "m" "setA" (func (type 1)))
    222  (func $test (type 2) (param i32) (result i32)
    223    local.get 0
    224    call 0
    225    call 1
    226  )
    227  (export "test" (func $test))
    228 )`, {m: {import42: import42, setA:AbeforeB.setA}});
    229 
    230  let wrapped_export = Promising(instance.exports.test);
    231 
    232 //  AbeforeB.showAbeforeB();
    233  let exported_promise = wrapped_export();
    234 //  AbeforeB.showAbeforeB();
    235 
    236  AbeforeB.setB();
    237 
    238  //print(await exported_promise);
    239  assert_equals(await exported_promise, 42);
    240 //  AbeforeB.showAbeforeB();
    241 
    242  assert_false(AbeforeB.isAbeforeB());
    243 }, "Make sure we actually suspend");
    244 
    245 promise_test(async () => {
    246  let AbeforeB = recordAbeforeB();
    247  let import42 = Suspending(()=>42);
    248  let instance = wasmEvalText(`(module
    249  (type (func (param i32) (result i32)))
    250  (type (func))
    251  (type (func (param i32) (result i32)))
    252  (import "m" "import42" (func (type 0)))
    253  (import "m" "setA" (func (type 1)))
    254  (func $test (type 2) (param i32) (result i32)
    255    local.get 0
    256    call 0
    257    call 1
    258  )
    259  (export "test" (func $test))
    260 )`, {
    261  m: {import42: import42, setA:AbeforeB.setA}});
    262 
    263  let wrapped_export = Promising(instance.exports.test);
    264 
    265  let exported_promise = wrapped_export();
    266  AbeforeB.setB();
    267 
    268  assert_equals(await exported_promise, 42);
    269  // AbeforeB.showAbeforeB();
    270 
    271  assert_false(AbeforeB.isAbeforeB());
    272 }, "Suspend if the import's return value is not a Promise");
    273 
    274 test(t => {
    275  console.log("Throw after the first suspension");
    276  let tag = new WebAssembly.Tag({parameters: []});
    277  function js_import() {
    278    return Promise.resolve();
    279  };
    280  let wasm_js_import = Suspending(js_import);
    281 
    282  let instance = wasmEvalText(`(module
    283  (type (func (param i32) (result i32)))
    284  (type (func))
    285  (type (func (param i32) (result i32)))
    286  (import "m" "import" (func (type 0)))
    287  (import "m" "tag" (tag (type 1)))
    288  (func $test (type 2) (param i32) (result i32)
    289    local.get 0
    290    call 0
    291    throw 0
    292  )
    293  (export "test" (func $test))
    294 )`, {m: {import: wasm_js_import, tag: tag}});
    295  let wrapped_export = Promising(instance.exports.test);
    296  let export_promise = wrapped_export();
    297  assert_true(export_promise instanceof Promise);
    298  promise_rejects(t, new WebAssembly.Exception(tag, []), export_promise);
    299 }, "Throw after the first suspension");
    300 
    301 promise_test(async (t) => {
    302  console.log("Rejecting promise");
    303  let tag = new WebAssembly.Tag({parameters: ['i32']});
    304  function js_import() {
    305    return Promise.reject(new WebAssembly.Exception(tag, [42]));
    306  };
    307  let wasm_js_import = Suspending(js_import);
    308 
    309  let instance = wasmEvalText(`(module
    310  (type (func (param i32) (result i32)))
    311  (type (func (param i32)))
    312  (type (func (param i32) (result i32)))
    313  (import "m" "import" (func (type 0)))
    314  (import "m" "tag" (tag (type 1) (param i32)))
    315  (func $test (type 2) (param i32) (result i32)
    316    try (result i32) ;; label = @1
    317      local.get 0
    318      call 0
    319    catch 0
    320    end
    321  )
    322  (export "test" (func $test))
    323 )`, {m: {import: wasm_js_import, tag: tag}});
    324  let wrapped_export = Promising(instance.exports.test);
    325  let export_promise = wrapped_export();
    326  assert_true(export_promise instanceof Promise);
    327  assert_equals(await export_promise, 42);
    328 }, "Rejecting promise");
    329 
    330 async function TestNestedSuspenders(suspend) {
    331  console.log("nested suspending "+suspend);
    332  // Nest two suspenders. The call chain looks like:
    333  // outer (wasm) -> outer (js) -> inner (wasm) -> inner (js)
    334  // If 'suspend' is true, the inner JS function returns a Promise, which
    335  // suspends the inner wasm function, which returns a Promise, which suspends
    336  // the outer wasm function, which returns a Promise. The inner Promise
    337  // resolves first, which resumes the inner continuation. Then the outer
    338  // promise resolves which resumes the outer continuation.
    339  // If 'suspend' is false, the inner JS function returns a regular value and
    340  // no computation is suspended.
    341 
    342  let inner = Suspending(() => suspend ? Promise.resolve(42) : 43);
    343 
    344  let export_inner;
    345  let outer = Suspending(() => export_inner());
    346 
    347  let instance = wasmEvalText(`(module
    348  (type (func (param i32) (result i32)))
    349  (type (func (param i32) (result i32)))
    350  (type (func (param i32) (result i32)))
    351  (type (func (param i32) (result i32)))
    352  (import "m" "inner" (func (type 0)))
    353  (import "m" "outer" (func (type 1)))
    354  (func $outer (type 2) (param i32) (result i32)
    355    local.get 0
    356    call 1
    357  )
    358  (func $inner (type 3) (param i32) (result i32)
    359    local.get 0
    360    call 0
    361  )
    362  (export "outer" (func $outer))
    363  (export "inner" (func $inner))
    364 )`, {m: {inner, outer}});
    365  export_inner = Promising(instance.exports.inner);
    366  let export_outer = Promising(instance.exports.outer);
    367  let result = export_outer();
    368  assert_true(result instanceof Promise);
    369  if(suspend)
    370    assert_equals(await result, 42);
    371  else
    372    assert_equals(await result, 43);
    373 }
    374 
    375 promise_test(async () => {
    376  TestNestedSuspenders(true);
    377 }, "Test nested suspenders with suspension");
    378 
    379 promise_test(async () => {
    380  TestNestedSuspenders(false);
    381 }, "Test nested suspenders with no suspension");
    382 
    383 test(() => {
    384  console.log("Call import with an invalid suspender");
    385  let js_import = Suspending(() => Promise.resolve(42));
    386  let instance = wasmEvalText(`(module
    387  (type (func (param i32) (result i32)))
    388  (type (func (param i32) (result i32)))
    389  (type (func (param i32) (result i32)))
    390  (import "m" "import" (func (type 0)))
    391  (func $test (type 1) (param i32) (result i32)
    392    local.get 0
    393    call 0
    394  )
    395  (func $return_suspender (type 2) (param i32) (result i32)
    396    local.get 0
    397  )
    398  (export "test" (func $test))
    399  (export "return_suspender" (func $return_suspender))
    400 )`, {m: {import: js_import}});
    401  let suspender = Promising(instance.exports.return_suspender)();
    402  for (s of [suspender, null, undefined, {}]) {
    403    assert_throws(WebAssembly.SuspendError, () => instance.exports.test(s));
    404  }
    405 }, "Call import with an invalid suspender");
    406 
    407 // Throw an exception before suspending. The export wrapper should return a
    408 // promise rejected with the exception.
    409 promise_test(async (t) => {
    410  let tag = new WebAssembly.Tag({parameters: []});
    411 
    412  let instance = wasmEvalText(`(module
    413  (type (func))
    414  (type (func (result i32)))
    415  (import "m" "tag" (tag (type 0)))
    416  (func $test (type 1) (result i32)
    417    throw 0
    418  )
    419  (export "test" (func $test))
    420 )`, {m: {tag: tag}});
    421  let wrapped_export = WebAssembly.promising(instance.exports.test);
    422  let export_promise = wrapped_export();
    423 
    424  promise_rejects(t, new WebAssembly.Exception(tag, []), export_promise);
    425 });
    426 
    427 // Throw an exception after the first resume event, which propagates to the
    428 // promise wrapper.
    429 promise_test(async (t) => {
    430  let tag = new WebAssembly.Tag({parameters: []});
    431  function js_import() {
    432    return Promise.resolve(42);
    433  };
    434  let wasm_js_import = new WebAssembly.Suspending(js_import);
    435 
    436  let instance = wasmEvalText(`(module
    437  (type (func (result i32)))
    438  (type (func))
    439  (type (func (result i32)))
    440  (import "m" "import" (func (type 0)))
    441  (import "m" "tag" (tag (type 1)))
    442  (func $test (type 2) (result i32)
    443    call 0
    444    throw 0
    445  )
    446  (export "test" (func $test))
    447 )`, {m: {import: wasm_js_import, tag: tag}});
    448  let wrapped_export = WebAssembly.promising(instance.exports.test);
    449  let export_promise = wrapped_export();
    450 
    451  promise_rejects(t, new WebAssembly.Exception(tag, []), export_promise);
    452 });
    453 
    454 promise_test(async () => {
    455  let tag = new WebAssembly.Tag({parameters: ['i32']});
    456  function js_import() {
    457    return Promise.reject(new WebAssembly.Exception(tag, [42]));
    458  };
    459  let wasm_js_import = new WebAssembly.Suspending(js_import);
    460 
    461  let instance = wasmEvalText(`(module
    462  (type (func (result i32)))
    463  (type (func (param i32)))
    464  (type (func (result i32)))
    465  (import "m" "import" (func (type 0)))
    466  (import "m" "tag" (tag (type 1) (param i32)))
    467  (func $test (type 2) (result i32)
    468    try (result i32) ;; label = @1
    469      call 0
    470    catch 0
    471    end
    472  )
    473  (export "test" (func $test))
    474 )`, {m: {import: wasm_js_import, tag: tag}});
    475  let wrapped_export = WebAssembly.promising(instance.exports.test);
    476  assert_equals(await wrapped_export(), 42);
    477 });
    478 
    479 test(() => {
    480  console.log("no return allowed");
    481  // Check that a promising function with no return is allowed.
    482  let instance = wasmEvalText(`(module
    483  (type (func))
    484  (func $export (type 0))
    485  (export "export" (func $export))
    486 )`);
    487  let export_wrapper = WebAssembly.promising(instance.exports.export);
    488  assert_true(export_wrapper instanceof Function);
    489 }, "wrapper type");
    490 
    491 promise_test(async (t) => {
    492  let instance = wasmEvalText(`(module
    493  (type (func (result i32)))
    494  (func $test (type 0) (result i32)
    495    call $test
    496  )
    497  (export "test" (func $test))
    498 )`);
    499  let wrapper = WebAssembly.promising(instance.exports.test);
    500 
    501  promise_rejects(t, new Error(), wrapper(), /Maximum call stack size exceeded/);
    502 });
    503 
    504 promise_test(async (t) => {
    505  // The call stack of this test looks like:
    506  // export1 -> import1 -> export2 -> import2
    507  // Where export1 is "promising" and import2 is "suspending". Returning a
    508  // promise from import2 should trap because of the JS import in the middle.
    509  let instance;
    510  function import1() {
    511    // import1 -> export2 (unwrapped)
    512    instance.exports.export2();
    513  }
    514  function import2() {
    515    return Promise.resolve(0);
    516  }
    517  import2 = new WebAssembly.Suspending(import2);
    518  instance = wasmEvalText(`(module
    519  (type (func (result i32)))
    520  (type (func (result i32)))
    521  (type (func (result i32)))
    522  (type (func (result i32)))
    523  (import "m" "import1" (func (type 0)))
    524  (import "m" "import2" (func (type 1)))
    525  (func $export1 (type 2) (result i32)
    526    call 0
    527  )
    528  (func $export2 (type 3) (result i32)
    529    call 1
    530  )
    531  (export "export1" (func $export1))
    532  (export "export2" (func $export2))
    533 )`,
    534      {'m':
    535        {'import1': import1,
    536         'import2': import2
    537        }});
    538  // export1 (promising)
    539  let wrapper = WebAssembly.promising(instance.exports.export1);
    540  promise_rejects(t, new WebAssembly.SuspendError(), wrapper(),
    541      /trying to suspend JS frames/);
    542 });
    543 
    544 promise_test(async () => {
    545  let js_import = new WebAssembly.Suspending(() => Promise.resolve(1));
    546  let instance1 = wasmEvalText(`(module
    547  (type (func (result i32)))
    548  (type (func (result i32)))
    549  (import "m" "import" (func (type 0)))
    550  (func $f (type 1) (result i32)
    551    call 0
    552    i32.const 1
    553    i32.add
    554  )
    555  (export "f" (func $f))
    556 )`, {m: {import: js_import}});
    557  let instance2 = wasmEvalText(`(module
    558  (type (func (result i32)))
    559  (type (func (result i32)))
    560  (import "m" "import" (func (type 0)))
    561  (func $main (type 1) (result i32)
    562    call 0
    563    i32.const 1
    564    i32.add
    565  )
    566  (export "main" (func $main))
    567 )`, {m: {import: instance1.exports.f}});
    568  let wrapped_export = WebAssembly.promising(instance2.exports.main);
    569  assert_equals(await wrapped_export(), 3);
    570 });
    571 
    572 tests.then(() => print('Done'));