tor-browser

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

basic.any.js (9709B)


      1 // META: global=window,dedicatedworker,jsshell
      2 // META: script=/wasm/jsapi/assertions.js
      3 // META: script=/wasm/jsapi/wasm-module-builder.js
      4 // META: script=/wasm/jsapi/js-string/polyfill.js
      5 
      6 // Generate two sets of exports, one from a polyfill implementation and another
      7 // from the builtins provided by the host.
      8 let polyfillExports;
      9 let builtinExports;
     10 setup(() => {
     11  // Compile a module that exports a function for each builtin that will call
     12  // it. We could just generate a module that re-exports the builtins, but that
     13  // would not catch any special codegen that could happen when direct calling
     14  // a known builtin function from wasm.
     15  const builder = new WasmModuleBuilder();
     16  const arrayIndex = builder.addArray(kWasmI16, true, kNoSuperType, true);
     17  const builtins = [
     18    {
     19      name: "test",
     20      params: [kWasmExternRef],
     21      results: [kWasmI32],
     22    },
     23    {
     24      name: "cast",
     25      params: [kWasmExternRef],
     26      results: [wasmRefType(kWasmExternRef)],
     27    },
     28    {
     29      name: "fromCharCodeArray",
     30      params: [wasmRefNullType(arrayIndex), kWasmI32, kWasmI32],
     31      results: [wasmRefType(kWasmExternRef)],
     32    },
     33    {
     34      name: "intoCharCodeArray",
     35      params: [kWasmExternRef, wasmRefNullType(arrayIndex), kWasmI32],
     36      results: [kWasmI32],
     37    },
     38    {
     39      name: "fromCharCode",
     40      params: [kWasmI32],
     41      results: [wasmRefType(kWasmExternRef)],
     42    },
     43    {
     44      name: "fromCodePoint",
     45      params: [kWasmI32],
     46      results: [wasmRefType(kWasmExternRef)],
     47    },
     48    {
     49      name: "charCodeAt",
     50      params: [kWasmExternRef, kWasmI32],
     51      results: [kWasmI32],
     52    },
     53    {
     54      name: "codePointAt",
     55      params: [kWasmExternRef, kWasmI32],
     56      results: [kWasmI32],
     57    },
     58    {
     59      name: "length",
     60      params: [kWasmExternRef],
     61      results: [kWasmI32],
     62    },
     63    {
     64      name: "concat",
     65      params: [kWasmExternRef, kWasmExternRef],
     66      results: [wasmRefType(kWasmExternRef)],
     67    },
     68    {
     69      name: "substring",
     70      params: [kWasmExternRef, kWasmI32, kWasmI32],
     71      results: [wasmRefType(kWasmExternRef)],
     72    },
     73    {
     74      name: "equals",
     75      params: [kWasmExternRef, kWasmExternRef],
     76      results: [kWasmI32],
     77    },
     78    {
     79      name: "compare",
     80      params: [kWasmExternRef, kWasmExternRef],
     81      results: [kWasmI32],
     82    },
     83  ];
     84 
     85  // Add a function type for each builtin
     86  for (let builtin of builtins) {
     87    builtin.type = builder.addType({
     88      params: builtin.params,
     89      results: builtin.results
     90    });
     91  }
     92 
     93  // Add an import for each builtin
     94  for (let builtin of builtins) {
     95    builtin.importFuncIndex = builder.addImport(
     96      "wasm:js-string",
     97      builtin.name,
     98      builtin.type);
     99  }
    100 
    101  // Generate an exported function to call the builtin
    102  for (let builtin of builtins) {
    103    let func = builder.addFunction(builtin.name + "Imp", builtin.type);
    104    func.addLocals(builtin.params.length);
    105    let body = [];
    106    for (let i = 0; i < builtin.params.length; i++) {
    107      body.push(kExprLocalGet);
    108      body.push(...wasmSignedLeb(i));
    109    }
    110    body.push(kExprCallFunction);
    111    body.push(...wasmSignedLeb(builtin.importFuncIndex));
    112    func.addBody(body);
    113    func.exportAs(builtin.name);
    114  }
    115 
    116  const buffer = builder.toBuffer();
    117 
    118  // Instantiate this module using the builtins from the host
    119  const builtinModule = new WebAssembly.Module(buffer, {
    120    builtins: ["js-string"]
    121  });
    122  const builtinInstance = new WebAssembly.Instance(builtinModule, {});
    123  builtinExports = builtinInstance.exports;
    124 
    125  // Instantiate this module using the polyfill module
    126  const polyfillModule = new WebAssembly.Module(buffer);
    127  const polyfillInstance = new WebAssembly.Instance(polyfillModule, {
    128    "wasm:js-string": polyfillImports
    129  });
    130  polyfillExports = polyfillInstance.exports;
    131 });
    132 
    133 // A helper function to assert that the behavior of two functions are the
    134 // same.
    135 function assert_same_behavior(funcA, funcB, ...params) {
    136  let resultA;
    137  let errA = null;
    138  try {
    139    resultA = funcA(...params);
    140  } catch (err) {
    141    errA = err;
    142  }
    143 
    144  let resultB;
    145  let errB = null;
    146  try {
    147    resultB = funcB(...params);
    148  } catch (err) {
    149    errB = err;
    150  }
    151 
    152  if (errA || errB) {
    153    assert_equals(errA === null, errB === null, errA ? errA.message : errB.message);
    154    assert_equals(Object.getPrototypeOf(errA), Object.getPrototypeOf(errB));
    155  }
    156  assert_equals(resultA, resultB);
    157 
    158  if (errA) {
    159    throw errA;
    160  }
    161  return resultA;
    162 }
    163 
    164 function assert_throws_if(func, shouldThrow, constructor) {
    165  let error = null;
    166  try {
    167    func();
    168  } catch (e) {
    169    error = e;
    170  }
    171  assert_equals(error !== null, shouldThrow, "shouldThrow mismatch");
    172  if (shouldThrow && error !== null) {
    173    assert_true(error instanceof constructor);
    174  }
    175 }
    176 
    177 // Constant values used in the tests below
    178 const testStrings = [
    179  "",
    180  "a",
    181  "1",
    182  "ab",
    183  "hello, world",
    184  "\n",
    185  "☺",
    186  "☺☺",
    187  String.fromCodePoint(0x10000, 0x10001)
    188 ];
    189 const testCharCodes = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff];
    190 const testCodePoints = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff, 0x10000, 0x10001];
    191 const testExternRefValues = [
    192  null,
    193  undefined,
    194  true,
    195  false,
    196  {x:1337},
    197  ["abracadabra"],
    198  13.37,
    199  -0,
    200  0x7fffffff + 0.1,
    201  -0x7fffffff - 0.1,
    202  0x80000000 + 0.1,
    203  -0x80000000 - 0.1,
    204  0xffffffff + 0.1,
    205  -0xffffffff - 0.1,
    206  Number.EPSILON,
    207  Number.MAX_SAFE_INTEGER,
    208  Number.MIN_SAFE_INTEGER,
    209  Number.MIN_VALUE,
    210  Number.MAX_VALUE,
    211  Number.NaN,
    212  "hi",
    213  37n,
    214  new Number(42),
    215  new Boolean(true),
    216  Symbol("status"),
    217  () => 1337,
    218 ];
    219 
    220 // Test that `test` and `cast` work on various JS values. Run all the
    221 // other builtins and assert that they also perform equivalent type
    222 // checks.
    223 test(() => {
    224  for (let a of testExternRefValues) {
    225    let isString = assert_same_behavior(
    226      builtinExports['test'],
    227      polyfillExports['test'],
    228      a
    229    );
    230 
    231    assert_throws_if(() => assert_same_behavior(
    232        builtinExports['cast'],
    233        polyfillExports['cast'],
    234        a
    235      ), !isString, WebAssembly.RuntimeError);
    236 
    237    let arrayMutI16 = helperExports.createArrayMutI16(10);
    238    assert_throws_if(() => assert_same_behavior(
    239        builtinExports['intoCharCodeArray'],
    240        polyfillExports['intoCharCodeArray'],
    241        a, arrayMutI16, 0
    242      ), !isString, WebAssembly.RuntimeError);
    243 
    244    assert_throws_if(() => assert_same_behavior(
    245        builtinExports['charCodeAt'],
    246        polyfillExports['charCodeAt'],
    247        a, 0
    248      ), !isString, WebAssembly.RuntimeError);
    249 
    250    assert_throws_if(() => assert_same_behavior(
    251        builtinExports['codePointAt'],
    252        polyfillExports['codePointAt'],
    253        a, 0
    254      ), !isString, WebAssembly.RuntimeError);
    255 
    256    assert_throws_if(() => assert_same_behavior(
    257        builtinExports['length'],
    258        polyfillExports['length'],
    259        a
    260      ), !isString, WebAssembly.RuntimeError);
    261 
    262    assert_throws_if(() => assert_same_behavior(
    263        builtinExports['concat'],
    264        polyfillExports['concat'],
    265        a, a
    266      ), !isString, WebAssembly.RuntimeError);
    267 
    268    assert_throws_if(() => assert_same_behavior(
    269        builtinExports['substring'],
    270        polyfillExports['substring'],
    271        a, 0, 0
    272      ), !isString, WebAssembly.RuntimeError);
    273 
    274    assert_throws_if(() => assert_same_behavior(
    275        builtinExports['equals'],
    276        polyfillExports['equals'],
    277        a, a
    278      ), a !== null && !isString, WebAssembly.RuntimeError);
    279 
    280    assert_throws_if(() => assert_same_behavior(
    281        builtinExports['compare'],
    282        polyfillExports['compare'],
    283        a, a
    284      ), !isString, WebAssembly.RuntimeError);
    285  }
    286 });
    287 
    288 // Test that `fromCharCode` works on various char codes
    289 test(() => {
    290  for (let a of testCharCodes) {
    291    assert_same_behavior(
    292      builtinExports['fromCharCode'],
    293      polyfillExports['fromCharCode'],
    294      a
    295    );
    296  }
    297 });
    298 
    299 // Test that `fromCodePoint` works on various code points
    300 test(() => {
    301  for (let a of testCodePoints) {
    302    assert_same_behavior(
    303      builtinExports['fromCodePoint'],
    304      polyfillExports['fromCodePoint'],
    305      a
    306    );
    307  }
    308 });
    309 
    310 // Perform tests on various strings
    311 test(() => {
    312  for (let a of testStrings) {
    313    let length = assert_same_behavior(
    314      builtinExports['length'],
    315      polyfillExports['length'],
    316      a
    317    );
    318 
    319    for (let i = 0; i < length; i++) {
    320      let charCode = assert_same_behavior(
    321        builtinExports['charCodeAt'],
    322        polyfillExports['charCodeAt'],
    323        a, i
    324      );
    325    }
    326 
    327    for (let i = 0; i < length; i++) {
    328      let charCode = assert_same_behavior(
    329        builtinExports['codePointAt'],
    330        polyfillExports['codePointAt'],
    331        a, i
    332      );
    333    }
    334 
    335    let arrayMutI16 = helperExports.createArrayMutI16(length);
    336    assert_same_behavior(
    337      builtinExports['intoCharCodeArray'],
    338      polyfillExports['intoCharCodeArray'],
    339      a, arrayMutI16, 0
    340    );
    341 
    342    assert_same_behavior(
    343      builtinExports['fromCharCodeArray'],
    344      polyfillExports['fromCharCodeArray'],
    345      arrayMutI16, 0, length
    346    );
    347 
    348    for (let i = 0; i < length; i++) {
    349      for (let j = 0; j < length; j++) {
    350        assert_same_behavior(
    351          builtinExports['substring'],
    352          polyfillExports['substring'],
    353          a, i, j
    354        );
    355      }
    356    }
    357  }
    358 });
    359 
    360 // Test various binary operations
    361 test(() => {
    362  for (let a of testStrings) {
    363    for (let b of testStrings) {
    364      assert_same_behavior(
    365        builtinExports['concat'],
    366        polyfillExports['concat'],
    367        a, b
    368      );
    369 
    370      assert_same_behavior(
    371        builtinExports['equals'],
    372        polyfillExports['equals'],
    373        a, b
    374      );
    375 
    376      assert_same_behavior(
    377        builtinExports['compare'],
    378        polyfillExports['compare'],
    379        a, b
    380      );
    381    }
    382  }
    383 });