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