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