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