atomics.js 22.2 KB
Newer Older
binji's avatar
binji committed
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
// Flags: --harmony-sharedarraybuffer
binji's avatar
binji committed
6 7
//

8 9 10 11 12 13 14 15 16 17 18 19
function toRangeWrapper(is_big) {
  return function _toRangeWrapped(raw_value) {
    var raw_range = this.max - this.min + (is_big ? 1n : 1);
    let range = is_big ? BigInt(raw_range) : raw_range;
    let value = is_big ? BigInt(raw_value) : raw_value;
    while (value < this.min) {
      value += range;
    }
    while (value > this.max) {
      value -= range;
    }
    return value;
20 21 22 23 24
  }
}

function makeConstructorObject(constr, min, max, toRange) {
  var o = {constr: constr, min: min, max: max};
25 26
  let is_big = constr.name.startsWith('Big')
  o.toRange = toRangeWrapper(is_big).bind(o);
27 28 29
  return o;
}

30 31 32 33 34 35 36 37 38 39 40 41
function IsBig(t) {
  return t.constructor.name.startsWith('Big');
}

function MaybeIntToBigInt(arr, i) {
  if (IsBig(arr)) {
    return BigInt(i);
  } else {
    return i;
  }
}

binji's avatar
binji committed
42
var IntegerTypedArrayConstructors = [
43 44 45
  makeConstructorObject(Int8Array, -128, 127),
  makeConstructorObject(Int16Array, -32768, 32767),
  makeConstructorObject(Int32Array, -0x80000000, 0x7fffffff),
46 47 48
  makeConstructorObject(BigInt64Array, -0x8000_0000_0000_0000n,
                        0x7fff_ffff_ffff_ffffn),
  makeConstructorObject(BigUint64Array, 0n, 0xffff_ffff_ffff_ffffn),
49 50 51
  makeConstructorObject(Uint8Array, 0, 255),
  makeConstructorObject(Uint16Array, 0, 65535),
  makeConstructorObject(Uint32Array, 0, 0xffffffff),
binji's avatar
binji committed
52 53 54 55 56 57 58 59
];

(function TestBadArray() {
  var ab = new ArrayBuffer(16);
  var u32a = new Uint32Array(16);
  var sab = new SharedArrayBuffer(128);
  var sf32a = new Float32Array(sab);
  var sf64a = new Float64Array(sab);
60
  var u8ca = new Uint8ClampedArray(sab);
binji's avatar
binji committed
61

62
  // Atomic ops required integer shared typed arrays
63 64 65 66
  var badArrayTypes = [
    undefined, 1, 'hi', 3.4, ab, u32a, sab, sf32a, sf64a, u8ca
  ];
  badArrayTypes.forEach(function(o) {
binji's avatar
binji committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    assertThrows(function() { Atomics.compareExchange(o, 0, 0, 0); },
                 TypeError);
    assertThrows(function() { Atomics.load(o, 0); }, TypeError);
    assertThrows(function() { Atomics.store(o, 0, 0); }, TypeError);
    assertThrows(function() { Atomics.add(o, 0, 0); }, TypeError);
    assertThrows(function() { Atomics.sub(o, 0, 0); }, TypeError);
    assertThrows(function() { Atomics.and(o, 0, 0); }, TypeError);
    assertThrows(function() { Atomics.or(o, 0, 0); }, TypeError);
    assertThrows(function() { Atomics.xor(o, 0, 0); }, TypeError);
    assertThrows(function() { Atomics.exchange(o, 0, 0); }, TypeError);
  });
})();

(function TestBadIndex() {
  var sab = new SharedArrayBuffer(8);
  var si32a = new Int32Array(sab);
83
  var si32a2 = new Int32Array(sab, 4);
binji's avatar
binji committed
84

85 86 87
  // Indexes that are out of bounds when coerced via ToIndex should throw
  // RangeError.
  [-Infinity, Infinity].forEach(function(i) {
88 89 90 91 92 93 94 95 96 97 98 99 100
    assertThrows(function() { Atomics.compareExchange(si32a, i, 0); },
                 RangeError);
    assertThrows(function() { Atomics.load(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.store(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.add(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.sub(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.and(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.or(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.xor(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.exchange(si32a, i, 0); }, RangeError);
  }, RangeError);

  // Out-of-bounds indexes should throw RangeError.
binji's avatar
binji committed
101
  [-1, 2, 100].forEach(function(i) {
102 103 104 105 106 107 108 109 110 111 112 113 114
    assertThrows(function() { Atomics.compareExchange(si32a, i, 0, 0); },
                 RangeError);
    assertThrows(function() { Atomics.load(si32a, i); }, RangeError);
    assertThrows(function() { Atomics.store(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.add(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.sub(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.and(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.or(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.xor(si32a, i, 0); }, RangeError);
    assertThrows(function() { Atomics.exchange(si32a, i, 0); }, RangeError);
  }, RangeError);

  // Out-of-bounds indexes for array with offset should throw RangeError.
115
  [-1, 1, 100].forEach(function(i) {
116 117 118 119 120 121 122 123 124
    assertThrows(function() { Atomics.compareExchange(si32a2, i, 0, 0); });
    assertThrows(function() { Atomics.load(si32a2, i); }, RangeError);
    assertThrows(function() { Atomics.store(si32a2, i, 0); }, RangeError);
    assertThrows(function() { Atomics.add(si32a2, i, 0); }, RangeError);
    assertThrows(function() { Atomics.sub(si32a2, i, 0); }, RangeError);
    assertThrows(function() { Atomics.and(si32a2, i, 0); }, RangeError);
    assertThrows(function() { Atomics.or(si32a2, i, 0); }, RangeError);
    assertThrows(function() { Atomics.xor(si32a2, i, 0); }, RangeError);
    assertThrows(function() { Atomics.exchange(si32a2, i, 0); }, RangeError);
125 126
  });

127
  // Monkey-patch length and make sure these functions still throw.
128 129
  Object.defineProperty(si32a, 'length', {get: function() { return 1000; }});
  [2, 100].forEach(function(i) {
130 131 132 133 134 135 136 137 138
    assertThrows(function() { Atomics.compareExchange(si32a, i, 0, 0); });
    assertThrows(function() { Atomics.load(si32a, i); });
    assertThrows(function() { Atomics.store(si32a, i, 0); });
    assertThrows(function() { Atomics.add(si32a, i, 0); });
    assertThrows(function() { Atomics.sub(si32a, i, 0); });
    assertThrows(function() { Atomics.and(si32a, i, 0); });
    assertThrows(function() { Atomics.or(si32a, i, 0); });
    assertThrows(function() { Atomics.xor(si32a, i, 0); });
    assertThrows(function() { Atomics.exchange(si32a, i, 0); });
binji's avatar
binji committed
139 140 141 142 143 144
  });
})();

(function TestGoodIndex() {
  var sab = new SharedArrayBuffer(64);
  var si32a = new Int32Array(sab);
145
  var si32a2 = new Int32Array(sab, 32);
binji's avatar
binji committed
146

147 148
  var testOp = function(op, ia, index, expectedIndex, name) {
    for (var i = 0; i < ia.length; ++i)
149
      ia[i] = i * 2;
150 151

    ia[expectedIndex] = 0;
152 153
    var result = op(ia, index, 0, 0);
    assertEquals(0, result, name);
154 155 156 157
    assertEquals(0, ia[expectedIndex], name);

    for (var i = 0; i < ia.length; ++i) {
      if (i == expectedIndex) continue;
158
      assertEquals(i * 2, ia[i], name);
159 160 161 162
    }
  };

  // These values all map to index 0
163 164
  [-0, 0, 0.0, null, false, NaN, {}, '0.2', 'hi', undefined].forEach(
      function(i) {
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    var name = String(i);
    [si32a, si32a2].forEach(function(array) {
      testOp(Atomics.compareExchange, array, i, 0, name);
      testOp(Atomics.load, array, i, 0, name);
      testOp(Atomics.store, array, i, 0, name);
      testOp(Atomics.add, array, i, 0, name);
      testOp(Atomics.sub, array, i, 0, name);
      testOp(Atomics.and, array, i, 0, name);
      testOp(Atomics.or, array, i, 0, name);
      testOp(Atomics.xor, array, i, 0, name);
      testOp(Atomics.exchange, array, i, 0, name);
    });
  });

  // These values all map to index 3
binji's avatar
binji committed
180 181
  var valueOf = {valueOf: function(){ return 3;}};
  var toString = {toString: function(){ return '3';}};
182
  [3, 3.0, '3', '3.0', valueOf, toString].forEach(function(i) {
binji's avatar
binji committed
183
    var name = String(i);
184
    [si32a, si32a2].forEach(function(array) {
185 186 187 188 189 190 191 192 193 194
      testOp(Atomics.compareExchange, array, i, 3, name);
      testOp(Atomics.load, array, i, 3, name);
      testOp(Atomics.store, array, i, 3, name);
      testOp(Atomics.add, array, i, 3, name);
      testOp(Atomics.sub, array, i, 3, name);
      testOp(Atomics.and, array, i, 3, name);
      testOp(Atomics.or, array, i, 3, name);
      testOp(Atomics.xor, array, i, 3, name);
      testOp(Atomics.exchange, array, i, 3, name);
    });
binji's avatar
binji committed
195 196 197
  });
})();

198 199 200 201 202 203 204
function clearArray(sab) {
  var ui8 = new Uint8Array(sab);
  for (var i = 0; i < sab.byteLength; ++i) {
    ui8[i] = 0;
  }
}

binji's avatar
binji committed
205
(function TestCompareExchange() {
206
  IntegerTypedArrayConstructors.forEach(function(t) {
binji's avatar
binji committed
207 208
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
209 210 211
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
212
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
213 214 215 216
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
        // array[i] == 0, CAS will store
217 218 219
        assertEquals(_i(0), Atomics.compareExchange(array, i, _i(0), _i(50)),
                    name);
        assertEquals(_i(50), array[i], name);
220 221

        // array[i] == 50, CAS will not store
222 223 224
        assertEquals(_i(50), Atomics.compareExchange(array, i, _i(0), _i(100)),
                     name);
        assertEquals(_i(50), array[i], name);
225 226
      }
    })
binji's avatar
binji committed
227 228 229 230
  });
})();

(function TestLoad() {
231
  IntegerTypedArrayConstructors.forEach(function(t) {
binji's avatar
binji committed
232 233
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
234 235 236
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
237
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
238 239 240
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
241 242 243 244
        array[i] = _i(0);
        assertEquals(_i(0), Atomics.load(array, i), name);
        array[i] = _i(50);
        assertEquals(_i(50), Atomics.load(array, i), name);
245 246
      }
    })
binji's avatar
binji committed
247
  });
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265

  // Test Smi range
  (function () {
    var sab = new SharedArrayBuffer(4);
    var i32 = new Int32Array(sab);
    var u32 = new Uint32Array(sab);

    function testLoad(signedValue, unsignedValue) {
      u32[0] = unsignedValue;
      assertEquals(unsignedValue, Atomics.load(u32, 0));
      assertEquals(signedValue, Atomics.load(i32, 0));
    }

    testLoad(0x3fffffff,  0x3fffffff); // 2**30-1 (always smi)
    testLoad(0x40000000,  0x40000000); // 2**30 (smi if signed and 32-bits)
    testLoad(0x80000000, -0x80000000); // 2**31 (smi if signed and 32-bits)
    testLoad(0xffffffff, -1);          // 2**31 (smi if signed)
  });
binji's avatar
binji committed
266 267 268
})();

(function TestStore() {
269
  IntegerTypedArrayConstructors.forEach(function(t) {
binji's avatar
binji committed
270 271
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
272 273 274
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
275
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
276 277 278
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
279 280
        assertEquals(_i(50), Atomics.store(array, i, _i(50)), name);
        assertEquals(_i(50), array[i], name);
281

282 283
        assertEquals(_i(100), Atomics.store(array, i, _i(100)), name);
        assertEquals(_i(100), array[i], name);
284 285
      }
    })
binji's avatar
binji committed
286 287 288 289 290 291 292
  });
})();

(function TestAdd() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
293 294 295
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
296
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
297 298 299
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
300 301
        assertEquals(_i(0), Atomics.add(array, i, _i(50)), name);
        assertEquals(_i(50), array[i], name);
302

303 304
        assertEquals(_i(50), Atomics.add(array, i, _i(70)), name);
        assertEquals(_i(120), array[i], name);
305 306
      }
    })
binji's avatar
binji committed
307 308 309 310 311 312 313
  });
})();

(function TestSub() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
314 315 316
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
317
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
318 319 320
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
321 322 323
        array[i] = _i(120);
        assertEquals(_i(120), Atomics.sub(array, i, _i(50)), name);
        assertEquals(_i(70), array[i], name);
324

325 326
        assertEquals(_i(70), Atomics.sub(array, i, _i(70)), name);
        assertEquals(_i(0), array[i], name);
327 328
      }
    })
binji's avatar
binji committed
329 330 331 332 333 334 335
  });
})();

(function TestAnd() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
336 337 338
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
339
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
340 341 342
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(sta);
      for (var i = 0; i < array.length; ++i) {
343 344 345
        array[i] = _i(0x3f);
        assertEquals(_i(0x3f), Atomics.and(array, i, _i(0x30)), name);
        assertEquals(_i(0x30), array[i], name);
346

347 348
        assertEquals(_i(0x30), Atomics.and(array, i, _i(0x20)), name);
        assertEquals(_i(0x20), array[i], name);
349 350
      }
    })
binji's avatar
binji committed
351 352 353 354 355 356 357
  });
})();

(function TestOr() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
358 359 360
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
361
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
362 363 364
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
365 366 367
        array[i] = _i(0x30);
        assertEquals(_i(0x30), Atomics.or(array, i, _i(0x1c)), name);
        assertEquals(_i(0x3c), array[i], name);
368

369 370
        assertEquals(_i(0x3c), Atomics.or(array, i, _i(0x09)), name);
        assertEquals(_i(0x3d), array[i], name);
371 372
      }
    })
binji's avatar
binji committed
373 374 375 376 377 378 379
  });
})();

(function TestXor() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
380 381 382
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
383
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
384 385 386
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
387 388 389
        array[i] = _i(0x30);
        assertEquals(_i(0x30), Atomics.xor(array, i, _i(0x1c)), name);
        assertEquals(_i(0x2c), array[i], name);
390

391 392
        assertEquals(_i(0x2c), Atomics.xor(array, i, _i(0x09)), name);
        assertEquals(_i(0x25), array[i], name);
393 394
      }
    })
binji's avatar
binji committed
395 396 397 398 399 400 401
  });
})();

(function TestExchange() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
402 403 404
    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);

    [sta, sta2].forEach(function(array) {
405
      let _i = (i) => { return MaybeIntToBigInt(array, i); }
406 407 408
      clearArray(array.buffer);
      var name = Object.prototype.toString.call(array);
      for (var i = 0; i < array.length; ++i) {
409 410 411
        array[i] = _i(0x30);
        assertEquals(_i(0x30), Atomics.exchange(array, i, _i(0x1c)), name);
        assertEquals(_i(0x1c), array[i], name);
412

413 414
        assertEquals(_i(0x1c), Atomics.exchange(array, i, _i(0x09)), name);
        assertEquals(_i(0x09), array[i], name);
415 416
      }
    })
binji's avatar
binji committed
417 418 419 420
  });
})();

(function TestIsLockFree() {
421 422 423 424 425 426 427 428 429
  // Various invalid cases.
  var valueOf = {valueOf: function(){ return 3;}};
  var toString = {toString: function(){ return '3';}};
  var invalid = [3.14, 'foo', Infinity, NaN, false, undefined, valueOf,
                 toString];
  invalid.forEach(function(v) {
    assertEquals(false, Atomics.isLockFree(v), JSON.stringify(v));
  });

binji's avatar
binji committed
430
  // For all platforms we support, 1, 2 and 4 bytes should be lock-free.
431 432 433 434 435 436 437 438 439 440 441
  assertTrue(Atomics.isLockFree(1));
  assertTrue(Atomics.isLockFree(2));
  assertTrue(Atomics.isLockFree(4));

  assertFalse(Atomics.isLockFree(0));
  assertFalse(Atomics.isLockFree(3));
  assertFalse(Atomics.isLockFree(5));
  assertFalse(Atomics.isLockFree(6));
  assertFalse(Atomics.isLockFree(7));
  // isLockFree(8) is platform dependent.
  for (var i = 9; i < 100; ++i) assertFalse(Atomics.isLockFree(i));
binji's avatar
binji committed
442
})();
443

444 445 446 447
(function TestToNumber() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(1 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
448
    let _i = (i) => { return MaybeIntToBigInt(sta, i); }
449

450
    var valueOf = {valueOf: function(){ return _i(3);}};
451 452 453
    var toString = {toString: function(){ return '3';}};

    [false, true, undefined, valueOf, toString].forEach(function(v) {
454 455 456 457 458
      if (v === undefined && IsBig(sta)) {
        // undefined does not convert to a BigInt.
        return;
      }
      let _v = () => { return IsBig(sta) ? _i(v) : (v|0); }
459 460 461
      var name = Object.prototype.toString.call(sta) + ' - ' + v;

      // CompareExchange
462 463
      sta[0] = _i(50);
      assertEquals(_i(50), Atomics.compareExchange(sta, 0, v, v), name);
464 465

      // Store
466 467
      assertEquals(_v(), Atomics.store(sta, 0, v), name);
      assertEquals(_v(), sta[0], name);
468 469

      // Add
470 471 472
      sta[0] = _i(120);
      assertEquals(_i(120), Atomics.add(sta, 0, v), name);
      assertEquals(_i(120) + _v(), sta[0], name);
473 474

      // Sub
475 476 477
      sta[0] = _i(70);
      assertEquals(_i(70), Atomics.sub(sta, 0, v), name);
      assertEquals(_i(70) - _v(), sta[0]);
478 479

      // And
480 481 482
      sta[0] = _i(0x20);
      assertEquals(_i(0x20), Atomics.and(sta, 0, v), name);
      assertEquals(_i(0x20) & _v(), sta[0]);
483 484

      // Or
485 486 487
      sta[0] = _i(0x3d);
      assertEquals(_i(0x3d), Atomics.or(sta, 0, v), name);
      assertEquals(_i(0x3d) | _v(), sta[0]);
488 489

      // Xor
490 491 492
      sta[0] = _i(0x25);
      assertEquals(_i(0x25), Atomics.xor(sta, 0, v), name);
      assertEquals(_i(0x25) ^ _v(), sta[0]);
493 494

      // Exchange
495 496 497
      sta[0] = _i(0x09);
      assertEquals(_i(0x09), Atomics.exchange(sta, 0, v), name);
      assertEquals(_v(), sta[0]);
498 499 500 501
    });
  });
})();

502 503 504 505 506
(function TestWrapping() {
  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
    var name = Object.prototype.toString.call(sta);
507 508
    let _i = (i) => { return MaybeIntToBigInt(sta, i); }
    var range = t.max - t.min + _i(1);
509 510 511 512 513 514 515
    var offset;
    var operand;
    var val, newVal;
    var valWrapped, newValWrapped;

    for (offset = -range; offset <= range; offset += range) {
      // CompareExchange
516 517
      sta[0] = val = _i(0);
      newVal = val + offset + _i(1);
518 519 520 521 522
      newValWrapped = t.toRange(newVal);
      assertEquals(val, Atomics.compareExchange(sta, 0, val, newVal), name);
      assertEquals(newValWrapped, sta[0], name);

      sta[0] = val = t.min;
523
      newVal = val + offset - _i(1);
524 525 526 527 528
      newValWrapped = t.toRange(newVal);
      assertEquals(val, Atomics.compareExchange(sta, 0, val, newVal), name);
      assertEquals(newValWrapped, sta[0], name);

      // Store
529 530
      sta[0] = _i(0);
      val = t.max + offset + _i(1);
531 532 533 534
      valWrapped = t.toRange(val);
      assertEquals(val, Atomics.store(sta, 0, val), name);
      assertEquals(valWrapped, sta[0], name);

535
      sta[0] = val = t.min + offset - _i(1);
536 537 538 539 540 541
      valWrapped = t.toRange(val);
      assertEquals(val, Atomics.store(sta, 0, val), name);
      assertEquals(valWrapped, sta[0], name);

      // Add
      sta[0] = val = t.max;
542
      operand = offset + _i(1);
543 544 545 546 547
      valWrapped = t.toRange(val + operand);
      assertEquals(val, Atomics.add(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);

      sta[0] = val = t.min;
548
      operand = offset - _i(1);
549 550 551 552 553 554
      valWrapped = t.toRange(val + operand);
      assertEquals(val, Atomics.add(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);

      // Sub
      sta[0] = val = t.max;
555
      operand = offset - _i(1);
556 557 558 559 560
      valWrapped = t.toRange(val - operand);
      assertEquals(val, Atomics.sub(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);

      sta[0] = val = t.min;
561
      operand = offset + _i(1);
562 563 564 565 566 567 568 569 570
      valWrapped = t.toRange(val - operand);
      assertEquals(val, Atomics.sub(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);

      // There's no way to wrap results with logical operators, just test that
      // using an out-of-range value is properly wrapped/clamped when written
      // to memory.

      // And
571 572
      sta[0] = val = _i(0xf);
      operand = _i(0x3) + offset;
573 574 575 576 577
      valWrapped = t.toRange(val & operand);
      assertEquals(val, Atomics.and(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);

      // Or
578 579
      sta[0] = val = _i(0x12);
      operand = _i(0x22) + offset;
580 581 582 583 584
      valWrapped = t.toRange(val | operand);
      assertEquals(val, Atomics.or(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);

      // Xor
585 586
      sta[0] = val = _i(0x12);
      operand = _i(0x22) + offset;
587 588 589 590 591
      valWrapped = t.toRange(val ^ operand);
      assertEquals(val, Atomics.xor(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);

      // Exchange
592 593
      sta[0] = val = _i(0x12);
      operand = _i(0x22) + offset;
594 595 596 597 598 599 600
      valWrapped = t.toRange(operand);
      assertEquals(val, Atomics.exchange(sta, 0, operand), name);
      assertEquals(valWrapped, sta[0], name);
    }

  });
})();
601 602 603 604 605 606 607 608 609

(function TestValidateIndexBeforeValue() {
  var testOp = function(op, sta, name) {
    var valueof_has_been_called = 0;
    var value = {valueOf: function() { valueof_has_been_called = 1; return 0;}};
    var index = -1;

    // The index should be checked before calling ToInteger on the value, so
    // valueof_has_been_called should not be modified.
610
    sta[0] = MaybeIntToBigInt(sta, 0);
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
    assertThrows(function() { op(sta, index, value, value); }, RangeError);
    assertEquals(0, valueof_has_been_called);
  };

  IntegerTypedArrayConstructors.forEach(function(t) {
    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
    var sta = new t.constr(sab);
    var name = Object.prototype.toString.call(sta);

    testOp(Atomics.compareExchange, sta, name);
    testOp(Atomics.load, sta, name);
    testOp(Atomics.store, sta, name);
    testOp(Atomics.add, sta, name);
    testOp(Atomics.sub, sta, name);
    testOp(Atomics.and, sta, name);
    testOp(Atomics.or, sta, name);
    testOp(Atomics.xor, sta, name);
    testOp(Atomics.exchange, sta, name);
  });
})();