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