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