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