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