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