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