1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Flags: --harmony-sharedarraybuffer
6//
7
8function toRangeWrapped(value) {
9  var range = this.max - this.min + 1;
10  while (value < this.min) {
11    value += range;
12  }
13  while (value > this.max) {
14    value -= range;
15  }
16  return value;
17}
18
19function toRangeClamped(value) {
20  if (value < this.min) return this.min;
21  if (value > this.max) return this.max;
22  return value;
23}
24
25function makeConstructorObject(constr, min, max, toRange) {
26  var o = {constr: constr, min: min, max: max};
27  o.toRange = toRange.bind(o);
28  return o;
29}
30
31var IntegerTypedArrayConstructors = [
32  makeConstructorObject(Int8Array, -128, 127, toRangeWrapped),
33  makeConstructorObject(Int16Array, -32768, 32767, toRangeWrapped),
34  makeConstructorObject(Int32Array, -0x80000000, 0x7fffffff, toRangeWrapped),
35  makeConstructorObject(Uint8Array, 0, 255, toRangeWrapped),
36  makeConstructorObject(Uint8ClampedArray, 0, 255, toRangeClamped),
37  makeConstructorObject(Uint16Array, 0, 65535, toRangeWrapped),
38  makeConstructorObject(Uint32Array, 0, 0xffffffff, toRangeWrapped),
39];
40
41(function TestBadArray() {
42  var ab = new ArrayBuffer(16);
43  var u32a = new Uint32Array(16);
44  var sab = new SharedArrayBuffer(128);
45  var sf32a = new Float32Array(sab);
46  var sf64a = new Float64Array(sab);
47
48  // Atomic ops required integer shared typed arrays
49  [undefined, 1, 'hi', 3.4, ab, u32a, sab, sf32a, sf64a].forEach(function(o) {
50    assertThrows(function() { Atomics.compareExchange(o, 0, 0, 0); },
51                 TypeError);
52    assertThrows(function() { Atomics.load(o, 0); }, TypeError);
53    assertThrows(function() { Atomics.store(o, 0, 0); }, TypeError);
54    assertThrows(function() { Atomics.add(o, 0, 0); }, TypeError);
55    assertThrows(function() { Atomics.sub(o, 0, 0); }, TypeError);
56    assertThrows(function() { Atomics.and(o, 0, 0); }, TypeError);
57    assertThrows(function() { Atomics.or(o, 0, 0); }, TypeError);
58    assertThrows(function() { Atomics.xor(o, 0, 0); }, TypeError);
59    assertThrows(function() { Atomics.exchange(o, 0, 0); }, TypeError);
60  });
61})();
62
63function testAtomicOp(op, ia, index, expectedIndex, name) {
64  for (var i = 0; i < ia.length; ++i)
65    ia[i] = 22;
66
67  ia[expectedIndex] = 0;
68  assertEquals(0, op(ia, index, 0, 0), name);
69  assertEquals(0, ia[expectedIndex], name);
70
71  for (var i = 0; i < ia.length; ++i) {
72    if (i == expectedIndex) continue;
73    assertEquals(22, ia[i], name);
74  }
75}
76
77(function TestBadIndex() {
78  var sab = new SharedArrayBuffer(8);
79  var si32a = new Int32Array(sab);
80  var si32a2 = new Int32Array(sab, 4);
81
82  // Non-integer indexes are converted to an integer first, so they should all
83  // operate on index 0.
84  [undefined, null, false, 'hi', {}].forEach(function(i) {
85
86    var name = String(i);
87    testAtomicOp(Atomics.compareExchange, si32a, i, 0, name);
88    testAtomicOp(Atomics.load, si32a, i, 0, name);
89    testAtomicOp(Atomics.store, si32a, i, 0, name);
90    testAtomicOp(Atomics.add, si32a, i, 0, name);
91    testAtomicOp(Atomics.sub, si32a, i, 0, name);
92    testAtomicOp(Atomics.and, si32a, i, 0, name);
93    testAtomicOp(Atomics.or, si32a, i, 0, name);
94    testAtomicOp(Atomics.xor, si32a, i, 0, name);
95    testAtomicOp(Atomics.exchange, si32a, i, 0, name);
96  });
97
98  // Out-of-bounds indexes should return undefined.
99  // TODO(binji): Should these throw RangeError instead?
100  [-1, 2, 100].forEach(function(i) {
101    var name = String(i);
102    assertEquals(undefined, Atomics.compareExchange(si32a, i, 0, 0), name);
103    assertEquals(undefined, Atomics.load(si32a, i), name);
104    assertEquals(undefined, Atomics.store(si32a, i, 0), name);
105    assertEquals(undefined, Atomics.add(si32a, i, 0), name);
106    assertEquals(undefined, Atomics.sub(si32a, i, 0), name);
107    assertEquals(undefined, Atomics.and(si32a, i, 0), name);
108    assertEquals(undefined, Atomics.or(si32a, i, 0), name);
109    assertEquals(undefined, Atomics.xor(si32a, i, 0), name);
110    assertEquals(undefined, Atomics.exchange(si32a, i, 0), name);
111  });
112
113  // Out-of-bounds indexes for offset-array
114  [-1, 1, 100].forEach(function(i) {
115    var name = String(i);
116    assertEquals(undefined, Atomics.compareExchange(si32a2, i, 0, 0), name);
117    assertEquals(undefined, Atomics.load(si32a2, i), name);
118    assertEquals(undefined, Atomics.store(si32a2, i, 0), name);
119    assertEquals(undefined, Atomics.add(si32a2, i, 0), name);
120    assertEquals(undefined, Atomics.sub(si32a2, i, 0), name);
121    assertEquals(undefined, Atomics.and(si32a2, i, 0), name);
122    assertEquals(undefined, Atomics.or(si32a2, i, 0), name);
123    assertEquals(undefined, Atomics.xor(si32a2, i, 0), name);
124    assertEquals(undefined, Atomics.exchange(si32a2, i, 0), name);
125  });
126
127  // Monkey-patch length and make sure these functions still return undefined.
128  Object.defineProperty(si32a, 'length', {get: function() { return 1000; }});
129  [2, 100].forEach(function(i) {
130    var name = String(i);
131    assertEquals(undefined, Atomics.compareExchange(si32a, i, 0, 0), name);
132    assertEquals(undefined, Atomics.load(si32a, i), name);
133    assertEquals(undefined, Atomics.store(si32a, i, 0), name);
134    assertEquals(undefined, Atomics.add(si32a, i, 0), name);
135    assertEquals(undefined, Atomics.sub(si32a, i, 0), name);
136    assertEquals(undefined, Atomics.and(si32a, i, 0), name);
137    assertEquals(undefined, Atomics.or(si32a, i, 0), name);
138    assertEquals(undefined, Atomics.xor(si32a, i, 0), name);
139    assertEquals(undefined, Atomics.exchange(si32a, i, 0), name);
140  });
141})();
142
143(function TestGoodIndex() {
144  var sab = new SharedArrayBuffer(64);
145  var si32a = new Int32Array(sab);
146  var si32a2 = new Int32Array(sab, 32);
147
148  var valueOf = {valueOf: function(){ return 3;}};
149  var toString = {toString: function(){ return '3';}};
150
151  [3, 3.5, '3', '3.5', valueOf, toString].forEach(function(i) {
152    var name = String(i);
153    [si32a, si32a2].forEach(function(array) {
154        testAtomicOp(Atomics.compareExchange, array, i, 3, name);
155        testAtomicOp(Atomics.load, array, i, 3, name);
156        testAtomicOp(Atomics.store, array, i, 3, name);
157        testAtomicOp(Atomics.add, array, i, 3, name);
158        testAtomicOp(Atomics.sub, array, i, 3, name);
159        testAtomicOp(Atomics.and, array, i, 3, name);
160        testAtomicOp(Atomics.or, array, i, 3, name);
161        testAtomicOp(Atomics.xor, array, i, 3, name);
162        testAtomicOp(Atomics.exchange, array, i, 3, name);
163      })
164  });
165})();
166
167function clearArray(sab) {
168  var ui8 = new Uint8Array(sab);
169  for (var i = 0; i < sab.byteLength; ++i) {
170    ui8[i] = 0;
171  }
172}
173
174(function TestCompareExchange() {
175  IntegerTypedArrayConstructors.forEach(function(t) {
176    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
177    var sta = new t.constr(sab);
178    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
179
180    [sta, sta2].forEach(function(array) {
181      clearArray(array.buffer);
182      var name = Object.prototype.toString.call(array);
183      for (var i = 0; i < array.length; ++i) {
184        // array[i] == 0, CAS will store
185        assertEquals(0, Atomics.compareExchange(array, i, 0, 50), name);
186        assertEquals(50, array[i], name);
187
188        // array[i] == 50, CAS will not store
189        assertEquals(50, Atomics.compareExchange(array, i, 0, 100), name);
190        assertEquals(50, array[i], name);
191      }
192    })
193  });
194})();
195
196(function TestLoad() {
197  IntegerTypedArrayConstructors.forEach(function(t) {
198    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
199    var sta = new t.constr(sab);
200    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
201
202    [sta, sta2].forEach(function(array) {
203      clearArray(array.buffer);
204      var name = Object.prototype.toString.call(array);
205      for (var i = 0; i < array.length; ++i) {
206        array[i] = 0;
207        assertEquals(0, Atomics.load(array, i), name);
208        array[i] = 50;
209        assertEquals(50, Atomics.load(array, i), name);
210      }
211    })
212  });
213})();
214
215(function TestStore() {
216  IntegerTypedArrayConstructors.forEach(function(t) {
217    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
218    var sta = new t.constr(sab);
219    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
220
221    [sta, sta2].forEach(function(array) {
222      clearArray(array.buffer);
223      var name = Object.prototype.toString.call(array);
224      for (var i = 0; i < array.length; ++i) {
225        assertEquals(50, Atomics.store(array, i, 50), name);
226        assertEquals(50, array[i], name);
227
228        assertEquals(100, Atomics.store(array, i, 100), name);
229        assertEquals(100, array[i], name);
230      }
231    })
232  });
233})();
234
235(function TestAdd() {
236  IntegerTypedArrayConstructors.forEach(function(t) {
237    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
238    var sta = new t.constr(sab);
239    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
240
241    [sta, sta2].forEach(function(array) {
242      clearArray(array.buffer);
243      var name = Object.prototype.toString.call(array);
244      for (var i = 0; i < array.length; ++i) {
245        assertEquals(0, Atomics.add(array, i, 50), name);
246        assertEquals(50, array[i], name);
247
248        assertEquals(50, Atomics.add(array, i, 70), name);
249        assertEquals(120, array[i], name);
250      }
251    })
252  });
253})();
254
255(function TestSub() {
256  IntegerTypedArrayConstructors.forEach(function(t) {
257    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
258    var sta = new t.constr(sab);
259    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
260
261    [sta, sta2].forEach(function(array) {
262      clearArray(array.buffer);
263      var name = Object.prototype.toString.call(array);
264      for (var i = 0; i < array.length; ++i) {
265        array[i] = 120;
266        assertEquals(120, Atomics.sub(array, i, 50), name);
267        assertEquals(70, array[i], name);
268
269        assertEquals(70, Atomics.sub(array, i, 70), name);
270        assertEquals(0, array[i], name);
271      }
272    })
273  });
274})();
275
276(function TestAnd() {
277  IntegerTypedArrayConstructors.forEach(function(t) {
278    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
279    var sta = new t.constr(sab);
280    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
281
282    [sta, sta2].forEach(function(array) {
283      clearArray(array.buffer);
284      var name = Object.prototype.toString.call(sta);
285      for (var i = 0; i < array.length; ++i) {
286        array[i] = 0x3f;
287        assertEquals(0x3f, Atomics.and(array, i, 0x30), name);
288        assertEquals(0x30, array[i], name);
289
290        assertEquals(0x30, Atomics.and(array, i, 0x20), name);
291        assertEquals(0x20, array[i], name);
292      }
293    })
294  });
295})();
296
297(function TestOr() {
298  IntegerTypedArrayConstructors.forEach(function(t) {
299    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
300    var sta = new t.constr(sab);
301    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
302
303    [sta, sta2].forEach(function(array) {
304      clearArray(array.buffer);
305      var name = Object.prototype.toString.call(array);
306      for (var i = 0; i < array.length; ++i) {
307        array[i] = 0x30;
308        assertEquals(0x30, Atomics.or(array, i, 0x1c), name);
309        assertEquals(0x3c, array[i], name);
310
311        assertEquals(0x3c, Atomics.or(array, i, 0x09), name);
312        assertEquals(0x3d, array[i], name);
313      }
314    })
315  });
316})();
317
318(function TestXor() {
319  IntegerTypedArrayConstructors.forEach(function(t) {
320    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
321    var sta = new t.constr(sab);
322    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
323
324    [sta, sta2].forEach(function(array) {
325      clearArray(array.buffer);
326      var name = Object.prototype.toString.call(array);
327      for (var i = 0; i < array.length; ++i) {
328        array[i] = 0x30;
329        assertEquals(0x30, Atomics.xor(array, i, 0x1c), name);
330        assertEquals(0x2c, array[i], name);
331
332        assertEquals(0x2c, Atomics.xor(array, i, 0x09), name);
333        assertEquals(0x25, array[i], name);
334      }
335    })
336  });
337})();
338
339(function TestExchange() {
340  IntegerTypedArrayConstructors.forEach(function(t) {
341    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
342    var sta = new t.constr(sab);
343    var sta2 = new t.constr(sab, 5 * t.constr.BYTES_PER_ELEMENT);
344
345    [sta, sta2].forEach(function(array) {
346      clearArray(array.buffer);
347      var name = Object.prototype.toString.call(array);
348      for (var i = 0; i < array.length; ++i) {
349        array[i] = 0x30;
350        assertEquals(0x30, Atomics.exchange(array, i, 0x1c), name);
351        assertEquals(0x1c, array[i], name);
352
353        assertEquals(0x1c, Atomics.exchange(array, i, 0x09), name);
354        assertEquals(0x09, array[i], name);
355      }
356    })
357  });
358})();
359
360(function TestIsLockFree() {
361  // For all platforms we support, 1, 2 and 4 bytes should be lock-free.
362  assertEquals(true, Atomics.isLockFree(1));
363  assertEquals(true, Atomics.isLockFree(2));
364  assertEquals(true, Atomics.isLockFree(4));
365
366  // Sizes that aren't equal to a typedarray BYTES_PER_ELEMENT always return
367  // false.
368  var validSizes = {};
369  IntegerTypedArrayConstructors.forEach(function(t) {
370    validSizes[t.constr.BYTES_PER_ELEMENT] = true;
371  });
372
373  for (var i = 0; i < 1000; ++i) {
374    if (!validSizes[i]) {
375      assertEquals(false, Atomics.isLockFree(i));
376    }
377  }
378})();
379
380(function TestToNumber() {
381  IntegerTypedArrayConstructors.forEach(function(t) {
382    var sab = new SharedArrayBuffer(1 * t.constr.BYTES_PER_ELEMENT);
383    var sta = new t.constr(sab);
384
385    var valueOf = {valueOf: function(){ return 3;}};
386    var toString = {toString: function(){ return '3';}};
387
388    [false, true, undefined, valueOf, toString].forEach(function(v) {
389      var name = Object.prototype.toString.call(sta) + ' - ' + v;
390
391      // CompareExchange
392      sta[0] = 50;
393      assertEquals(50, Atomics.compareExchange(sta, 0, v, v), name);
394
395      // Store
396      assertEquals(+v, Atomics.store(sta, 0, v), name);
397      assertEquals(v|0, sta[0], name);
398
399      // Add
400      sta[0] = 120;
401      assertEquals(120, Atomics.add(sta, 0, v), name);
402      assertEquals(120 + (v|0), sta[0], name);
403
404      // Sub
405      sta[0] = 70;
406      assertEquals(70, Atomics.sub(sta, 0, v), name);
407      assertEquals(70 - (v|0), sta[0]);
408
409      // And
410      sta[0] = 0x20;
411      assertEquals(0x20, Atomics.and(sta, 0, v), name);
412      assertEquals(0x20 & (v|0), sta[0]);
413
414      // Or
415      sta[0] = 0x3d;
416      assertEquals(0x3d, Atomics.or(sta, 0, v), name);
417      assertEquals(0x3d | (v|0), sta[0]);
418
419      // Xor
420      sta[0] = 0x25;
421      assertEquals(0x25, Atomics.xor(sta, 0, v), name);
422      assertEquals(0x25 ^ (v|0), sta[0]);
423
424      // Exchange
425      sta[0] = 0x09;
426      assertEquals(0x09, Atomics.exchange(sta, 0, v), name);
427      assertEquals(v|0, sta[0]);
428    });
429  });
430})();
431
432(function TestWrapping() {
433  IntegerTypedArrayConstructors.forEach(function(t) {
434    var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
435    var sta = new t.constr(sab);
436    var name = Object.prototype.toString.call(sta);
437    var range = t.max - t.min + 1;
438    var offset;
439    var operand;
440    var val, newVal;
441    var valWrapped, newValWrapped;
442
443    for (offset = -range; offset <= range; offset += range) {
444      // CompareExchange
445      sta[0] = val = 0;
446      newVal = val + offset + 1;
447      newValWrapped = t.toRange(newVal);
448      assertEquals(val, Atomics.compareExchange(sta, 0, val, newVal), name);
449      assertEquals(newValWrapped, sta[0], name);
450
451      sta[0] = val = t.min;
452      newVal = val + offset - 1;
453      newValWrapped = t.toRange(newVal);
454      assertEquals(val, Atomics.compareExchange(sta, 0, val, newVal), name);
455      assertEquals(newValWrapped, sta[0], name);
456
457      // Store
458      sta[0] = 0;
459      val = t.max + offset + 1;
460      valWrapped = t.toRange(val);
461      assertEquals(val, Atomics.store(sta, 0, val), name);
462      assertEquals(valWrapped, sta[0], name);
463
464      sta[0] = val = t.min + offset - 1;
465      valWrapped = t.toRange(val);
466      assertEquals(val, Atomics.store(sta, 0, val), name);
467      assertEquals(valWrapped, sta[0], name);
468
469      // Add
470      sta[0] = val = t.max;
471      operand = offset + 1;
472      valWrapped = t.toRange(val + operand);
473      assertEquals(val, Atomics.add(sta, 0, operand), name);
474      assertEquals(valWrapped, sta[0], name);
475
476      sta[0] = val = t.min;
477      operand = offset - 1;
478      valWrapped = t.toRange(val + operand);
479      assertEquals(val, Atomics.add(sta, 0, operand), name);
480      assertEquals(valWrapped, sta[0], name);
481
482      // Sub
483      sta[0] = val = t.max;
484      operand = offset - 1;
485      valWrapped = t.toRange(val - operand);
486      assertEquals(val, Atomics.sub(sta, 0, operand), name);
487      assertEquals(valWrapped, sta[0], name);
488
489      sta[0] = val = t.min;
490      operand = offset + 1;
491      valWrapped = t.toRange(val - operand);
492      assertEquals(val, Atomics.sub(sta, 0, operand), name);
493      assertEquals(valWrapped, sta[0], name);
494
495      // There's no way to wrap results with logical operators, just test that
496      // using an out-of-range value is properly wrapped/clamped when written
497      // to memory.
498
499      // And
500      sta[0] = val = 0xf;
501      operand = 0x3 + offset;
502      valWrapped = t.toRange(val & operand);
503      assertEquals(val, Atomics.and(sta, 0, operand), name);
504      assertEquals(valWrapped, sta[0], name);
505
506      // Or
507      sta[0] = val = 0x12;
508      operand = 0x22 + offset;
509      valWrapped = t.toRange(val | operand);
510      assertEquals(val, Atomics.or(sta, 0, operand), name);
511      assertEquals(valWrapped, sta[0], name);
512
513      // Xor
514      sta[0] = val = 0x12;
515      operand = 0x22 + offset;
516      valWrapped = t.toRange(val ^ operand);
517      assertEquals(val, Atomics.xor(sta, 0, operand), name);
518      assertEquals(valWrapped, sta[0], name);
519
520      // Exchange
521      sta[0] = val = 0x12;
522      operand = 0x22 + offset;
523      valWrapped = t.toRange(operand);
524      assertEquals(val, Atomics.exchange(sta, 0, operand), name);
525      assertEquals(valWrapped, sta[0], name);
526    }
527
528  });
529})();
530