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