tor-browser

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

basic-tests.js (15922B)


      1 // Basic functional tests for the Atomics primitives.
      2 //
      3 // These do not test atomicity, just that calling and coercions and
      4 // indexing and exception behavior all work right.
      5 //
      6 // These do not test the wait/wake operations.
      7 
      8 load(libdir + "asserts.js");
      9 
     10 var DEBUG = false;		// Set to true for useful printouts
     11 
     12 function dprint(...xs) {
     13    if (!DEBUG)
     14 return;
     15    var s = "";
     16    for ( var x in xs )
     17 s += String(xs[x]);
     18    print(s);
     19 }
     20 
     21 // Clone a function so that we get reliable inlining of primitives with --ion-eager.
     22 // For eg testMethod and testFunction that are polymorphic in the array a,
     23 // the inliner gets confused and stops inlining after Int8 -- not what we want.
     24 function CLONE(f) {
     25    return this.eval("(" + f.toString() + ")");
     26 }
     27 
     28 function testMethod(a, ...indices) {
     29    dprint("Method: " + a.constructor.name);
     30    var poison;
     31    switch (a.BYTES_PER_ELEMENT) {
     32    case 1: poison = 0x5A; break;
     33    case 2: poison = 0x5A5A; break;
     34    case 4: poison = 0x5A5A5A5A; break;
     35    }
     36    for ( var i=0 ; i < indices.length ; i++ ) {
     37 var x = indices[i];
     38 if (x > 0)
     39     a[x-1] = poison;
     40 if (x < a.length-1)
     41     a[x+1] = poison;
     42 
     43 // val = 0
     44 assertEq(Atomics.compareExchange(a, x, 0, 37), 0);
     45 // val = 37
     46 assertEq(Atomics.compareExchange(a, x, 37, 5), 37);
     47 // val = 5
     48 assertEq(Atomics.compareExchange(a, x, 7, 8), 5); // ie should fail
     49 // val = 5
     50 assertEq(Atomics.compareExchange(a, x, 5, 9), 5);
     51 // val = 9
     52 assertEq(Atomics.compareExchange(a, x, 5, 0), 9); // should also fail
     53 
     54 	// val = 9
     55 assertEq(Atomics.exchange(a, x, 4), 9);
     56 // val = 4
     57 assertEq(Atomics.exchange(a, x, 9), 4);
     58 
     59 // val = 9
     60 assertEq(Atomics.load(a, x), 9);
     61 // val = 9
     62 assertEq(Atomics.store(a, x, 14), 14); // What about coercion?
     63 // val = 14
     64 assertEq(Atomics.load(a, x), 14);
     65 // val = 14
     66 Atomics.store(a, x, 0);
     67 // val = 0
     68 
     69 // val = 0
     70 assertEq(Atomics.add(a, x, 3), 0);
     71 // val = 3
     72 assertEq(Atomics.sub(a, x, 2), 3);
     73 // val = 1
     74 assertEq(Atomics.or(a, x, 6), 1);
     75 // val = 7
     76 assertEq(Atomics.and(a, x, 14), 7);
     77 // val = 6
     78 assertEq(Atomics.xor(a, x, 5), 6);
     79 // val = 3
     80 assertEq(Atomics.load(a, x), 3);
     81 // val = 3
     82 Atomics.store(a, x, 0);
     83 // val = 0
     84 
     85 // Check adjacent elements were not affected
     86 if (x > 0) {
     87     assertEq(a[x-1], poison);
     88     a[x-1] = 0;
     89 }
     90 if (x < a.length-1) {
     91     assertEq(a[x+1], poison);
     92     a[x+1] = 0;
     93 }
     94    }
     95 }
     96 
     97 function testFunction(a, ...indices) {
     98    dprint("Function: " + a.constructor.name);
     99    var poison;
    100    switch (a.BYTES_PER_ELEMENT) {
    101    case 1: poison = 0x5A; break;
    102    case 2: poison = 0x5A5A; break;
    103    case 4: poison = 0x5A5A5A5A; break;
    104    }
    105    for ( var i=0 ; i < indices.length ; i++ ) {
    106 var x = indices[i];
    107 if (x > 0)
    108     a[x-1] = poison;
    109 if (x < a.length-1)
    110     a[x+1] = poison;
    111 
    112 // val = 0
    113 assertEq(gAtomics_compareExchange(a, x, 0, 37), 0);
    114 // val = 37
    115 assertEq(gAtomics_compareExchange(a, x, 37, 5), 37);
    116 // val = 5
    117 assertEq(gAtomics_compareExchange(a, x, 7, 8), 5); // ie should fail
    118 // val = 5
    119 assertEq(gAtomics_compareExchange(a, x, 5, 9), 5);
    120 // val = 9
    121 assertEq(gAtomics_compareExchange(a, x, 5, 0), 9); // should also fail
    122 
    123 // val = 9
    124 assertEq(gAtomics_exchange(a, x, 4), 9);
    125 // val = 4
    126 assertEq(gAtomics_exchange(a, x, 9), 4);
    127 
    128 // val = 9
    129 assertEq(gAtomics_load(a, x), 9);
    130 // val = 9
    131 assertEq(gAtomics_store(a, x, 14), 14); // What about coercion?
    132 // val = 14
    133 assertEq(gAtomics_load(a, x), 14);
    134 // val = 14
    135 gAtomics_store(a, x, 0);
    136 // val = 0
    137 
    138 // val = 0
    139 assertEq(gAtomics_add(a, x, 3), 0);
    140 // val = 3
    141 assertEq(gAtomics_sub(a, x, 2), 3);
    142 // val = 1
    143 assertEq(gAtomics_or(a, x, 6), 1);
    144 // val = 7
    145 assertEq(gAtomics_and(a, x, 14), 7);
    146 // val = 6
    147 assertEq(gAtomics_xor(a, x, 5), 6);
    148 // val = 3
    149 assertEq(gAtomics_load(a, x), 3);
    150 // val = 3
    151 gAtomics_store(a, x, 0);
    152 // val = 0
    153 
    154 // Check adjacent elements were not affected
    155 if (x > 0) {
    156     assertEq(a[x-1], poison);
    157     a[x-1] = 0;
    158 }
    159 if (x < a.length-1) {
    160     assertEq(a[x+1], poison);
    161     a[x+1] = 0;
    162 }
    163    }
    164 }
    165 
    166 function testTypeCAS(a) {
    167    dprint("Type: " + a.constructor.name);
    168 
    169    var thrown = false;
    170    try {
    171 Atomics.compareExchange([0], 0, 0, 1);
    172    }
    173    catch (e) {
    174 thrown = true;
    175 assertEq(e instanceof TypeError, true);
    176    }
    177    assertEq(thrown, true);
    178 
    179    // All these variants should be OK
    180    Atomics.compareExchange(a, 0, 0.7, 1.8);
    181    Atomics.compareExchange(a, 0, "0", 1);
    182    Atomics.compareExchange(a, 0, 0, "1");
    183    Atomics.compareExchange(a, 0, 0);
    184 }
    185 
    186 function testTypeBinop(a, op) {
    187    dprint("Type: " + a.constructor.name);
    188 
    189    var thrown = false;
    190    try {
    191 op([0], 0, 1);
    192    }
    193    catch (e) {
    194 thrown = true;
    195 assertEq(e instanceof TypeError, true);
    196    }
    197    assertEq(thrown, true);
    198 
    199    // These are all OK
    200    op(a, 0, 0.7);
    201    op(a, 0, "0");
    202    op(a, 0);
    203 }
    204 
    205 var globlength = 0;		// Will be set later
    206 
    207 function testRangeCAS(a) {
    208    dprint("Range: " + a.constructor.name);
    209 
    210    var msg = /out-of-range index/; // A generic message
    211 
    212    assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg);
    213    assertEq(a[0], 0);
    214 
    215    // Converted to 0
    216    assertEq(Atomics.compareExchange(a, "hi", 0, 33), 0);
    217    assertEq(a[0], 33);
    218    a[0] = 0;
    219 
    220    assertErrorMessage(() => Atomics.compareExchange(a, a.length + 5, 0, 1), RangeError, msg);
    221    assertEq(a[0], 0);
    222 
    223    assertErrorMessage(() => Atomics.compareExchange(a, globlength, 0, 1), RangeError, msg);
    224    assertEq(a[0], 0);
    225 }
    226 
    227 // Ad-hoc tests for extreme and out-of-range values.
    228 // None of these should throw
    229 
    230 function testInt8Extremes(a) {
    231    dprint("Int8 extremes");
    232 
    233    a[10] = 0;
    234    a[11] = 0;
    235 
    236    Atomics.store(a, 10, 255);
    237    assertEq(a[10], -1);
    238    assertEq(Atomics.load(a, 10), -1);
    239 
    240    Atomics.add(a, 10, 255); // should coerce to -1
    241    assertEq(a[10], -2);
    242    assertEq(Atomics.load(a, 10), -2);
    243 
    244    Atomics.add(a, 10, -1);
    245    assertEq(a[10], -3);
    246    assertEq(Atomics.load(a, 10), -3);
    247 
    248    Atomics.sub(a, 10, 255);	// should coerce to -1
    249    assertEq(a[10], -2);
    250    assertEq(Atomics.load(a, 10), -2);
    251 
    252    Atomics.sub(a, 10, 256);	// should coerce to 0
    253    assertEq(a[10], -2);
    254    assertEq(Atomics.load(a, 10), -2);
    255 
    256    Atomics.and(a, 10, -1);	// Preserve all
    257    assertEq(a[10], -2);
    258    assertEq(Atomics.load(a, 10), -2);
    259 
    260    Atomics.and(a, 10, 256);	// Preserve none
    261    assertEq(a[10], 0);
    262    assertEq(Atomics.load(a, 10), 0);
    263 
    264    Atomics.store(a, 10, 255);
    265    assertEq(Atomics.exchange(a, 10, 0), -1);
    266 
    267    assertEq(a[11], 0);
    268 }
    269 
    270 function testUint8Extremes(a) {
    271    dprint("Uint8 extremes");
    272 
    273    a[10] = 0;
    274    a[11] = 0;
    275 
    276    Atomics.store(a, 10, 255);
    277    assertEq(a[10], 255);
    278    assertEq(Atomics.load(a, 10), 255);
    279 
    280    Atomics.add(a, 10, 255);
    281    assertEq(a[10], 254);
    282    assertEq(Atomics.load(a, 10), 254);
    283 
    284    Atomics.add(a, 10, -1);
    285    assertEq(a[10], 253);
    286    assertEq(Atomics.load(a, 10), 253);
    287 
    288    Atomics.sub(a, 10, 255);
    289    assertEq(a[10], 254);
    290    assertEq(Atomics.load(a, 10), 254);
    291 
    292    Atomics.and(a, 10, -1);	// Preserve all
    293    assertEq(a[10], 254);
    294    assertEq(Atomics.load(a, 10), 254);
    295 
    296    Atomics.and(a, 10, 256);	// Preserve none
    297    assertEq(a[10], 0);
    298    assertEq(Atomics.load(a, 10), 0);
    299 
    300    Atomics.store(a, 10, 255);
    301    assertEq(Atomics.exchange(a, 10, 0), 255);
    302 
    303    assertEq(a[11], 0);
    304 }
    305 
    306 function testInt16Extremes(a) {
    307    dprint("Int16 extremes");
    308 
    309    a[10] = 0;
    310    a[11] = 0;
    311 
    312    Atomics.store(a, 10, 65535);
    313    assertEq(a[10], -1);
    314    assertEq(Atomics.load(a, 10), -1);
    315 
    316    Atomics.add(a, 10, 65535); // should coerce to -1
    317    assertEq(a[10], -2);
    318    assertEq(Atomics.load(a, 10), -2);
    319 
    320    Atomics.add(a, 10, -1);
    321    assertEq(a[10], -3);
    322    assertEq(Atomics.load(a, 10), -3);
    323 
    324    Atomics.sub(a, 10, 65535);	// should coerce to -1
    325    assertEq(a[10], -2);
    326    assertEq(Atomics.load(a, 10), -2);
    327 
    328    Atomics.sub(a, 10, 65536);	// should coerce to 0
    329    assertEq(a[10], -2);
    330    assertEq(Atomics.load(a, 10), -2);
    331 
    332    Atomics.and(a, 10, -1);	// Preserve all
    333    assertEq(a[10], -2);
    334    assertEq(Atomics.load(a, 10), -2);
    335 
    336    Atomics.and(a, 10, 65536);	// Preserve none
    337    assertEq(a[10], 0);
    338    assertEq(Atomics.load(a, 10), 0);
    339 
    340    assertEq(a[11], 0);
    341 }
    342 
    343 function testUint32(a) {
    344    var k = 0;
    345    for ( var i=0 ; i < 20 ; i++ ) {
    346 a[i] = i+5;
    347 k += a[i];
    348    }
    349 
    350    var sum = 0;
    351    for ( var i=0 ; i < 20 ; i++ )
    352 sum += Atomics.add(a, i, 1);
    353 
    354    assertEq(sum, k);
    355 }
    356 
    357 // This test is a reliable test of sign extension in the JIT where
    358 // testInt8Extremes is not (because there may not be enough type
    359 // information without a loop - see bug 1181062 for a description
    360 // of the general problem).
    361 
    362 function exchangeLoop(ta) {
    363    var sum = 0;
    364    for ( var i=0 ; i < 100000 ; i++ )
    365 sum += Atomics.exchange(ta, i & 15, 255);
    366    return sum;
    367 }
    368 
    369 function adHocExchange(SharedOrUnsharedArrayBuffer) {
    370    var a = new Int8Array(new SharedOrUnsharedArrayBuffer(16));
    371    for ( var i=0 ; i < a.length ; i++ )
    372 a[i] = 255;
    373    assertEq(exchangeLoop(a), -100000);
    374 }
    375 
    376 // isLockFree(n) may return true only if there is an integer array
    377 // on which atomic operations is allowed whose byte size is n,
    378 // ie, it must return false for n=7.
    379 //
    380 // SpiderMonkey has isLockFree(1), isLockFree(2), isLockFree(8), isLockFree(4)
    381 // on all supported platforms, only the last is guaranteed by the spec.
    382 
    383 var sizes   = [    1,     2,     3,     4,     5,     6,     7,  8,
    384                   9,    10,    11,    12];
    385 var answers = [ true,  true, false,  true, false, false, false, true,
    386        false, false, false, false];
    387 
    388 function testIsLockFree() {
    389    // This ought to defeat most compile-time resolution.
    390    for ( var i=0 ; i < sizes.length ; i++ ) {
    391 var v = Atomics.isLockFree(sizes[i]);
    392 var a = answers[i];
    393 assertEq(typeof v, 'boolean');
    394 assertEq(v, a);
    395    }
    396 
    397    // This ought to be optimizable.
    398    assertEq(Atomics.isLockFree(1), true);
    399    assertEq(Atomics.isLockFree(2), true);
    400    assertEq(Atomics.isLockFree(3), false);
    401    assertEq(Atomics.isLockFree(4), true);
    402    assertEq(Atomics.isLockFree(5), false);
    403    assertEq(Atomics.isLockFree(6), false);
    404    assertEq(Atomics.isLockFree(7), false);
    405    assertEq(Atomics.isLockFree(8), true);
    406    assertEq(Atomics.isLockFree(9), false);
    407    assertEq(Atomics.isLockFree(10), false);
    408    assertEq(Atomics.isLockFree(11), false);
    409    assertEq(Atomics.isLockFree(12), false);
    410 }
    411 
    412 function testIsLockFree2() {
    413    assertEq(Atomics.isLockFree(0), false);
    414    assertEq(Atomics.isLockFree(0/-1), false);
    415    assertEq(Atomics.isLockFree(3.5), false);
    416    assertEq(Atomics.isLockFree(Number.NaN), false);  // NaN => +0
    417    assertEq(Atomics.isLockFree(Number.POSITIVE_INFINITY), false);
    418    assertEq(Atomics.isLockFree(Number.NEGATIVE_INFINITY), false);
    419    assertEq(Atomics.isLockFree(-4), false);
    420    assertEq(Atomics.isLockFree('4'), true);
    421    assertEq(Atomics.isLockFree('-4'), false);
    422    assertEq(Atomics.isLockFree('4.5'), true);
    423    assertEq(Atomics.isLockFree('5.5'), false);
    424    assertEq(Atomics.isLockFree(new Number(4)), true);
    425    assertEq(Atomics.isLockFree(new String('4')), true);
    426    assertEq(Atomics.isLockFree(new Boolean(true)), true);
    427    var thrown = false;
    428    try {
    429 Atomics.isLockFree(Symbol('1'));
    430    } catch (e) {
    431 thrown = e;
    432    }
    433    assertEq(thrown instanceof TypeError, true);
    434    assertEq(Atomics.isLockFree(true), true);
    435    assertEq(Atomics.isLockFree(false), false);
    436    assertEq(Atomics.isLockFree(undefined), false);
    437    assertEq(Atomics.isLockFree(null), false);
    438    assertEq(Atomics.isLockFree({toString: () => '4'}), true);
    439    assertEq(Atomics.isLockFree({valueOf: () => 4}), true);
    440    assertEq(Atomics.isLockFree({valueOf: () => 5}), false);
    441    assertEq(Atomics.isLockFree({password: "qumquat"}), false);
    442 }
    443 
    444 function testUint8Clamped(sab) {
    445    var ta = new Uint8ClampedArray(sab);
    446    var thrown = false;
    447    try {
    448 CLONE(testMethod)(ta, 0);
    449    }
    450    catch (e) {
    451 thrown = true;
    452 assertEq(e instanceof TypeError, true);
    453    }
    454    assertEq(thrown, true);
    455 }
    456 
    457 function testWeirdIndices(SharedOrUnsharedArrayBuffer) {
    458    var a = new Int8Array(new SharedOrUnsharedArrayBuffer(16));
    459    a[3] = 10;
    460    assertEq(Atomics.load(a, "0x03"), 10);
    461    assertEq(Atomics.load(a, {valueOf: () => 3}), 10);
    462 }
    463 
    464 function isLittleEndian() {
    465    var xxx = new ArrayBuffer(2);
    466    var xxa = new Int16Array(xxx);
    467    var xxb = new Int8Array(xxx);
    468    xxa[0] = 37;
    469    var is_little = xxb[0] == 37;
    470    return is_little;
    471 }
    472 
    473 function runTests(SharedOrUnsharedArrayBuffer) {
    474    var is_little = isLittleEndian();
    475 
    476    // Currently the SharedArrayBuffer needs to be a multiple of 4K bytes in size.
    477    var sab = new SharedOrUnsharedArrayBuffer(4096);
    478 
    479    // Test that two arrays created on the same storage alias
    480    var t1 = new Int8Array(sab);
    481    var t2 = new Uint16Array(sab);
    482 
    483    assertEq(t1[0], 0);
    484    assertEq(t2[0], 0);
    485    t1[0] = 37;
    486    if (is_little)
    487 assertEq(t2[0], 37);
    488    else
    489 assertEq(t2[0], 37 << 8);
    490    t1[0] = 0;
    491 
    492    // Test that invoking as Atomics.whatever() works, on correct arguments.
    493    CLONE(testMethod)(new Int8Array(sab), 0, 42, 4095);
    494    CLONE(testMethod)(new Uint8Array(sab), 0, 42, 4095);
    495    CLONE(testMethod)(new Int16Array(sab), 0, 42, 2047);
    496    CLONE(testMethod)(new Uint16Array(sab), 0, 42, 2047);
    497    CLONE(testMethod)(new Int32Array(sab), 0, 42, 1023);
    498    CLONE(testMethod)(new Uint32Array(sab), 0, 42, 1023);
    499 
    500    // Test that invoking as v = Atomics.whatever; v() works, on correct arguments.
    501    gAtomics_compareExchange = Atomics.compareExchange;
    502    gAtomics_exchange = Atomics.exchange;
    503    gAtomics_load = Atomics.load;
    504    gAtomics_store = Atomics.store;
    505    gAtomics_add = Atomics.add;
    506    gAtomics_sub = Atomics.sub;
    507    gAtomics_and = Atomics.and;
    508    gAtomics_or = Atomics.or;
    509    gAtomics_xor = Atomics.xor;
    510 
    511    CLONE(testFunction)(new Int8Array(sab), 0, 42, 4095);
    512    CLONE(testFunction)(new Uint8Array(sab), 0, 42, 4095);
    513    CLONE(testFunction)(new Int16Array(sab), 0, 42, 2047);
    514    CLONE(testFunction)(new Uint16Array(sab), 0, 42, 2047);
    515    CLONE(testFunction)(new Int32Array(sab), 0, 42, 1023);
    516    CLONE(testFunction)(new Uint32Array(sab), 0, 42, 1023);
    517 
    518    // Test various range and type conditions
    519    var v8 = new Int8Array(sab);
    520    var v32 = new Int32Array(sab);
    521 
    522    CLONE(testTypeCAS)(v8);
    523    CLONE(testTypeCAS)(v32);
    524 
    525    CLONE(testTypeBinop)(v8, Atomics.add);
    526    CLONE(testTypeBinop)(v8, Atomics.sub);
    527    CLONE(testTypeBinop)(v8, Atomics.and);
    528    CLONE(testTypeBinop)(v8, Atomics.or);
    529    CLONE(testTypeBinop)(v8, Atomics.xor);
    530 
    531    CLONE(testTypeBinop)(v32, Atomics.add);
    532    CLONE(testTypeBinop)(v32, Atomics.sub);
    533    CLONE(testTypeBinop)(v32, Atomics.and);
    534    CLONE(testTypeBinop)(v32, Atomics.or);
    535    CLONE(testTypeBinop)(v32, Atomics.xor);
    536 
    537    // Test out-of-range references
    538    globlength = v8.length + 5;
    539    CLONE(testRangeCAS)(v8);
    540    globlength = v32.length + 5;
    541    CLONE(testRangeCAS)(v32);
    542 
    543    // Test extreme values
    544    testInt8Extremes(new Int8Array(sab));
    545    testUint8Extremes(new Uint8Array(sab));
    546    testInt16Extremes(new Int16Array(sab));
    547    testUint32(new Uint32Array(sab));
    548 
    549    // Test that Uint8ClampedArray is not accepted.
    550    testUint8Clamped(sab);
    551 
    552    // Misc ad-hoc tests
    553    adHocExchange(SharedOrUnsharedArrayBuffer);
    554 
    555    // Misc
    556    testIsLockFree();
    557    testIsLockFree2();
    558    testWeirdIndices(SharedOrUnsharedArrayBuffer);
    559 
    560    assertEq(Atomics[Symbol.toStringTag], "Atomics");
    561 }
    562 
    563 runTests(SharedArrayBuffer);
    564 runTests(ArrayBuffer);
    565 
    566 if (ArrayBuffer.prototype.resize) {
    567    class ResizableArrayBuffer {
    568        constructor(byteLength = 0) {
    569            return new ArrayBuffer(byteLength, {maxByteLength: byteLength});
    570        }
    571    }
    572    runTests(ResizableArrayBuffer);
    573 }
    574 
    575 if (SharedArrayBuffer.prototype.grow) {
    576    class GrowableSharedArrayBuffer {
    577        constructor(byteLength = 0) {
    578            return new SharedArrayBuffer(byteLength, {maxByteLength: byteLength});
    579        }
    580    }
    581    runTests(GrowableSharedArrayBuffer);
    582 }