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