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