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