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 unscopablesSymbol = utils.ImportNow("unscopables_symbol");
25
26utils.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
37function ArraySpeciesCreate(array, length) {
38  length = INVERT_NEG_ZERO(length);
39  var constructor = %ArraySpeciesConstructor(array);
40  return new constructor(length);
41}
42
43
44function KeySortCompare(a, b) {
45  return a - b;
46}
47
48function 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
65function 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 ''.
78function 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
88function 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
104function 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.
111Stack.prototype.length = null;
112Stack.prototype.values = null;
113
114function StackPush(stack, value) {
115  stack.values[stack.length++] = value;
116}
117
118function StackPop(stack) {
119  stack.values[--stack.length] = null
120}
121
122function 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.
133var visited_arrays = new Stack();
134
135function 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
166function 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
189function 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.
197function 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.
225function 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.
286function 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
297function 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
332function 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
352function InnerArrayToLocaleString(array, length) {
353  return Join(array, TO_LENGTH(length), ',', true);
354}
355
356
357function ArrayToLocaleString() {
358  var array = TO_OBJECT(this);
359  var arrayLen = array.length;
360  return InnerArrayToLocaleString(array, arrayLen);
361}
362
363
364function 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
382function 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.
394function 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.
414function 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.
437function 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
479function 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
491function 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
516function 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
535function 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
562function 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
586function 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
628function 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
638function 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
659function 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
709function 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
996function 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.
1008function 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
1024function 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
1037function 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
1058function 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
1069function 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.
1084function 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
1095function 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
1107function 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
1118function 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
1137function 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
1187function 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
1196function 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
1222function 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
1234function 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
1261function 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 ] )
1275function 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
1328function 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
1345function 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
1355function 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
1372function 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
1383function 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
1412function 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
1423function 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
1476function 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.
1496var 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.
1513utils.InstallFunctions(GlobalArray, DONT_ENUM, [
1514  "from", ArrayFrom,
1515  "of", ArrayOf
1516]);
1517
1518var specialFunctions = %SpecialArrayFunctions();
1519
1520function 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().
1533var 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.
1544utils.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
1575utils.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.
1587utils.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
1597utils.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.
1606utils.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
1618utils.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