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