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
14var GetIterator;
15var GetMethod;
16var GlobalArray = global.Array;
17var InternalArray = utils.InternalArray;
18var InternalPackedArray = utils.InternalPackedArray;
19var MaxSimple;
20var MinSimple;
21var ObjectHasOwnProperty;
22var ObjectToString = utils.ImportNow("object_to_string");
23var iteratorSymbol = utils.ImportNow("iterator_symbol");
24var speciesSymbol = utils.ImportNow("species_symbol");
25var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
26
27utils.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
38function ArraySpeciesCreate(array, length) {
39  length = INVERT_NEG_ZERO(length);
40  var constructor = %ArraySpeciesConstructor(array);
41  return new constructor(length);
42}
43
44
45function KeySortCompare(a, b) {
46  return a - b;
47}
48
49function 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
66function 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 ''.
79function 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
89function 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
105function 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.
112Stack.prototype.length = null;
113Stack.prototype.values = null;
114
115function StackPush(stack, value) {
116  stack.values[stack.length++] = value;
117}
118
119function StackPop(stack) {
120  stack.values[--stack.length] = null
121}
122
123function 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.
134var visited_arrays = new Stack();
135
136function 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
167function 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
190function 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.
198function 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.
226function 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.
287function 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
298function 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
333function 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
353function InnerArrayToLocaleString(array, length) {
354  return Join(array, TO_LENGTH(length), ',', true);
355}
356
357
358function ArrayToLocaleString() {
359  var array = TO_OBJECT(this);
360  var arrayLen = array.length;
361  return InnerArrayToLocaleString(array, arrayLen);
362}
363
364
365function 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
383function 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.
395function 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.
415function 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.
438function 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
480function 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
492function 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
517function 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
536function 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
563function 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
587function 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
629function 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
639function 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
660function 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
710function 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
997function 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.
1009function 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
1025function 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
1038function 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
1059function 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
1070function 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.
1085function 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
1096function 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
1108function 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
1119function 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
1138function 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
1188function 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
1197function 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
1223function 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
1235function 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
1262function 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
1274function 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
1323function 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
1333function 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
1350function 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
1360function 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
1377function 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
1388function 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
1417function 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
1428function 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
1481function 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
1494function 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.
1507var 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.
1524utils.InstallFunctions(GlobalArray, DONT_ENUM, [
1525  "from", ArrayFrom,
1526  "of", ArrayOf
1527]);
1528
1529var specialFunctions = %SpecialArrayFunctions();
1530
1531var 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
1542var 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.
1548utils.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
1581utils.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.
1588utils.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
1598utils.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.
1607utils.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
1619utils.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