Home | History | Annotate | Download | only in src
      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.
     36 var 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.
     41 function 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 
     70 function 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 ''.
     90 function 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 
    111 function UseSparseVariant(object, length, is_array) {
    112    return is_array &&
    113        length > 1000 &&
    114        (!%_IsSmi(length) ||
    115         %EstimateNumberOfElements(object) < (length >> 2));
    116 }
    117 
    118 
    119 function 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 
    195 function 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 
    203 function 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.
    218 function 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.
    261 function 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.
    324 function 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 
    338 function 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 
    381 function 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 
    401 function 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 
    410 function 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.
    431 function 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.
    452 function 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 
    468 function 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.
    486 function 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 
    529 function 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 
    565 function 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 
    592 function 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 
    617 function 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 
    661 function 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 
    737 function 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.
   1007 function 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 
   1043 function 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.
   1074 function 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 
   1104 function 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 
   1133 function 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 
   1166 function 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 
   1228 function 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 
   1286 function 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 
   1323 function 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
   1361 function ArrayIsArray(obj) {
   1362   return IS_ARRAY(obj);
   1363 }
   1364 
   1365 
   1366 // -------------------------------------------------------------------
   1367 function 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 
   1431 SetUpArray();
   1432