tor-browser

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

i31ref.js (4966B)


      1 // -0 is a valid value with which to create an i31ref, but because it
      2 // roundtrips to +0, it wrecks a lot of our WASM tests (which use Object.is for
      3 // comparison). Therefore we don't include -0 in the main list, and deal with
      4 // that complexity in this file specifically.
      5 WasmI31refValues.push(-0);
      6 
      7 let InvalidI31Values = [
      8  null,
      9  Number.EPSILON,
     10  Number.MAX_SAFE_INTEGER,
     11  Number.MIN_SAFE_INTEGER,
     12  Number.MIN_VALUE,
     13  Number.MAX_VALUE,
     14  Infinity,
     15  -Infinity,
     16  Number.NaN,
     17  // Number objects are not coerced
     18  ...WasmI31refValues.map(n => new Number(n)),
     19  // Non-integers are not valid
     20  ...WasmI31refValues.map(n => n + 0.1),
     21  ...WasmI31refValues.map(n => n + 0.5),
     22  ...WasmI31refValues.map(n => n + 0.9)
     23 ];
     24 
     25 // Return an equivalent JS number for if a JS number is converted to i31ref
     26 // and then zero extended back to 32-bits.
     27 function valueAsI31GetU(value) {
     28  // Zero extending will drop the sign bit, if any
     29  return value & 0x7fffffff;
     30 }
     31 
     32 let identity = (n) => n;
     33 
     34 let {
     35  castFromAnyref,
     36  castFromExternref,
     37  refI31,
     38  refI31Identity,
     39  i31GetU,
     40  i31GetS,
     41  i31EqualsI31,
     42  i31EqualsEq
     43 } = wasmEvalText(`(module
     44  (func $identity (import "" "identity") (param anyref) (result anyref))
     45 
     46  (func (export "castFromAnyref") (param anyref) (result i32)
     47    local.get 0
     48    ref.test (ref i31)
     49  )
     50  (func (export "castFromExternref") (param externref) (result i32)
     51    local.get 0
     52    any.convert_extern
     53    ref.test (ref i31)
     54  )
     55  (func (export "refI31") (param i32) (result anyref)
     56    local.get 0
     57    ref.i31
     58  )
     59  (func (export "refI31Identity") (param i32) (result anyref)
     60    local.get 0
     61    ref.i31
     62    call $identity
     63  )
     64  (func (export "i31GetU") (param i32) (result i32)
     65    local.get 0
     66    ref.i31
     67    i31.get_u
     68  )
     69  (func (export "i31GetS") (param i32) (result i32)
     70    local.get 0
     71    ref.i31
     72    i31.get_s
     73  )
     74  (func (export "i31EqualsI31") (param i32) (param i32) (result i32)
     75    (ref.eq
     76      (ref.i31 local.get 0)
     77      (ref.i31 local.get 1)
     78    )
     79  )
     80  (func (export "i31EqualsEq") (param i32) (param eqref) (result i32)
     81    (ref.eq
     82      (ref.i31 local.get 0)
     83      local.get 1
     84    )
     85  )
     86 )`, {"": {identity}}).exports;
     87 
     88 // Test that wasm will represent JS number values that are 31-bit integers as
     89 // an i31ref
     90 for (let i of WasmI31refValues) {
     91  assertEq(castFromAnyref(i), 1);
     92  assertEq(castFromExternref(i), 1);
     93 }
     94 
     95 // Test that wasm will not represent a JS value that is not a 31-bit number as
     96 // an i31ref
     97 for (let i of InvalidI31Values) {
     98  assertEq(castFromAnyref(i), 0);
     99  assertEq(castFromExternref(i), 0);
    100 }
    101 
    102 // Test that we can roundtrip 31-bit integers through the i31ref type
    103 // faithfully.
    104 for (let i of WasmI31refValues) {
    105  assertEq(refI31(i), Object.is(i, -0) ? 0 : i);
    106  assertEq(refI31Identity(i), Object.is(i, -0) ? 0 : i);
    107  assertEq(i31GetU(i), valueAsI31GetU(i));
    108  assertEq(i31GetS(i), Object.is(i, -0) ? 0 : i);
    109 }
    110 
    111 // Test that i31ref values are truncated when given a 32-bit value
    112 for (let i of WasmI31refValues) {
    113  let adjusted = i | 0x80000000;
    114  assertEq(refI31(adjusted), Object.is(i, -0) ? 0 : i);
    115 }
    116 
    117 // Test that comparing identical i31 values works
    118 for (let a of WasmI31refValues) {
    119  for (let b of WasmI31refValues) {
    120    assertEq(!!i31EqualsI31(a, b), a === b);
    121  }
    122 }
    123 
    124 // Test that an i31ref is never mistaken for a different kind of reference
    125 for (let a of WasmI31refValues) {
    126  for (let b of WasmEqrefValues) {
    127    assertEq(!!i31EqualsEq(a, b), a === b);
    128  }
    129 }
    130 
    131 // Test that i32 values get wrapped correctly
    132 const bigI32Tests = [
    133  {
    134    input: MaxI31refValue + 1,
    135    expected: MinI31refValue,
    136  },
    137  {
    138    input: MinI31refValue - 1,
    139    expected: MaxI31refValue,
    140  },
    141 ]
    142 for (const {input, expected} of bigI32Tests) {
    143  const { get, getElem } = wasmEvalText(`(module
    144    (func (export "get") (param i32) (result i32)
    145      (i31.get_s (ref.i31 (local.get 0)))
    146    )
    147 
    148    (table i31ref (elem (item (ref.i31 (i32.const ${input})))))
    149    (func (export "getElem") (result i32)
    150      (i31.get_s (table.get 0 (i32.const 0)))
    151    )
    152  )`).exports;
    153  assertEq(get(input), expected);
    154  assertEq(getElem(), expected);
    155 }
    156 
    157 // Test that (ref.i31 (i32 const value)) optimization is correct
    158 for (let value of WasmI31refValues) {
    159  let {compare} = wasmEvalText(`(module
    160  (func $innerCompare (param i32) (param i31ref) (result i32)
    161    (ref.eq
    162      (ref.i31 local.get 0)
    163      local.get 1
    164    )
    165  )
    166  (func (export "compare") (result i32)
    167    i32.const ${value}
    168    (ref.i31 i32.const ${value})
    169    call $innerCompare
    170  )
    171 )`).exports;
    172  assertEq(compare(value), 1);
    173 }
    174 
    175 const { i31GetU_null, i31GetS_null } = wasmEvalText(`(module
    176  (func (export "i31GetU_null") (result i32)
    177    ref.null i31
    178    i31.get_u
    179  )
    180  (func (export "i31GetS_null") (result i32)
    181    ref.null i31
    182    i31.get_s
    183  )
    184 )`).exports;
    185 
    186 assertErrorMessage(() => i31GetU_null(), WebAssembly.RuntimeError, /dereferencing null pointer/);
    187 assertErrorMessage(() => i31GetS_null(), WebAssembly.RuntimeError, /dereferencing null pointer/);