1// Copyright 2012 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(function(global, utils, extrasUtils) { 6 7"use strict"; 8 9%CheckIsBootstrapping(); 10 11// ------------------------------------------------------------------- 12// Imports 13 14var AddIndexedProperty; 15var FLAG_harmony_tolength; 16var FLAG_harmony_species; 17var GetIterator; 18var GetMethod; 19var GlobalArray = global.Array; 20var InternalArray = utils.InternalArray; 21var InternalPackedArray = utils.InternalPackedArray; 22var MakeTypeError; 23var MaxSimple; 24var MinSimple; 25var ObjectDefineProperty; 26var ObjectHasOwnProperty; 27var ObjectToString = utils.ImportNow("object_to_string"); 28var ObserveBeginPerformSplice; 29var ObserveEndPerformSplice; 30var ObserveEnqueueSpliceRecord; 31var SameValueZero; 32var iteratorSymbol = utils.ImportNow("iterator_symbol"); 33var unscopablesSymbol = utils.ImportNow("unscopables_symbol"); 34 35utils.Import(function(from) { 36 AddIndexedProperty = from.AddIndexedProperty; 37 GetIterator = from.GetIterator; 38 GetMethod = from.GetMethod; 39 MakeTypeError = from.MakeTypeError; 40 MaxSimple = from.MaxSimple; 41 MinSimple = from.MinSimple; 42 ObjectDefineProperty = from.ObjectDefineProperty; 43 ObjectHasOwnProperty = from.ObjectHasOwnProperty; 44 ObserveBeginPerformSplice = from.ObserveBeginPerformSplice; 45 ObserveEndPerformSplice = from.ObserveEndPerformSplice; 46 ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord; 47 SameValueZero = from.SameValueZero; 48}); 49 50utils.ImportFromExperimental(function(from) { 51 FLAG_harmony_tolength = from.FLAG_harmony_tolength; 52 FLAG_harmony_species = from.FLAG_harmony_species; 53}); 54 55// ------------------------------------------------------------------- 56 57 58function ArraySpeciesCreate(array, length) { 59 var constructor; 60 if (FLAG_harmony_species) { 61 constructor = %ArraySpeciesConstructor(array); 62 } else { 63 constructor = GlobalArray; 64 } 65 return new constructor(length); 66} 67 68 69function DefineIndexedProperty(array, i, value) { 70 if (FLAG_harmony_species) { 71 var result = ObjectDefineProperty(array, i, { 72 value: value, writable: true, configurable: true, enumerable: true 73 }); 74 if (!result) throw MakeTypeError(kStrictCannotAssign, i); 75 } else { 76 AddIndexedProperty(array, i, value); 77 } 78} 79 80 81// Global list of arrays visited during toString, toLocaleString and 82// join invocations. 83var visited_arrays = new InternalArray(); 84 85 86// Gets a sorted array of array keys. Useful for operations on sparse 87// arrays. Dupes have not been removed. 88function GetSortedArrayKeys(array, indices) { 89 var keys = new InternalArray(); 90 if (IS_NUMBER(indices)) { 91 // It's an interval 92 var limit = indices; 93 for (var i = 0; i < limit; ++i) { 94 var e = array[i]; 95 if (!IS_UNDEFINED(e) || i in array) { 96 keys.push(i); 97 } 98 } 99 } else { 100 var length = indices.length; 101 for (var k = 0; k < length; ++k) { 102 var key = indices[k]; 103 if (!IS_UNDEFINED(key)) { 104 var e = array[key]; 105 if (!IS_UNDEFINED(e) || key in array) { 106 keys.push(key); 107 } 108 } 109 } 110 keys.sort(function(a, b) { return a - b; }); 111 } 112 return keys; 113} 114 115 116function SparseJoinWithSeparatorJS(array, len, convert, separator) { 117 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); 118 var totalLength = 0; 119 var elements = new InternalArray(keys.length * 2); 120 var previousKey = -1; 121 for (var i = 0; i < keys.length; i++) { 122 var key = keys[i]; 123 if (key != previousKey) { // keys may contain duplicates. 124 var e = array[key]; 125 if (!IS_STRING(e)) e = convert(e); 126 elements[i * 2] = key; 127 elements[i * 2 + 1] = e; 128 previousKey = key; 129 } 130 } 131 return %SparseJoinWithSeparator(elements, len, separator); 132} 133 134 135// Optimized for sparse arrays if separator is ''. 136function SparseJoin(array, len, convert) { 137 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); 138 var last_key = -1; 139 var keys_length = keys.length; 140 141 var elements = new InternalArray(keys_length); 142 var elements_length = 0; 143 144 for (var i = 0; i < keys_length; i++) { 145 var key = keys[i]; 146 if (key != last_key) { 147 var e = array[key]; 148 if (!IS_STRING(e)) e = convert(e); 149 elements[elements_length++] = e; 150 last_key = key; 151 } 152 } 153 return %StringBuilderConcat(elements, elements_length, ''); 154} 155 156 157function UseSparseVariant(array, length, is_array, touched) { 158 // Only use the sparse variant on arrays that are likely to be sparse and the 159 // number of elements touched in the operation is relatively small compared to 160 // the overall size of the array. 161 if (!is_array || length < 1000 || %IsObserved(array) || 162 %HasComplexElements(array)) { 163 return false; 164 } 165 if (!%_IsSmi(length)) { 166 return true; 167 } 168 var elements_threshold = length >> 2; // No more than 75% holes 169 var estimated_elements = %EstimateNumberOfElements(array); 170 return (estimated_elements < elements_threshold) && 171 (touched > estimated_elements * 4); 172} 173 174 175function Join(array, length, separator, convert) { 176 if (length == 0) return ''; 177 178 var is_array = IS_ARRAY(array); 179 180 if (is_array) { 181 // If the array is cyclic, return the empty string for already 182 // visited arrays. 183 if (!%PushIfAbsent(visited_arrays, array)) return ''; 184 } 185 186 // Attempt to convert the elements. 187 try { 188 if (UseSparseVariant(array, length, is_array, length)) { 189 %NormalizeElements(array); 190 if (separator.length == 0) { 191 return SparseJoin(array, length, convert); 192 } else { 193 return SparseJoinWithSeparatorJS(array, length, convert, separator); 194 } 195 } 196 197 // Fast case for one-element arrays. 198 if (length == 1) { 199 var e = array[0]; 200 if (IS_STRING(e)) return e; 201 return convert(e); 202 } 203 204 // Construct an array for the elements. 205 var elements = new InternalArray(length); 206 207 // We pull the empty separator check outside the loop for speed! 208 if (separator.length == 0) { 209 var elements_length = 0; 210 for (var i = 0; i < length; i++) { 211 var e = array[i]; 212 if (!IS_STRING(e)) e = convert(e); 213 elements[elements_length++] = e; 214 } 215 elements.length = elements_length; 216 var result = %_FastOneByteArrayJoin(elements, ''); 217 if (!IS_UNDEFINED(result)) return result; 218 return %StringBuilderConcat(elements, elements_length, ''); 219 } 220 // Non-empty separator case. 221 // If the first element is a number then use the heuristic that the 222 // remaining elements are also likely to be numbers. 223 if (!IS_NUMBER(array[0])) { 224 for (var i = 0; i < length; i++) { 225 var e = array[i]; 226 if (!IS_STRING(e)) e = convert(e); 227 elements[i] = e; 228 } 229 } else { 230 for (var i = 0; i < length; i++) { 231 var e = array[i]; 232 if (IS_NUMBER(e)) { 233 e = %_NumberToString(e); 234 } else if (!IS_STRING(e)) { 235 e = convert(e); 236 } 237 elements[i] = e; 238 } 239 } 240 var result = %_FastOneByteArrayJoin(elements, separator); 241 if (!IS_UNDEFINED(result)) return result; 242 243 return %StringBuilderJoin(elements, length, separator); 244 } finally { 245 // Make sure to remove the last element of the visited array no 246 // matter what happens. 247 if (is_array) visited_arrays.length = visited_arrays.length - 1; 248 } 249} 250 251 252function ConvertToString(x) { 253 if (IS_NULL_OR_UNDEFINED(x)) { 254 return ''; 255 } else { 256 return TO_STRING(x); 257 } 258} 259 260 261function ConvertToLocaleString(e) { 262 if (IS_NULL_OR_UNDEFINED(e)) { 263 return ''; 264 } else { 265 return TO_STRING(e.toLocaleString()); 266 } 267} 268 269 270// This function implements the optimized splice implementation that can use 271// special array operations to handle sparse arrays in a sensible fashion. 272function SparseSlice(array, start_i, del_count, len, deleted_elements) { 273 // Move deleted elements to a new array (the return value from splice). 274 var indices = %GetArrayKeys(array, start_i + del_count); 275 if (IS_NUMBER(indices)) { 276 var limit = indices; 277 for (var i = start_i; i < limit; ++i) { 278 var current = array[i]; 279 if (!IS_UNDEFINED(current) || i in array) { 280 DefineIndexedProperty(deleted_elements, i - start_i, current); 281 } 282 } 283 } else { 284 var length = indices.length; 285 for (var k = 0; k < length; ++k) { 286 var key = indices[k]; 287 if (!IS_UNDEFINED(key)) { 288 if (key >= start_i) { 289 var current = array[key]; 290 if (!IS_UNDEFINED(current) || key in array) { 291 DefineIndexedProperty(deleted_elements, key - start_i, current); 292 } 293 } 294 } 295 } 296 } 297} 298 299 300// This function implements the optimized splice implementation that can use 301// special array operations to handle sparse arrays in a sensible fashion. 302function SparseMove(array, start_i, del_count, len, num_additional_args) { 303 // Bail out if no moving is necessary. 304 if (num_additional_args === del_count) return; 305 // Move data to new array. 306 var new_array = new InternalArray( 307 // Clamp array length to 2^32-1 to avoid early RangeError. 308 MinSimple(len - del_count + num_additional_args, 0xffffffff)); 309 var big_indices; 310 var indices = %GetArrayKeys(array, len); 311 if (IS_NUMBER(indices)) { 312 var limit = indices; 313 for (var i = 0; i < start_i && i < limit; ++i) { 314 var current = array[i]; 315 if (!IS_UNDEFINED(current) || i in array) { 316 new_array[i] = current; 317 } 318 } 319 for (var i = start_i + del_count; i < limit; ++i) { 320 var current = array[i]; 321 if (!IS_UNDEFINED(current) || i in array) { 322 new_array[i - del_count + num_additional_args] = current; 323 } 324 } 325 } else { 326 var length = indices.length; 327 for (var k = 0; k < length; ++k) { 328 var key = indices[k]; 329 if (!IS_UNDEFINED(key)) { 330 if (key < start_i) { 331 var current = array[key]; 332 if (!IS_UNDEFINED(current) || key in array) { 333 new_array[key] = current; 334 } 335 } else if (key >= start_i + del_count) { 336 var current = array[key]; 337 if (!IS_UNDEFINED(current) || key in array) { 338 var new_key = key - del_count + num_additional_args; 339 new_array[new_key] = current; 340 if (new_key > 0xfffffffe) { 341 big_indices = big_indices || new InternalArray(); 342 big_indices.push(new_key); 343 } 344 } 345 } 346 } 347 } 348 } 349 // Move contents of new_array into this array 350 %MoveArrayContents(new_array, array); 351 // Add any moved values that aren't elements anymore. 352 if (!IS_UNDEFINED(big_indices)) { 353 var length = big_indices.length; 354 for (var i = 0; i < length; ++i) { 355 var key = big_indices[i]; 356 array[key] = new_array[key]; 357 } 358 } 359} 360 361 362// This is part of the old simple-minded splice. We are using it either 363// because the receiver is not an array (so we have no choice) or because we 364// know we are not deleting or moving a lot of elements. 365function SimpleSlice(array, start_i, del_count, len, deleted_elements) { 366 var is_array = IS_ARRAY(array); 367 for (var i = 0; i < del_count; i++) { 368 var index = start_i + i; 369 if (HAS_INDEX(array, index, is_array)) { 370 var current = array[index]; 371 DefineIndexedProperty(deleted_elements, i, current); 372 } 373 } 374} 375 376 377function SimpleMove(array, start_i, del_count, len, num_additional_args) { 378 var is_array = IS_ARRAY(array); 379 if (num_additional_args !== del_count) { 380 // Move the existing elements after the elements to be deleted 381 // to the right position in the resulting array. 382 if (num_additional_args > del_count) { 383 for (var i = len - del_count; i > start_i; i--) { 384 var from_index = i + del_count - 1; 385 var to_index = i + num_additional_args - 1; 386 if (HAS_INDEX(array, from_index, is_array)) { 387 array[to_index] = array[from_index]; 388 } else { 389 delete array[to_index]; 390 } 391 } 392 } else { 393 for (var i = start_i; i < len - del_count; i++) { 394 var from_index = i + del_count; 395 var to_index = i + num_additional_args; 396 if (HAS_INDEX(array, from_index, is_array)) { 397 array[to_index] = array[from_index]; 398 } else { 399 delete array[to_index]; 400 } 401 } 402 for (var i = len; i > len - del_count + num_additional_args; i--) { 403 delete array[i - 1]; 404 } 405 } 406 } 407} 408 409 410// ------------------------------------------------------------------- 411 412 413function ArrayToString() { 414 var array; 415 var func; 416 if (IS_ARRAY(this)) { 417 func = this.join; 418 if (func === ArrayJoin) { 419 return Join(this, this.length, ',', ConvertToString); 420 } 421 array = this; 422 } else { 423 array = TO_OBJECT(this); 424 func = array.join; 425 } 426 if (!IS_CALLABLE(func)) { 427 return %_Call(ObjectToString, array); 428 } 429 return %_Call(func, array); 430} 431 432 433function InnerArrayToLocaleString(array, length) { 434 var len = TO_LENGTH_OR_UINT32(length); 435 if (len === 0) return ""; 436 return Join(array, len, ',', ConvertToLocaleString); 437} 438 439 440function ArrayToLocaleString() { 441 var array = TO_OBJECT(this); 442 var arrayLen = array.length; 443 return InnerArrayToLocaleString(array, arrayLen); 444} 445 446 447function InnerArrayJoin(separator, array, length) { 448 if (IS_UNDEFINED(separator)) { 449 separator = ','; 450 } else { 451 separator = TO_STRING(separator); 452 } 453 454 var result = %_FastOneByteArrayJoin(array, separator); 455 if (!IS_UNDEFINED(result)) return result; 456 457 // Fast case for one-element arrays. 458 if (length === 1) { 459 var e = array[0]; 460 if (IS_NULL_OR_UNDEFINED(e)) return ''; 461 return TO_STRING(e); 462 } 463 464 return Join(array, length, separator, ConvertToString); 465} 466 467 468function ArrayJoin(separator) { 469 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join"); 470 471 var array = TO_OBJECT(this); 472 var length = TO_LENGTH_OR_UINT32(array.length); 473 474 return InnerArrayJoin(separator, array, length); 475} 476 477 478function ObservedArrayPop(n) { 479 n--; 480 var value = this[n]; 481 482 try { 483 ObserveBeginPerformSplice(this); 484 delete this[n]; 485 this.length = n; 486 } finally { 487 ObserveEndPerformSplice(this); 488 ObserveEnqueueSpliceRecord(this, n, [value], 0); 489 } 490 491 return value; 492} 493 494 495// Removes the last element from the array and returns it. See 496// ECMA-262, section 15.4.4.6. 497function ArrayPop() { 498 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop"); 499 500 var array = TO_OBJECT(this); 501 var n = TO_LENGTH_OR_UINT32(array.length); 502 if (n == 0) { 503 array.length = n; 504 return; 505 } 506 507 if (%IsObserved(array)) 508 return ObservedArrayPop.call(array, n); 509 510 n--; 511 var value = array[n]; 512 %DeleteProperty_Strict(array, n); 513 array.length = n; 514 return value; 515} 516 517 518function ObservedArrayPush() { 519 var n = TO_LENGTH_OR_UINT32(this.length); 520 var m = %_ArgumentsLength(); 521 522 try { 523 ObserveBeginPerformSplice(this); 524 for (var i = 0; i < m; i++) { 525 this[i+n] = %_Arguments(i); 526 } 527 var new_length = n + m; 528 this.length = new_length; 529 } finally { 530 ObserveEndPerformSplice(this); 531 ObserveEnqueueSpliceRecord(this, n, [], m); 532 } 533 534 return new_length; 535} 536 537 538// Appends the arguments to the end of the array and returns the new 539// length of the array. See ECMA-262, section 15.4.4.7. 540function ArrayPush() { 541 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push"); 542 543 if (%IsObserved(this)) 544 return ObservedArrayPush.apply(this, arguments); 545 546 var array = TO_OBJECT(this); 547 var n = TO_LENGTH_OR_UINT32(array.length); 548 var m = %_ArgumentsLength(); 549 550 // It appears that there is no enforced, absolute limit on the number of 551 // arguments, but it would surely blow the stack to use 2**30 or more. 552 // To avoid integer overflow, do the comparison to the max safe integer 553 // after subtracting 2**30 from both sides. (2**31 would seem like a 554 // natural value, but it is negative in JS, and 2**32 is 1.) 555 if (m > (1 << 30) || (n - (1 << 30)) + m > kMaxSafeInteger - (1 << 30)) { 556 throw MakeTypeError(kPushPastSafeLength, m, n); 557 } 558 559 for (var i = 0; i < m; i++) { 560 array[i+n] = %_Arguments(i); 561 } 562 563 var new_length = n + m; 564 array.length = new_length; 565 return new_length; 566} 567 568 569// For implementing reverse() on large, sparse arrays. 570function SparseReverse(array, len) { 571 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); 572 var high_counter = keys.length - 1; 573 var low_counter = 0; 574 while (low_counter <= high_counter) { 575 var i = keys[low_counter]; 576 var j = keys[high_counter]; 577 578 var j_complement = len - j - 1; 579 var low, high; 580 581 if (j_complement <= i) { 582 high = j; 583 while (keys[--high_counter] == j) { } 584 low = j_complement; 585 } 586 if (j_complement >= i) { 587 low = i; 588 while (keys[++low_counter] == i) { } 589 high = len - i - 1; 590 } 591 592 var current_i = array[low]; 593 if (!IS_UNDEFINED(current_i) || low in array) { 594 var current_j = array[high]; 595 if (!IS_UNDEFINED(current_j) || high in array) { 596 array[low] = current_j; 597 array[high] = current_i; 598 } else { 599 array[high] = current_i; 600 delete array[low]; 601 } 602 } else { 603 var current_j = array[high]; 604 if (!IS_UNDEFINED(current_j) || high in array) { 605 array[low] = current_j; 606 delete array[high]; 607 } 608 } 609 } 610} 611 612function PackedArrayReverse(array, len) { 613 var j = len - 1; 614 for (var i = 0; i < j; i++, j--) { 615 var current_i = array[i]; 616 var current_j = array[j]; 617 array[i] = current_j; 618 array[j] = current_i; 619 } 620 return array; 621} 622 623 624function GenericArrayReverse(array, len) { 625 var j = len - 1; 626 for (var i = 0; i < j; i++, j--) { 627 if (i in array) { 628 var current_i = array[i]; 629 if (j in array) { 630 var current_j = array[j]; 631 array[i] = current_j; 632 array[j] = current_i; 633 } else { 634 array[j] = current_i; 635 delete array[i]; 636 } 637 } else { 638 if (j in array) { 639 var current_j = array[j]; 640 array[i] = current_j; 641 delete array[j]; 642 } 643 } 644 } 645 return array; 646} 647 648 649function ArrayReverse() { 650 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse"); 651 652 var array = TO_OBJECT(this); 653 var len = TO_LENGTH_OR_UINT32(array.length); 654 var isArray = IS_ARRAY(array); 655 656 if (UseSparseVariant(array, len, isArray, len)) { 657 %NormalizeElements(array); 658 SparseReverse(array, len); 659 return array; 660 } else if (isArray && %_HasFastPackedElements(array)) { 661 return PackedArrayReverse(array, len); 662 } else { 663 return GenericArrayReverse(array, len); 664 } 665} 666 667 668function ObservedArrayShift(len) { 669 var first = this[0]; 670 671 try { 672 ObserveBeginPerformSplice(this); 673 SimpleMove(this, 0, 1, len, 0); 674 this.length = len - 1; 675 } finally { 676 ObserveEndPerformSplice(this); 677 ObserveEnqueueSpliceRecord(this, 0, [first], 0); 678 } 679 680 return first; 681} 682 683 684function ArrayShift() { 685 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift"); 686 687 var array = TO_OBJECT(this); 688 var len = TO_LENGTH_OR_UINT32(array.length); 689 690 if (len === 0) { 691 array.length = 0; 692 return; 693 } 694 695 if (%object_is_sealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed); 696 697 if (%IsObserved(array)) 698 return ObservedArrayShift.call(array, len); 699 700 var first = array[0]; 701 702 if (UseSparseVariant(array, len, IS_ARRAY(array), len)) { 703 SparseMove(array, 0, 1, len, 0); 704 } else { 705 SimpleMove(array, 0, 1, len, 0); 706 } 707 708 array.length = len - 1; 709 710 return first; 711} 712 713 714function ObservedArrayUnshift() { 715 var len = TO_LENGTH_OR_UINT32(this.length); 716 var num_arguments = %_ArgumentsLength(); 717 718 try { 719 ObserveBeginPerformSplice(this); 720 SimpleMove(this, 0, 0, len, num_arguments); 721 for (var i = 0; i < num_arguments; i++) { 722 this[i] = %_Arguments(i); 723 } 724 var new_length = len + num_arguments; 725 this.length = new_length; 726 } finally { 727 ObserveEndPerformSplice(this); 728 ObserveEnqueueSpliceRecord(this, 0, [], num_arguments); 729 } 730 731 return new_length; 732} 733 734 735function ArrayUnshift(arg1) { // length == 1 736 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift"); 737 738 if (%IsObserved(this)) 739 return ObservedArrayUnshift.apply(this, arguments); 740 741 var array = TO_OBJECT(this); 742 var len = TO_LENGTH_OR_UINT32(array.length); 743 var num_arguments = %_ArgumentsLength(); 744 745 if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) && 746 !%object_is_sealed(array)) { 747 SparseMove(array, 0, 0, len, num_arguments); 748 } else { 749 SimpleMove(array, 0, 0, len, num_arguments); 750 } 751 752 for (var i = 0; i < num_arguments; i++) { 753 array[i] = %_Arguments(i); 754 } 755 756 var new_length = len + num_arguments; 757 array.length = new_length; 758 return new_length; 759} 760 761 762function ArraySlice(start, end) { 763 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice"); 764 765 var array = TO_OBJECT(this); 766 var len = TO_LENGTH_OR_UINT32(array.length); 767 var start_i = TO_INTEGER(start); 768 var end_i = len; 769 770 if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end); 771 772 if (start_i < 0) { 773 start_i += len; 774 if (start_i < 0) start_i = 0; 775 } else { 776 if (start_i > len) start_i = len; 777 } 778 779 if (end_i < 0) { 780 end_i += len; 781 if (end_i < 0) end_i = 0; 782 } else { 783 if (end_i > len) end_i = len; 784 } 785 786 var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0)); 787 788 if (end_i < start_i) return result; 789 790 if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) { 791 %NormalizeElements(array); 792 %NormalizeElements(result); 793 SparseSlice(array, start_i, end_i - start_i, len, result); 794 } else { 795 SimpleSlice(array, start_i, end_i - start_i, len, result); 796 } 797 798 result.length = end_i - start_i; 799 800 return result; 801} 802 803 804function ComputeSpliceStartIndex(start_i, len) { 805 if (start_i < 0) { 806 start_i += len; 807 return start_i < 0 ? 0 : start_i; 808 } 809 810 return start_i > len ? len : start_i; 811} 812 813 814function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) { 815 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is 816 // given as a request to delete all the elements from the start. 817 // And it differs from the case of undefined delete count. 818 // This does not follow ECMA-262, but we do the same for 819 // compatibility. 820 var del_count = 0; 821 if (num_arguments == 1) 822 return len - start_i; 823 824 del_count = TO_INTEGER(delete_count); 825 if (del_count < 0) 826 return 0; 827 828 if (del_count > len - start_i) 829 return len - start_i; 830 831 return del_count; 832} 833 834 835function ObservedArraySplice(start, delete_count) { 836 var num_arguments = %_ArgumentsLength(); 837 var len = TO_LENGTH_OR_UINT32(this.length); 838 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len); 839 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len, 840 start_i); 841 var deleted_elements = []; 842 deleted_elements.length = del_count; 843 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0; 844 845 try { 846 ObserveBeginPerformSplice(this); 847 848 SimpleSlice(this, start_i, del_count, len, deleted_elements); 849 SimpleMove(this, start_i, del_count, len, num_elements_to_add); 850 851 // Insert the arguments into the resulting array in 852 // place of the deleted elements. 853 var i = start_i; 854 var arguments_index = 2; 855 var arguments_length = %_ArgumentsLength(); 856 while (arguments_index < arguments_length) { 857 this[i++] = %_Arguments(arguments_index++); 858 } 859 this.length = len - del_count + num_elements_to_add; 860 861 } finally { 862 ObserveEndPerformSplice(this); 863 if (deleted_elements.length || num_elements_to_add) { 864 ObserveEnqueueSpliceRecord(this, 865 start_i, 866 deleted_elements.slice(), 867 num_elements_to_add); 868 } 869 } 870 871 // Return the deleted elements. 872 return deleted_elements; 873} 874 875 876function ArraySplice(start, delete_count) { 877 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice"); 878 879 if (%IsObserved(this)) 880 return ObservedArraySplice.apply(this, arguments); 881 882 var num_arguments = %_ArgumentsLength(); 883 var array = TO_OBJECT(this); 884 var len = TO_LENGTH_OR_UINT32(array.length); 885 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len); 886 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len, 887 start_i); 888 var deleted_elements = ArraySpeciesCreate(array, del_count); 889 deleted_elements.length = del_count; 890 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0; 891 892 if (del_count != num_elements_to_add && %object_is_sealed(array)) { 893 throw MakeTypeError(kArrayFunctionsOnSealed); 894 } else if (del_count > 0 && %object_is_frozen(array)) { 895 throw MakeTypeError(kArrayFunctionsOnFrozen); 896 } 897 898 var changed_elements = del_count; 899 if (num_elements_to_add != del_count) { 900 // If the slice needs to do a actually move elements after the insertion 901 // point, then include those in the estimate of changed elements. 902 changed_elements += len - start_i - del_count; 903 } 904 if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) { 905 %NormalizeElements(array); 906 %NormalizeElements(deleted_elements); 907 SparseSlice(array, start_i, del_count, len, deleted_elements); 908 SparseMove(array, start_i, del_count, len, num_elements_to_add); 909 } else { 910 SimpleSlice(array, start_i, del_count, len, deleted_elements); 911 SimpleMove(array, start_i, del_count, len, num_elements_to_add); 912 } 913 914 // Insert the arguments into the resulting array in 915 // place of the deleted elements. 916 var i = start_i; 917 var arguments_index = 2; 918 var arguments_length = %_ArgumentsLength(); 919 while (arguments_index < arguments_length) { 920 array[i++] = %_Arguments(arguments_index++); 921 } 922 array.length = len - del_count + num_elements_to_add; 923 924 // Return the deleted elements. 925 return deleted_elements; 926} 927 928 929function InnerArraySort(array, length, comparefn) { 930 // In-place QuickSort algorithm. 931 // For short (length <= 22) arrays, insertion sort is used for efficiency. 932 933 if (!IS_CALLABLE(comparefn)) { 934 comparefn = function (x, y) { 935 if (x === y) return 0; 936 if (%_IsSmi(x) && %_IsSmi(y)) { 937 return %SmiLexicographicCompare(x, y); 938 } 939 x = TO_STRING(x); 940 y = TO_STRING(y); 941 if (x == y) return 0; 942 else return x < y ? -1 : 1; 943 }; 944 } 945 var InsertionSort = function InsertionSort(a, from, to) { 946 for (var i = from + 1; i < to; i++) { 947 var element = a[i]; 948 for (var j = i - 1; j >= from; j--) { 949 var tmp = a[j]; 950 var order = comparefn(tmp, element); 951 if (order > 0) { 952 a[j + 1] = tmp; 953 } else { 954 break; 955 } 956 } 957 a[j + 1] = element; 958 } 959 }; 960 961 var GetThirdIndex = function(a, from, to) { 962 var t_array = new InternalArray(); 963 // Use both 'from' and 'to' to determine the pivot candidates. 964 var increment = 200 + ((to - from) & 15); 965 var j = 0; 966 from += 1; 967 to -= 1; 968 for (var i = from; i < to; i += increment) { 969 t_array[j] = [i, a[i]]; 970 j++; 971 } 972 t_array.sort(function(a, b) { 973 return comparefn(a[1], b[1]); 974 }); 975 var third_index = t_array[t_array.length >> 1][0]; 976 return third_index; 977 } 978 979 var QuickSort = function QuickSort(a, from, to) { 980 var third_index = 0; 981 while (true) { 982 // Insertion sort is faster for short arrays. 983 if (to - from <= 10) { 984 InsertionSort(a, from, to); 985 return; 986 } 987 if (to - from > 1000) { 988 third_index = GetThirdIndex(a, from, to); 989 } else { 990 third_index = from + ((to - from) >> 1); 991 } 992 // Find a pivot as the median of first, last and middle element. 993 var v0 = a[from]; 994 var v1 = a[to - 1]; 995 var v2 = a[third_index]; 996 var c01 = comparefn(v0, v1); 997 if (c01 > 0) { 998 // v1 < v0, so swap them. 999 var tmp = v0; 1000 v0 = v1; 1001 v1 = tmp; 1002 } // v0 <= v1. 1003 var c02 = comparefn(v0, v2); 1004 if (c02 >= 0) { 1005 // v2 <= v0 <= v1. 1006 var tmp = v0; 1007 v0 = v2; 1008 v2 = v1; 1009 v1 = tmp; 1010 } else { 1011 // v0 <= v1 && v0 < v2 1012 var c12 = comparefn(v1, v2); 1013 if (c12 > 0) { 1014 // v0 <= v2 < v1 1015 var tmp = v1; 1016 v1 = v2; 1017 v2 = tmp; 1018 } 1019 } 1020 // v0 <= v1 <= v2 1021 a[from] = v0; 1022 a[to - 1] = v2; 1023 var pivot = v1; 1024 var low_end = from + 1; // Upper bound of elements lower than pivot. 1025 var high_start = to - 1; // Lower bound of elements greater than pivot. 1026 a[third_index] = a[low_end]; 1027 a[low_end] = pivot; 1028 1029 // From low_end to i are elements equal to pivot. 1030 // From i to high_start are elements that haven't been compared yet. 1031 partition: for (var i = low_end + 1; i < high_start; i++) { 1032 var element = a[i]; 1033 var order = comparefn(element, pivot); 1034 if (order < 0) { 1035 a[i] = a[low_end]; 1036 a[low_end] = element; 1037 low_end++; 1038 } else if (order > 0) { 1039 do { 1040 high_start--; 1041 if (high_start == i) break partition; 1042 var top_elem = a[high_start]; 1043 order = comparefn(top_elem, pivot); 1044 } while (order > 0); 1045 a[i] = a[high_start]; 1046 a[high_start] = element; 1047 if (order < 0) { 1048 element = a[i]; 1049 a[i] = a[low_end]; 1050 a[low_end] = element; 1051 low_end++; 1052 } 1053 } 1054 } 1055 if (to - high_start < low_end - from) { 1056 QuickSort(a, high_start, to); 1057 to = low_end; 1058 } else { 1059 QuickSort(a, from, low_end); 1060 from = high_start; 1061 } 1062 } 1063 }; 1064 1065 // Copy elements in the range 0..length from obj's prototype chain 1066 // to obj itself, if obj has holes. Return one more than the maximal index 1067 // of a prototype property. 1068 var CopyFromPrototype = function CopyFromPrototype(obj, length) { 1069 var max = 0; 1070 for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) { 1071 var indices = %GetArrayKeys(proto, length); 1072 if (IS_NUMBER(indices)) { 1073 // It's an interval. 1074 var proto_length = indices; 1075 for (var i = 0; i < proto_length; i++) { 1076 if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) { 1077 obj[i] = proto[i]; 1078 if (i >= max) { max = i + 1; } 1079 } 1080 } 1081 } else { 1082 for (var i = 0; i < indices.length; i++) { 1083 var index = indices[i]; 1084 if (!IS_UNDEFINED(index) && !HAS_OWN_PROPERTY(obj, index) 1085 && HAS_OWN_PROPERTY(proto, index)) { 1086 obj[index] = proto[index]; 1087 if (index >= max) { max = index + 1; } 1088 } 1089 } 1090 } 1091 } 1092 return max; 1093 }; 1094 1095 // Set a value of "undefined" on all indices in the range from..to 1096 // where a prototype of obj has an element. I.e., shadow all prototype 1097 // elements in that range. 1098 var ShadowPrototypeElements = function(obj, from, to) { 1099 for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) { 1100 var indices = %GetArrayKeys(proto, to); 1101 if (IS_NUMBER(indices)) { 1102 // It's an interval. 1103 var proto_length = indices; 1104 for (var i = from; i < proto_length; i++) { 1105 if (HAS_OWN_PROPERTY(proto, i)) { 1106 obj[i] = UNDEFINED; 1107 } 1108 } 1109 } else { 1110 for (var i = 0; i < indices.length; i++) { 1111 var index = indices[i]; 1112 if (!IS_UNDEFINED(index) && from <= index && 1113 HAS_OWN_PROPERTY(proto, index)) { 1114 obj[index] = UNDEFINED; 1115 } 1116 } 1117 } 1118 } 1119 }; 1120 1121 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) { 1122 // Copy defined elements from the end to fill in all holes and undefineds 1123 // in the beginning of the array. Write undefineds and holes at the end 1124 // after loop is finished. 1125 var first_undefined = 0; 1126 var last_defined = length - 1; 1127 var num_holes = 0; 1128 while (first_undefined < last_defined) { 1129 // Find first undefined element. 1130 while (first_undefined < last_defined && 1131 !IS_UNDEFINED(obj[first_undefined])) { 1132 first_undefined++; 1133 } 1134 // Maintain the invariant num_holes = the number of holes in the original 1135 // array with indices <= first_undefined or > last_defined. 1136 if (!HAS_OWN_PROPERTY(obj, first_undefined)) { 1137 num_holes++; 1138 } 1139 1140 // Find last defined element. 1141 while (first_undefined < last_defined && 1142 IS_UNDEFINED(obj[last_defined])) { 1143 if (!HAS_OWN_PROPERTY(obj, last_defined)) { 1144 num_holes++; 1145 } 1146 last_defined--; 1147 } 1148 if (first_undefined < last_defined) { 1149 // Fill in hole or undefined. 1150 obj[first_undefined] = obj[last_defined]; 1151 obj[last_defined] = UNDEFINED; 1152 } 1153 } 1154 // If there were any undefineds in the entire array, first_undefined 1155 // points to one past the last defined element. Make this true if 1156 // there were no undefineds, as well, so that first_undefined == number 1157 // of defined elements. 1158 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++; 1159 // Fill in the undefineds and the holes. There may be a hole where 1160 // an undefined should be and vice versa. 1161 var i; 1162 for (i = first_undefined; i < length - num_holes; i++) { 1163 obj[i] = UNDEFINED; 1164 } 1165 for (i = length - num_holes; i < length; i++) { 1166 // For compatability with Webkit, do not expose elements in the prototype. 1167 if (i in %_GetPrototype(obj)) { 1168 obj[i] = UNDEFINED; 1169 } else { 1170 delete obj[i]; 1171 } 1172 } 1173 1174 // Return the number of defined elements. 1175 return first_undefined; 1176 }; 1177 1178 if (length < 2) return array; 1179 1180 var is_array = IS_ARRAY(array); 1181 var max_prototype_element; 1182 if (!is_array) { 1183 // For compatibility with JSC, we also sort elements inherited from 1184 // the prototype chain on non-Array objects. 1185 // We do this by copying them to this object and sorting only 1186 // own elements. This is not very efficient, but sorting with 1187 // inherited elements happens very, very rarely, if at all. 1188 // The specification allows "implementation dependent" behavior 1189 // if an element on the prototype chain has an element that 1190 // might interact with sorting. 1191 max_prototype_element = CopyFromPrototype(array, length); 1192 } 1193 1194 // %RemoveArrayHoles returns -1 if fast removal is not supported. 1195 var num_non_undefined = %RemoveArrayHoles(array, length); 1196 1197 if (num_non_undefined == -1) { 1198 // The array is observed, or there were indexed accessors in the array. 1199 // Move array holes and undefineds to the end using a Javascript function 1200 // that is safe in the presence of accessors and is observable. 1201 num_non_undefined = SafeRemoveArrayHoles(array); 1202 } 1203 1204 QuickSort(array, 0, num_non_undefined); 1205 1206 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) { 1207 // For compatibility with JSC, we shadow any elements in the prototype 1208 // chain that has become exposed by sort moving a hole to its position. 1209 ShadowPrototypeElements(array, num_non_undefined, max_prototype_element); 1210 } 1211 1212 return array; 1213} 1214 1215 1216function ArraySort(comparefn) { 1217 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort"); 1218 1219 var array = TO_OBJECT(this); 1220 var length = TO_LENGTH_OR_UINT32(array.length); 1221 return InnerArraySort(array, length, comparefn); 1222} 1223 1224 1225// The following functions cannot be made efficient on sparse arrays while 1226// preserving the semantics, since the calls to the receiver function can add 1227// or delete elements from the array. 1228function InnerArrayFilter(f, receiver, array, length, result) { 1229 var result_length = 0; 1230 var is_array = IS_ARRAY(array); 1231 for (var i = 0; i < length; i++) { 1232 if (HAS_INDEX(array, i, is_array)) { 1233 var element = array[i]; 1234 if (%_Call(f, receiver, element, i, array)) { 1235 DefineIndexedProperty(result, result_length, element); 1236 result_length++; 1237 } 1238 } 1239 } 1240 return result; 1241} 1242 1243 1244 1245function ArrayFilter(f, receiver) { 1246 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter"); 1247 1248 // Pull out the length so that modifications to the length in the 1249 // loop will not affect the looping and side effects are visible. 1250 var array = TO_OBJECT(this); 1251 var length = TO_LENGTH_OR_UINT32(array.length); 1252 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); 1253 var result = ArraySpeciesCreate(array, 0); 1254 return InnerArrayFilter(f, receiver, array, length, result); 1255} 1256 1257 1258function InnerArrayForEach(f, receiver, array, length) { 1259 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); 1260 1261 var is_array = IS_ARRAY(array); 1262 for (var i = 0; i < length; i++) { 1263 if (HAS_INDEX(array, i, is_array)) { 1264 var element = array[i]; 1265 %_Call(f, receiver, element, i, array); 1266 } 1267 } 1268} 1269 1270 1271function ArrayForEach(f, receiver) { 1272 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach"); 1273 1274 // Pull out the length so that modifications to the length in the 1275 // loop will not affect the looping and side effects are visible. 1276 var array = TO_OBJECT(this); 1277 var length = TO_LENGTH_OR_UINT32(array.length); 1278 InnerArrayForEach(f, receiver, array, length); 1279} 1280 1281 1282function InnerArraySome(f, receiver, array, length) { 1283 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); 1284 1285 var is_array = IS_ARRAY(array); 1286 for (var i = 0; i < length; i++) { 1287 if (HAS_INDEX(array, i, is_array)) { 1288 var element = array[i]; 1289 if (%_Call(f, receiver, element, i, array)) return true; 1290 } 1291 } 1292 return false; 1293} 1294 1295 1296// Executes the function once for each element present in the 1297// array until it finds one where callback returns true. 1298function ArraySome(f, receiver) { 1299 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some"); 1300 1301 // Pull out the length so that modifications to the length in the 1302 // loop will not affect the looping and side effects are visible. 1303 var array = TO_OBJECT(this); 1304 var length = TO_LENGTH_OR_UINT32(array.length); 1305 return InnerArraySome(f, receiver, array, length); 1306} 1307 1308 1309function InnerArrayEvery(f, receiver, array, length) { 1310 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); 1311 1312 var is_array = IS_ARRAY(array); 1313 for (var i = 0; i < length; i++) { 1314 if (HAS_INDEX(array, i, is_array)) { 1315 var element = array[i]; 1316 if (!%_Call(f, receiver, element, i, array)) return false; 1317 } 1318 } 1319 return true; 1320} 1321 1322function ArrayEvery(f, receiver) { 1323 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every"); 1324 1325 // Pull out the length so that modifications to the length in the 1326 // loop will not affect the looping and side effects are visible. 1327 var array = TO_OBJECT(this); 1328 var length = TO_LENGTH_OR_UINT32(array.length); 1329 return InnerArrayEvery(f, receiver, array, length); 1330} 1331 1332 1333function ArrayMap(f, receiver) { 1334 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map"); 1335 1336 // Pull out the length so that modifications to the length in the 1337 // loop will not affect the looping and side effects are visible. 1338 var array = TO_OBJECT(this); 1339 var length = TO_LENGTH_OR_UINT32(array.length); 1340 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); 1341 var result = ArraySpeciesCreate(array, length); 1342 var is_array = IS_ARRAY(array); 1343 for (var i = 0; i < length; i++) { 1344 if (HAS_INDEX(array, i, is_array)) { 1345 var element = array[i]; 1346 DefineIndexedProperty(result, i, %_Call(f, receiver, element, i, array)); 1347 } 1348 } 1349 return result; 1350} 1351 1352 1353// For .indexOf, we don't need to pass in the number of arguments 1354// at the callsite since ToInteger(undefined) == 0; however, for 1355// .lastIndexOf, we need to pass it, since the behavior for passing 1356// undefined is 0 but for not including the argument is length-1. 1357function InnerArrayIndexOf(array, element, index, length) { 1358 if (length == 0) return -1; 1359 if (IS_UNDEFINED(index)) { 1360 index = 0; 1361 } else { 1362 index = TO_INTEGER(index); 1363 // If index is negative, index from the end of the array. 1364 if (index < 0) { 1365 index = length + index; 1366 // If index is still negative, search the entire array. 1367 if (index < 0) index = 0; 1368 } 1369 } 1370 var min = index; 1371 var max = length; 1372 if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) { 1373 %NormalizeElements(array); 1374 var indices = %GetArrayKeys(array, length); 1375 if (IS_NUMBER(indices)) { 1376 // It's an interval. 1377 max = indices; // Capped by length already. 1378 // Fall through to loop below. 1379 } else { 1380 if (indices.length == 0) return -1; 1381 // Get all the keys in sorted order. 1382 var sortedKeys = GetSortedArrayKeys(array, indices); 1383 var n = sortedKeys.length; 1384 var i = 0; 1385 while (i < n && sortedKeys[i] < index) i++; 1386 while (i < n) { 1387 var key = sortedKeys[i]; 1388 if (!IS_UNDEFINED(key) && array[key] === element) return key; 1389 i++; 1390 } 1391 return -1; 1392 } 1393 } 1394 // Lookup through the array. 1395 if (!IS_UNDEFINED(element)) { 1396 for (var i = min; i < max; i++) { 1397 if (array[i] === element) return i; 1398 } 1399 return -1; 1400 } 1401 // Lookup through the array. 1402 for (var i = min; i < max; i++) { 1403 if (IS_UNDEFINED(array[i]) && i in array) { 1404 return i; 1405 } 1406 } 1407 return -1; 1408} 1409 1410 1411function ArrayIndexOf(element, index) { 1412 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf"); 1413 1414 var length = TO_LENGTH_OR_UINT32(this.length); 1415 return InnerArrayIndexOf(this, element, index, length); 1416} 1417 1418 1419function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) { 1420 if (length == 0) return -1; 1421 if (argumentsLength < 2) { 1422 index = length - 1; 1423 } else { 1424 index = TO_INTEGER(index); 1425 // If index is negative, index from end of the array. 1426 if (index < 0) index += length; 1427 // If index is still negative, do not search the array. 1428 if (index < 0) return -1; 1429 else if (index >= length) index = length - 1; 1430 } 1431 var min = 0; 1432 var max = index; 1433 if (UseSparseVariant(array, length, IS_ARRAY(array), index)) { 1434 %NormalizeElements(array); 1435 var indices = %GetArrayKeys(array, index + 1); 1436 if (IS_NUMBER(indices)) { 1437 // It's an interval. 1438 max = indices; // Capped by index already. 1439 // Fall through to loop below. 1440 } else { 1441 if (indices.length == 0) return -1; 1442 // Get all the keys in sorted order. 1443 var sortedKeys = GetSortedArrayKeys(array, indices); 1444 var i = sortedKeys.length - 1; 1445 while (i >= 0) { 1446 var key = sortedKeys[i]; 1447 if (!IS_UNDEFINED(key) && array[key] === element) return key; 1448 i--; 1449 } 1450 return -1; 1451 } 1452 } 1453 // Lookup through the array. 1454 if (!IS_UNDEFINED(element)) { 1455 for (var i = max; i >= min; i--) { 1456 if (array[i] === element) return i; 1457 } 1458 return -1; 1459 } 1460 for (var i = max; i >= min; i--) { 1461 if (IS_UNDEFINED(array[i]) && i in array) { 1462 return i; 1463 } 1464 } 1465 return -1; 1466} 1467 1468 1469function ArrayLastIndexOf(element, index) { 1470 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf"); 1471 1472 var length = TO_LENGTH_OR_UINT32(this.length); 1473 return InnerArrayLastIndexOf(this, element, index, length, 1474 %_ArgumentsLength()); 1475} 1476 1477 1478function InnerArrayReduce(callback, current, array, length, argumentsLength) { 1479 if (!IS_CALLABLE(callback)) { 1480 throw MakeTypeError(kCalledNonCallable, callback); 1481 } 1482 1483 var is_array = IS_ARRAY(array); 1484 var i = 0; 1485 find_initial: if (argumentsLength < 2) { 1486 for (; i < length; i++) { 1487 if (HAS_INDEX(array, i, is_array)) { 1488 current = array[i++]; 1489 break find_initial; 1490 } 1491 } 1492 throw MakeTypeError(kReduceNoInitial); 1493 } 1494 1495 for (; i < length; i++) { 1496 if (HAS_INDEX(array, i, is_array)) { 1497 var element = array[i]; 1498 current = callback(current, element, i, array); 1499 } 1500 } 1501 return current; 1502} 1503 1504 1505function ArrayReduce(callback, current) { 1506 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce"); 1507 1508 // Pull out the length so that modifications to the length in the 1509 // loop will not affect the looping and side effects are visible. 1510 var array = TO_OBJECT(this); 1511 var length = TO_LENGTH_OR_UINT32(array.length); 1512 return InnerArrayReduce(callback, current, array, length, 1513 %_ArgumentsLength()); 1514} 1515 1516 1517function InnerArrayReduceRight(callback, current, array, length, 1518 argumentsLength) { 1519 if (!IS_CALLABLE(callback)) { 1520 throw MakeTypeError(kCalledNonCallable, callback); 1521 } 1522 1523 var is_array = IS_ARRAY(array); 1524 var i = length - 1; 1525 find_initial: if (argumentsLength < 2) { 1526 for (; i >= 0; i--) { 1527 if (HAS_INDEX(array, i, is_array)) { 1528 current = array[i--]; 1529 break find_initial; 1530 } 1531 } 1532 throw MakeTypeError(kReduceNoInitial); 1533 } 1534 1535 for (; i >= 0; i--) { 1536 if (HAS_INDEX(array, i, is_array)) { 1537 var element = array[i]; 1538 current = callback(current, element, i, array); 1539 } 1540 } 1541 return current; 1542} 1543 1544 1545function ArrayReduceRight(callback, current) { 1546 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight"); 1547 1548 // Pull out the length so that side effects are visible before the 1549 // callback function is checked. 1550 var array = TO_OBJECT(this); 1551 var length = TO_LENGTH_OR_UINT32(array.length); 1552 return InnerArrayReduceRight(callback, current, array, length, 1553 %_ArgumentsLength()); 1554} 1555 1556 1557function InnerArrayCopyWithin(target, start, end, array, length) { 1558 target = TO_INTEGER(target); 1559 var to; 1560 if (target < 0) { 1561 to = MaxSimple(length + target, 0); 1562 } else { 1563 to = MinSimple(target, length); 1564 } 1565 1566 start = TO_INTEGER(start); 1567 var from; 1568 if (start < 0) { 1569 from = MaxSimple(length + start, 0); 1570 } else { 1571 from = MinSimple(start, length); 1572 } 1573 1574 end = IS_UNDEFINED(end) ? length : TO_INTEGER(end); 1575 var final; 1576 if (end < 0) { 1577 final = MaxSimple(length + end, 0); 1578 } else { 1579 final = MinSimple(end, length); 1580 } 1581 1582 var count = MinSimple(final - from, length - to); 1583 var direction = 1; 1584 if (from < to && to < (from + count)) { 1585 direction = -1; 1586 from = from + count - 1; 1587 to = to + count - 1; 1588 } 1589 1590 while (count > 0) { 1591 if (from in array) { 1592 array[to] = array[from]; 1593 } else { 1594 delete array[to]; 1595 } 1596 from = from + direction; 1597 to = to + direction; 1598 count--; 1599 } 1600 1601 return array; 1602} 1603 1604 1605// ES6 draft 03-17-15, section 22.1.3.3 1606function ArrayCopyWithin(target, start, end) { 1607 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin"); 1608 1609 var array = TO_OBJECT(this); 1610 var length = TO_LENGTH(array.length); 1611 1612 return InnerArrayCopyWithin(target, start, end, array, length); 1613} 1614 1615 1616function InnerArrayFind(predicate, thisArg, array, length) { 1617 if (!IS_CALLABLE(predicate)) { 1618 throw MakeTypeError(kCalledNonCallable, predicate); 1619 } 1620 1621 for (var i = 0; i < length; i++) { 1622 var element = array[i]; 1623 if (%_Call(predicate, thisArg, element, i, array)) { 1624 return element; 1625 } 1626 } 1627 1628 return; 1629} 1630 1631 1632// ES6 draft 07-15-13, section 15.4.3.23 1633function ArrayFind(predicate, thisArg) { 1634 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find"); 1635 1636 var array = TO_OBJECT(this); 1637 var length = TO_INTEGER(array.length); 1638 1639 return InnerArrayFind(predicate, thisArg, array, length); 1640} 1641 1642 1643function InnerArrayFindIndex(predicate, thisArg, array, length) { 1644 if (!IS_CALLABLE(predicate)) { 1645 throw MakeTypeError(kCalledNonCallable, predicate); 1646 } 1647 1648 for (var i = 0; i < length; i++) { 1649 var element = array[i]; 1650 if (%_Call(predicate, thisArg, element, i, array)) { 1651 return i; 1652 } 1653 } 1654 1655 return -1; 1656} 1657 1658 1659// ES6 draft 07-15-13, section 15.4.3.24 1660function ArrayFindIndex(predicate, thisArg) { 1661 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex"); 1662 1663 var array = TO_OBJECT(this); 1664 var length = TO_INTEGER(array.length); 1665 1666 return InnerArrayFindIndex(predicate, thisArg, array, length); 1667} 1668 1669 1670// ES6, draft 04-05-14, section 22.1.3.6 1671function InnerArrayFill(value, start, end, array, length) { 1672 var i = IS_UNDEFINED(start) ? 0 : TO_INTEGER(start); 1673 var end = IS_UNDEFINED(end) ? length : TO_INTEGER(end); 1674 1675 if (i < 0) { 1676 i += length; 1677 if (i < 0) i = 0; 1678 } else { 1679 if (i > length) i = length; 1680 } 1681 1682 if (end < 0) { 1683 end += length; 1684 if (end < 0) end = 0; 1685 } else { 1686 if (end > length) end = length; 1687 } 1688 1689 if ((end - i) > 0 && %object_is_frozen(array)) { 1690 throw MakeTypeError(kArrayFunctionsOnFrozen); 1691 } 1692 1693 for (; i < end; i++) 1694 array[i] = value; 1695 return array; 1696} 1697 1698 1699// ES6, draft 04-05-14, section 22.1.3.6 1700function ArrayFill(value, start, end) { 1701 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill"); 1702 1703 var array = TO_OBJECT(this); 1704 var length = TO_LENGTH_OR_UINT32(array.length); 1705 1706 return InnerArrayFill(value, start, end, array, length); 1707} 1708 1709 1710function InnerArrayIncludes(searchElement, fromIndex, array, length) { 1711 if (length === 0) { 1712 return false; 1713 } 1714 1715 var n = TO_INTEGER(fromIndex); 1716 1717 var k; 1718 if (n >= 0) { 1719 k = n; 1720 } else { 1721 k = length + n; 1722 if (k < 0) { 1723 k = 0; 1724 } 1725 } 1726 1727 while (k < length) { 1728 var elementK = array[k]; 1729 if (SameValueZero(searchElement, elementK)) { 1730 return true; 1731 } 1732 1733 ++k; 1734 } 1735 1736 return false; 1737} 1738 1739 1740// ES2016 draft, section 22.1.3.11 1741function ArrayIncludes(searchElement, fromIndex) { 1742 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.includes"); 1743 1744 var array = TO_OBJECT(this); 1745 var length = TO_LENGTH(array.length); 1746 1747 return InnerArrayIncludes(searchElement, fromIndex, array, length); 1748} 1749 1750 1751function AddArrayElement(constructor, array, i, value) { 1752 if (constructor === GlobalArray) { 1753 AddIndexedProperty(array, i, value); 1754 } else { 1755 ObjectDefineProperty(array, i, { 1756 value: value, writable: true, configurable: true, enumerable: true 1757 }); 1758 } 1759} 1760 1761 1762// ES6, draft 10-14-14, section 22.1.2.1 1763function ArrayFrom(arrayLike, mapfn, receiver) { 1764 var items = TO_OBJECT(arrayLike); 1765 var mapping = !IS_UNDEFINED(mapfn); 1766 1767 if (mapping) { 1768 if (!IS_CALLABLE(mapfn)) { 1769 throw MakeTypeError(kCalledNonCallable, mapfn); 1770 } 1771 } 1772 1773 var iterable = GetMethod(items, iteratorSymbol); 1774 var k; 1775 var result; 1776 var mappedValue; 1777 var nextValue; 1778 1779 if (!IS_UNDEFINED(iterable)) { 1780 result = %IsConstructor(this) ? new this() : []; 1781 1782 var iterator = GetIterator(items, iterable); 1783 1784 k = 0; 1785 while (true) { 1786 var next = iterator.next(); 1787 1788 if (!IS_RECEIVER(next)) { 1789 throw MakeTypeError(kIteratorResultNotAnObject, next); 1790 } 1791 1792 if (next.done) { 1793 result.length = k; 1794 return result; 1795 } 1796 1797 nextValue = next.value; 1798 if (mapping) { 1799 mappedValue = %_Call(mapfn, receiver, nextValue, k); 1800 } else { 1801 mappedValue = nextValue; 1802 } 1803 AddArrayElement(this, result, k, mappedValue); 1804 k++; 1805 } 1806 } else { 1807 var len = TO_LENGTH(items.length); 1808 result = %IsConstructor(this) ? new this(len) : new GlobalArray(len); 1809 1810 for (k = 0; k < len; ++k) { 1811 nextValue = items[k]; 1812 if (mapping) { 1813 mappedValue = %_Call(mapfn, receiver, nextValue, k); 1814 } else { 1815 mappedValue = nextValue; 1816 } 1817 AddArrayElement(this, result, k, mappedValue); 1818 } 1819 1820 result.length = k; 1821 return result; 1822 } 1823} 1824 1825 1826// ES6, draft 05-22-14, section 22.1.2.3 1827function ArrayOf() { 1828 var length = %_ArgumentsLength(); 1829 var constructor = this; 1830 // TODO: Implement IsConstructor (ES6 section 7.2.5) 1831 var array = %IsConstructor(constructor) ? new constructor(length) : []; 1832 for (var i = 0; i < length; i++) { 1833 AddArrayElement(constructor, array, i, %_Arguments(i)); 1834 } 1835 array.length = length; 1836 return array; 1837} 1838 1839// ------------------------------------------------------------------- 1840 1841// Set up non-enumerable constructor property on the Array.prototype 1842// object. 1843%AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray, 1844 DONT_ENUM); 1845 1846// Set up unscopable properties on the Array.prototype object. 1847var unscopables = { 1848 __proto__: null, 1849 copyWithin: true, 1850 entries: true, 1851 fill: true, 1852 find: true, 1853 findIndex: true, 1854 keys: true, 1855}; 1856 1857%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables, 1858 DONT_ENUM | READ_ONLY); 1859 1860%FunctionSetLength(ArrayFrom, 1); 1861 1862// Set up non-enumerable functions on the Array object. 1863utils.InstallFunctions(GlobalArray, DONT_ENUM, [ 1864 "from", ArrayFrom, 1865 "of", ArrayOf 1866]); 1867 1868var specialFunctions = %SpecialArrayFunctions(); 1869 1870var getFunction = function(name, jsBuiltin, len) { 1871 var f = jsBuiltin; 1872 if (specialFunctions.hasOwnProperty(name)) { 1873 f = specialFunctions[name]; 1874 } 1875 if (!IS_UNDEFINED(len)) { 1876 %FunctionSetLength(f, len); 1877 } 1878 return f; 1879}; 1880 1881// Set up non-enumerable functions of the Array.prototype object and 1882// set their names. 1883// Manipulate the length of some of the functions to meet 1884// expectations set by ECMA-262 or Mozilla. 1885utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [ 1886 "toString", getFunction("toString", ArrayToString), 1887 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString), 1888 "join", getFunction("join", ArrayJoin), 1889 "pop", getFunction("pop", ArrayPop), 1890 "push", getFunction("push", ArrayPush, 1), 1891 "reverse", getFunction("reverse", ArrayReverse), 1892 "shift", getFunction("shift", ArrayShift), 1893 "unshift", getFunction("unshift", ArrayUnshift, 1), 1894 "slice", getFunction("slice", ArraySlice, 2), 1895 "splice", getFunction("splice", ArraySplice, 2), 1896 "sort", getFunction("sort", ArraySort), 1897 "filter", getFunction("filter", ArrayFilter, 1), 1898 "forEach", getFunction("forEach", ArrayForEach, 1), 1899 "some", getFunction("some", ArraySome, 1), 1900 "every", getFunction("every", ArrayEvery, 1), 1901 "map", getFunction("map", ArrayMap, 1), 1902 "indexOf", getFunction("indexOf", ArrayIndexOf, 1), 1903 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1), 1904 "reduce", getFunction("reduce", ArrayReduce, 1), 1905 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1), 1906 "copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2), 1907 "find", getFunction("find", ArrayFind, 1), 1908 "findIndex", getFunction("findIndex", ArrayFindIndex, 1), 1909 "fill", getFunction("fill", ArrayFill, 1), 1910 "includes", getFunction("includes", ArrayIncludes, 1), 1911]); 1912 1913%FinishArrayPrototypeSetup(GlobalArray.prototype); 1914 1915// The internal Array prototype doesn't need to be fancy, since it's never 1916// exposed to user code. 1917// Adding only the functions that are actually used. 1918utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [ 1919 "indexOf", getFunction("indexOf", ArrayIndexOf), 1920 "join", getFunction("join", ArrayJoin), 1921 "pop", getFunction("pop", ArrayPop), 1922 "push", getFunction("push", ArrayPush), 1923 "shift", getFunction("shift", ArrayShift), 1924 "sort", getFunction("sort", ArraySort), 1925 "splice", getFunction("splice", ArraySplice) 1926]); 1927 1928utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [ 1929 "join", getFunction("join", ArrayJoin), 1930 "pop", getFunction("pop", ArrayPop), 1931 "push", getFunction("push", ArrayPush), 1932 "shift", getFunction("shift", ArrayShift) 1933]); 1934 1935// V8 extras get a separate copy of InternalPackedArray. We give them the basic 1936// manipulation methods. 1937utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [ 1938 "push", getFunction("push", ArrayPush), 1939 "pop", getFunction("pop", ArrayPop), 1940 "shift", getFunction("shift", ArrayShift), 1941 "unshift", getFunction("unshift", ArrayUnshift), 1942 "splice", getFunction("splice", ArraySplice), 1943 "slice", getFunction("slice", ArraySlice) 1944]); 1945 1946// ------------------------------------------------------------------- 1947// Exports 1948 1949utils.Export(function(to) { 1950 to.ArrayFrom = ArrayFrom; 1951 to.ArrayIndexOf = ArrayIndexOf; 1952 to.ArrayJoin = ArrayJoin; 1953 to.ArrayPush = ArrayPush; 1954 to.ArrayToString = ArrayToString; 1955 to.InnerArrayCopyWithin = InnerArrayCopyWithin; 1956 to.InnerArrayEvery = InnerArrayEvery; 1957 to.InnerArrayFill = InnerArrayFill; 1958 to.InnerArrayFilter = InnerArrayFilter; 1959 to.InnerArrayFind = InnerArrayFind; 1960 to.InnerArrayFindIndex = InnerArrayFindIndex; 1961 to.InnerArrayForEach = InnerArrayForEach; 1962 to.InnerArrayIncludes = InnerArrayIncludes; 1963 to.InnerArrayIndexOf = InnerArrayIndexOf; 1964 to.InnerArrayJoin = InnerArrayJoin; 1965 to.InnerArrayLastIndexOf = InnerArrayLastIndexOf; 1966 to.InnerArrayReduce = InnerArrayReduce; 1967 to.InnerArrayReduceRight = InnerArrayReduceRight; 1968 to.InnerArraySome = InnerArraySome; 1969 to.InnerArraySort = InnerArraySort; 1970 to.InnerArrayToLocaleString = InnerArrayToLocaleString; 1971 to.PackedArrayReverse = PackedArrayReverse; 1972}); 1973 1974%InstallToContext([ 1975 "array_pop", ArrayPop, 1976 "array_push", ArrayPush, 1977 "array_shift", ArrayShift, 1978 "array_splice", ArraySplice, 1979 "array_slice", ArraySlice, 1980 "array_unshift", ArrayUnshift, 1981]); 1982 1983}); 1984