basic-tests.js (15922B)
1 // Basic functional tests for the Atomics primitives. 2 // 3 // These do not test atomicity, just that calling and coercions and 4 // indexing and exception behavior all work right. 5 // 6 // These do not test the wait/wake operations. 7 8 load(libdir + "asserts.js"); 9 10 var DEBUG = false; // Set to true for useful printouts 11 12 function dprint(...xs) { 13 if (!DEBUG) 14 return; 15 var s = ""; 16 for ( var x in xs ) 17 s += String(xs[x]); 18 print(s); 19 } 20 21 // Clone a function so that we get reliable inlining of primitives with --ion-eager. 22 // For eg testMethod and testFunction that are polymorphic in the array a, 23 // the inliner gets confused and stops inlining after Int8 -- not what we want. 24 function CLONE(f) { 25 return this.eval("(" + f.toString() + ")"); 26 } 27 28 function testMethod(a, ...indices) { 29 dprint("Method: " + a.constructor.name); 30 var poison; 31 switch (a.BYTES_PER_ELEMENT) { 32 case 1: poison = 0x5A; break; 33 case 2: poison = 0x5A5A; break; 34 case 4: poison = 0x5A5A5A5A; break; 35 } 36 for ( var i=0 ; i < indices.length ; i++ ) { 37 var x = indices[i]; 38 if (x > 0) 39 a[x-1] = poison; 40 if (x < a.length-1) 41 a[x+1] = poison; 42 43 // val = 0 44 assertEq(Atomics.compareExchange(a, x, 0, 37), 0); 45 // val = 37 46 assertEq(Atomics.compareExchange(a, x, 37, 5), 37); 47 // val = 5 48 assertEq(Atomics.compareExchange(a, x, 7, 8), 5); // ie should fail 49 // val = 5 50 assertEq(Atomics.compareExchange(a, x, 5, 9), 5); 51 // val = 9 52 assertEq(Atomics.compareExchange(a, x, 5, 0), 9); // should also fail 53 54 // val = 9 55 assertEq(Atomics.exchange(a, x, 4), 9); 56 // val = 4 57 assertEq(Atomics.exchange(a, x, 9), 4); 58 59 // val = 9 60 assertEq(Atomics.load(a, x), 9); 61 // val = 9 62 assertEq(Atomics.store(a, x, 14), 14); // What about coercion? 63 // val = 14 64 assertEq(Atomics.load(a, x), 14); 65 // val = 14 66 Atomics.store(a, x, 0); 67 // val = 0 68 69 // val = 0 70 assertEq(Atomics.add(a, x, 3), 0); 71 // val = 3 72 assertEq(Atomics.sub(a, x, 2), 3); 73 // val = 1 74 assertEq(Atomics.or(a, x, 6), 1); 75 // val = 7 76 assertEq(Atomics.and(a, x, 14), 7); 77 // val = 6 78 assertEq(Atomics.xor(a, x, 5), 6); 79 // val = 3 80 assertEq(Atomics.load(a, x), 3); 81 // val = 3 82 Atomics.store(a, x, 0); 83 // val = 0 84 85 // Check adjacent elements were not affected 86 if (x > 0) { 87 assertEq(a[x-1], poison); 88 a[x-1] = 0; 89 } 90 if (x < a.length-1) { 91 assertEq(a[x+1], poison); 92 a[x+1] = 0; 93 } 94 } 95 } 96 97 function testFunction(a, ...indices) { 98 dprint("Function: " + a.constructor.name); 99 var poison; 100 switch (a.BYTES_PER_ELEMENT) { 101 case 1: poison = 0x5A; break; 102 case 2: poison = 0x5A5A; break; 103 case 4: poison = 0x5A5A5A5A; break; 104 } 105 for ( var i=0 ; i < indices.length ; i++ ) { 106 var x = indices[i]; 107 if (x > 0) 108 a[x-1] = poison; 109 if (x < a.length-1) 110 a[x+1] = poison; 111 112 // val = 0 113 assertEq(gAtomics_compareExchange(a, x, 0, 37), 0); 114 // val = 37 115 assertEq(gAtomics_compareExchange(a, x, 37, 5), 37); 116 // val = 5 117 assertEq(gAtomics_compareExchange(a, x, 7, 8), 5); // ie should fail 118 // val = 5 119 assertEq(gAtomics_compareExchange(a, x, 5, 9), 5); 120 // val = 9 121 assertEq(gAtomics_compareExchange(a, x, 5, 0), 9); // should also fail 122 123 // val = 9 124 assertEq(gAtomics_exchange(a, x, 4), 9); 125 // val = 4 126 assertEq(gAtomics_exchange(a, x, 9), 4); 127 128 // val = 9 129 assertEq(gAtomics_load(a, x), 9); 130 // val = 9 131 assertEq(gAtomics_store(a, x, 14), 14); // What about coercion? 132 // val = 14 133 assertEq(gAtomics_load(a, x), 14); 134 // val = 14 135 gAtomics_store(a, x, 0); 136 // val = 0 137 138 // val = 0 139 assertEq(gAtomics_add(a, x, 3), 0); 140 // val = 3 141 assertEq(gAtomics_sub(a, x, 2), 3); 142 // val = 1 143 assertEq(gAtomics_or(a, x, 6), 1); 144 // val = 7 145 assertEq(gAtomics_and(a, x, 14), 7); 146 // val = 6 147 assertEq(gAtomics_xor(a, x, 5), 6); 148 // val = 3 149 assertEq(gAtomics_load(a, x), 3); 150 // val = 3 151 gAtomics_store(a, x, 0); 152 // val = 0 153 154 // Check adjacent elements were not affected 155 if (x > 0) { 156 assertEq(a[x-1], poison); 157 a[x-1] = 0; 158 } 159 if (x < a.length-1) { 160 assertEq(a[x+1], poison); 161 a[x+1] = 0; 162 } 163 } 164 } 165 166 function testTypeCAS(a) { 167 dprint("Type: " + a.constructor.name); 168 169 var thrown = false; 170 try { 171 Atomics.compareExchange([0], 0, 0, 1); 172 } 173 catch (e) { 174 thrown = true; 175 assertEq(e instanceof TypeError, true); 176 } 177 assertEq(thrown, true); 178 179 // All these variants should be OK 180 Atomics.compareExchange(a, 0, 0.7, 1.8); 181 Atomics.compareExchange(a, 0, "0", 1); 182 Atomics.compareExchange(a, 0, 0, "1"); 183 Atomics.compareExchange(a, 0, 0); 184 } 185 186 function testTypeBinop(a, op) { 187 dprint("Type: " + a.constructor.name); 188 189 var thrown = false; 190 try { 191 op([0], 0, 1); 192 } 193 catch (e) { 194 thrown = true; 195 assertEq(e instanceof TypeError, true); 196 } 197 assertEq(thrown, true); 198 199 // These are all OK 200 op(a, 0, 0.7); 201 op(a, 0, "0"); 202 op(a, 0); 203 } 204 205 var globlength = 0; // Will be set later 206 207 function testRangeCAS(a) { 208 dprint("Range: " + a.constructor.name); 209 210 var msg = /out-of-range index/; // A generic message 211 212 assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg); 213 assertEq(a[0], 0); 214 215 // Converted to 0 216 assertEq(Atomics.compareExchange(a, "hi", 0, 33), 0); 217 assertEq(a[0], 33); 218 a[0] = 0; 219 220 assertErrorMessage(() => Atomics.compareExchange(a, a.length + 5, 0, 1), RangeError, msg); 221 assertEq(a[0], 0); 222 223 assertErrorMessage(() => Atomics.compareExchange(a, globlength, 0, 1), RangeError, msg); 224 assertEq(a[0], 0); 225 } 226 227 // Ad-hoc tests for extreme and out-of-range values. 228 // None of these should throw 229 230 function testInt8Extremes(a) { 231 dprint("Int8 extremes"); 232 233 a[10] = 0; 234 a[11] = 0; 235 236 Atomics.store(a, 10, 255); 237 assertEq(a[10], -1); 238 assertEq(Atomics.load(a, 10), -1); 239 240 Atomics.add(a, 10, 255); // should coerce to -1 241 assertEq(a[10], -2); 242 assertEq(Atomics.load(a, 10), -2); 243 244 Atomics.add(a, 10, -1); 245 assertEq(a[10], -3); 246 assertEq(Atomics.load(a, 10), -3); 247 248 Atomics.sub(a, 10, 255); // should coerce to -1 249 assertEq(a[10], -2); 250 assertEq(Atomics.load(a, 10), -2); 251 252 Atomics.sub(a, 10, 256); // should coerce to 0 253 assertEq(a[10], -2); 254 assertEq(Atomics.load(a, 10), -2); 255 256 Atomics.and(a, 10, -1); // Preserve all 257 assertEq(a[10], -2); 258 assertEq(Atomics.load(a, 10), -2); 259 260 Atomics.and(a, 10, 256); // Preserve none 261 assertEq(a[10], 0); 262 assertEq(Atomics.load(a, 10), 0); 263 264 Atomics.store(a, 10, 255); 265 assertEq(Atomics.exchange(a, 10, 0), -1); 266 267 assertEq(a[11], 0); 268 } 269 270 function testUint8Extremes(a) { 271 dprint("Uint8 extremes"); 272 273 a[10] = 0; 274 a[11] = 0; 275 276 Atomics.store(a, 10, 255); 277 assertEq(a[10], 255); 278 assertEq(Atomics.load(a, 10), 255); 279 280 Atomics.add(a, 10, 255); 281 assertEq(a[10], 254); 282 assertEq(Atomics.load(a, 10), 254); 283 284 Atomics.add(a, 10, -1); 285 assertEq(a[10], 253); 286 assertEq(Atomics.load(a, 10), 253); 287 288 Atomics.sub(a, 10, 255); 289 assertEq(a[10], 254); 290 assertEq(Atomics.load(a, 10), 254); 291 292 Atomics.and(a, 10, -1); // Preserve all 293 assertEq(a[10], 254); 294 assertEq(Atomics.load(a, 10), 254); 295 296 Atomics.and(a, 10, 256); // Preserve none 297 assertEq(a[10], 0); 298 assertEq(Atomics.load(a, 10), 0); 299 300 Atomics.store(a, 10, 255); 301 assertEq(Atomics.exchange(a, 10, 0), 255); 302 303 assertEq(a[11], 0); 304 } 305 306 function testInt16Extremes(a) { 307 dprint("Int16 extremes"); 308 309 a[10] = 0; 310 a[11] = 0; 311 312 Atomics.store(a, 10, 65535); 313 assertEq(a[10], -1); 314 assertEq(Atomics.load(a, 10), -1); 315 316 Atomics.add(a, 10, 65535); // should coerce to -1 317 assertEq(a[10], -2); 318 assertEq(Atomics.load(a, 10), -2); 319 320 Atomics.add(a, 10, -1); 321 assertEq(a[10], -3); 322 assertEq(Atomics.load(a, 10), -3); 323 324 Atomics.sub(a, 10, 65535); // should coerce to -1 325 assertEq(a[10], -2); 326 assertEq(Atomics.load(a, 10), -2); 327 328 Atomics.sub(a, 10, 65536); // should coerce to 0 329 assertEq(a[10], -2); 330 assertEq(Atomics.load(a, 10), -2); 331 332 Atomics.and(a, 10, -1); // Preserve all 333 assertEq(a[10], -2); 334 assertEq(Atomics.load(a, 10), -2); 335 336 Atomics.and(a, 10, 65536); // Preserve none 337 assertEq(a[10], 0); 338 assertEq(Atomics.load(a, 10), 0); 339 340 assertEq(a[11], 0); 341 } 342 343 function testUint32(a) { 344 var k = 0; 345 for ( var i=0 ; i < 20 ; i++ ) { 346 a[i] = i+5; 347 k += a[i]; 348 } 349 350 var sum = 0; 351 for ( var i=0 ; i < 20 ; i++ ) 352 sum += Atomics.add(a, i, 1); 353 354 assertEq(sum, k); 355 } 356 357 // This test is a reliable test of sign extension in the JIT where 358 // testInt8Extremes is not (because there may not be enough type 359 // information without a loop - see bug 1181062 for a description 360 // of the general problem). 361 362 function exchangeLoop(ta) { 363 var sum = 0; 364 for ( var i=0 ; i < 100000 ; i++ ) 365 sum += Atomics.exchange(ta, i & 15, 255); 366 return sum; 367 } 368 369 function adHocExchange(SharedOrUnsharedArrayBuffer) { 370 var a = new Int8Array(new SharedOrUnsharedArrayBuffer(16)); 371 for ( var i=0 ; i < a.length ; i++ ) 372 a[i] = 255; 373 assertEq(exchangeLoop(a), -100000); 374 } 375 376 // isLockFree(n) may return true only if there is an integer array 377 // on which atomic operations is allowed whose byte size is n, 378 // ie, it must return false for n=7. 379 // 380 // SpiderMonkey has isLockFree(1), isLockFree(2), isLockFree(8), isLockFree(4) 381 // on all supported platforms, only the last is guaranteed by the spec. 382 383 var sizes = [ 1, 2, 3, 4, 5, 6, 7, 8, 384 9, 10, 11, 12]; 385 var answers = [ true, true, false, true, false, false, false, true, 386 false, false, false, false]; 387 388 function testIsLockFree() { 389 // This ought to defeat most compile-time resolution. 390 for ( var i=0 ; i < sizes.length ; i++ ) { 391 var v = Atomics.isLockFree(sizes[i]); 392 var a = answers[i]; 393 assertEq(typeof v, 'boolean'); 394 assertEq(v, a); 395 } 396 397 // This ought to be optimizable. 398 assertEq(Atomics.isLockFree(1), true); 399 assertEq(Atomics.isLockFree(2), true); 400 assertEq(Atomics.isLockFree(3), false); 401 assertEq(Atomics.isLockFree(4), true); 402 assertEq(Atomics.isLockFree(5), false); 403 assertEq(Atomics.isLockFree(6), false); 404 assertEq(Atomics.isLockFree(7), false); 405 assertEq(Atomics.isLockFree(8), true); 406 assertEq(Atomics.isLockFree(9), false); 407 assertEq(Atomics.isLockFree(10), false); 408 assertEq(Atomics.isLockFree(11), false); 409 assertEq(Atomics.isLockFree(12), false); 410 } 411 412 function testIsLockFree2() { 413 assertEq(Atomics.isLockFree(0), false); 414 assertEq(Atomics.isLockFree(0/-1), false); 415 assertEq(Atomics.isLockFree(3.5), false); 416 assertEq(Atomics.isLockFree(Number.NaN), false); // NaN => +0 417 assertEq(Atomics.isLockFree(Number.POSITIVE_INFINITY), false); 418 assertEq(Atomics.isLockFree(Number.NEGATIVE_INFINITY), false); 419 assertEq(Atomics.isLockFree(-4), false); 420 assertEq(Atomics.isLockFree('4'), true); 421 assertEq(Atomics.isLockFree('-4'), false); 422 assertEq(Atomics.isLockFree('4.5'), true); 423 assertEq(Atomics.isLockFree('5.5'), false); 424 assertEq(Atomics.isLockFree(new Number(4)), true); 425 assertEq(Atomics.isLockFree(new String('4')), true); 426 assertEq(Atomics.isLockFree(new Boolean(true)), true); 427 var thrown = false; 428 try { 429 Atomics.isLockFree(Symbol('1')); 430 } catch (e) { 431 thrown = e; 432 } 433 assertEq(thrown instanceof TypeError, true); 434 assertEq(Atomics.isLockFree(true), true); 435 assertEq(Atomics.isLockFree(false), false); 436 assertEq(Atomics.isLockFree(undefined), false); 437 assertEq(Atomics.isLockFree(null), false); 438 assertEq(Atomics.isLockFree({toString: () => '4'}), true); 439 assertEq(Atomics.isLockFree({valueOf: () => 4}), true); 440 assertEq(Atomics.isLockFree({valueOf: () => 5}), false); 441 assertEq(Atomics.isLockFree({password: "qumquat"}), false); 442 } 443 444 function testUint8Clamped(sab) { 445 var ta = new Uint8ClampedArray(sab); 446 var thrown = false; 447 try { 448 CLONE(testMethod)(ta, 0); 449 } 450 catch (e) { 451 thrown = true; 452 assertEq(e instanceof TypeError, true); 453 } 454 assertEq(thrown, true); 455 } 456 457 function testWeirdIndices(SharedOrUnsharedArrayBuffer) { 458 var a = new Int8Array(new SharedOrUnsharedArrayBuffer(16)); 459 a[3] = 10; 460 assertEq(Atomics.load(a, "0x03"), 10); 461 assertEq(Atomics.load(a, {valueOf: () => 3}), 10); 462 } 463 464 function isLittleEndian() { 465 var xxx = new ArrayBuffer(2); 466 var xxa = new Int16Array(xxx); 467 var xxb = new Int8Array(xxx); 468 xxa[0] = 37; 469 var is_little = xxb[0] == 37; 470 return is_little; 471 } 472 473 function runTests(SharedOrUnsharedArrayBuffer) { 474 var is_little = isLittleEndian(); 475 476 // Currently the SharedArrayBuffer needs to be a multiple of 4K bytes in size. 477 var sab = new SharedOrUnsharedArrayBuffer(4096); 478 479 // Test that two arrays created on the same storage alias 480 var t1 = new Int8Array(sab); 481 var t2 = new Uint16Array(sab); 482 483 assertEq(t1[0], 0); 484 assertEq(t2[0], 0); 485 t1[0] = 37; 486 if (is_little) 487 assertEq(t2[0], 37); 488 else 489 assertEq(t2[0], 37 << 8); 490 t1[0] = 0; 491 492 // Test that invoking as Atomics.whatever() works, on correct arguments. 493 CLONE(testMethod)(new Int8Array(sab), 0, 42, 4095); 494 CLONE(testMethod)(new Uint8Array(sab), 0, 42, 4095); 495 CLONE(testMethod)(new Int16Array(sab), 0, 42, 2047); 496 CLONE(testMethod)(new Uint16Array(sab), 0, 42, 2047); 497 CLONE(testMethod)(new Int32Array(sab), 0, 42, 1023); 498 CLONE(testMethod)(new Uint32Array(sab), 0, 42, 1023); 499 500 // Test that invoking as v = Atomics.whatever; v() works, on correct arguments. 501 gAtomics_compareExchange = Atomics.compareExchange; 502 gAtomics_exchange = Atomics.exchange; 503 gAtomics_load = Atomics.load; 504 gAtomics_store = Atomics.store; 505 gAtomics_add = Atomics.add; 506 gAtomics_sub = Atomics.sub; 507 gAtomics_and = Atomics.and; 508 gAtomics_or = Atomics.or; 509 gAtomics_xor = Atomics.xor; 510 511 CLONE(testFunction)(new Int8Array(sab), 0, 42, 4095); 512 CLONE(testFunction)(new Uint8Array(sab), 0, 42, 4095); 513 CLONE(testFunction)(new Int16Array(sab), 0, 42, 2047); 514 CLONE(testFunction)(new Uint16Array(sab), 0, 42, 2047); 515 CLONE(testFunction)(new Int32Array(sab), 0, 42, 1023); 516 CLONE(testFunction)(new Uint32Array(sab), 0, 42, 1023); 517 518 // Test various range and type conditions 519 var v8 = new Int8Array(sab); 520 var v32 = new Int32Array(sab); 521 522 CLONE(testTypeCAS)(v8); 523 CLONE(testTypeCAS)(v32); 524 525 CLONE(testTypeBinop)(v8, Atomics.add); 526 CLONE(testTypeBinop)(v8, Atomics.sub); 527 CLONE(testTypeBinop)(v8, Atomics.and); 528 CLONE(testTypeBinop)(v8, Atomics.or); 529 CLONE(testTypeBinop)(v8, Atomics.xor); 530 531 CLONE(testTypeBinop)(v32, Atomics.add); 532 CLONE(testTypeBinop)(v32, Atomics.sub); 533 CLONE(testTypeBinop)(v32, Atomics.and); 534 CLONE(testTypeBinop)(v32, Atomics.or); 535 CLONE(testTypeBinop)(v32, Atomics.xor); 536 537 // Test out-of-range references 538 globlength = v8.length + 5; 539 CLONE(testRangeCAS)(v8); 540 globlength = v32.length + 5; 541 CLONE(testRangeCAS)(v32); 542 543 // Test extreme values 544 testInt8Extremes(new Int8Array(sab)); 545 testUint8Extremes(new Uint8Array(sab)); 546 testInt16Extremes(new Int16Array(sab)); 547 testUint32(new Uint32Array(sab)); 548 549 // Test that Uint8ClampedArray is not accepted. 550 testUint8Clamped(sab); 551 552 // Misc ad-hoc tests 553 adHocExchange(SharedOrUnsharedArrayBuffer); 554 555 // Misc 556 testIsLockFree(); 557 testIsLockFree2(); 558 testWeirdIndices(SharedOrUnsharedArrayBuffer); 559 560 assertEq(Atomics[Symbol.toStringTag], "Atomics"); 561 } 562 563 runTests(SharedArrayBuffer); 564 runTests(ArrayBuffer); 565 566 if (ArrayBuffer.prototype.resize) { 567 class ResizableArrayBuffer { 568 constructor(byteLength = 0) { 569 return new ArrayBuffer(byteLength, {maxByteLength: byteLength}); 570 } 571 } 572 runTests(ResizableArrayBuffer); 573 } 574 575 if (SharedArrayBuffer.prototype.grow) { 576 class GrowableSharedArrayBuffer { 577 constructor(byteLength = 0) { 578 return new SharedArrayBuffer(byteLength, {maxByteLength: byteLength}); 579 } 580 } 581 runTests(GrowableSharedArrayBuffer); 582 }