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