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