js/src/builtin/Array.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /* ES5 15.4.4.14. */
michael@0 6 function ArrayIndexOf(searchElement/*, fromIndex*/) {
michael@0 7 /* Step 1. */
michael@0 8 var O = ToObject(this);
michael@0 9
michael@0 10 /* Steps 2-3. */
michael@0 11 var len = TO_UINT32(O.length);
michael@0 12
michael@0 13 /* Step 4. */
michael@0 14 if (len === 0)
michael@0 15 return -1;
michael@0 16
michael@0 17 /* Step 5. */
michael@0 18 var n = arguments.length > 1 ? ToInteger(arguments[1]) : 0;
michael@0 19
michael@0 20 /* Step 6. */
michael@0 21 if (n >= len)
michael@0 22 return -1;
michael@0 23
michael@0 24 var k;
michael@0 25 /* Step 7. */
michael@0 26 if (n >= 0)
michael@0 27 k = n;
michael@0 28 /* Step 8. */
michael@0 29 else {
michael@0 30 /* Step a. */
michael@0 31 k = len + n;
michael@0 32 /* Step b. */
michael@0 33 if (k < 0)
michael@0 34 k = 0;
michael@0 35 }
michael@0 36
michael@0 37 /* Step 9. */
michael@0 38 if (IsPackedArray(O)) {
michael@0 39 for (; k < len; k++) {
michael@0 40 if (O[k] === searchElement)
michael@0 41 return k;
michael@0 42 }
michael@0 43 } else {
michael@0 44 for (; k < len; k++) {
michael@0 45 if (k in O && O[k] === searchElement)
michael@0 46 return k;
michael@0 47 }
michael@0 48 }
michael@0 49
michael@0 50 /* Step 10. */
michael@0 51 return -1;
michael@0 52 }
michael@0 53
michael@0 54 function ArrayStaticIndexOf(list, searchElement/*, fromIndex*/) {
michael@0 55 if (arguments.length < 1)
michael@0 56 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.indexOf');
michael@0 57 var fromIndex = arguments.length > 2 ? arguments[2] : 0;
michael@0 58 return callFunction(ArrayIndexOf, list, searchElement, fromIndex);
michael@0 59 }
michael@0 60
michael@0 61 /* ES5 15.4.4.15. */
michael@0 62 function ArrayLastIndexOf(searchElement/*, fromIndex*/) {
michael@0 63 /* Step 1. */
michael@0 64 var O = ToObject(this);
michael@0 65
michael@0 66 /* Steps 2-3. */
michael@0 67 var len = TO_UINT32(O.length);
michael@0 68
michael@0 69 /* Step 4. */
michael@0 70 if (len === 0)
michael@0 71 return -1;
michael@0 72
michael@0 73 /* Step 5. */
michael@0 74 var n = arguments.length > 1 ? ToInteger(arguments[1]) : len - 1;
michael@0 75
michael@0 76 /* Steps 6-7. */
michael@0 77 var k;
michael@0 78 if (n > len - 1)
michael@0 79 k = len - 1;
michael@0 80 else if (n < 0)
michael@0 81 k = len + n;
michael@0 82 else
michael@0 83 k = n;
michael@0 84
michael@0 85 /* Step 8. */
michael@0 86 if (IsPackedArray(O)) {
michael@0 87 for (; k >= 0; k--) {
michael@0 88 if (O[k] === searchElement)
michael@0 89 return k;
michael@0 90 }
michael@0 91 } else {
michael@0 92 for (; k >= 0; k--) {
michael@0 93 if (k in O && O[k] === searchElement)
michael@0 94 return k;
michael@0 95 }
michael@0 96 }
michael@0 97
michael@0 98 /* Step 9. */
michael@0 99 return -1;
michael@0 100 }
michael@0 101
michael@0 102 function ArrayStaticLastIndexOf(list, searchElement/*, fromIndex*/) {
michael@0 103 if (arguments.length < 1)
michael@0 104 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.lastIndexOf');
michael@0 105 var fromIndex;
michael@0 106 if (arguments.length > 2) {
michael@0 107 fromIndex = arguments[2];
michael@0 108 } else {
michael@0 109 var O = ToObject(list);
michael@0 110 var len = TO_UINT32(O.length);
michael@0 111 fromIndex = len - 1;
michael@0 112 }
michael@0 113 return callFunction(ArrayLastIndexOf, list, searchElement, fromIndex);
michael@0 114 }
michael@0 115
michael@0 116 /* ES5 15.4.4.16. */
michael@0 117 function ArrayEvery(callbackfn/*, thisArg*/) {
michael@0 118 /* Step 1. */
michael@0 119 var O = ToObject(this);
michael@0 120
michael@0 121 /* Steps 2-3. */
michael@0 122 var len = TO_UINT32(O.length);
michael@0 123
michael@0 124 /* Step 4. */
michael@0 125 if (arguments.length === 0)
michael@0 126 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.every');
michael@0 127 if (!IsCallable(callbackfn))
michael@0 128 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
michael@0 129
michael@0 130 /* Step 5. */
michael@0 131 var T = arguments.length > 1 ? arguments[1] : void 0;
michael@0 132
michael@0 133 /* Steps 6-7. */
michael@0 134 /* Steps a (implicit), and d. */
michael@0 135 for (var k = 0; k < len; k++) {
michael@0 136 /* Step b */
michael@0 137 if (k in O) {
michael@0 138 /* Step c. */
michael@0 139 if (!callFunction(callbackfn, T, O[k], k, O))
michael@0 140 return false;
michael@0 141 }
michael@0 142 }
michael@0 143
michael@0 144 /* Step 8. */
michael@0 145 return true;
michael@0 146 }
michael@0 147
michael@0 148 function ArrayStaticEvery(list, callbackfn/*, thisArg*/) {
michael@0 149 if (arguments.length < 2)
michael@0 150 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.every');
michael@0 151 if (!IsCallable(callbackfn))
michael@0 152 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
michael@0 153 var T = arguments.length > 2 ? arguments[2] : void 0;
michael@0 154 return callFunction(ArrayEvery, list, callbackfn, T);
michael@0 155 }
michael@0 156
michael@0 157 /* ES5 15.4.4.17. */
michael@0 158 function ArraySome(callbackfn/*, thisArg*/) {
michael@0 159 /* Step 1. */
michael@0 160 var O = ToObject(this);
michael@0 161
michael@0 162 /* Steps 2-3. */
michael@0 163 var len = TO_UINT32(O.length);
michael@0 164
michael@0 165 /* Step 4. */
michael@0 166 if (arguments.length === 0)
michael@0 167 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.some');
michael@0 168 if (!IsCallable(callbackfn))
michael@0 169 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
michael@0 170
michael@0 171 /* Step 5. */
michael@0 172 var T = arguments.length > 1 ? arguments[1] : void 0;
michael@0 173
michael@0 174 /* Steps 6-7. */
michael@0 175 /* Steps a (implicit), and d. */
michael@0 176 for (var k = 0; k < len; k++) {
michael@0 177 /* Step b */
michael@0 178 if (k in O) {
michael@0 179 /* Step c. */
michael@0 180 if (callFunction(callbackfn, T, O[k], k, O))
michael@0 181 return true;
michael@0 182 }
michael@0 183 }
michael@0 184
michael@0 185 /* Step 8. */
michael@0 186 return false;
michael@0 187 }
michael@0 188
michael@0 189 function ArrayStaticSome(list, callbackfn/*, thisArg*/) {
michael@0 190 if (arguments.length < 2)
michael@0 191 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.some');
michael@0 192 if (!IsCallable(callbackfn))
michael@0 193 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
michael@0 194 var T = arguments.length > 2 ? arguments[2] : void 0;
michael@0 195 return callFunction(ArraySome, list, callbackfn, T);
michael@0 196 }
michael@0 197
michael@0 198 /* ES5 15.4.4.18. */
michael@0 199 function ArrayForEach(callbackfn/*, thisArg*/) {
michael@0 200 /* Step 1. */
michael@0 201 var O = ToObject(this);
michael@0 202
michael@0 203 /* Steps 2-3. */
michael@0 204 var len = TO_UINT32(O.length);
michael@0 205
michael@0 206 /* Step 4. */
michael@0 207 if (arguments.length === 0)
michael@0 208 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.forEach');
michael@0 209 if (!IsCallable(callbackfn))
michael@0 210 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
michael@0 211
michael@0 212 /* Step 5. */
michael@0 213 var T = arguments.length > 1 ? arguments[1] : void 0;
michael@0 214
michael@0 215 /* Steps 6-7. */
michael@0 216 /* Steps a (implicit), and d. */
michael@0 217 for (var k = 0; k < len; k++) {
michael@0 218 /* Step b */
michael@0 219 if (k in O) {
michael@0 220 /* Step c. */
michael@0 221 callFunction(callbackfn, T, O[k], k, O);
michael@0 222 }
michael@0 223 }
michael@0 224
michael@0 225 /* Step 8. */
michael@0 226 return void 0;
michael@0 227 }
michael@0 228
michael@0 229 /* ES5 15.4.4.19. */
michael@0 230 function ArrayMap(callbackfn/*, thisArg*/) {
michael@0 231 /* Step 1. */
michael@0 232 var O = ToObject(this);
michael@0 233
michael@0 234 /* Step 2-3. */
michael@0 235 var len = TO_UINT32(O.length);
michael@0 236
michael@0 237 /* Step 4. */
michael@0 238 if (arguments.length === 0)
michael@0 239 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.map');
michael@0 240 if (!IsCallable(callbackfn))
michael@0 241 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
michael@0 242
michael@0 243 /* Step 5. */
michael@0 244 var T = arguments.length > 1 ? arguments[1] : void 0;
michael@0 245
michael@0 246 /* Step 6. */
michael@0 247 var A = NewDenseArray(len);
michael@0 248
michael@0 249 /* Step 7-8. */
michael@0 250 /* Step a (implicit), and d. */
michael@0 251 for (var k = 0; k < len; k++) {
michael@0 252 /* Step b */
michael@0 253 if (k in O) {
michael@0 254 /* Step c.i-iii. */
michael@0 255 var mappedValue = callFunction(callbackfn, T, O[k], k, O);
michael@0 256 // UnsafePutElements doesn't invoke setters, so we can use it here.
michael@0 257 UnsafePutElements(A, k, mappedValue);
michael@0 258 }
michael@0 259 }
michael@0 260
michael@0 261 /* Step 9. */
michael@0 262 return A;
michael@0 263 }
michael@0 264
michael@0 265 function ArrayStaticMap(list, callbackfn/*, thisArg*/) {
michael@0 266 if (arguments.length < 2)
michael@0 267 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.map');
michael@0 268 if (!IsCallable(callbackfn))
michael@0 269 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
michael@0 270 var T = arguments.length > 2 ? arguments[2] : void 0;
michael@0 271 return callFunction(ArrayMap, list, callbackfn, T);
michael@0 272 }
michael@0 273
michael@0 274 function ArrayStaticForEach(list, callbackfn/*, thisArg*/) {
michael@0 275 if (arguments.length < 2)
michael@0 276 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.forEach');
michael@0 277 if (!IsCallable(callbackfn))
michael@0 278 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
michael@0 279 var T = arguments.length > 2 ? arguments[2] : void 0;
michael@0 280 callFunction(ArrayForEach, list, callbackfn, T);
michael@0 281 }
michael@0 282
michael@0 283 /* ES5 15.4.4.21. */
michael@0 284 function ArrayReduce(callbackfn/*, initialValue*/) {
michael@0 285 /* Step 1. */
michael@0 286 var O = ToObject(this);
michael@0 287
michael@0 288 /* Steps 2-3. */
michael@0 289 var len = TO_UINT32(O.length);
michael@0 290
michael@0 291 /* Step 4. */
michael@0 292 if (arguments.length === 0)
michael@0 293 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce');
michael@0 294 if (!IsCallable(callbackfn))
michael@0 295 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
michael@0 296
michael@0 297 /* Step 6. */
michael@0 298 var k = 0;
michael@0 299
michael@0 300 /* Steps 5, 7-8. */
michael@0 301 var accumulator;
michael@0 302 if (arguments.length > 1) {
michael@0 303 accumulator = arguments[1];
michael@0 304 } else {
michael@0 305 /* Step 5. */
michael@0 306 if (len === 0)
michael@0 307 ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
michael@0 308 if (IsPackedArray(O)) {
michael@0 309 accumulator = O[k++];
michael@0 310 } else {
michael@0 311 var kPresent = false;
michael@0 312 for (; k < len; k++) {
michael@0 313 if (k in O) {
michael@0 314 accumulator = O[k];
michael@0 315 kPresent = true;
michael@0 316 k++;
michael@0 317 break;
michael@0 318 }
michael@0 319 }
michael@0 320 if (!kPresent)
michael@0 321 ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
michael@0 322 }
michael@0 323 }
michael@0 324
michael@0 325 /* Step 9. */
michael@0 326 /* Steps a (implicit), and d. */
michael@0 327 for (; k < len; k++) {
michael@0 328 /* Step b */
michael@0 329 if (k in O) {
michael@0 330 /* Step c. */
michael@0 331 accumulator = callbackfn(accumulator, O[k], k, O);
michael@0 332 }
michael@0 333 }
michael@0 334
michael@0 335 /* Step 10. */
michael@0 336 return accumulator;
michael@0 337 }
michael@0 338
michael@0 339 function ArrayStaticReduce(list, callbackfn) {
michael@0 340 if (arguments.length < 2)
michael@0 341 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduce');
michael@0 342 if (!IsCallable(callbackfn))
michael@0 343 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
michael@0 344 if (arguments.length > 2)
michael@0 345 return callFunction(ArrayReduce, list, callbackfn, arguments[2]);
michael@0 346 else
michael@0 347 return callFunction(ArrayReduce, list, callbackfn);
michael@0 348 }
michael@0 349
michael@0 350 /* ES5 15.4.4.22. */
michael@0 351 function ArrayReduceRight(callbackfn/*, initialValue*/) {
michael@0 352 /* Step 1. */
michael@0 353 var O = ToObject(this);
michael@0 354
michael@0 355 /* Steps 2-3. */
michael@0 356 var len = TO_UINT32(O.length);
michael@0 357
michael@0 358 /* Step 4. */
michael@0 359 if (arguments.length === 0)
michael@0 360 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce');
michael@0 361 if (!IsCallable(callbackfn))
michael@0 362 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
michael@0 363
michael@0 364 /* Step 6. */
michael@0 365 var k = len - 1;
michael@0 366
michael@0 367 /* Steps 5, 7-8. */
michael@0 368 var accumulator;
michael@0 369 if (arguments.length > 1) {
michael@0 370 accumulator = arguments[1];
michael@0 371 } else {
michael@0 372 /* Step 5. */
michael@0 373 if (len === 0)
michael@0 374 ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
michael@0 375 if (IsPackedArray(O)) {
michael@0 376 accumulator = O[k--];
michael@0 377 } else {
michael@0 378 var kPresent = false;
michael@0 379 for (; k >= 0; k--) {
michael@0 380 if (k in O) {
michael@0 381 accumulator = O[k];
michael@0 382 kPresent = true;
michael@0 383 k--;
michael@0 384 break;
michael@0 385 }
michael@0 386 }
michael@0 387 if (!kPresent)
michael@0 388 ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
michael@0 389 }
michael@0 390 }
michael@0 391
michael@0 392 /* Step 9. */
michael@0 393 /* Steps a (implicit), and d. */
michael@0 394 for (; k >= 0; k--) {
michael@0 395 /* Step b */
michael@0 396 if (k in O) {
michael@0 397 /* Step c. */
michael@0 398 accumulator = callbackfn(accumulator, O[k], k, O);
michael@0 399 }
michael@0 400 }
michael@0 401
michael@0 402 /* Step 10. */
michael@0 403 return accumulator;
michael@0 404 }
michael@0 405
michael@0 406 function ArrayStaticReduceRight(list, callbackfn) {
michael@0 407 if (arguments.length < 2)
michael@0 408 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduceRight');
michael@0 409 if (!IsCallable(callbackfn))
michael@0 410 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
michael@0 411 if (arguments.length > 2)
michael@0 412 return callFunction(ArrayReduceRight, list, callbackfn, arguments[2]);
michael@0 413 else
michael@0 414 return callFunction(ArrayReduceRight, list, callbackfn);
michael@0 415 }
michael@0 416
michael@0 417 /* ES6 draft 2013-05-14 15.4.3.23. */
michael@0 418 function ArrayFind(predicate/*, thisArg*/) {
michael@0 419 /* Steps 1-2. */
michael@0 420 var O = ToObject(this);
michael@0 421
michael@0 422 /* Steps 3-5. */
michael@0 423 var len = ToInteger(O.length);
michael@0 424
michael@0 425 /* Step 6. */
michael@0 426 if (arguments.length === 0)
michael@0 427 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find');
michael@0 428 if (!IsCallable(predicate))
michael@0 429 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate));
michael@0 430
michael@0 431 /* Step 7. */
michael@0 432 var T = arguments.length > 1 ? arguments[1] : undefined;
michael@0 433
michael@0 434 /* Steps 8-9. */
michael@0 435 /* Steps a (implicit), and e. */
michael@0 436 /* Note: this will hang in some corner-case situations, because of IEEE-754 numbers'
michael@0 437 * imprecision for large values. Example:
michael@0 438 * var obj = { 18014398509481984: true, length: 18014398509481988 };
michael@0 439 * Array.prototype.find.call(obj, () => true);
michael@0 440 */
michael@0 441 for (var k = 0; k < len; k++) {
michael@0 442 /* Steps b and c (implicit) */
michael@0 443 if (k in O) {
michael@0 444 /* Step d. */
michael@0 445 var kValue = O[k];
michael@0 446 if (callFunction(predicate, T, kValue, k, O))
michael@0 447 return kValue;
michael@0 448 }
michael@0 449 }
michael@0 450
michael@0 451 /* Step 10. */
michael@0 452 return undefined;
michael@0 453 }
michael@0 454
michael@0 455 /* ES6 draft 2013-05-14 15.4.3.23. */
michael@0 456 function ArrayFindIndex(predicate/*, thisArg*/) {
michael@0 457 /* Steps 1-2. */
michael@0 458 var O = ToObject(this);
michael@0 459
michael@0 460 /* Steps 3-5. */
michael@0 461 var len = ToInteger(O.length);
michael@0 462
michael@0 463 /* Step 6. */
michael@0 464 if (arguments.length === 0)
michael@0 465 ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find');
michael@0 466 if (!IsCallable(predicate))
michael@0 467 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate));
michael@0 468
michael@0 469 /* Step 7. */
michael@0 470 var T = arguments.length > 1 ? arguments[1] : undefined;
michael@0 471
michael@0 472 /* Steps 8-9. */
michael@0 473 /* Steps a (implicit), and e. */
michael@0 474 /* Note: this will hang in some corner-case situations, because of IEEE-754 numbers'
michael@0 475 * imprecision for large values. Example:
michael@0 476 * var obj = { 18014398509481984: true, length: 18014398509481988 };
michael@0 477 * Array.prototype.find.call(obj, () => true);
michael@0 478 */
michael@0 479 for (var k = 0; k < len; k++) {
michael@0 480 /* Steps b and c (implicit) */
michael@0 481 if (k in O) {
michael@0 482 /* Step d. */
michael@0 483 if (callFunction(predicate, T, O[k], k, O))
michael@0 484 return k;
michael@0 485 }
michael@0 486 }
michael@0 487
michael@0 488 /* Step 10. */
michael@0 489 return -1;
michael@0 490 }
michael@0 491
michael@0 492 // ES6 draft 2014-04-05 22.1.3.6
michael@0 493 function ArrayFill(value, start = 0, end = undefined) {
michael@0 494 // Steps 1-2.
michael@0 495 var O = ToObject(this);
michael@0 496
michael@0 497 // Steps 3-5.
michael@0 498 // FIXME: Array operations should use ToLength (bug 924058).
michael@0 499 var len = ToInteger(O.length);
michael@0 500
michael@0 501 // Steps 6-7.
michael@0 502 var relativeStart = ToInteger(start);
michael@0 503
michael@0 504 // Step 8.
michael@0 505 var k = relativeStart < 0
michael@0 506 ? std_Math_max(len + relativeStart, 0)
michael@0 507 : std_Math_min(relativeStart, len);
michael@0 508
michael@0 509 // Steps 9-10.
michael@0 510 var relativeEnd = end === undefined ? len : ToInteger(end);
michael@0 511
michael@0 512 // Step 11.
michael@0 513 var final = relativeEnd < 0
michael@0 514 ? std_Math_max(len + relativeEnd, 0)
michael@0 515 : std_Math_min(relativeEnd, len);
michael@0 516
michael@0 517 // Step 12.
michael@0 518 for (; k < final; k++) {
michael@0 519 O[k] = value;
michael@0 520 }
michael@0 521
michael@0 522 // Step 13.
michael@0 523 return O;
michael@0 524 }
michael@0 525
michael@0 526 #define ARRAY_ITERATOR_SLOT_ITERATED_OBJECT 0
michael@0 527 #define ARRAY_ITERATOR_SLOT_NEXT_INDEX 1
michael@0 528 #define ARRAY_ITERATOR_SLOT_ITEM_KIND 2
michael@0 529
michael@0 530 #define ITEM_KIND_VALUE 0
michael@0 531 #define ITEM_KIND_KEY_AND_VALUE 1
michael@0 532 #define ITEM_KIND_KEY 2
michael@0 533
michael@0 534 // ES6 draft specification, section 22.1.5.1, version 2013-09-05.
michael@0 535 function CreateArrayIteratorAt(obj, kind, n) {
michael@0 536 var iteratedObject = ToObject(obj);
michael@0 537 var iterator = NewArrayIterator();
michael@0 538 UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT, iteratedObject);
michael@0 539 UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_NEXT_INDEX, n);
michael@0 540 UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITEM_KIND, kind);
michael@0 541 return iterator;
michael@0 542 }
michael@0 543 function CreateArrayIterator(obj, kind) {
michael@0 544 return CreateArrayIteratorAt(obj, kind, 0);
michael@0 545 }
michael@0 546
michael@0 547 function ArrayIteratorIdentity() {
michael@0 548 return this;
michael@0 549 }
michael@0 550
michael@0 551 function ArrayIteratorNext() {
michael@0 552 // FIXME: ArrayIterator prototype should not pass this test. Bug 924059.
michael@0 553 if (!IsObject(this) || !IsArrayIterator(this))
michael@0 554 ThrowError(JSMSG_INCOMPATIBLE_METHOD, "ArrayIterator", "next", ToString(this));
michael@0 555
michael@0 556 var a = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT);
michael@0 557 var index = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX);
michael@0 558 var itemKind = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_ITEM_KIND);
michael@0 559
michael@0 560 // FIXME: This should be ToLength, which clamps at 2**53. Bug 924058.
michael@0 561 if (index >= TO_UINT32(a.length)) {
michael@0 562 // When the above is changed to ToLength, use +1/0 here instead
michael@0 563 // of MAX_UINT32.
michael@0 564 UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, 0xffffffff);
michael@0 565 return { value: undefined, done: true };
michael@0 566 }
michael@0 567
michael@0 568 UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, index + 1);
michael@0 569
michael@0 570 if (itemKind === ITEM_KIND_VALUE)
michael@0 571 return { value: a[index], done: false };
michael@0 572
michael@0 573 if (itemKind === ITEM_KIND_KEY_AND_VALUE) {
michael@0 574 var pair = NewDenseArray(2);
michael@0 575 pair[0] = index;
michael@0 576 pair[1] = a[index];
michael@0 577 return { value: pair, done : false };
michael@0 578 }
michael@0 579
michael@0 580 assert(itemKind === ITEM_KIND_KEY, itemKind);
michael@0 581 return { value: index, done: false };
michael@0 582 }
michael@0 583
michael@0 584 function ArrayValuesAt(n) {
michael@0 585 return CreateArrayIteratorAt(this, ITEM_KIND_VALUE, n);
michael@0 586 }
michael@0 587
michael@0 588 function ArrayValues() {
michael@0 589 return CreateArrayIterator(this, ITEM_KIND_VALUE);
michael@0 590 }
michael@0 591
michael@0 592 function ArrayEntries() {
michael@0 593 return CreateArrayIterator(this, ITEM_KIND_KEY_AND_VALUE);
michael@0 594 }
michael@0 595
michael@0 596 function ArrayKeys() {
michael@0 597 return CreateArrayIterator(this, ITEM_KIND_KEY);
michael@0 598 }
michael@0 599
michael@0 600 #ifdef ENABLE_PARALLEL_JS
michael@0 601
michael@0 602 /*
michael@0 603 * Strawman spec:
michael@0 604 * http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism
michael@0 605 */
michael@0 606
michael@0 607 /**
michael@0 608 * Creates a new array by applying |func(e, i, self)| for each element |e|
michael@0 609 * with index |i|.
michael@0 610 */
michael@0 611 function ArrayMapPar(func, mode) {
michael@0 612 if (!IsCallable(func))
michael@0 613 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func));
michael@0 614
michael@0 615 var self = ToObject(this);
michael@0 616 var length = self.length;
michael@0 617 var buffer = NewDenseArray(length);
michael@0 618
michael@0 619 parallel: for (;;) {
michael@0 620 // Avoid parallel compilation if we are already nested in another
michael@0 621 // parallel section or the user told us not to parallelize. The
michael@0 622 // use of a for (;;) loop is working around some ion limitations:
michael@0 623 //
michael@0 624 // - Breaking out of named blocks does not currently work (bug 684384);
michael@0 625 // - Unreachable Code Elim. can't properly handle if (a && b) (bug 669796)
michael@0 626 if (ShouldForceSequential())
michael@0 627 break parallel;
michael@0 628 if (!TRY_PARALLEL(mode))
michael@0 629 break parallel;
michael@0 630
michael@0 631 var slicesInfo = ComputeSlicesInfo(length);
michael@0 632 ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode));
michael@0 633 return buffer;
michael@0 634 }
michael@0 635
michael@0 636 // Sequential fallback:
michael@0 637 ASSERT_SEQUENTIAL_IS_OK(mode);
michael@0 638 for (var i = 0; i < length; i++)
michael@0 639 UnsafePutElements(buffer, i, func(self[i], i, self));
michael@0 640 return buffer;
michael@0 641
michael@0 642 function mapThread(workerId, sliceStart, sliceEnd) {
michael@0 643 var sliceShift = slicesInfo.shift;
michael@0 644 var sliceId;
michael@0 645 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 646 var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 647 var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
michael@0 648 for (var i = indexStart; i < indexEnd; i++)
michael@0 649 UnsafePutElements(buffer, i, func(self[i], i, self));
michael@0 650 }
michael@0 651 return sliceId;
michael@0 652 }
michael@0 653
michael@0 654 return undefined;
michael@0 655 }
michael@0 656
michael@0 657 /**
michael@0 658 * Reduces the elements in an array in parallel. Order is not fixed and |func|
michael@0 659 * is assumed to be associative.
michael@0 660 */
michael@0 661 function ArrayReducePar(func, mode) {
michael@0 662 if (!IsCallable(func))
michael@0 663 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func));
michael@0 664
michael@0 665 var self = ToObject(this);
michael@0 666 var length = self.length;
michael@0 667
michael@0 668 if (length === 0)
michael@0 669 ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
michael@0 670
michael@0 671 parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc
michael@0 672 if (ShouldForceSequential())
michael@0 673 break parallel;
michael@0 674 if (!TRY_PARALLEL(mode))
michael@0 675 break parallel;
michael@0 676
michael@0 677 var slicesInfo = ComputeSlicesInfo(length);
michael@0 678 var numSlices = slicesInfo.count;
michael@0 679 var subreductions = NewDenseArray(numSlices);
michael@0 680
michael@0 681 ForkJoin(reduceThread, 0, numSlices, ForkJoinMode(mode));
michael@0 682
michael@0 683 var accumulator = subreductions[0];
michael@0 684 for (var i = 1; i < numSlices; i++)
michael@0 685 accumulator = func(accumulator, subreductions[i]);
michael@0 686 return accumulator;
michael@0 687 }
michael@0 688
michael@0 689 // Sequential fallback:
michael@0 690 ASSERT_SEQUENTIAL_IS_OK(mode);
michael@0 691 var accumulator = self[0];
michael@0 692 for (var i = 1; i < length; i++)
michael@0 693 accumulator = func(accumulator, self[i]);
michael@0 694 return accumulator;
michael@0 695
michael@0 696 function reduceThread(workerId, sliceStart, sliceEnd) {
michael@0 697 var sliceShift = slicesInfo.shift;
michael@0 698 var sliceId;
michael@0 699 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 700 var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 701 var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
michael@0 702 var accumulator = self[indexStart];
michael@0 703 for (var i = indexStart + 1; i < indexEnd; i++)
michael@0 704 accumulator = func(accumulator, self[i]);
michael@0 705 UnsafePutElements(subreductions, sliceId, accumulator);
michael@0 706 }
michael@0 707 return sliceId;
michael@0 708 }
michael@0 709
michael@0 710 return undefined;
michael@0 711 }
michael@0 712
michael@0 713 /**
michael@0 714 * Returns an array [s_0, ..., s_N] where |s_i| is equal to the reduction (as
michael@0 715 * per |reduce()|) of elements |0..i|. This is the generalization of partial
michael@0 716 * sum.
michael@0 717 */
michael@0 718 function ArrayScanPar(func, mode) {
michael@0 719 if (!IsCallable(func))
michael@0 720 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func));
michael@0 721
michael@0 722 var self = ToObject(this);
michael@0 723 var length = self.length;
michael@0 724
michael@0 725 if (length === 0)
michael@0 726 ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
michael@0 727
michael@0 728 var buffer = NewDenseArray(length);
michael@0 729
michael@0 730 parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc
michael@0 731 if (ShouldForceSequential())
michael@0 732 break parallel;
michael@0 733 if (!TRY_PARALLEL(mode))
michael@0 734 break parallel;
michael@0 735
michael@0 736 var slicesInfo = ComputeSlicesInfo(length);
michael@0 737 var numSlices = slicesInfo.count;
michael@0 738
michael@0 739 // Scan slices individually (see comment on phase1()).
michael@0 740 ForkJoin(phase1, 0, numSlices, ForkJoinMode(mode));
michael@0 741
michael@0 742 // Compute intermediates array (see comment on phase2()).
michael@0 743 var intermediates = [];
michael@0 744 var accumulator = buffer[finalElement(0)];
michael@0 745 ARRAY_PUSH(intermediates, accumulator);
michael@0 746 for (var i = 1; i < numSlices - 1; i++) {
michael@0 747 accumulator = func(accumulator, buffer[finalElement(i)]);
michael@0 748 ARRAY_PUSH(intermediates, accumulator);
michael@0 749 }
michael@0 750
michael@0 751 // Complete each slice using intermediates array (see comment on phase2()).
michael@0 752 //
michael@0 753 // We start from slice 1 instead of 0 since there is no work to be done
michael@0 754 // for slice 0.
michael@0 755 if (numSlices > 1)
michael@0 756 ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode));
michael@0 757 return buffer;
michael@0 758 }
michael@0 759
michael@0 760 // Sequential fallback:
michael@0 761 ASSERT_SEQUENTIAL_IS_OK(mode);
michael@0 762 scan(self[0], 0, length);
michael@0 763 return buffer;
michael@0 764
michael@0 765 function scan(accumulator, start, end) {
michael@0 766 UnsafePutElements(buffer, start, accumulator);
michael@0 767 for (var i = start + 1; i < end; i++) {
michael@0 768 accumulator = func(accumulator, self[i]);
michael@0 769 UnsafePutElements(buffer, i, accumulator);
michael@0 770 }
michael@0 771 return accumulator;
michael@0 772 }
michael@0 773
michael@0 774 /**
michael@0 775 * In phase 1, we divide the source array into |numSlices| slices and
michael@0 776 * compute scan on each slice sequentially as if it were the entire
michael@0 777 * array. This function is responsible for computing one of those
michael@0 778 * slices.
michael@0 779 *
michael@0 780 * So, if we have an array [A,B,C,D,E,F,G,H,I], |numSlices === 3|,
michael@0 781 * and our function |func| is sum, then we would wind up computing a
michael@0 782 * result array like:
michael@0 783 *
michael@0 784 * [A, A+B, A+B+C, D, D+E, D+E+F, G, G+H, G+H+I]
michael@0 785 * ^~~~~~~~~~~~^ ^~~~~~~~~~~~^ ^~~~~~~~~~~~~^
michael@0 786 * Slice 0 Slice 1 Slice 2
michael@0 787 *
michael@0 788 * Read on in phase2 to see what we do next!
michael@0 789 */
michael@0 790 function phase1(workerId, sliceStart, sliceEnd) {
michael@0 791 var sliceShift = slicesInfo.shift;
michael@0 792 var sliceId;
michael@0 793 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 794 var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 795 var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
michael@0 796 scan(self[indexStart], indexStart, indexEnd);
michael@0 797 }
michael@0 798 return sliceId;
michael@0 799 }
michael@0 800
michael@0 801 /**
michael@0 802 * Computes the index of the final element computed by the slice |sliceId|.
michael@0 803 */
michael@0 804 function finalElement(sliceId) {
michael@0 805 var sliceShift = slicesInfo.shift;
michael@0 806 return SLICE_END_INDEX(sliceShift, SLICE_START_INDEX(sliceShift, sliceId), length) - 1;
michael@0 807 }
michael@0 808
michael@0 809 /**
michael@0 810 * After computing the phase1 results, we compute an
michael@0 811 * |intermediates| array. |intermediates[i]| contains the result
michael@0 812 * of reducing the final value from each preceding slice j<i with
michael@0 813 * the final value of slice i. So, to continue our previous
michael@0 814 * example, the intermediates array would contain:
michael@0 815 *
michael@0 816 * [A+B+C, (A+B+C)+(D+E+F), ((A+B+C)+(D+E+F))+(G+H+I)]
michael@0 817 *
michael@0 818 * Here I have used parenthesization to make clear the order of
michael@0 819 * evaluation in each case.
michael@0 820 *
michael@0 821 * An aside: currently the intermediates array is computed
michael@0 822 * sequentially. In principle, we could compute it in parallel,
michael@0 823 * at the cost of doing duplicate work. This did not seem
michael@0 824 * particularly advantageous to me, particularly as the number
michael@0 825 * of slices is typically quite small (one per core), so I opted
michael@0 826 * to just compute it sequentially.
michael@0 827 *
michael@0 828 * Phase 2 combines the results of phase1 with the intermediates
michael@0 829 * array to produce the final scan results. The idea is to
michael@0 830 * reiterate over each element S[i] in the slice |sliceId|, which
michael@0 831 * currently contains the result of reducing with S[0]...S[i]
michael@0 832 * (where S0 is the first thing in the slice), and combine that
michael@0 833 * with |intermediate[sliceId-1]|, which represents the result of
michael@0 834 * reducing everything in the input array prior to the slice.
michael@0 835 *
michael@0 836 * To continue with our example, in phase 1 we computed slice 1 to
michael@0 837 * be [D, D+E, D+E+F]. We will combine those results with
michael@0 838 * |intermediates[1-1]|, which is |A+B+C|, so that the final
michael@0 839 * result is [(A+B+C)+D, (A+B+C)+(D+E), (A+B+C)+(D+E+F)]. Again I
michael@0 840 * am using parentheses to clarify how these results were reduced.
michael@0 841 */
michael@0 842 function phase2(workerId, sliceStart, sliceEnd) {
michael@0 843 var sliceShift = slicesInfo.shift;
michael@0 844 var sliceId;
michael@0 845 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 846 var indexPos = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 847 var indexEnd = SLICE_END_INDEX(sliceShift, indexPos, length);
michael@0 848 var intermediate = intermediates[sliceId - 1];
michael@0 849 for (; indexPos < indexEnd; indexPos++)
michael@0 850 UnsafePutElements(buffer, indexPos, func(intermediate, buffer[indexPos]));
michael@0 851 }
michael@0 852 return sliceId;
michael@0 853 }
michael@0 854
michael@0 855 return undefined;
michael@0 856 }
michael@0 857
michael@0 858 /**
michael@0 859 * |scatter()| redistributes the elements in the array into a new array.
michael@0 860 *
michael@0 861 * - targets: The index targets[i] indicates where the ith element
michael@0 862 * should appear in the result.
michael@0 863 *
michael@0 864 * - defaultValue: what value to use for indices in the output array that
michael@0 865 * are never targeted.
michael@0 866 *
michael@0 867 * - conflictFunc: The conflict function. Used to resolve what
michael@0 868 * happens if two indices i and j in the source array are targeted
michael@0 869 * as the same destination (i.e., targets[i] === targets[j]), then
michael@0 870 * the final result is determined by applying func(targets[i],
michael@0 871 * targets[j]). If no conflict function is provided, it is an error
michael@0 872 * if targets[i] === targets[j].
michael@0 873 *
michael@0 874 * - length: length of the output array (if not specified, uses the
michael@0 875 * length of the input).
michael@0 876 *
michael@0 877 * - mode: internal debugging specification.
michael@0 878 */
michael@0 879 function ArrayScatterPar(targets, defaultValue, conflictFunc, length, mode) {
michael@0 880 if (conflictFunc && !IsCallable(conflictFunc))
michael@0 881 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(2, conflictFunc));
michael@0 882
michael@0 883 var self = ToObject(this);
michael@0 884
michael@0 885 if (length === undefined)
michael@0 886 length = self.length;
michael@0 887
michael@0 888 var targetsLength = std_Math_min(targets.length, self.length);
michael@0 889
michael@0 890 if (!IS_UINT32(targetsLength) || !IS_UINT32(length))
michael@0 891 ThrowError(JSMSG_BAD_ARRAY_LENGTH);
michael@0 892
michael@0 893 // FIXME: Bug 965609: Find a better parallel startegy for scatter.
michael@0 894
michael@0 895 // Sequential fallback:
michael@0 896 ASSERT_SEQUENTIAL_IS_OK(mode);
michael@0 897 return seq();
michael@0 898
michael@0 899 function seq() {
michael@0 900 var buffer = NewDenseArray(length);
michael@0 901 var conflicts = NewDenseArray(length);
michael@0 902
michael@0 903 for (var i = 0; i < length; i++) {
michael@0 904 UnsafePutElements(buffer, i, defaultValue);
michael@0 905 UnsafePutElements(conflicts, i, false);
michael@0 906 }
michael@0 907
michael@0 908 for (var i = 0; i < targetsLength; i++) {
michael@0 909 var x = self[i];
michael@0 910 var t = checkTarget(i, targets[i]);
michael@0 911 if (conflicts[t])
michael@0 912 x = collide(x, buffer[t]);
michael@0 913
michael@0 914 UnsafePutElements(buffer, t, x, conflicts, t, true);
michael@0 915 }
michael@0 916
michael@0 917 return buffer;
michael@0 918 }
michael@0 919
michael@0 920 function collide(elem1, elem2) {
michael@0 921 if (conflictFunc === undefined)
michael@0 922 ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT);
michael@0 923
michael@0 924 return conflictFunc(elem1, elem2);
michael@0 925 }
michael@0 926
michael@0 927 function checkTarget(i, t) {
michael@0 928 if (TO_INT32(t) !== t)
michael@0 929 ThrowError(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, i);
michael@0 930
michael@0 931 if (t < 0 || t >= length)
michael@0 932 ThrowError(JSMSG_PAR_ARRAY_SCATTER_BOUNDS);
michael@0 933
michael@0 934 // It's not enough to return t, as -0 | 0 === -0.
michael@0 935 return TO_INT32(t);
michael@0 936 }
michael@0 937
michael@0 938 return undefined;
michael@0 939 }
michael@0 940
michael@0 941 /**
michael@0 942 * The filter operation applied in parallel.
michael@0 943 */
michael@0 944 function ArrayFilterPar(func, mode) {
michael@0 945 if (!IsCallable(func))
michael@0 946 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func));
michael@0 947
michael@0 948 var self = ToObject(this);
michael@0 949 var length = self.length;
michael@0 950
michael@0 951 parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc
michael@0 952 if (ShouldForceSequential())
michael@0 953 break parallel;
michael@0 954 if (!TRY_PARALLEL(mode))
michael@0 955 break parallel;
michael@0 956
michael@0 957 var slicesInfo = ComputeSlicesInfo(length);
michael@0 958
michael@0 959 // Step 1. Compute which items from each slice of the result buffer should
michael@0 960 // be preserved. When we're done, we have a uint8 array |survivors|
michael@0 961 // containing 0 or 1 for each source element, indicating which members of
michael@0 962 // the chunk survived. We also keep an array |counts| containing the total
michael@0 963 // number of items that are being preserved from within one slice.
michael@0 964 var numSlices = slicesInfo.count;
michael@0 965 var counts = NewDenseArray(numSlices);
michael@0 966 for (var i = 0; i < numSlices; i++)
michael@0 967 UnsafePutElements(counts, i, 0);
michael@0 968
michael@0 969 var survivors = new Uint8Array(length);
michael@0 970 ForkJoin(findSurvivorsThread, 0, numSlices, ForkJoinMode(mode));
michael@0 971
michael@0 972 // Step 2. Compress the slices into one contiguous set.
michael@0 973 var count = 0;
michael@0 974 for (var i = 0; i < numSlices; i++)
michael@0 975 count += counts[i];
michael@0 976 var buffer = NewDenseArray(count);
michael@0 977 if (count > 0)
michael@0 978 ForkJoin(copySurvivorsThread, 0, numSlices, ForkJoinMode(mode));
michael@0 979
michael@0 980 return buffer;
michael@0 981 }
michael@0 982
michael@0 983 // Sequential fallback:
michael@0 984 ASSERT_SEQUENTIAL_IS_OK(mode);
michael@0 985 var buffer = [];
michael@0 986 for (var i = 0; i < length; i++) {
michael@0 987 var elem = self[i];
michael@0 988 if (func(elem, i, self))
michael@0 989 ARRAY_PUSH(buffer, elem);
michael@0 990 }
michael@0 991 return buffer;
michael@0 992
michael@0 993 /**
michael@0 994 * As described above, our goal is to determine which items we will preserve
michael@0 995 * from a given slice, storing "to-keep" bits into 32-bit chunks.
michael@0 996 */
michael@0 997 function findSurvivorsThread(workerId, sliceStart, sliceEnd) {
michael@0 998 var sliceShift = slicesInfo.shift;
michael@0 999 var sliceId;
michael@0 1000 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 1001 var count = 0;
michael@0 1002 var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 1003 var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
michael@0 1004 for (var indexPos = indexStart; indexPos < indexEnd; indexPos++) {
michael@0 1005 var keep = !!func(self[indexPos], indexPos, self);
michael@0 1006 UnsafePutElements(survivors, indexPos, keep);
michael@0 1007 count += keep;
michael@0 1008 }
michael@0 1009 UnsafePutElements(counts, sliceId, count);
michael@0 1010 }
michael@0 1011 return sliceId;
michael@0 1012 }
michael@0 1013
michael@0 1014 /**
michael@0 1015 * Copies the survivors from this slice into the correct position. Note
michael@0 1016 * that this is an idempotent operation that does not invoke user
michael@0 1017 * code. Therefore, we don't expect bailouts and make an effort to proceed
michael@0 1018 * chunk by chunk or avoid duplicating work.
michael@0 1019 */
michael@0 1020 function copySurvivorsThread(workerId, sliceStart, sliceEnd) {
michael@0 1021 var sliceShift = slicesInfo.shift;
michael@0 1022 var sliceId;
michael@0 1023 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 1024 // Total up the items preserved by previous slices.
michael@0 1025 var total = 0;
michael@0 1026 for (var i = 0; i < sliceId + 1; i++)
michael@0 1027 total += counts[i];
michael@0 1028
michael@0 1029 // Are we done?
michael@0 1030 var count = total - counts[sliceId];
michael@0 1031 if (count === total)
michael@0 1032 continue;
michael@0 1033
michael@0 1034 var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 1035 var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
michael@0 1036 for (var indexPos = indexStart; indexPos < indexEnd; indexPos++) {
michael@0 1037 if (survivors[indexPos]) {
michael@0 1038 UnsafePutElements(buffer, count++, self[indexPos]);
michael@0 1039 if (count == total)
michael@0 1040 break;
michael@0 1041 }
michael@0 1042 }
michael@0 1043 }
michael@0 1044
michael@0 1045 return sliceId;
michael@0 1046 }
michael@0 1047
michael@0 1048 return undefined;
michael@0 1049 }
michael@0 1050
michael@0 1051 /**
michael@0 1052 * "Comprehension form": This is the function invoked for
michael@0 1053 * |Array.{build,buildPar}(len, fn)| It creates a new array with length |len|
michael@0 1054 * where index |i| is equal to |fn(i)|.
michael@0 1055 *
michael@0 1056 * The final |mode| argument is an internal argument used only during our
michael@0 1057 * unit-testing.
michael@0 1058 */
michael@0 1059 function ArrayStaticBuild(length, func) {
michael@0 1060 if (!IS_UINT32(length))
michael@0 1061 ThrowError(JSMSG_BAD_ARRAY_LENGTH);
michael@0 1062 if (!IsCallable(func))
michael@0 1063 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, func));
michael@0 1064
michael@0 1065 var buffer = NewDenseArray(length);
michael@0 1066
michael@0 1067 for (var i = 0; i < length; i++)
michael@0 1068 UnsafePutElements(buffer, i, func(i));
michael@0 1069
michael@0 1070 return buffer;
michael@0 1071 }
michael@0 1072
michael@0 1073 function ArrayStaticBuildPar(length, func, mode) {
michael@0 1074 if (!IS_UINT32(length))
michael@0 1075 ThrowError(JSMSG_BAD_ARRAY_LENGTH);
michael@0 1076 if (!IsCallable(func))
michael@0 1077 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, func));
michael@0 1078
michael@0 1079 var buffer = NewDenseArray(length);
michael@0 1080
michael@0 1081 parallel: for (;;) {
michael@0 1082 if (ShouldForceSequential())
michael@0 1083 break parallel;
michael@0 1084 if (!TRY_PARALLEL(mode))
michael@0 1085 break parallel;
michael@0 1086
michael@0 1087 var slicesInfo = ComputeSlicesInfo(length);
michael@0 1088 ForkJoin(constructThread, 0, slicesInfo.count, ForkJoinMode(mode));
michael@0 1089 return buffer;
michael@0 1090 }
michael@0 1091
michael@0 1092 // Sequential fallback:
michael@0 1093 ASSERT_SEQUENTIAL_IS_OK(mode);
michael@0 1094 for (var i = 0; i < length; i++)
michael@0 1095 UnsafePutElements(buffer, i, func(i));
michael@0 1096 return buffer;
michael@0 1097
michael@0 1098 function constructThread(workerId, sliceStart, sliceEnd) {
michael@0 1099 var sliceShift = slicesInfo.shift;
michael@0 1100 var sliceId;
michael@0 1101 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 1102 var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 1103 var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
michael@0 1104 for (var i = indexStart; i < indexEnd; i++)
michael@0 1105 UnsafePutElements(buffer, i, func(i));
michael@0 1106 }
michael@0 1107 return sliceId;
michael@0 1108 }
michael@0 1109
michael@0 1110 return undefined;
michael@0 1111 }
michael@0 1112
michael@0 1113 /*
michael@0 1114 * Mark the main operations as clone-at-callsite for better precision.
michael@0 1115 * This is slightly overkill, as all that we really need is to
michael@0 1116 * specialize to the receiver and the elemental function, but in
michael@0 1117 * practice this is likely not so different, since element functions
michael@0 1118 * are often used in exactly one place.
michael@0 1119 */
michael@0 1120 SetScriptHints(ArrayMapPar, { cloneAtCallsite: true });
michael@0 1121 SetScriptHints(ArrayReducePar, { cloneAtCallsite: true });
michael@0 1122 SetScriptHints(ArrayScanPar, { cloneAtCallsite: true });
michael@0 1123 SetScriptHints(ArrayScatterPar, { cloneAtCallsite: true });
michael@0 1124 SetScriptHints(ArrayFilterPar, { cloneAtCallsite: true });
michael@0 1125 SetScriptHints(ArrayStaticBuildPar, { cloneAtCallsite: true });
michael@0 1126
michael@0 1127 #endif /* ENABLE_PARALLEL_JS */

mercurial