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