The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / src / array.js @ 40c0f755

History | View | Annotate | Download (26.5 KB)

1
// Copyright 2006-2008 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
// const $Array = global.Array;
31

    
32
// -------------------------------------------------------------------
33

    
34
// Global list of arrays visited during toString, toLocaleString and
35
// join invocations.
36
var visited_arrays = new $Array();
37

    
38

    
39
// Gets a sorted array of array keys.  Useful for operations on sparse
40
// arrays.  Dupes have not been removed.
41
function 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

    
70
// Optimized for sparse arrays if separator is ''.
71
function SparseJoin(array, len, convert) {
72
  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
73
  var builder = new StringBuilder();
74
  var last_key = -1;
75
  var keys_length = keys.length;
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
      builder.add(convert(e));
81
      last_key = key;
82
    }
83
  }
84
  return builder.generate();
85
}
86

    
87

    
88
function UseSparseVariant(object, length, is_array) {
89
   return is_array &&
90
       length > 1000 &&
91
       (!%_IsSmi(length) ||
92
        %EstimateNumberOfElements(object) < (length >> 2));
93
}
94

    
95

    
96
function Join(array, length, separator, convert) {
97
  if (length == 0) return '';
98

    
99
  var is_array = IS_ARRAY(array);
100

    
101
  if (is_array) {
102
    // If the array is cyclic, return the empty string for already
103
    // visited arrays.
104
    if (!%PushIfAbsent(visited_arrays, array)) return '';
105
  }
106

    
107
  // Attempt to convert the elements.
108
  try {
109
    if (UseSparseVariant(array, length, is_array) && separator === '') {
110
      return SparseJoin(array, length, convert);
111
    }
112

    
113
    // Fast case for one-element arrays.
114
    if (length == 1) {
115
      var e = array[0];
116
      if (!IS_UNDEFINED(e) || (0 in array)) {
117
        return convert(e);
118
      }
119
    }
120

    
121
    var builder = new StringBuilder();
122

    
123
    for (var i = 0; i < length; i++) {
124
      var e = array[i];
125
      if (i != 0) builder.add(separator);
126
      if (!IS_UNDEFINED(e) || (i in array)) {
127
        builder.add(convert(e));
128
      }
129
    }
130
    return builder.generate();
131
  } finally {
132
    // Make sure to pop the visited array no matter what happens.
133
    if (is_array) visited_arrays.pop();
134
  }
135
}
136

    
137

    
138
function ConvertToString(e) {
139
  if (e == null) return '';
140
  else return ToString(e);
141
}
142

    
143

    
144
function ConvertToLocaleString(e) {
145
  if (e == null) return '';
146
  else {
147
    // e_obj's toLocaleString might be overwritten, check if it is a function.
148
    // Call ToString if toLocaleString is not a function.
149
    // See issue 877615.
150
    var e_obj = ToObject(e);
151
    if (IS_FUNCTION(e_obj.toLocaleString))
152
      return e_obj.toLocaleString();
153
    else
154
      return ToString(e);
155
  }
156
}
157

    
158

    
159
// This function implements the optimized splice implementation that can use
160
// special array operations to handle sparse arrays in a sensible fashion.
161
function SmartSlice(array, start_i, del_count, len, deleted_elements) {
162
  // Move deleted elements to a new array (the return value from splice).
163
  // Intervals array can contain keys and intervals.  See comment in Concat.
164
  var intervals = %GetArrayKeys(array, start_i + del_count);
165
  var length = intervals.length;
166
  for (var k = 0; k < length; k++) {
167
    var key = intervals[k];
168
    if (key < 0) {
169
      var j = -1 - key;
170
      var interval_limit = j + intervals[++k];
171
      if (j < start_i) {
172
        j = start_i;
173
      }
174
      for (; j < interval_limit; j++) {
175
        // ECMA-262 15.4.4.12 line 10.  The spec could also be
176
        // interpreted such that %HasLocalProperty would be the
177
        // appropriate test.  We follow KJS in consulting the
178
        // prototype.
179
        var current = array[j];
180
        if (!IS_UNDEFINED(current) || j in array) {
181
          deleted_elements[j - start_i] = current;
182
        }
183
      }
184
    } else {
185
      if (!IS_UNDEFINED(key)) {
186
        if (key >= start_i) {
187
          // ECMA-262 15.4.4.12 line 10.  The spec could also be
188
          // interpreted such that %HasLocalProperty would be the
189
          // appropriate test.  We follow KJS in consulting the
190
          // prototype.
191
          var current = array[key];
192
          if (!IS_UNDEFINED(current) || key in array) {
193
            deleted_elements[key - start_i] = current;
194
          }
195
        }
196
      }
197
    }
198
  }
199
}
200

    
201

    
202
// This function implements the optimized splice implementation that can use
203
// special array operations to handle sparse arrays in a sensible fashion.
204
function SmartMove(array, start_i, del_count, len, num_additional_args) {
205
  // Move data to new array.
206
  var new_array = new $Array(len - del_count + num_additional_args);
207
  var intervals = %GetArrayKeys(array, len);
208
  var length = intervals.length;
209
  for (var k = 0; k < length; k++) {
210
    var key = intervals[k];
211
    if (key < 0) {
212
      var j = -1 - key;
213
      var interval_limit = j + intervals[++k];
214
      while (j < start_i && j < interval_limit) {
215
        // The spec could also be interpreted such that
216
        // %HasLocalProperty would be the appropriate test.  We follow
217
        // KJS in consulting the prototype.
218
        var current = array[j];
219
        if (!IS_UNDEFINED(current) || j in array) {
220
          new_array[j] = current;
221
        }
222
        j++;
223
      }
224
      j = start_i + del_count;
225
      while (j < interval_limit) {
226
        // ECMA-262 15.4.4.12 lines 24 and 41.  The spec could also be
227
        // interpreted such that %HasLocalProperty would be the
228
        // appropriate test.  We follow KJS in consulting the
229
        // prototype.
230
        var current = array[j];
231
        if (!IS_UNDEFINED(current) || j in array) {
232
          new_array[j - del_count + num_additional_args] = current;
233
        }
234
        j++;
235
      }
236
    } else {
237
      if (!IS_UNDEFINED(key)) {
238
        if (key < start_i) {
239
          // The spec could also be interpreted such that
240
          // %HasLocalProperty would be the appropriate test.  We follow
241
          // KJS in consulting the prototype.
242
          var current = array[key];
243
          if (!IS_UNDEFINED(current) || key in array) {
244
            new_array[key] = current;
245
          }
246
        } else if (key >= start_i + del_count) {
247
          // ECMA-262 15.4.4.12 lines 24 and 41.  The spec could also
248
          // be interpreted such that %HasLocalProperty would be the
249
          // appropriate test.  We follow KJS in consulting the
250
          // prototype.
251
          var current = array[key];
252
          if (!IS_UNDEFINED(current) || key in array) {
253
            new_array[key - del_count + num_additional_args] = current;
254
          }
255
        }
256
      }
257
    }
258
  }
259
  // Move contents of new_array into this array
260
  %MoveArrayContents(new_array, array);
261
}
262

    
263

    
264
// This is part of the old simple-minded splice.  We are using it either
265
// because the receiver is not an array (so we have no choice) or because we
266
// know we are not deleting or moving a lot of elements.
267
function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
268
  for (var i = 0; i < del_count; i++) {
269
    var index = start_i + i;
270
    // The spec could also be interpreted such that %HasLocalProperty
271
    // would be the appropriate test.  We follow KJS in consulting the
272
    // prototype.
273
    var current = array[index];
274
    if (!IS_UNDEFINED(current) || index in array)
275
      deleted_elements[i] = current;
276
  }
277
}
278

    
279

    
280
function SimpleMove(array, start_i, del_count, len, num_additional_args) {
281
  if (num_additional_args !== del_count) {
282
    // Move the existing elements after the elements to be deleted
283
    // to the right position in the resulting array.
284
    if (num_additional_args > del_count) {
285
      for (var i = len - del_count; i > start_i; i--) {
286
        var from_index = i + del_count - 1;
287
        var to_index = i + num_additional_args - 1;
288
        // The spec could also be interpreted such that
289
        // %HasLocalProperty would be the appropriate test.  We follow
290
        // KJS in consulting the prototype.
291
        var current = array[from_index];
292
        if (!IS_UNDEFINED(current) || from_index in array) {
293
          array[to_index] = current;
294
        } else {
295
          delete array[to_index];
296
        }
297
      }
298
    } else {
299
      for (var i = start_i; i < len - del_count; i++) {
300
        var from_index = i + del_count;
301
        var to_index = i + num_additional_args;
302
        // The spec could also be interpreted such that
303
        // %HasLocalProperty would be the appropriate test.  We follow
304
        // KJS in consulting the prototype.
305
        var current = array[from_index];
306
        if (!IS_UNDEFINED(current) || from_index in array) {
307
          array[to_index] = current;
308
        } else {
309
          delete array[to_index];
310
        }
311
      }
312
      for (var i = len; i > len - del_count + num_additional_args; i--) {
313
        delete array[i - 1];
314
      }
315
    }
316
  }
317
}
318

    
319

    
320
// -------------------------------------------------------------------
321

    
322

    
323
function ArrayToString() {
324
  if (!IS_ARRAY(this)) {
325
    throw new $TypeError('Array.prototype.toString is not generic');
326
  }
327
  return Join(this, this.length, ',', ConvertToString);
328
}
329

    
330

    
331
function ArrayToLocaleString() {
332
  if (!IS_ARRAY(this)) {
333
    throw new $TypeError('Array.prototype.toString is not generic');
334
  }
335
  return Join(this, this.length, ',', ConvertToLocaleString);
336
}
337

    
338

    
339
function ArrayJoin(separator) {
340
  if (IS_UNDEFINED(separator)) separator = ',';
341
  else separator = ToString(separator);
342
  return Join(this, ToUint32(this.length), separator, ConvertToString);
343
}
344

    
345

    
346
// Removes the last element from the array and returns it. See
347
// ECMA-262, section 15.4.4.6.
348
function ArrayPop() {
349
  var n = ToUint32(this.length);
350
  if (n == 0) {
351
    this.length = n;
352
    return;
353
  }
354
  n--;
355
  var value = this[n];
356
  this.length = n;
357
  delete this[n];
358
  return value;
359
}
360

    
361

    
362
// Appends the arguments to the end of the array and returns the new
363
// length of the array. See ECMA-262, section 15.4.4.7.
364
function ArrayPush() {
365
  var n = ToUint32(this.length);
366
  var m = %_ArgumentsLength();
367
  for (var i = 0; i < m; i++) {
368
    this[i+n] = %_Arguments(i);
369
  }
370
  this.length = n + m;
371
  return this.length;
372
}
373

    
374

    
375
function ArrayConcat(arg1) {  // length == 1
376
  // TODO: can we just use arguments?
377
  var arg_count = %_ArgumentsLength();
378
  var arrays = new $Array(1 + arg_count);
379
  arrays[0] = this;
380
  for (var i = 0; i < arg_count; i++) {
381
    arrays[i + 1] = %_Arguments(i);
382
  }
383

    
384
  return %ArrayConcat(arrays);
385
}
386

    
387

    
388
// For implementing reverse() on large, sparse arrays.
389
function SparseReverse(array, len) {
390
  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
391
  var high_counter = keys.length - 1;
392
  var low_counter = 0;
393
  while (low_counter <= high_counter) {
394
    var i = keys[low_counter];
395
    var j = keys[high_counter];
396

    
397
    var j_complement = len - j - 1;
398
    var low, high;
399

    
400
    if (j_complement <= i) {
401
      high = j;
402
      while (keys[--high_counter] == j);
403
      low = j_complement;
404
    }
405
    if (j_complement >= i) {
406
      low = i;
407
      while (keys[++low_counter] == i);
408
      high = len - i - 1;
409
    }
410

    
411
    var current_i = array[low];
412
    if (!IS_UNDEFINED(current_i) || low in array) {
413
      var current_j = array[high];
414
      if (!IS_UNDEFINED(current_j) || high in array) {
415
        array[low] = current_j;
416
        array[high] = current_i;
417
      } else {
418
        array[high] = current_i;
419
        delete array[low];
420
      }
421
    } else {
422
      var current_j = array[high];
423
      if (!IS_UNDEFINED(current_j) || high in array) {
424
        array[low] = current_j;
425
        delete array[high];
426
      }
427
    }
428
  }
429
}
430

    
431

    
432
function ArrayReverse() {
433
  var j = ToUint32(this.length) - 1;
434

    
435
  if (UseSparseVariant(this, j, IS_ARRAY(this))) {
436
    SparseReverse(this, j+1);
437
    return this;
438
  }
439

    
440
  for (var i = 0; i < j; i++, j--) {
441
    var current_i = this[i];
442
    if (!IS_UNDEFINED(current_i) || i in this) {
443
      var current_j = this[j];
444
      if (!IS_UNDEFINED(current_j) || j in this) {
445
        this[i] = current_j;
446
        this[j] = current_i;
447
      } else {
448
        this[j] = current_i;
449
        delete this[i];
450
      }
451
    } else {
452
      var current_j = this[j];
453
      if (!IS_UNDEFINED(current_j) || j in this) {
454
        this[i] = current_j;
455
        delete this[j];
456
      }
457
    }
458
  }
459
  return this;
460
}
461

    
462

    
463
function ArrayShift() {
464
  var len = ToUint32(this.length);
465

    
466
  if (len === 0) {
467
    this.length = 0;
468
    return;
469
  }
470

    
471
  var first = this[0];
472

    
473
  if (IS_ARRAY(this))
474
    SmartMove(this, 0, 1, len, 0);
475
  else
476
    SimpleMove(this, 0, 1, len, 0);
477

    
478
  this.length = len - 1;
479

    
480
  return first;
481
}
482

    
483

    
484
function ArrayUnshift(arg1) {  // length == 1
485
  var len = ToUint32(this.length);
486
  var num_arguments = %_ArgumentsLength();
487

    
488
  if (IS_ARRAY(this))
489
    SmartMove(this, 0, 0, len, num_arguments);
490
  else
491
    SimpleMove(this, 0, 0, len, num_arguments);
492

    
493
  for (var i = 0; i < num_arguments; i++) {
494
    this[i] = %_Arguments(i);
495
  }
496

    
497
  this.length = len + num_arguments;
498

    
499
  return len + num_arguments;
500
}
501

    
502

    
503
function ArraySlice(start, end) {
504
  var len = ToUint32(this.length);
505
  var start_i = TO_INTEGER(start);
506
  var end_i = len;
507

    
508
  if (end !== void 0) end_i = TO_INTEGER(end);
509

    
510
  if (start_i < 0) {
511
    start_i += len;
512
    if (start_i < 0) start_i = 0;
513
  } else {
514
    if (start_i > len) start_i = len;
515
  }
516

    
517
  if (end_i < 0) {
518
    end_i += len;
519
    if (end_i < 0) end_i = 0;
520
  } else {
521
    if (end_i > len) end_i = len;
522
  }
523

    
524
  var result = [];
525

    
526
  if (end_i < start_i) return result;
527

    
528
  if (IS_ARRAY(this)) {
529
    SmartSlice(this, start_i, end_i - start_i, len, result);
530
  } else {
531
    SimpleSlice(this, start_i, end_i - start_i, len, result);
532
  }
533

    
534
  result.length = end_i - start_i;
535

    
536
  return result;
537
}
538

    
539

    
540
function ArraySplice(start, delete_count) {
541
  var num_arguments = %_ArgumentsLength();
542

    
543
  // SpiderMonkey and KJS return undefined in the case where no
544
  // arguments are given instead of using the implicit undefined
545
  // arguments.  This does not follow ECMA-262, but we do the same for
546
  // compatibility.
547
  if (num_arguments == 0) return;
548

    
549
  var len = ToUint32(this.length);
550
  var start_i = TO_INTEGER(start);
551

    
552
  if (start_i < 0) {
553
    start_i += len;
554
    if (start_i < 0) start_i = 0;
555
  } else {
556
    if (start_i > len) start_i = len;
557
  }
558

    
559
  // SpiderMonkey and KJS treat the case where no delete count is
560
  // given differently from when an undefined delete count is given.
561
  // This does not follow ECMA-262, but we do the same for
562
  // compatibility.
563
  var del_count = 0;
564
  if (num_arguments > 1) {
565
    del_count = TO_INTEGER(delete_count);
566
    if (del_count < 0) del_count = 0;
567
    if (del_count > len - start_i) del_count = len - start_i;
568
  } else {
569
    del_count = len - start_i;
570
  }
571

    
572
  var deleted_elements = [];
573
  deleted_elements.length = del_count;
574

    
575
  // Number of elements to add.
576
  var num_additional_args = 0;
577
  if (num_arguments > 2) {
578
    num_additional_args = num_arguments - 2;
579
  }
580

    
581
  var use_simple_splice = true;
582

    
583
  if (IS_ARRAY(this) && num_additional_args !== del_count) {
584
    // If we are only deleting/moving a few things near the end of the
585
    // array then the simple version is going to be faster, because it
586
    // doesn't touch most of the array.
587
    var estimated_non_hole_elements = %EstimateNumberOfElements(this);
588
    if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
589
      use_simple_splice = false;
590
    }
591
  }
592

    
593
  if (use_simple_splice) {
594
    SimpleSlice(this, start_i, del_count, len, deleted_elements);
595
    SimpleMove(this, start_i, del_count, len, num_additional_args);
596
  } else {
597
    SmartSlice(this, start_i, del_count, len, deleted_elements);
598
    SmartMove(this, start_i, del_count, len, num_additional_args);
599
  }
600

    
601
  // Insert the arguments into the resulting array in
602
  // place of the deleted elements.
603
  var i = start_i;
604
  var arguments_index = 2;
605
  var arguments_length = %_ArgumentsLength();
606
  while (arguments_index < arguments_length) {
607
    this[i++] = %_Arguments(arguments_index++);
608
  }
609
  this.length = len - del_count + num_additional_args;
610

    
611
  // Return the deleted elements.
612
  return deleted_elements;
613
}
614

    
615

    
616
function ArraySort(comparefn) {
617
  // In-place QuickSort algorithm.
618
  // For short (length <= 22) arrays, insertion sort is used for efficiency.
619

    
620
  var custom_compare = IS_FUNCTION(comparefn);
621

    
622
  function Compare(x,y) {
623
    // Assume the comparefn, if any, is a consistent comparison function.
624
    // If it isn't, we are allowed arbitrary behavior by ECMA 15.4.4.11.
625
    if (x === y) return 0;
626
    if (custom_compare) {
627
      // Don't call directly to avoid exposing the builtin's global object.
628
      return comparefn.call(null, x, y);
629
    }
630
    if (%_IsSmi(x) && %_IsSmi(y)) {
631
      return %SmiLexicographicCompare(x, y);
632
    }
633
    x = ToString(x);
634
    y = ToString(y);
635
    if (x == y) return 0;
636
    else return x < y ? -1 : 1;
637
  };
638

    
639
  function InsertionSort(a, from, to) {
640
    for (var i = from + 1; i < to; i++) {
641
      var element = a[i];
642
      // Pre-convert the element to a string for comparison if we know
643
      // it will happen on each compare anyway.
644
      var key =
645
          (custom_compare || %_IsSmi(element)) ? element : ToString(element);
646
      // place element in a[from..i[
647
      // binary search
648
      var min = from;
649
      var max = i;
650
      // The search interval is a[min..max[
651
      while (min < max) {
652
        var mid = min + ((max - min) >> 1);
653
        var order = Compare(a[mid], key);
654
        if (order == 0) {
655
          min = max = mid;
656
          break;
657
        }
658
        if (order < 0) {
659
          min = mid + 1;
660
        } else {
661
          max = mid;
662
        }
663
      }
664
      // place element at position min==max.
665
      for (var j = i; j > min; j--) {
666
        a[j] = a[j - 1];
667
      }
668
      a[min] = element;
669
    }
670
  }
671

    
672
  function QuickSort(a, from, to) {
673
    // Insertion sort is faster for short arrays.
674
    if (to - from <= 22) {
675
      InsertionSort(a, from, to);
676
      return;
677
    }
678
    var pivot_index = $floor($random() * (to - from)) + from;
679
    var pivot = a[pivot_index];
680
    // Pre-convert the element to a string for comparison if we know
681
    // it will happen on each compare anyway.
682
    var pivot_key =
683
      (custom_compare || %_IsSmi(pivot)) ? pivot : ToString(pivot);
684
    // Issue 95: Keep the pivot element out of the comparisons to avoid
685
    // infinite recursion if comparefn(pivot, pivot) != 0.
686
    a[pivot_index] = a[from];
687
    a[from] = pivot;
688
    var low_end = from;   // Upper bound of the elements lower than pivot.
689
    var high_start = to;  // Lower bound of the elements greater than pivot.
690
    // From low_end to i are elements equal to pivot.
691
    // From i to high_start are elements that haven't been compared yet.
692
    for (var i = from + 1; i < high_start; ) {
693
      var element = a[i];
694
      var order = Compare(element, pivot_key);
695
      if (order < 0) {
696
        a[i] = a[low_end];
697
        a[low_end] = element;
698
        i++;
699
        low_end++;
700
      } else if (order > 0) {
701
        high_start--;
702
        a[i] = a[high_start];
703
        a[high_start] = element;
704
      } else {  // order == 0
705
        i++;
706
      }
707
    }
708
    QuickSort(a, from, low_end);
709
    QuickSort(a, high_start, to);
710
  }
711

    
712
  var old_length = ToUint32(this.length);
713
  if (old_length < 2) return this;
714

    
715
  %RemoveArrayHoles(this);
716

    
717
  var length = ToUint32(this.length);
718

    
719
  // Move undefined elements to the end of the array.
720
  for (var i = 0; i < length; ) {
721
    if (IS_UNDEFINED(this[i])) {
722
      length--;
723
      this[i] = this[length];
724
      this[length] = void 0;
725
    } else {
726
      i++;
727
    }
728
  }
729

    
730
  QuickSort(this, 0, length);
731

    
732
  // We only changed the length of the this object (in
733
  // RemoveArrayHoles) if it was an array.  We are not allowed to set
734
  // the length of the this object if it is not an array because this
735
  // might introduce a new length property.
736
  if (IS_ARRAY(this)) {
737
    this.length = old_length;
738
  }
739

    
740
  return this;
741
}
742

    
743

    
744
// The following functions cannot be made efficient on sparse arrays while
745
// preserving the semantics, since the calls to the receiver function can add
746
// or delete elements from the array.
747
function ArrayFilter(f, receiver) {
748
  if (!IS_FUNCTION(f)) {
749
    throw MakeTypeError('called_non_callable', [ f ]);
750
  }
751
  // Pull out the length so that modifications to the length in the
752
  // loop will not affect the looping.
753
  var length = this.length;
754
  var result = [];
755
  var result_length = 0;
756
  for (var i = 0; i < length; i++) {
757
    var current = this[i];
758
    if (!IS_UNDEFINED(current) || i in this) {
759
      if (f.call(receiver, current, i, this)) result[result_length++] = current;
760
    }
761
  }
762
  return result;
763
}
764

    
765

    
766
function ArrayForEach(f, receiver) {
767
  if (!IS_FUNCTION(f)) {
768
    throw MakeTypeError('called_non_callable', [ f ]);
769
  }
770
  // Pull out the length so that modifications to the length in the
771
  // loop will not affect the looping.
772
  var length = this.length;
773
  for (var i = 0; i < length; i++) {
774
    var current = this[i];
775
    if (!IS_UNDEFINED(current) || i in this) {
776
      f.call(receiver, current, i, this);
777
    }
778
  }
779
}
780

    
781

    
782
// Executes the function once for each element present in the
783
// array until it finds one where callback returns true.
784
function ArraySome(f, receiver) {
785
  if (!IS_FUNCTION(f)) {
786
    throw MakeTypeError('called_non_callable', [ f ]);
787
  }
788
  // Pull out the length so that modifications to the length in the
789
  // loop will not affect the looping.
790
  var length = this.length;
791
  for (var i = 0; i < length; i++) {
792
    var current = this[i];
793
    if (!IS_UNDEFINED(current) || i in this) {
794
      if (f.call(receiver, current, i, this)) return true;
795
    }
796
  }
797
  return false;
798
}
799

    
800

    
801
function ArrayEvery(f, receiver) {
802
  if (!IS_FUNCTION(f)) {
803
    throw MakeTypeError('called_non_callable', [ f ]);
804
  }
805
  // Pull out the length so that modifications to the length in the
806
  // loop will not affect the looping.
807
  var length = this.length;
808
  for (var i = 0; i < length; i++) {
809
    var current = this[i];
810
    if (!IS_UNDEFINED(current) || i in this) {
811
      if (!f.call(receiver, current, i, this)) return false;
812
    }
813
  }
814

    
815
  return true;
816
}
817

    
818

    
819
function ArrayMap(f, receiver) {
820
  if (!IS_FUNCTION(f)) {
821
    throw MakeTypeError('called_non_callable', [ f ]);
822
  }
823
  // Pull out the length so that modifications to the length in the
824
  // loop will not affect the looping.
825
  var length = this.length;
826
  var result = new $Array(length);
827
  for (var i = 0; i < length; i++) {
828
    var current = this[i];
829
    if (!IS_UNDEFINED(current) || i in this) {
830
      result[i] = f.call(receiver, current, i, this);
831
    }
832
  }
833
  return result;
834
}
835

    
836

    
837
function ArrayIndexOf(element, index) {
838
  var length = this.length;
839
  if (index == null) {
840
    index = 0;
841
  } else {
842
    index = TO_INTEGER(index);
843
    // If index is negative, index from the end of the array.
844
    if (index < 0) index = length + index;
845
    // If index is still negative, search the entire array.
846
    if (index < 0) index = 0;
847
  }
848
  // Lookup through the array.
849
  for (var i = index; i < length; i++) {
850
    var current = this[i];
851
    if (!IS_UNDEFINED(current) || i in this) {
852
      if (current === element) return i;
853
    }
854
  }
855
  return -1;
856
}
857

    
858

    
859
function ArrayLastIndexOf(element, index) {
860
  var length = this.length;
861
  if (index == null) {
862
    index = length - 1;
863
  } else {
864
    index = TO_INTEGER(index);
865
    // If index is negative, index from end of the array.
866
    if (index < 0) index = length + index;
867
    // If index is still negative, do not search the array.
868
    if (index < 0) index = -1;
869
    else if (index >= length) index = length - 1;
870
  }
871
  // Lookup through the array.
872
  for (var i = index; i >= 0; i--) {
873
    var current = this[i];
874
    if (!IS_UNDEFINED(current) || i in this) {
875
      if (current === element) return i;
876
    }
877
  }
878
  return -1;
879
}
880

    
881

    
882
// -------------------------------------------------------------------
883

    
884

    
885
function UpdateFunctionLengths(lengths) {
886
  for (var key in lengths) {
887
    %FunctionSetLength(this[key], lengths[key]);
888
  }
889
}
890

    
891

    
892
// -------------------------------------------------------------------
893

    
894
function SetupArray() {
895
  // Setup non-enumerable constructor property on the Array.prototype
896
  // object.
897
  %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
898

    
899
  // Setup non-enumerable functions of the Array.prototype object and
900
  // set their names.
901
  InstallFunctions($Array.prototype, DONT_ENUM, $Array(
902
    "toString", ArrayToString,
903
    "toLocaleString", ArrayToLocaleString,
904
    "join", ArrayJoin,
905
    "pop", ArrayPop,
906
    "push", ArrayPush,
907
    "concat", ArrayConcat,
908
    "reverse", ArrayReverse,
909
    "shift", ArrayShift,
910
    "unshift", ArrayUnshift,
911
    "slice", ArraySlice,
912
    "splice", ArraySplice,
913
    "sort", ArraySort,
914
    "filter", ArrayFilter,
915
    "forEach", ArrayForEach,
916
    "some", ArraySome,
917
    "every", ArrayEvery,
918
    "map", ArrayMap,
919
    "indexOf", ArrayIndexOf,
920
    "lastIndexOf", ArrayLastIndexOf
921
  ));
922

    
923
  // Manipulate the length of some of the functions to meet
924
  // expectations set by ECMA-262 or Mozilla.
925
  UpdateFunctionLengths({
926
    ArrayFilter: 1,
927
    ArrayForEach: 1,
928
    ArraySome: 1,
929
    ArrayEvery: 1,
930
    ArrayMap: 1,
931
    ArrayIndexOf: 1,
932
    ArrayLastIndexOf: 1,
933
    ArrayPush: 1
934
  });
935
}
936

    
937

    
938
SetupArray();