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