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 @ f230a1cf

History | View | Annotate | Download (48.9 KB)

1
// Copyright 2012 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.
36
var 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.
41
function GetSortedArrayKeys(array, indices) {
42
  var keys = new InternalArray();
43
  if (IS_NUMBER(indices)) {
44
    // It's an interval
45
    var limit = indices;
46
    for (var i = 0; i < limit; ++i) {
47
      var e = array[i];
48
      if (!IS_UNDEFINED(e) || i in array) {
49
        keys.push(i);
50
      }
51
    }
52
  } else {
53
    var length = indices.length;
54
    for (var k = 0; k < length; ++k) {
55
      var key = indices[k];
56
      if (!IS_UNDEFINED(key)) {
57
        var e = array[key];
58
        if (!IS_UNDEFINED(e) || key in array) {
59
          keys.push(key);
60
        }
61
      }
62
    }
63
    %_CallFunction(keys, function(a, b) { return a - b; }, ArraySort);
64
  }
65
  return keys;
66
}
67

    
68

    
69
function SparseJoinWithSeparator(array, len, convert, separator) {
70
  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
71
  var totalLength = 0;
72
  var elements = new InternalArray(keys.length * 2);
73
  var previousKey = -1;
74
  for (var i = 0; i < keys.length; i++) {
75
    var key = keys[i];
76
    if (key != previousKey) {  // keys may contain duplicates.
77
      var e = array[key];
78
      if (!IS_STRING(e)) e = convert(e);
79
      elements[i * 2] = key;
80
      elements[i * 2 + 1] = e;
81
      previousKey = key;
82
    }
83
  }
84
  return %SparseJoinWithSeparator(elements, len, separator);
85
}
86

    
87

    
88
// Optimized for sparse arrays if separator is ''.
89
function SparseJoin(array, len, convert) {
90
  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
91
  var last_key = -1;
92
  var keys_length = keys.length;
93

    
94
  var elements = new InternalArray(keys_length);
95
  var elements_length = 0;
96

    
97
  for (var i = 0; i < keys_length; i++) {
98
    var key = keys[i];
99
    if (key != last_key) {
100
      var e = array[key];
101
      if (!IS_STRING(e)) e = convert(e);
102
      elements[elements_length++] = e;
103
      last_key = key;
104
    }
105
  }
106
  return %StringBuilderConcat(elements, elements_length, '');
107
}
108

    
109

    
110
function UseSparseVariant(object, length, is_array) {
111
   return is_array &&
112
       length > 1000 &&
113
       (!%_IsSmi(length) ||
114
        %EstimateNumberOfElements(object) < (length >> 2));
115
}
116

    
117

    
118
function Join(array, length, separator, convert) {
119
  if (length == 0) return '';
120

    
121
  var is_array = IS_ARRAY(array);
122

    
123
  if (is_array) {
124
    // If the array is cyclic, return the empty string for already
125
    // visited arrays.
126
    if (!%PushIfAbsent(visited_arrays, array)) return '';
127
  }
128

    
129
  // Attempt to convert the elements.
130
  try {
131
    if (UseSparseVariant(array, length, is_array)) {
132
      if (separator.length == 0) {
133
        return SparseJoin(array, length, convert);
134
      } else {
135
        return SparseJoinWithSeparator(array, length, convert, separator);
136
      }
137
    }
138

    
139
    // Fast case for one-element arrays.
140
    if (length == 1) {
141
      var e = array[0];
142
      if (IS_STRING(e)) return e;
143
      return convert(e);
144
    }
145

    
146
    // Construct an array for the elements.
147
    var elements = new InternalArray(length);
148

    
149
    // We pull the empty separator check outside the loop for speed!
150
    if (separator.length == 0) {
151
      var elements_length = 0;
152
      for (var i = 0; i < length; i++) {
153
        var e = array[i];
154
        if (!IS_STRING(e)) e = convert(e);
155
        elements[elements_length++] = e;
156
      }
157
      elements.length = elements_length;
158
      var result = %_FastAsciiArrayJoin(elements, '');
159
      if (!IS_UNDEFINED(result)) return result;
160
      return %StringBuilderConcat(elements, elements_length, '');
161
    }
162
    // Non-empty separator case.
163
    // If the first element is a number then use the heuristic that the
164
    // remaining elements are also likely to be numbers.
165
    if (!IS_NUMBER(array[0])) {
166
      for (var i = 0; i < length; i++) {
167
        var e = array[i];
168
        if (!IS_STRING(e)) e = convert(e);
169
        elements[i] = e;
170
      }
171
    } else {
172
      for (var i = 0; i < length; i++) {
173
        var e = array[i];
174
        if (IS_NUMBER(e)) {
175
          e = %_NumberToString(e);
176
        } else if (!IS_STRING(e)) {
177
          e = convert(e);
178
        }
179
        elements[i] = e;
180
      }
181
    }
182
    var result = %_FastAsciiArrayJoin(elements, separator);
183
    if (!IS_UNDEFINED(result)) return result;
184

    
185
    return %StringBuilderJoin(elements, length, separator);
186
  } finally {
187
    // Make sure to remove the last element of the visited array no
188
    // matter what happens.
189
    if (is_array) visited_arrays.length = visited_arrays.length - 1;
190
  }
191
}
192

    
193

    
194
function ConvertToString(x) {
195
  // Assumes x is a non-string.
196
  if (IS_NUMBER(x)) return %_NumberToString(x);
197
  if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
198
  return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
199
}
200

    
201

    
202
function ConvertToLocaleString(e) {
203
  if (IS_NULL_OR_UNDEFINED(e)) {
204
    return '';
205
  } else {
206
    // According to ES5, section 15.4.4.3, the toLocaleString conversion
207
    // must throw a TypeError if ToObject(e).toLocaleString isn't
208
    // callable.
209
    var e_obj = ToObject(e);
210
    return %ToString(e_obj.toLocaleString());
211
  }
212
}
213

    
214

    
215
// This function implements the optimized splice implementation that can use
216
// special array operations to handle sparse arrays in a sensible fashion.
217
function SmartSlice(array, start_i, del_count, len, deleted_elements) {
218
  // Move deleted elements to a new array (the return value from splice).
219
  var indices = %GetArrayKeys(array, start_i + del_count);
220
  if (IS_NUMBER(indices)) {
221
    var limit = indices;
222
    for (var i = start_i; i < limit; ++i) {
223
      var current = array[i];
224
      if (!IS_UNDEFINED(current) || i in array) {
225
        deleted_elements[i - start_i] = current;
226
      }
227
    }
228
  } else {
229
    var length = indices.length;
230
    for (var k = 0; k < length; ++k) {
231
      var key = indices[k];
232
      if (!IS_UNDEFINED(key)) {
233
        if (key >= start_i) {
234
          var current = array[key];
235
          if (!IS_UNDEFINED(current) || key in array) {
236
            deleted_elements[key - start_i] = current;
237
          }
238
        }
239
      }
240
    }
241
  }
242
}
243

    
244

    
245
// This function implements the optimized splice implementation that can use
246
// special array operations to handle sparse arrays in a sensible fashion.
247
function SmartMove(array, start_i, del_count, len, num_additional_args) {
248
  // Move data to new array.
249
  var new_array = new InternalArray(len - del_count + num_additional_args);
250
  var indices = %GetArrayKeys(array, len);
251
  if (IS_NUMBER(indices)) {
252
    var limit = indices;
253
    for (var i = 0; i < start_i && i < limit; ++i) {
254
      var current = array[i];
255
      if (!IS_UNDEFINED(current) || i in array) {
256
        new_array[i] = current;
257
      }
258
    }
259
    for (var i = start_i + del_count; i < limit; ++i) {
260
      var current = array[i];
261
      if (!IS_UNDEFINED(current) || i in array) {
262
        new_array[i - del_count + num_additional_args] = current;
263
      }
264
    }
265
  } else {
266
    var length = indices.length;
267
    for (var k = 0; k < length; ++k) {
268
      var key = indices[k];
269
      if (!IS_UNDEFINED(key)) {
270
        if (key < start_i) {
271
          var current = array[key];
272
          if (!IS_UNDEFINED(current) || key in array) {
273
            new_array[key] = current;
274
          }
275
        } else if (key >= start_i + del_count) {
276
          var current = array[key];
277
          if (!IS_UNDEFINED(current) || key in array) {
278
            new_array[key - del_count + num_additional_args] = current;
279
          }
280
        }
281
      }
282
    }
283
  }
284
  // Move contents of new_array into this array
285
  %MoveArrayContents(new_array, array);
286
}
287

    
288

    
289
// This is part of the old simple-minded splice.  We are using it either
290
// because the receiver is not an array (so we have no choice) or because we
291
// know we are not deleting or moving a lot of elements.
292
function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
293
  for (var i = 0; i < del_count; i++) {
294
    var index = start_i + i;
295
    // The spec could also be interpreted such that %HasLocalProperty
296
    // would be the appropriate test.  We follow KJS in consulting the
297
    // prototype.
298
    var current = array[index];
299
    if (!IS_UNDEFINED(current) || index in array) {
300
      deleted_elements[i] = current;
301
    }
302
  }
303
}
304

    
305

    
306
function SimpleMove(array, start_i, del_count, len, num_additional_args) {
307
  if (num_additional_args !== del_count) {
308
    // Move the existing elements after the elements to be deleted
309
    // to the right position in the resulting array.
310
    if (num_additional_args > del_count) {
311
      for (var i = len - del_count; i > start_i; i--) {
312
        var from_index = i + del_count - 1;
313
        var to_index = i + num_additional_args - 1;
314
        // The spec could also be interpreted such that
315
        // %HasLocalProperty would be the appropriate test.  We follow
316
        // KJS in consulting the prototype.
317
        var current = array[from_index];
318
        if (!IS_UNDEFINED(current) || from_index in array) {
319
          array[to_index] = current;
320
        } else {
321
          delete array[to_index];
322
        }
323
      }
324
    } else {
325
      for (var i = start_i; i < len - del_count; i++) {
326
        var from_index = i + del_count;
327
        var to_index = i + num_additional_args;
328
        // The spec could also be interpreted such that
329
        // %HasLocalProperty would be the appropriate test.  We follow
330
        // KJS in consulting the prototype.
331
        var current = array[from_index];
332
        if (!IS_UNDEFINED(current) || from_index in array) {
333
          array[to_index] = current;
334
        } else {
335
          delete array[to_index];
336
        }
337
      }
338
      for (var i = len; i > len - del_count + num_additional_args; i--) {
339
        delete array[i - 1];
340
      }
341
    }
342
  }
343
}
344

    
345

    
346
// -------------------------------------------------------------------
347

    
348

    
349
function ArrayToString() {
350
  var array;
351
  var func;
352
  if (IS_ARRAY(this)) {
353
    func = this.join;
354
    if (func === ArrayJoin) {
355
      return Join(this, this.length, ',', ConvertToString);
356
    }
357
    array = this;
358
  } else {
359
    array = ToObject(this);
360
    func = array.join;
361
  }
362
  if (!IS_SPEC_FUNCTION(func)) {
363
    return %_CallFunction(array, ObjectToString);
364
  }
365
  return %_CallFunction(array, func);
366
}
367

    
368

    
369
function ArrayToLocaleString() {
370
  var array = ToObject(this);
371
  var arrayLen = array.length;
372
  var len = TO_UINT32(arrayLen);
373
  if (len === 0) return "";
374
  return Join(array, len, ',', ConvertToLocaleString);
375
}
376

    
377

    
378
function ArrayJoin(separator) {
379
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
380
    throw MakeTypeError("called_on_null_or_undefined",
381
                        ["Array.prototype.join"]);
382
  }
383

    
384
  var length = TO_UINT32(this.length);
385
  if (IS_UNDEFINED(separator)) {
386
    separator = ',';
387
  } else if (!IS_STRING(separator)) {
388
    separator = NonStringToString(separator);
389
  }
390

    
391
  var result = %_FastAsciiArrayJoin(this, separator);
392
  if (!IS_UNDEFINED(result)) return result;
393

    
394
  return Join(this, length, separator, ConvertToString);
395
}
396

    
397

    
398
function ObservedArrayPop(n) {
399
  n--;
400
  var value = this[n];
401

    
402
  try {
403
    BeginPerformSplice(this);
404
    delete this[n];
405
    this.length = n;
406
  } finally {
407
    EndPerformSplice(this);
408
    EnqueueSpliceRecord(this, n, [value], 0);
409
  }
410

    
411
  return value;
412
}
413

    
414
// Removes the last element from the array and returns it. See
415
// ECMA-262, section 15.4.4.6.
416
function ArrayPop() {
417
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
418
    throw MakeTypeError("called_on_null_or_undefined",
419
                        ["Array.prototype.pop"]);
420
  }
421

    
422
  var n = TO_UINT32(this.length);
423
  if (n == 0) {
424
    this.length = n;
425
    return;
426
  }
427

    
428
  if (%IsObserved(this))
429
    return ObservedArrayPop.call(this, n);
430

    
431
  n--;
432
  var value = this[n];
433
  Delete(this, ToName(n), true);
434
  this.length = n;
435
  return value;
436
}
437

    
438

    
439
function ObservedArrayPush() {
440
  var n = TO_UINT32(this.length);
441
  var m = %_ArgumentsLength();
442

    
443
  try {
444
    BeginPerformSplice(this);
445
    for (var i = 0; i < m; i++) {
446
      this[i+n] = %_Arguments(i);
447
    }
448
    this.length = n + m;
449
  } finally {
450
    EndPerformSplice(this);
451
    EnqueueSpliceRecord(this, n, [], m);
452
  }
453

    
454
  return this.length;
455
}
456

    
457
// Appends the arguments to the end of the array and returns the new
458
// length of the array. See ECMA-262, section 15.4.4.7.
459
function ArrayPush() {
460
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
461
    throw MakeTypeError("called_on_null_or_undefined",
462
                        ["Array.prototype.push"]);
463
  }
464

    
465
  if (%IsObserved(this))
466
    return ObservedArrayPush.apply(this, arguments);
467

    
468
  var n = TO_UINT32(this.length);
469
  var m = %_ArgumentsLength();
470
  for (var i = 0; i < m; i++) {
471
    this[i+n] = %_Arguments(i);
472
  }
473
  this.length = n + m;
474
  return this.length;
475
}
476

    
477

    
478
// Returns an array containing the array elements of the object followed
479
// by the array elements of each argument in order. See ECMA-262,
480
// section 15.4.4.7.
481
function ArrayConcat(arg1) {  // length == 1
482
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
483
    throw MakeTypeError("called_on_null_or_undefined",
484
                        ["Array.prototype.concat"]);
485
  }
486

    
487
  var array = ToObject(this);
488
  var arg_count = %_ArgumentsLength();
489
  var arrays = new InternalArray(1 + arg_count);
490
  arrays[0] = array;
491
  for (var i = 0; i < arg_count; i++) {
492
    arrays[i + 1] = %_Arguments(i);
493
  }
494

    
495
  return %ArrayConcat(arrays);
496
}
497

    
498

    
499
// For implementing reverse() on large, sparse arrays.
500
function SparseReverse(array, len) {
501
  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
502
  var high_counter = keys.length - 1;
503
  var low_counter = 0;
504
  while (low_counter <= high_counter) {
505
    var i = keys[low_counter];
506
    var j = keys[high_counter];
507

    
508
    var j_complement = len - j - 1;
509
    var low, high;
510

    
511
    if (j_complement <= i) {
512
      high = j;
513
      while (keys[--high_counter] == j) { }
514
      low = j_complement;
515
    }
516
    if (j_complement >= i) {
517
      low = i;
518
      while (keys[++low_counter] == i) { }
519
      high = len - i - 1;
520
    }
521

    
522
    var current_i = array[low];
523
    if (!IS_UNDEFINED(current_i) || low in array) {
524
      var current_j = array[high];
525
      if (!IS_UNDEFINED(current_j) || high in array) {
526
        array[low] = current_j;
527
        array[high] = current_i;
528
      } else {
529
        array[high] = current_i;
530
        delete array[low];
531
      }
532
    } else {
533
      var current_j = array[high];
534
      if (!IS_UNDEFINED(current_j) || high in array) {
535
        array[low] = current_j;
536
        delete array[high];
537
      }
538
    }
539
  }
540
}
541

    
542

    
543
function ArrayReverse() {
544
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
545
    throw MakeTypeError("called_on_null_or_undefined",
546
                        ["Array.prototype.reverse"]);
547
  }
548

    
549
  var j = TO_UINT32(this.length) - 1;
550

    
551
  if (UseSparseVariant(this, j, IS_ARRAY(this))) {
552
    SparseReverse(this, j+1);
553
    return this;
554
  }
555

    
556
  for (var i = 0; i < j; i++, j--) {
557
    var current_i = this[i];
558
    if (!IS_UNDEFINED(current_i) || i in this) {
559
      var current_j = this[j];
560
      if (!IS_UNDEFINED(current_j) || j in this) {
561
        this[i] = current_j;
562
        this[j] = current_i;
563
      } else {
564
        this[j] = current_i;
565
        delete this[i];
566
      }
567
    } else {
568
      var current_j = this[j];
569
      if (!IS_UNDEFINED(current_j) || j in this) {
570
        this[i] = current_j;
571
        delete this[j];
572
      }
573
    }
574
  }
575
  return this;
576
}
577

    
578

    
579
function ObservedArrayShift(len) {
580
  var first = this[0];
581

    
582
  try {
583
    BeginPerformSplice(this);
584
    SimpleMove(this, 0, 1, len, 0);
585
    this.length = len - 1;
586
  } finally {
587
    EndPerformSplice(this);
588
    EnqueueSpliceRecord(this, 0, [first], 0);
589
  }
590

    
591
  return first;
592
}
593

    
594
function ArrayShift() {
595
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
596
    throw MakeTypeError("called_on_null_or_undefined",
597
                        ["Array.prototype.shift"]);
598
  }
599

    
600
  var len = TO_UINT32(this.length);
601

    
602
  if (len === 0) {
603
    this.length = 0;
604
    return;
605
  }
606

    
607
  if (%IsObserved(this))
608
    return ObservedArrayShift.call(this, len);
609

    
610
  var first = this[0];
611

    
612
  if (IS_ARRAY(this)) {
613
    SmartMove(this, 0, 1, len, 0);
614
  } else {
615
    SimpleMove(this, 0, 1, len, 0);
616
  }
617

    
618
  this.length = len - 1;
619

    
620
  return first;
621
}
622

    
623
function ObservedArrayUnshift() {
624
  var len = TO_UINT32(this.length);
625
  var num_arguments = %_ArgumentsLength();
626

    
627
  try {
628
    BeginPerformSplice(this);
629
    SimpleMove(this, 0, 0, len, num_arguments);
630
    for (var i = 0; i < num_arguments; i++) {
631
      this[i] = %_Arguments(i);
632
    }
633
    this.length = len + num_arguments;
634
  } finally {
635
    EndPerformSplice(this);
636
    EnqueueSpliceRecord(this, 0, [], num_arguments);
637
  }
638

    
639
  return len + num_arguments;
640
}
641

    
642
function ArrayUnshift(arg1) {  // length == 1
643
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
644
    throw MakeTypeError("called_on_null_or_undefined",
645
                        ["Array.prototype.unshift"]);
646
  }
647

    
648
  if (%IsObserved(this))
649
    return ObservedArrayUnshift.apply(this, arguments);
650

    
651
  var len = TO_UINT32(this.length);
652
  var num_arguments = %_ArgumentsLength();
653

    
654
  if (IS_ARRAY(this)) {
655
    SmartMove(this, 0, 0, len, num_arguments);
656
  } else {
657
    SimpleMove(this, 0, 0, len, num_arguments);
658
  }
659

    
660
  for (var i = 0; i < num_arguments; i++) {
661
    this[i] = %_Arguments(i);
662
  }
663

    
664
  this.length = len + num_arguments;
665

    
666
  return len + num_arguments;
667
}
668

    
669

    
670
function ArraySlice(start, end) {
671
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
672
    throw MakeTypeError("called_on_null_or_undefined",
673
                        ["Array.prototype.slice"]);
674
  }
675

    
676
  var len = TO_UINT32(this.length);
677
  var start_i = TO_INTEGER(start);
678
  var end_i = len;
679

    
680
  if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
681

    
682
  if (start_i < 0) {
683
    start_i += len;
684
    if (start_i < 0) start_i = 0;
685
  } else {
686
    if (start_i > len) start_i = len;
687
  }
688

    
689
  if (end_i < 0) {
690
    end_i += len;
691
    if (end_i < 0) end_i = 0;
692
  } else {
693
    if (end_i > len) end_i = len;
694
  }
695

    
696
  var result = [];
697

    
698
  if (end_i < start_i) return result;
699

    
700
  if (IS_ARRAY(this) &&
701
      !%IsObserved(this) &&
702
      (end_i > 1000) &&
703
      (%EstimateNumberOfElements(this) < end_i)) {
704
    SmartSlice(this, start_i, end_i - start_i, len, result);
705
  } else {
706
    SimpleSlice(this, start_i, end_i - start_i, len, result);
707
  }
708

    
709
  result.length = end_i - start_i;
710

    
711
  return result;
712
}
713

    
714

    
715
function ComputeSpliceStartIndex(start_i, len) {
716
  if (start_i < 0) {
717
    start_i += len;
718
    return start_i < 0 ? 0 : start_i;
719
  }
720

    
721
  return start_i > len ? len : start_i;
722
}
723

    
724

    
725
function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
726
  // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
727
  // given as a request to delete all the elements from the start.
728
  // And it differs from the case of undefined delete count.
729
  // This does not follow ECMA-262, but we do the same for
730
  // compatibility.
731
  var del_count = 0;
732
  if (num_arguments == 1)
733
    return len - start_i;
734

    
735
  del_count = TO_INTEGER(delete_count);
736
  if (del_count < 0)
737
    return 0;
738

    
739
  if (del_count > len - start_i)
740
    return len - start_i;
741

    
742
  return del_count;
743
}
744

    
745

    
746
function ObservedArraySplice(start, delete_count) {
747
  var num_arguments = %_ArgumentsLength();
748
  var len = TO_UINT32(this.length);
749
  var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
750
  var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
751
                                           start_i);
752
  var deleted_elements = [];
753
  deleted_elements.length = del_count;
754
  var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
755

    
756
  try {
757
    BeginPerformSplice(this);
758

    
759
    SimpleSlice(this, start_i, del_count, len, deleted_elements);
760
    SimpleMove(this, start_i, del_count, len, num_elements_to_add);
761

    
762
    // Insert the arguments into the resulting array in
763
    // place of the deleted elements.
764
    var i = start_i;
765
    var arguments_index = 2;
766
    var arguments_length = %_ArgumentsLength();
767
    while (arguments_index < arguments_length) {
768
      this[i++] = %_Arguments(arguments_index++);
769
    }
770
    this.length = len - del_count + num_elements_to_add;
771

    
772
  } finally {
773
    EndPerformSplice(this);
774
    if (deleted_elements.length || num_elements_to_add) {
775
       EnqueueSpliceRecord(this,
776
                           start_i,
777
                           deleted_elements.slice(),
778
                           num_elements_to_add);
779
    }
780
  }
781

    
782
  // Return the deleted elements.
783
  return deleted_elements;
784
}
785

    
786

    
787
function ArraySplice(start, delete_count) {
788
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
789
    throw MakeTypeError("called_on_null_or_undefined",
790
                        ["Array.prototype.splice"]);
791
  }
792

    
793
  if (%IsObserved(this))
794
    return ObservedArraySplice.apply(this, arguments);
795

    
796
  var num_arguments = %_ArgumentsLength();
797
  var len = TO_UINT32(this.length);
798
  var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
799
  var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
800
                                           start_i);
801
  var deleted_elements = [];
802
  deleted_elements.length = del_count;
803
  var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
804

    
805
  var use_simple_splice = true;
806
  if (IS_ARRAY(this) &&
807
      num_elements_to_add !== del_count) {
808
    // If we are only deleting/moving a few things near the end of the
809
    // array then the simple version is going to be faster, because it
810
    // doesn't touch most of the array.
811
    var estimated_non_hole_elements = %EstimateNumberOfElements(this);
812
    if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
813
      use_simple_splice = false;
814
    }
815
  }
816

    
817
  if (use_simple_splice) {
818
    SimpleSlice(this, start_i, del_count, len, deleted_elements);
819
    SimpleMove(this, start_i, del_count, len, num_elements_to_add);
820
  } else {
821
    SmartSlice(this, start_i, del_count, len, deleted_elements);
822
    SmartMove(this, start_i, del_count, len, num_elements_to_add);
823
  }
824

    
825
  // Insert the arguments into the resulting array in
826
  // place of the deleted elements.
827
  var i = start_i;
828
  var arguments_index = 2;
829
  var arguments_length = %_ArgumentsLength();
830
  while (arguments_index < arguments_length) {
831
    this[i++] = %_Arguments(arguments_index++);
832
  }
833
  this.length = len - del_count + num_elements_to_add;
834

    
835
  // Return the deleted elements.
836
  return deleted_elements;
837
}
838

    
839

    
840
function ArraySort(comparefn) {
841
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
842
    throw MakeTypeError("called_on_null_or_undefined",
843
                        ["Array.prototype.sort"]);
844
  }
845

    
846
  // In-place QuickSort algorithm.
847
  // For short (length <= 22) arrays, insertion sort is used for efficiency.
848

    
849
  if (!IS_SPEC_FUNCTION(comparefn)) {
850
    comparefn = function (x, y) {
851
      if (x === y) return 0;
852
      if (%_IsSmi(x) && %_IsSmi(y)) {
853
        return %SmiLexicographicCompare(x, y);
854
      }
855
      x = ToString(x);
856
      y = ToString(y);
857
      if (x == y) return 0;
858
      else return x < y ? -1 : 1;
859
    };
860
  }
861
  var receiver = %GetDefaultReceiver(comparefn);
862

    
863
  var InsertionSort = function InsertionSort(a, from, to) {
864
    for (var i = from + 1; i < to; i++) {
865
      var element = a[i];
866
      for (var j = i - 1; j >= from; j--) {
867
        var tmp = a[j];
868
        var order = %_CallFunction(receiver, tmp, element, comparefn);
869
        if (order > 0) {
870
          a[j + 1] = tmp;
871
        } else {
872
          break;
873
        }
874
      }
875
      a[j + 1] = element;
876
    }
877
  };
878

    
879
  var GetThirdIndex = function(a, from, to) {
880
    var t_array = [];
881
    // Use both 'from' and 'to' to determine the pivot candidates.
882
    var increment = 200 + ((to - from) & 15);
883
    for (var i = from + 1; i < to - 1; i += increment) {
884
      t_array.push([i, a[i]]);
885
    }
886
    t_array.sort(function(a, b) {
887
        return %_CallFunction(receiver, a[1], b[1], comparefn) } );
888
    var third_index = t_array[t_array.length >> 1][0];
889
    return third_index;
890
  }
891

    
892
  var QuickSort = function QuickSort(a, from, to) {
893
    var third_index = 0;
894
    while (true) {
895
      // Insertion sort is faster for short arrays.
896
      if (to - from <= 10) {
897
        InsertionSort(a, from, to);
898
        return;
899
      }
900
      if (to - from > 1000) {
901
        third_index = GetThirdIndex(a, from, to);
902
      } else {
903
        third_index = from + ((to - from) >> 1);
904
      }
905
      // Find a pivot as the median of first, last and middle element.
906
      var v0 = a[from];
907
      var v1 = a[to - 1];
908
      var v2 = a[third_index];
909
      var c01 = %_CallFunction(receiver, v0, v1, comparefn);
910
      if (c01 > 0) {
911
        // v1 < v0, so swap them.
912
        var tmp = v0;
913
        v0 = v1;
914
        v1 = tmp;
915
      } // v0 <= v1.
916
      var c02 = %_CallFunction(receiver, v0, v2, comparefn);
917
      if (c02 >= 0) {
918
        // v2 <= v0 <= v1.
919
        var tmp = v0;
920
        v0 = v2;
921
        v2 = v1;
922
        v1 = tmp;
923
      } else {
924
        // v0 <= v1 && v0 < v2
925
        var c12 = %_CallFunction(receiver, v1, v2, comparefn);
926
        if (c12 > 0) {
927
          // v0 <= v2 < v1
928
          var tmp = v1;
929
          v1 = v2;
930
          v2 = tmp;
931
        }
932
      }
933
      // v0 <= v1 <= v2
934
      a[from] = v0;
935
      a[to - 1] = v2;
936
      var pivot = v1;
937
      var low_end = from + 1;   // Upper bound of elements lower than pivot.
938
      var high_start = to - 1;  // Lower bound of elements greater than pivot.
939
      a[third_index] = a[low_end];
940
      a[low_end] = pivot;
941

    
942
      // From low_end to i are elements equal to pivot.
943
      // From i to high_start are elements that haven't been compared yet.
944
      partition: for (var i = low_end + 1; i < high_start; i++) {
945
        var element = a[i];
946
        var order = %_CallFunction(receiver, element, pivot, comparefn);
947
        if (order < 0) {
948
          a[i] = a[low_end];
949
          a[low_end] = element;
950
          low_end++;
951
        } else if (order > 0) {
952
          do {
953
            high_start--;
954
            if (high_start == i) break partition;
955
            var top_elem = a[high_start];
956
            order = %_CallFunction(receiver, top_elem, pivot, comparefn);
957
          } while (order > 0);
958
          a[i] = a[high_start];
959
          a[high_start] = element;
960
          if (order < 0) {
961
            element = a[i];
962
            a[i] = a[low_end];
963
            a[low_end] = element;
964
            low_end++;
965
          }
966
        }
967
      }
968
      if (to - high_start < low_end - from) {
969
        QuickSort(a, high_start, to);
970
        to = low_end;
971
      } else {
972
        QuickSort(a, from, low_end);
973
        from = high_start;
974
      }
975
    }
976
  };
977

    
978
  // Copy elements in the range 0..length from obj's prototype chain
979
  // to obj itself, if obj has holes. Return one more than the maximal index
980
  // of a prototype property.
981
  var CopyFromPrototype = function CopyFromPrototype(obj, length) {
982
    var max = 0;
983
    for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
984
      var indices = %GetArrayKeys(proto, length);
985
      if (IS_NUMBER(indices)) {
986
        // It's an interval.
987
        var proto_length = indices;
988
        for (var i = 0; i < proto_length; i++) {
989
          if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
990
            obj[i] = proto[i];
991
            if (i >= max) { max = i + 1; }
992
          }
993
        }
994
      } else {
995
        for (var i = 0; i < indices.length; i++) {
996
          var index = indices[i];
997
          if (!IS_UNDEFINED(index) &&
998
              !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
999
            obj[index] = proto[index];
1000
            if (index >= max) { max = index + 1; }
1001
          }
1002
        }
1003
      }
1004
    }
1005
    return max;
1006
  };
1007

    
1008
  // Set a value of "undefined" on all indices in the range from..to
1009
  // where a prototype of obj has an element. I.e., shadow all prototype
1010
  // elements in that range.
1011
  var ShadowPrototypeElements = function(obj, from, to) {
1012
    for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
1013
      var indices = %GetArrayKeys(proto, to);
1014
      if (IS_NUMBER(indices)) {
1015
        // It's an interval.
1016
        var proto_length = indices;
1017
        for (var i = from; i < proto_length; i++) {
1018
          if (proto.hasOwnProperty(i)) {
1019
            obj[i] = UNDEFINED;
1020
          }
1021
        }
1022
      } else {
1023
        for (var i = 0; i < indices.length; i++) {
1024
          var index = indices[i];
1025
          if (!IS_UNDEFINED(index) && from <= index &&
1026
              proto.hasOwnProperty(index)) {
1027
            obj[index] = UNDEFINED;
1028
          }
1029
        }
1030
      }
1031
    }
1032
  };
1033

    
1034
  var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
1035
    // Copy defined elements from the end to fill in all holes and undefineds
1036
    // in the beginning of the array.  Write undefineds and holes at the end
1037
    // after loop is finished.
1038
    var first_undefined = 0;
1039
    var last_defined = length - 1;
1040
    var num_holes = 0;
1041
    while (first_undefined < last_defined) {
1042
      // Find first undefined element.
1043
      while (first_undefined < last_defined &&
1044
             !IS_UNDEFINED(obj[first_undefined])) {
1045
        first_undefined++;
1046
      }
1047
      // Maintain the invariant num_holes = the number of holes in the original
1048
      // array with indices <= first_undefined or > last_defined.
1049
      if (!obj.hasOwnProperty(first_undefined)) {
1050
        num_holes++;
1051
      }
1052

    
1053
      // Find last defined element.
1054
      while (first_undefined < last_defined &&
1055
             IS_UNDEFINED(obj[last_defined])) {
1056
        if (!obj.hasOwnProperty(last_defined)) {
1057
          num_holes++;
1058
        }
1059
        last_defined--;
1060
      }
1061
      if (first_undefined < last_defined) {
1062
        // Fill in hole or undefined.
1063
        obj[first_undefined] = obj[last_defined];
1064
        obj[last_defined] = UNDEFINED;
1065
      }
1066
    }
1067
    // If there were any undefineds in the entire array, first_undefined
1068
    // points to one past the last defined element.  Make this true if
1069
    // there were no undefineds, as well, so that first_undefined == number
1070
    // of defined elements.
1071
    if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
1072
    // Fill in the undefineds and the holes.  There may be a hole where
1073
    // an undefined should be and vice versa.
1074
    var i;
1075
    for (i = first_undefined; i < length - num_holes; i++) {
1076
      obj[i] = UNDEFINED;
1077
    }
1078
    for (i = length - num_holes; i < length; i++) {
1079
      // For compatability with Webkit, do not expose elements in the prototype.
1080
      if (i in %GetPrototype(obj)) {
1081
        obj[i] = UNDEFINED;
1082
      } else {
1083
        delete obj[i];
1084
      }
1085
    }
1086

    
1087
    // Return the number of defined elements.
1088
    return first_undefined;
1089
  };
1090

    
1091
  var length = TO_UINT32(this.length);
1092
  if (length < 2) return this;
1093

    
1094
  var is_array = IS_ARRAY(this);
1095
  var max_prototype_element;
1096
  if (!is_array) {
1097
    // For compatibility with JSC, we also sort elements inherited from
1098
    // the prototype chain on non-Array objects.
1099
    // We do this by copying them to this object and sorting only
1100
    // local elements. This is not very efficient, but sorting with
1101
    // inherited elements happens very, very rarely, if at all.
1102
    // The specification allows "implementation dependent" behavior
1103
    // if an element on the prototype chain has an element that
1104
    // might interact with sorting.
1105
    max_prototype_element = CopyFromPrototype(this, length);
1106
  }
1107

    
1108
  var num_non_undefined = %IsObserved(this) ?
1109
      -1 : %RemoveArrayHoles(this, length);
1110

    
1111
  if (num_non_undefined == -1) {
1112
    // The array is observed, or there were indexed accessors in the array.
1113
    // Move array holes and undefineds to the end using a Javascript function
1114
    // that is safe in the presence of accessors and is observable.
1115
    num_non_undefined = SafeRemoveArrayHoles(this);
1116
  }
1117

    
1118
  QuickSort(this, 0, num_non_undefined);
1119

    
1120
  if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
1121
    // For compatibility with JSC, we shadow any elements in the prototype
1122
    // chain that has become exposed by sort moving a hole to its position.
1123
    ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
1124
  }
1125

    
1126
  return this;
1127
}
1128

    
1129

    
1130
// The following functions cannot be made efficient on sparse arrays while
1131
// preserving the semantics, since the calls to the receiver function can add
1132
// or delete elements from the array.
1133
function ArrayFilter(f, receiver) {
1134
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1135
    throw MakeTypeError("called_on_null_or_undefined",
1136
                        ["Array.prototype.filter"]);
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 = ToUint32(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) && %IsClassicModeFunction(f)) {
1150
    receiver = ToObject(receiver);
1151
  }
1152

    
1153
  var result = new $Array();
1154
  var accumulator = new InternalArray();
1155
  var accumulator_length = 0;
1156
  if (%DebugCallbackSupportsStepping(f)) {
1157
    for (var i = 0; i < length; i++) {
1158
      if (i in array) {
1159
        var element = array[i];
1160
        // Prepare break slots for debugger step in.
1161
        %DebugPrepareStepInIfStepping(f);
1162
        if (%_CallFunction(receiver, element, i, array, f)) {
1163
          accumulator[accumulator_length++] = element;
1164
        }
1165
      }
1166
    }
1167
  } else {
1168
    // This is a duplicate of the previous loop sans debug stepping.
1169
    for (var i = 0; i < length; i++) {
1170
      if (i in array) {
1171
        var element = array[i];
1172
        if (%_CallFunction(receiver, element, i, array, f)) {
1173
          accumulator[accumulator_length++] = element;
1174
        }
1175
      }
1176
    }
1177
    // End of duplicate.
1178
  }
1179
  %MoveArrayContents(accumulator, result);
1180
  return result;
1181
}
1182

    
1183

    
1184
function ArrayForEach(f, receiver) {
1185
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1186
    throw MakeTypeError("called_on_null_or_undefined",
1187
                        ["Array.prototype.forEach"]);
1188
  }
1189

    
1190
  // Pull out the length so that modifications to the length in the
1191
  // loop will not affect the looping and side effects are visible.
1192
  var array = ToObject(this);
1193
  var length = TO_UINT32(array.length);
1194

    
1195
  if (!IS_SPEC_FUNCTION(f)) {
1196
    throw MakeTypeError('called_non_callable', [ f ]);
1197
  }
1198
  if (IS_NULL_OR_UNDEFINED(receiver)) {
1199
    receiver = %GetDefaultReceiver(f) || receiver;
1200
  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
1201
    receiver = ToObject(receiver);
1202
  }
1203

    
1204
  if (%DebugCallbackSupportsStepping(f)) {
1205
    for (var i = 0; i < length; i++) {
1206
      if (i in array) {
1207
        var element = array[i];
1208
        // Prepare break slots for debugger step in.
1209
        %DebugPrepareStepInIfStepping(f);
1210
        %_CallFunction(receiver, element, i, array, f);
1211
      }
1212
    }
1213
  } else {
1214
    // This is a duplicate of the previous loop sans debug stepping.
1215
    for (var i = 0; i < length; i++) {
1216
      if (i in array) {
1217
        var element = array[i];
1218
        %_CallFunction(receiver, element, i, array, f);
1219
      }
1220
    }
1221
    // End of duplicate.
1222
  }
1223
}
1224

    
1225

    
1226
// Executes the function once for each element present in the
1227
// array until it finds one where callback returns true.
1228
function ArraySome(f, receiver) {
1229
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1230
    throw MakeTypeError("called_on_null_or_undefined",
1231
                        ["Array.prototype.some"]);
1232
  }
1233

    
1234
  // Pull out the length so that modifications to the length in the
1235
  // loop will not affect the looping and side effects are visible.
1236
  var array = ToObject(this);
1237
  var length = TO_UINT32(array.length);
1238

    
1239
  if (!IS_SPEC_FUNCTION(f)) {
1240
    throw MakeTypeError('called_non_callable', [ f ]);
1241
  }
1242
  if (IS_NULL_OR_UNDEFINED(receiver)) {
1243
    receiver = %GetDefaultReceiver(f) || receiver;
1244
  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
1245
    receiver = ToObject(receiver);
1246
  }
1247

    
1248
  if (%DebugCallbackSupportsStepping(f)) {
1249
    for (var i = 0; i < length; i++) {
1250
      if (i in array) {
1251
        var element = array[i];
1252
        // Prepare break slots for debugger step in.
1253
        %DebugPrepareStepInIfStepping(f);
1254
        if (%_CallFunction(receiver, element, i, array, f)) return true;
1255
      }
1256
    }
1257
  } else {
1258
    // This is a duplicate of the previous loop sans debug stepping.
1259
    for (var i = 0; i < length; i++) {
1260
      if (i in array) {
1261
        var element = array[i];
1262
        if (%_CallFunction(receiver, element, i, array, f)) return true;
1263
      }
1264
    }
1265
    // End of duplicate.
1266
  }
1267
  return false;
1268
}
1269

    
1270

    
1271
function ArrayEvery(f, receiver) {
1272
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1273
    throw MakeTypeError("called_on_null_or_undefined",
1274
                        ["Array.prototype.every"]);
1275
  }
1276

    
1277
  // Pull out the length so that modifications to the length in the
1278
  // loop will not affect the looping and side effects are visible.
1279
  var array = ToObject(this);
1280
  var length = TO_UINT32(array.length);
1281

    
1282
  if (!IS_SPEC_FUNCTION(f)) {
1283
    throw MakeTypeError('called_non_callable', [ f ]);
1284
  }
1285
  if (IS_NULL_OR_UNDEFINED(receiver)) {
1286
    receiver = %GetDefaultReceiver(f) || receiver;
1287
  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
1288
    receiver = ToObject(receiver);
1289
  }
1290

    
1291
  if (%DebugCallbackSupportsStepping(f)) {
1292
    for (var i = 0; i < length; i++) {
1293
      if (i in array) {
1294
        var element = array[i];
1295
        // Prepare break slots for debugger step in.
1296
        %DebugPrepareStepInIfStepping(f);
1297
        if (!%_CallFunction(receiver, element, i, array, f)) return false;
1298
      }
1299
    }
1300
  } else {
1301
    // This is a duplicate of the previous loop sans debug stepping.
1302
    for (var i = 0; i < length; i++) {
1303
      if (i in array) {
1304
        var element = array[i];
1305
        if (!%_CallFunction(receiver, element, i, array, f)) return false;
1306
      }
1307
    }
1308
    // End of duplicate.
1309
  }
1310
  return true;
1311
}
1312

    
1313
function ArrayMap(f, receiver) {
1314
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1315
    throw MakeTypeError("called_on_null_or_undefined",
1316
                        ["Array.prototype.map"]);
1317
  }
1318

    
1319
  // Pull out the length so that modifications to the length in the
1320
  // loop will not affect the looping and side effects are visible.
1321
  var array = ToObject(this);
1322
  var length = TO_UINT32(array.length);
1323

    
1324
  if (!IS_SPEC_FUNCTION(f)) {
1325
    throw MakeTypeError('called_non_callable', [ f ]);
1326
  }
1327
  if (IS_NULL_OR_UNDEFINED(receiver)) {
1328
    receiver = %GetDefaultReceiver(f) || receiver;
1329
  } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(f)) {
1330
    receiver = ToObject(receiver);
1331
  }
1332

    
1333
  var result = new $Array();
1334
  var accumulator = new InternalArray(length);
1335
  if (%DebugCallbackSupportsStepping(f)) {
1336
    for (var i = 0; i < length; i++) {
1337
      if (i in array) {
1338
        var element = array[i];
1339
        // Prepare break slots for debugger step in.
1340
        %DebugPrepareStepInIfStepping(f);
1341
        accumulator[i] = %_CallFunction(receiver, element, i, array, f);
1342
      }
1343
    }
1344
  } else {
1345
    // This is a duplicate of the previous loop sans debug stepping.
1346
    for (var i = 0; i < length; i++) {
1347
      if (i in array) {
1348
        var element = array[i];
1349
        accumulator[i] = %_CallFunction(receiver, element, i, array, f);
1350
      }
1351
    }
1352
    // End of duplicate.
1353
  }
1354
  %MoveArrayContents(accumulator, result);
1355
  return result;
1356
}
1357

    
1358

    
1359
function ArrayIndexOf(element, index) {
1360
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1361
    throw MakeTypeError("called_on_null_or_undefined",
1362
                        ["Array.prototype.indexOf"]);
1363
  }
1364

    
1365
  var length = TO_UINT32(this.length);
1366
  if (length == 0) return -1;
1367
  if (IS_UNDEFINED(index)) {
1368
    index = 0;
1369
  } else {
1370
    index = TO_INTEGER(index);
1371
    // If index is negative, index from the end of the array.
1372
    if (index < 0) {
1373
      index = length + index;
1374
      // If index is still negative, search the entire array.
1375
      if (index < 0) index = 0;
1376
    }
1377
  }
1378
  var min = index;
1379
  var max = length;
1380
  if (UseSparseVariant(this, length, IS_ARRAY(this))) {
1381
    var indices = %GetArrayKeys(this, length);
1382
    if (IS_NUMBER(indices)) {
1383
      // It's an interval.
1384
      max = indices;  // Capped by length already.
1385
      // Fall through to loop below.
1386
    } else {
1387
      if (indices.length == 0) return -1;
1388
      // Get all the keys in sorted order.
1389
      var sortedKeys = GetSortedArrayKeys(this, indices);
1390
      var n = sortedKeys.length;
1391
      var i = 0;
1392
      while (i < n && sortedKeys[i] < index) i++;
1393
      while (i < n) {
1394
        var key = sortedKeys[i];
1395
        if (!IS_UNDEFINED(key) && this[key] === element) return key;
1396
        i++;
1397
      }
1398
      return -1;
1399
    }
1400
  }
1401
  // Lookup through the array.
1402
  if (!IS_UNDEFINED(element)) {
1403
    for (var i = min; i < max; i++) {
1404
      if (this[i] === element) return i;
1405
    }
1406
    return -1;
1407
  }
1408
  // Lookup through the array.
1409
  for (var i = min; i < max; i++) {
1410
    if (IS_UNDEFINED(this[i]) && i in this) {
1411
      return i;
1412
    }
1413
  }
1414
  return -1;
1415
}
1416

    
1417

    
1418
function ArrayLastIndexOf(element, index) {
1419
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1420
    throw MakeTypeError("called_on_null_or_undefined",
1421
                        ["Array.prototype.lastIndexOf"]);
1422
  }
1423

    
1424
  var length = TO_UINT32(this.length);
1425
  if (length == 0) return -1;
1426
  if (%_ArgumentsLength() < 2) {
1427
    index = length - 1;
1428
  } else {
1429
    index = TO_INTEGER(index);
1430
    // If index is negative, index from end of the array.
1431
    if (index < 0) index += length;
1432
    // If index is still negative, do not search the array.
1433
    if (index < 0) return -1;
1434
    else if (index >= length) index = length - 1;
1435
  }
1436
  var min = 0;
1437
  var max = index;
1438
  if (UseSparseVariant(this, length, IS_ARRAY(this))) {
1439
    var indices = %GetArrayKeys(this, index + 1);
1440
    if (IS_NUMBER(indices)) {
1441
      // It's an interval.
1442
      max = indices;  // Capped by index already.
1443
      // Fall through to loop below.
1444
    } else {
1445
      if (indices.length == 0) return -1;
1446
      // Get all the keys in sorted order.
1447
      var sortedKeys = GetSortedArrayKeys(this, indices);
1448
      var i = sortedKeys.length - 1;
1449
      while (i >= 0) {
1450
        var key = sortedKeys[i];
1451
        if (!IS_UNDEFINED(key) && this[key] === element) return key;
1452
        i--;
1453
      }
1454
      return -1;
1455
    }
1456
  }
1457
  // Lookup through the array.
1458
  if (!IS_UNDEFINED(element)) {
1459
    for (var i = max; i >= min; i--) {
1460
      if (this[i] === element) return i;
1461
    }
1462
    return -1;
1463
  }
1464
  for (var i = max; i >= min; i--) {
1465
    if (IS_UNDEFINED(this[i]) && i in this) {
1466
      return i;
1467
    }
1468
  }
1469
  return -1;
1470
}
1471

    
1472

    
1473
function ArrayReduce(callback, current) {
1474
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1475
    throw MakeTypeError("called_on_null_or_undefined",
1476
                        ["Array.prototype.reduce"]);
1477
  }
1478

    
1479
  // Pull out the length so that modifications to the length in the
1480
  // loop will not affect the looping and side effects are visible.
1481
  var array = ToObject(this);
1482
  var length = ToUint32(array.length);
1483

    
1484
  if (!IS_SPEC_FUNCTION(callback)) {
1485
    throw MakeTypeError('called_non_callable', [callback]);
1486
  }
1487

    
1488
  var i = 0;
1489
  find_initial: if (%_ArgumentsLength() < 2) {
1490
    for (; i < length; i++) {
1491
      current = array[i];
1492
      if (!IS_UNDEFINED(current) || i in array) {
1493
        i++;
1494
        break find_initial;
1495
      }
1496
    }
1497
    throw MakeTypeError('reduce_no_initial', []);
1498
  }
1499

    
1500
  var receiver = %GetDefaultReceiver(callback);
1501

    
1502
  if (%DebugCallbackSupportsStepping(callback)) {
1503
    for (; i < length; i++) {
1504
      if (i in array) {
1505
        var element = array[i];
1506
        // Prepare break slots for debugger step in.
1507
        %DebugPrepareStepInIfStepping(callback);
1508
        current =
1509
          %_CallFunction(receiver, current, element, i, array, callback);
1510
      }
1511
    }
1512
  } else {
1513
    // This is a duplicate of the previous loop sans debug stepping.
1514
    for (; i < length; i++) {
1515
      if (i in array) {
1516
        var element = array[i];
1517
        current =
1518
          %_CallFunction(receiver, current, element, i, array, callback);
1519
      }
1520
    }
1521
    // End of duplicate.
1522
  }
1523
  return current;
1524
}
1525

    
1526
function ArrayReduceRight(callback, current) {
1527
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1528
    throw MakeTypeError("called_on_null_or_undefined",
1529
                        ["Array.prototype.reduceRight"]);
1530
  }
1531

    
1532
  // Pull out the length so that side effects are visible before the
1533
  // callback function is checked.
1534
  var array = ToObject(this);
1535
  var length = ToUint32(array.length);
1536

    
1537
  if (!IS_SPEC_FUNCTION(callback)) {
1538
    throw MakeTypeError('called_non_callable', [callback]);
1539
  }
1540

    
1541
  var i = length - 1;
1542
  find_initial: if (%_ArgumentsLength() < 2) {
1543
    for (; i >= 0; i--) {
1544
      current = array[i];
1545
      if (!IS_UNDEFINED(current) || i in array) {
1546
        i--;
1547
        break find_initial;
1548
      }
1549
    }
1550
    throw MakeTypeError('reduce_no_initial', []);
1551
  }
1552

    
1553
  var receiver = %GetDefaultReceiver(callback);
1554

    
1555
  if (%DebugCallbackSupportsStepping(callback)) {
1556
    for (; i >= 0; i--) {
1557
      if (i in array) {
1558
        var element = array[i];
1559
        // Prepare break slots for debugger step in.
1560
        %DebugPrepareStepInIfStepping(callback);
1561
        current =
1562
          %_CallFunction(receiver, current, element, i, array, callback);
1563
      }
1564
    }
1565
  } else {
1566
    // This is a duplicate of the previous loop sans debug stepping.
1567
    for (; i >= 0; i--) {
1568
      if (i in array) {
1569
        var element = array[i];
1570
        current =
1571
          %_CallFunction(receiver, current, element, i, array, callback);
1572
      }
1573
    }
1574
    // End of duplicate.
1575
  }
1576
  return current;
1577
}
1578

    
1579
// ES5, 15.4.3.2
1580
function ArrayIsArray(obj) {
1581
  return IS_ARRAY(obj);
1582
}
1583

    
1584

    
1585
// -------------------------------------------------------------------
1586

    
1587
function SetUpArray() {
1588
  %CheckIsBootstrapping();
1589

    
1590
  // Set up non-enumerable constructor property on the Array.prototype
1591
  // object.
1592
  %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1593

    
1594
  // Set up non-enumerable functions on the Array object.
1595
  InstallFunctions($Array, DONT_ENUM, $Array(
1596
    "isArray", ArrayIsArray
1597
  ));
1598

    
1599
  var specialFunctions = %SpecialArrayFunctions({});
1600

    
1601
  var getFunction = function(name, jsBuiltin, len) {
1602
    var f = jsBuiltin;
1603
    if (specialFunctions.hasOwnProperty(name)) {
1604
      f = specialFunctions[name];
1605
    }
1606
    if (!IS_UNDEFINED(len)) {
1607
      %FunctionSetLength(f, len);
1608
    }
1609
    return f;
1610
  };
1611

    
1612
  // Set up non-enumerable functions of the Array.prototype object and
1613
  // set their names.
1614
  // Manipulate the length of some of the functions to meet
1615
  // expectations set by ECMA-262 or Mozilla.
1616
  InstallFunctions($Array.prototype, DONT_ENUM, $Array(
1617
    "toString", getFunction("toString", ArrayToString),
1618
    "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1619
    "join", getFunction("join", ArrayJoin),
1620
    "pop", getFunction("pop", ArrayPop),
1621
    "push", getFunction("push", ArrayPush, 1),
1622
    "concat", getFunction("concat", ArrayConcat, 1),
1623
    "reverse", getFunction("reverse", ArrayReverse),
1624
    "shift", getFunction("shift", ArrayShift),
1625
    "unshift", getFunction("unshift", ArrayUnshift, 1),
1626
    "slice", getFunction("slice", ArraySlice, 2),
1627
    "splice", getFunction("splice", ArraySplice, 2),
1628
    "sort", getFunction("sort", ArraySort),
1629
    "filter", getFunction("filter", ArrayFilter, 1),
1630
    "forEach", getFunction("forEach", ArrayForEach, 1),
1631
    "some", getFunction("some", ArraySome, 1),
1632
    "every", getFunction("every", ArrayEvery, 1),
1633
    "map", getFunction("map", ArrayMap, 1),
1634
    "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1635
    "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1636
    "reduce", getFunction("reduce", ArrayReduce, 1),
1637
    "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1638
  ));
1639

    
1640
  %FinishArrayPrototypeSetup($Array.prototype);
1641

    
1642
  // The internal Array prototype doesn't need to be fancy, since it's never
1643
  // exposed to user code.
1644
  // Adding only the functions that are actually used.
1645
  SetUpLockedPrototype(InternalArray, $Array(), $Array(
1646
    "concat", getFunction("concat", ArrayConcat),
1647
    "indexOf", getFunction("indexOf", ArrayIndexOf),
1648
    "join", getFunction("join", ArrayJoin),
1649
    "pop", getFunction("pop", ArrayPop),
1650
    "push", getFunction("push", ArrayPush),
1651
    "splice", getFunction("splice", ArraySplice)
1652
  ));
1653

    
1654
  SetUpLockedPrototype(InternalPackedArray, $Array(), $Array(
1655
    "join", getFunction("join", ArrayJoin),
1656
    "pop", getFunction("pop", ArrayPop),
1657
    "push", getFunction("push", ArrayPush)
1658
  ));
1659
}
1660

    
1661
SetUpArray();