blob: f9cf161191ac614766f648185dac7510501147d3 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5(function(global, utils, extrasUtils) {
6
7"use strict";
8
9%CheckIsBootstrapping();
10
11// -------------------------------------------------------------------
12// Imports
13
14var AddIndexedProperty;
15var FLAG_harmony_tolength;
16var FLAG_harmony_species;
17var GetIterator;
18var GetMethod;
19var GlobalArray = global.Array;
20var InternalArray = utils.InternalArray;
21var InternalPackedArray = utils.InternalPackedArray;
22var MakeTypeError;
23var MaxSimple;
24var MinSimple;
25var ObjectDefineProperty;
26var ObjectHasOwnProperty;
27var ObjectToString = utils.ImportNow("object_to_string");
28var ObserveBeginPerformSplice;
29var ObserveEndPerformSplice;
30var ObserveEnqueueSpliceRecord;
31var SameValueZero;
32var iteratorSymbol = utils.ImportNow("iterator_symbol");
33var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
34
35utils.Import(function(from) {
36 AddIndexedProperty = from.AddIndexedProperty;
37 GetIterator = from.GetIterator;
38 GetMethod = from.GetMethod;
39 MakeTypeError = from.MakeTypeError;
40 MaxSimple = from.MaxSimple;
41 MinSimple = from.MinSimple;
42 ObjectDefineProperty = from.ObjectDefineProperty;
43 ObjectHasOwnProperty = from.ObjectHasOwnProperty;
44 ObserveBeginPerformSplice = from.ObserveBeginPerformSplice;
45 ObserveEndPerformSplice = from.ObserveEndPerformSplice;
46 ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord;
47 SameValueZero = from.SameValueZero;
48});
49
50utils.ImportFromExperimental(function(from) {
51 FLAG_harmony_tolength = from.FLAG_harmony_tolength;
52 FLAG_harmony_species = from.FLAG_harmony_species;
53});
54
55// -------------------------------------------------------------------
56
57
58function ArraySpeciesCreate(array, length) {
59 var constructor;
60 if (FLAG_harmony_species) {
61 constructor = %ArraySpeciesConstructor(array);
62 } else {
63 constructor = GlobalArray;
64 }
65 return new constructor(length);
66}
67
68
69function DefineIndexedProperty(array, i, value) {
70 if (FLAG_harmony_species) {
71 var result = ObjectDefineProperty(array, i, {
72 value: value, writable: true, configurable: true, enumerable: true
73 });
74 if (!result) throw MakeTypeError(kStrictCannotAssign, i);
75 } else {
76 AddIndexedProperty(array, i, value);
77 }
78}
79
80
81// Global list of arrays visited during toString, toLocaleString and
82// join invocations.
83var visited_arrays = new InternalArray();
84
85
86// Gets a sorted array of array keys. Useful for operations on sparse
87// arrays. Dupes have not been removed.
88function GetSortedArrayKeys(array, indices) {
89 var keys = new InternalArray();
90 if (IS_NUMBER(indices)) {
91 // It's an interval
92 var limit = indices;
93 for (var i = 0; i < limit; ++i) {
94 var e = array[i];
95 if (!IS_UNDEFINED(e) || i in array) {
96 keys.push(i);
97 }
98 }
99 } else {
100 var length = indices.length;
101 for (var k = 0; k < length; ++k) {
102 var key = indices[k];
103 if (!IS_UNDEFINED(key)) {
104 var e = array[key];
105 if (!IS_UNDEFINED(e) || key in array) {
106 keys.push(key);
107 }
108 }
109 }
110 keys.sort(function(a, b) { return a - b; });
111 }
112 return keys;
113}
114
115
116function SparseJoinWithSeparatorJS(array, len, convert, separator) {
117 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
118 var totalLength = 0;
119 var elements = new InternalArray(keys.length * 2);
120 var previousKey = -1;
121 for (var i = 0; i < keys.length; i++) {
122 var key = keys[i];
123 if (key != previousKey) { // keys may contain duplicates.
124 var e = array[key];
125 if (!IS_STRING(e)) e = convert(e);
126 elements[i * 2] = key;
127 elements[i * 2 + 1] = e;
128 previousKey = key;
129 }
130 }
131 return %SparseJoinWithSeparator(elements, len, separator);
132}
133
134
135// Optimized for sparse arrays if separator is ''.
136function SparseJoin(array, len, convert) {
137 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
138 var last_key = -1;
139 var keys_length = keys.length;
140
141 var elements = new InternalArray(keys_length);
142 var elements_length = 0;
143
144 for (var i = 0; i < keys_length; i++) {
145 var key = keys[i];
146 if (key != last_key) {
147 var e = array[key];
148 if (!IS_STRING(e)) e = convert(e);
149 elements[elements_length++] = e;
150 last_key = key;
151 }
152 }
153 return %StringBuilderConcat(elements, elements_length, '');
154}
155
156
157function UseSparseVariant(array, length, is_array, touched) {
158 // Only use the sparse variant on arrays that are likely to be sparse and the
159 // number of elements touched in the operation is relatively small compared to
160 // the overall size of the array.
161 if (!is_array || length < 1000 || %IsObserved(array) ||
162 %HasComplexElements(array)) {
163 return false;
164 }
165 if (!%_IsSmi(length)) {
166 return true;
167 }
168 var elements_threshold = length >> 2; // No more than 75% holes
169 var estimated_elements = %EstimateNumberOfElements(array);
170 return (estimated_elements < elements_threshold) &&
171 (touched > estimated_elements * 4);
172}
173
174
175function Join(array, length, separator, convert) {
176 if (length == 0) return '';
177
178 var is_array = IS_ARRAY(array);
179
180 if (is_array) {
181 // If the array is cyclic, return the empty string for already
182 // visited arrays.
183 if (!%PushIfAbsent(visited_arrays, array)) return '';
184 }
185
186 // Attempt to convert the elements.
187 try {
188 if (UseSparseVariant(array, length, is_array, length)) {
189 %NormalizeElements(array);
190 if (separator.length == 0) {
191 return SparseJoin(array, length, convert);
192 } else {
193 return SparseJoinWithSeparatorJS(array, length, convert, separator);
194 }
195 }
196
197 // Fast case for one-element arrays.
198 if (length == 1) {
199 var e = array[0];
200 if (IS_STRING(e)) return e;
201 return convert(e);
202 }
203
204 // Construct an array for the elements.
205 var elements = new InternalArray(length);
206
207 // We pull the empty separator check outside the loop for speed!
208 if (separator.length == 0) {
209 var elements_length = 0;
210 for (var i = 0; i < length; i++) {
211 var e = array[i];
212 if (!IS_STRING(e)) e = convert(e);
213 elements[elements_length++] = e;
214 }
215 elements.length = elements_length;
216 var result = %_FastOneByteArrayJoin(elements, '');
217 if (!IS_UNDEFINED(result)) return result;
218 return %StringBuilderConcat(elements, elements_length, '');
219 }
220 // Non-empty separator case.
221 // If the first element is a number then use the heuristic that the
222 // remaining elements are also likely to be numbers.
223 if (!IS_NUMBER(array[0])) {
224 for (var i = 0; i < length; i++) {
225 var e = array[i];
226 if (!IS_STRING(e)) e = convert(e);
227 elements[i] = e;
228 }
229 } else {
230 for (var i = 0; i < length; i++) {
231 var e = array[i];
232 if (IS_NUMBER(e)) {
233 e = %_NumberToString(e);
234 } else if (!IS_STRING(e)) {
235 e = convert(e);
236 }
237 elements[i] = e;
238 }
239 }
240 var result = %_FastOneByteArrayJoin(elements, separator);
241 if (!IS_UNDEFINED(result)) return result;
242
243 return %StringBuilderJoin(elements, length, separator);
244 } finally {
245 // Make sure to remove the last element of the visited array no
246 // matter what happens.
247 if (is_array) visited_arrays.length = visited_arrays.length - 1;
248 }
249}
250
251
252function ConvertToString(x) {
253 if (IS_NULL_OR_UNDEFINED(x)) {
254 return '';
255 } else {
256 return TO_STRING(x);
257 }
258}
259
260
261function ConvertToLocaleString(e) {
262 if (IS_NULL_OR_UNDEFINED(e)) {
263 return '';
264 } else {
265 return TO_STRING(e.toLocaleString());
266 }
267}
268
269
270// This function implements the optimized splice implementation that can use
271// special array operations to handle sparse arrays in a sensible fashion.
272function SparseSlice(array, start_i, del_count, len, deleted_elements) {
273 // Move deleted elements to a new array (the return value from splice).
274 var indices = %GetArrayKeys(array, start_i + del_count);
275 if (IS_NUMBER(indices)) {
276 var limit = indices;
277 for (var i = start_i; i < limit; ++i) {
278 var current = array[i];
279 if (!IS_UNDEFINED(current) || i in array) {
280 DefineIndexedProperty(deleted_elements, i - start_i, current);
281 }
282 }
283 } else {
284 var length = indices.length;
285 for (var k = 0; k < length; ++k) {
286 var key = indices[k];
287 if (!IS_UNDEFINED(key)) {
288 if (key >= start_i) {
289 var current = array[key];
290 if (!IS_UNDEFINED(current) || key in array) {
291 DefineIndexedProperty(deleted_elements, key - start_i, current);
292 }
293 }
294 }
295 }
296 }
297}
298
299
300// This function implements the optimized splice implementation that can use
301// special array operations to handle sparse arrays in a sensible fashion.
302function SparseMove(array, start_i, del_count, len, num_additional_args) {
303 // Bail out if no moving is necessary.
304 if (num_additional_args === del_count) return;
305 // Move data to new array.
306 var new_array = new InternalArray(
307 // Clamp array length to 2^32-1 to avoid early RangeError.
308 MinSimple(len - del_count + num_additional_args, 0xffffffff));
309 var big_indices;
310 var indices = %GetArrayKeys(array, len);
311 if (IS_NUMBER(indices)) {
312 var limit = indices;
313 for (var i = 0; i < start_i && i < limit; ++i) {
314 var current = array[i];
315 if (!IS_UNDEFINED(current) || i in array) {
316 new_array[i] = current;
317 }
318 }
319 for (var i = start_i + del_count; i < limit; ++i) {
320 var current = array[i];
321 if (!IS_UNDEFINED(current) || i in array) {
322 new_array[i - del_count + num_additional_args] = current;
323 }
324 }
325 } else {
326 var length = indices.length;
327 for (var k = 0; k < length; ++k) {
328 var key = indices[k];
329 if (!IS_UNDEFINED(key)) {
330 if (key < start_i) {
331 var current = array[key];
332 if (!IS_UNDEFINED(current) || key in array) {
333 new_array[key] = current;
334 }
335 } else if (key >= start_i + del_count) {
336 var current = array[key];
337 if (!IS_UNDEFINED(current) || key in array) {
338 var new_key = key - del_count + num_additional_args;
339 new_array[new_key] = current;
340 if (new_key > 0xfffffffe) {
341 big_indices = big_indices || new InternalArray();
342 big_indices.push(new_key);
343 }
344 }
345 }
346 }
347 }
348 }
349 // Move contents of new_array into this array
350 %MoveArrayContents(new_array, array);
351 // Add any moved values that aren't elements anymore.
352 if (!IS_UNDEFINED(big_indices)) {
353 var length = big_indices.length;
354 for (var i = 0; i < length; ++i) {
355 var key = big_indices[i];
356 array[key] = new_array[key];
357 }
358 }
359}
360
361
362// This is part of the old simple-minded splice. We are using it either
363// because the receiver is not an array (so we have no choice) or because we
364// know we are not deleting or moving a lot of elements.
365function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
366 var is_array = IS_ARRAY(array);
367 for (var i = 0; i < del_count; i++) {
368 var index = start_i + i;
369 if (HAS_INDEX(array, index, is_array)) {
370 var current = array[index];
371 DefineIndexedProperty(deleted_elements, i, current);
372 }
373 }
374}
375
376
377function SimpleMove(array, start_i, del_count, len, num_additional_args) {
378 var is_array = IS_ARRAY(array);
379 if (num_additional_args !== del_count) {
380 // Move the existing elements after the elements to be deleted
381 // to the right position in the resulting array.
382 if (num_additional_args > del_count) {
383 for (var i = len - del_count; i > start_i; i--) {
384 var from_index = i + del_count - 1;
385 var to_index = i + num_additional_args - 1;
386 if (HAS_INDEX(array, from_index, is_array)) {
387 array[to_index] = array[from_index];
388 } else {
389 delete array[to_index];
390 }
391 }
392 } else {
393 for (var i = start_i; i < len - del_count; i++) {
394 var from_index = i + del_count;
395 var to_index = i + num_additional_args;
396 if (HAS_INDEX(array, from_index, is_array)) {
397 array[to_index] = array[from_index];
398 } else {
399 delete array[to_index];
400 }
401 }
402 for (var i = len; i > len - del_count + num_additional_args; i--) {
403 delete array[i - 1];
404 }
405 }
406 }
407}
408
409
410// -------------------------------------------------------------------
411
412
413function ArrayToString() {
414 var array;
415 var func;
416 if (IS_ARRAY(this)) {
417 func = this.join;
418 if (func === ArrayJoin) {
419 return Join(this, this.length, ',', ConvertToString);
420 }
421 array = this;
422 } else {
423 array = TO_OBJECT(this);
424 func = array.join;
425 }
426 if (!IS_CALLABLE(func)) {
427 return %_Call(ObjectToString, array);
428 }
429 return %_Call(func, array);
430}
431
432
433function InnerArrayToLocaleString(array, length) {
434 var len = TO_LENGTH_OR_UINT32(length);
435 if (len === 0) return "";
436 return Join(array, len, ',', ConvertToLocaleString);
437}
438
439
440function ArrayToLocaleString() {
441 var array = TO_OBJECT(this);
442 var arrayLen = array.length;
443 return InnerArrayToLocaleString(array, arrayLen);
444}
445
446
447function InnerArrayJoin(separator, array, length) {
448 if (IS_UNDEFINED(separator)) {
449 separator = ',';
450 } else {
451 separator = TO_STRING(separator);
452 }
453
454 var result = %_FastOneByteArrayJoin(array, separator);
455 if (!IS_UNDEFINED(result)) return result;
456
457 // Fast case for one-element arrays.
458 if (length === 1) {
459 var e = array[0];
460 if (IS_NULL_OR_UNDEFINED(e)) return '';
461 return TO_STRING(e);
462 }
463
464 return Join(array, length, separator, ConvertToString);
465}
466
467
468function ArrayJoin(separator) {
469 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
470
471 var array = TO_OBJECT(this);
472 var length = TO_LENGTH_OR_UINT32(array.length);
473
474 return InnerArrayJoin(separator, array, length);
475}
476
477
478function ObservedArrayPop(n) {
479 n--;
480 var value = this[n];
481
482 try {
483 ObserveBeginPerformSplice(this);
484 delete this[n];
485 this.length = n;
486 } finally {
487 ObserveEndPerformSplice(this);
488 ObserveEnqueueSpliceRecord(this, n, [value], 0);
489 }
490
491 return value;
492}
493
494
495// Removes the last element from the array and returns it. See
496// ECMA-262, section 15.4.4.6.
497function ArrayPop() {
498 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
499
500 var array = TO_OBJECT(this);
501 var n = TO_LENGTH_OR_UINT32(array.length);
502 if (n == 0) {
503 array.length = n;
504 return;
505 }
506
507 if (%IsObserved(array))
508 return ObservedArrayPop.call(array, n);
509
510 n--;
511 var value = array[n];
512 %DeleteProperty_Strict(array, n);
513 array.length = n;
514 return value;
515}
516
517
518function ObservedArrayPush() {
519 var n = TO_LENGTH_OR_UINT32(this.length);
520 var m = %_ArgumentsLength();
521
522 try {
523 ObserveBeginPerformSplice(this);
524 for (var i = 0; i < m; i++) {
525 this[i+n] = %_Arguments(i);
526 }
527 var new_length = n + m;
528 this.length = new_length;
529 } finally {
530 ObserveEndPerformSplice(this);
531 ObserveEnqueueSpliceRecord(this, n, [], m);
532 }
533
534 return new_length;
535}
536
537
538// Appends the arguments to the end of the array and returns the new
539// length of the array. See ECMA-262, section 15.4.4.7.
540function ArrayPush() {
541 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
542
543 if (%IsObserved(this))
544 return ObservedArrayPush.apply(this, arguments);
545
546 var array = TO_OBJECT(this);
547 var n = TO_LENGTH_OR_UINT32(array.length);
548 var m = %_ArgumentsLength();
549
550 // It appears that there is no enforced, absolute limit on the number of
551 // arguments, but it would surely blow the stack to use 2**30 or more.
552 // To avoid integer overflow, do the comparison to the max safe integer
553 // after subtracting 2**30 from both sides. (2**31 would seem like a
554 // natural value, but it is negative in JS, and 2**32 is 1.)
555 if (m > (1 << 30) || (n - (1 << 30)) + m > kMaxSafeInteger - (1 << 30)) {
556 throw MakeTypeError(kPushPastSafeLength, m, n);
557 }
558
559 for (var i = 0; i < m; i++) {
560 array[i+n] = %_Arguments(i);
561 }
562
563 var new_length = n + m;
564 array.length = new_length;
565 return new_length;
566}
567
568
569// For implementing reverse() on large, sparse arrays.
570function SparseReverse(array, len) {
571 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
572 var high_counter = keys.length - 1;
573 var low_counter = 0;
574 while (low_counter <= high_counter) {
575 var i = keys[low_counter];
576 var j = keys[high_counter];
577
578 var j_complement = len - j - 1;
579 var low, high;
580
581 if (j_complement <= i) {
582 high = j;
583 while (keys[--high_counter] == j) { }
584 low = j_complement;
585 }
586 if (j_complement >= i) {
587 low = i;
588 while (keys[++low_counter] == i) { }
589 high = len - i - 1;
590 }
591
592 var current_i = array[low];
593 if (!IS_UNDEFINED(current_i) || low in array) {
594 var current_j = array[high];
595 if (!IS_UNDEFINED(current_j) || high in array) {
596 array[low] = current_j;
597 array[high] = current_i;
598 } else {
599 array[high] = current_i;
600 delete array[low];
601 }
602 } else {
603 var current_j = array[high];
604 if (!IS_UNDEFINED(current_j) || high in array) {
605 array[low] = current_j;
606 delete array[high];
607 }
608 }
609 }
610}
611
612function PackedArrayReverse(array, len) {
613 var j = len - 1;
614 for (var i = 0; i < j; i++, j--) {
615 var current_i = array[i];
616 var current_j = array[j];
617 array[i] = current_j;
618 array[j] = current_i;
619 }
620 return array;
621}
622
623
624function GenericArrayReverse(array, len) {
625 var j = len - 1;
626 for (var i = 0; i < j; i++, j--) {
627 if (i in array) {
628 var current_i = array[i];
629 if (j in array) {
630 var current_j = array[j];
631 array[i] = current_j;
632 array[j] = current_i;
633 } else {
634 array[j] = current_i;
635 delete array[i];
636 }
637 } else {
638 if (j in array) {
639 var current_j = array[j];
640 array[i] = current_j;
641 delete array[j];
642 }
643 }
644 }
645 return array;
646}
647
648
649function ArrayReverse() {
650 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
651
652 var array = TO_OBJECT(this);
653 var len = TO_LENGTH_OR_UINT32(array.length);
654 var isArray = IS_ARRAY(array);
655
656 if (UseSparseVariant(array, len, isArray, len)) {
657 %NormalizeElements(array);
658 SparseReverse(array, len);
659 return array;
660 } else if (isArray && %_HasFastPackedElements(array)) {
661 return PackedArrayReverse(array, len);
662 } else {
663 return GenericArrayReverse(array, len);
664 }
665}
666
667
668function ObservedArrayShift(len) {
669 var first = this[0];
670
671 try {
672 ObserveBeginPerformSplice(this);
673 SimpleMove(this, 0, 1, len, 0);
674 this.length = len - 1;
675 } finally {
676 ObserveEndPerformSplice(this);
677 ObserveEnqueueSpliceRecord(this, 0, [first], 0);
678 }
679
680 return first;
681}
682
683
684function ArrayShift() {
685 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
686
687 var array = TO_OBJECT(this);
688 var len = TO_LENGTH_OR_UINT32(array.length);
689
690 if (len === 0) {
691 array.length = 0;
692 return;
693 }
694
695 if (%object_is_sealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed);
696
697 if (%IsObserved(array))
698 return ObservedArrayShift.call(array, len);
699
700 var first = array[0];
701
702 if (UseSparseVariant(array, len, IS_ARRAY(array), len)) {
703 SparseMove(array, 0, 1, len, 0);
704 } else {
705 SimpleMove(array, 0, 1, len, 0);
706 }
707
708 array.length = len - 1;
709
710 return first;
711}
712
713
714function ObservedArrayUnshift() {
715 var len = TO_LENGTH_OR_UINT32(this.length);
716 var num_arguments = %_ArgumentsLength();
717
718 try {
719 ObserveBeginPerformSplice(this);
720 SimpleMove(this, 0, 0, len, num_arguments);
721 for (var i = 0; i < num_arguments; i++) {
722 this[i] = %_Arguments(i);
723 }
724 var new_length = len + num_arguments;
725 this.length = new_length;
726 } finally {
727 ObserveEndPerformSplice(this);
728 ObserveEnqueueSpliceRecord(this, 0, [], num_arguments);
729 }
730
731 return new_length;
732}
733
734
735function ArrayUnshift(arg1) { // length == 1
736 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
737
738 if (%IsObserved(this))
739 return ObservedArrayUnshift.apply(this, arguments);
740
741 var array = TO_OBJECT(this);
742 var len = TO_LENGTH_OR_UINT32(array.length);
743 var num_arguments = %_ArgumentsLength();
744
745 if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) &&
746 !%object_is_sealed(array)) {
747 SparseMove(array, 0, 0, len, num_arguments);
748 } else {
749 SimpleMove(array, 0, 0, len, num_arguments);
750 }
751
752 for (var i = 0; i < num_arguments; i++) {
753 array[i] = %_Arguments(i);
754 }
755
756 var new_length = len + num_arguments;
757 array.length = new_length;
758 return new_length;
759}
760
761
762function ArraySlice(start, end) {
763 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
764
765 var array = TO_OBJECT(this);
766 var len = TO_LENGTH_OR_UINT32(array.length);
767 var start_i = TO_INTEGER(start);
768 var end_i = len;
769
770 if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
771
772 if (start_i < 0) {
773 start_i += len;
774 if (start_i < 0) start_i = 0;
775 } else {
776 if (start_i > len) start_i = len;
777 }
778
779 if (end_i < 0) {
780 end_i += len;
781 if (end_i < 0) end_i = 0;
782 } else {
783 if (end_i > len) end_i = len;
784 }
785
786 var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
787
788 if (end_i < start_i) return result;
789
790 if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
791 %NormalizeElements(array);
792 %NormalizeElements(result);
793 SparseSlice(array, start_i, end_i - start_i, len, result);
794 } else {
795 SimpleSlice(array, start_i, end_i - start_i, len, result);
796 }
797
798 result.length = end_i - start_i;
799
800 return result;
801}
802
803
804function ComputeSpliceStartIndex(start_i, len) {
805 if (start_i < 0) {
806 start_i += len;
807 return start_i < 0 ? 0 : start_i;
808 }
809
810 return start_i > len ? len : start_i;
811}
812
813
814function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
815 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
816 // given as a request to delete all the elements from the start.
817 // And it differs from the case of undefined delete count.
818 // This does not follow ECMA-262, but we do the same for
819 // compatibility.
820 var del_count = 0;
821 if (num_arguments == 1)
822 return len - start_i;
823
824 del_count = TO_INTEGER(delete_count);
825 if (del_count < 0)
826 return 0;
827
828 if (del_count > len - start_i)
829 return len - start_i;
830
831 return del_count;
832}
833
834
835function ObservedArraySplice(start, delete_count) {
836 var num_arguments = %_ArgumentsLength();
837 var len = TO_LENGTH_OR_UINT32(this.length);
838 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
839 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
840 start_i);
841 var deleted_elements = [];
842 deleted_elements.length = del_count;
843 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
844
845 try {
846 ObserveBeginPerformSplice(this);
847
848 SimpleSlice(this, start_i, del_count, len, deleted_elements);
849 SimpleMove(this, start_i, del_count, len, num_elements_to_add);
850
851 // Insert the arguments into the resulting array in
852 // place of the deleted elements.
853 var i = start_i;
854 var arguments_index = 2;
855 var arguments_length = %_ArgumentsLength();
856 while (arguments_index < arguments_length) {
857 this[i++] = %_Arguments(arguments_index++);
858 }
859 this.length = len - del_count + num_elements_to_add;
860
861 } finally {
862 ObserveEndPerformSplice(this);
863 if (deleted_elements.length || num_elements_to_add) {
864 ObserveEnqueueSpliceRecord(this,
865 start_i,
866 deleted_elements.slice(),
867 num_elements_to_add);
868 }
869 }
870
871 // Return the deleted elements.
872 return deleted_elements;
873}
874
875
876function ArraySplice(start, delete_count) {
877 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
878
879 if (%IsObserved(this))
880 return ObservedArraySplice.apply(this, arguments);
881
882 var num_arguments = %_ArgumentsLength();
883 var array = TO_OBJECT(this);
884 var len = TO_LENGTH_OR_UINT32(array.length);
885 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
886 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
887 start_i);
888 var deleted_elements = ArraySpeciesCreate(array, del_count);
889 deleted_elements.length = del_count;
890 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
891
892 if (del_count != num_elements_to_add && %object_is_sealed(array)) {
893 throw MakeTypeError(kArrayFunctionsOnSealed);
894 } else if (del_count > 0 && %object_is_frozen(array)) {
895 throw MakeTypeError(kArrayFunctionsOnFrozen);
896 }
897
898 var changed_elements = del_count;
899 if (num_elements_to_add != del_count) {
900 // If the slice needs to do a actually move elements after the insertion
901 // point, then include those in the estimate of changed elements.
902 changed_elements += len - start_i - del_count;
903 }
904 if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
905 %NormalizeElements(array);
906 %NormalizeElements(deleted_elements);
907 SparseSlice(array, start_i, del_count, len, deleted_elements);
908 SparseMove(array, start_i, del_count, len, num_elements_to_add);
909 } else {
910 SimpleSlice(array, start_i, del_count, len, deleted_elements);
911 SimpleMove(array, start_i, del_count, len, num_elements_to_add);
912 }
913
914 // Insert the arguments into the resulting array in
915 // place of the deleted elements.
916 var i = start_i;
917 var arguments_index = 2;
918 var arguments_length = %_ArgumentsLength();
919 while (arguments_index < arguments_length) {
920 array[i++] = %_Arguments(arguments_index++);
921 }
922 array.length = len - del_count + num_elements_to_add;
923
924 // Return the deleted elements.
925 return deleted_elements;
926}
927
928
929function InnerArraySort(array, length, comparefn) {
930 // In-place QuickSort algorithm.
931 // For short (length <= 22) arrays, insertion sort is used for efficiency.
932
933 if (!IS_CALLABLE(comparefn)) {
934 comparefn = function (x, y) {
935 if (x === y) return 0;
936 if (%_IsSmi(x) && %_IsSmi(y)) {
937 return %SmiLexicographicCompare(x, y);
938 }
939 x = TO_STRING(x);
940 y = TO_STRING(y);
941 if (x == y) return 0;
942 else return x < y ? -1 : 1;
943 };
944 }
945 var InsertionSort = function InsertionSort(a, from, to) {
946 for (var i = from + 1; i < to; i++) {
947 var element = a[i];
948 for (var j = i - 1; j >= from; j--) {
949 var tmp = a[j];
950 var order = comparefn(tmp, element);
951 if (order > 0) {
952 a[j + 1] = tmp;
953 } else {
954 break;
955 }
956 }
957 a[j + 1] = element;
958 }
959 };
960
961 var GetThirdIndex = function(a, from, to) {
962 var t_array = new InternalArray();
963 // Use both 'from' and 'to' to determine the pivot candidates.
964 var increment = 200 + ((to - from) & 15);
965 var j = 0;
966 from += 1;
967 to -= 1;
968 for (var i = from; i < to; i += increment) {
969 t_array[j] = [i, a[i]];
970 j++;
971 }
972 t_array.sort(function(a, b) {
973 return comparefn(a[1], b[1]);
974 });
975 var third_index = t_array[t_array.length >> 1][0];
976 return third_index;
977 }
978
979 var QuickSort = function QuickSort(a, from, to) {
980 var third_index = 0;
981 while (true) {
982 // Insertion sort is faster for short arrays.
983 if (to - from <= 10) {
984 InsertionSort(a, from, to);
985 return;
986 }
987 if (to - from > 1000) {
988 third_index = GetThirdIndex(a, from, to);
989 } else {
990 third_index = from + ((to - from) >> 1);
991 }
992 // Find a pivot as the median of first, last and middle element.
993 var v0 = a[from];
994 var v1 = a[to - 1];
995 var v2 = a[third_index];
996 var c01 = comparefn(v0, v1);
997 if (c01 > 0) {
998 // v1 < v0, so swap them.
999 var tmp = v0;
1000 v0 = v1;
1001 v1 = tmp;
1002 } // v0 <= v1.
1003 var c02 = comparefn(v0, v2);
1004 if (c02 >= 0) {
1005 // v2 <= v0 <= v1.
1006 var tmp = v0;
1007 v0 = v2;
1008 v2 = v1;
1009 v1 = tmp;
1010 } else {
1011 // v0 <= v1 && v0 < v2
1012 var c12 = comparefn(v1, v2);
1013 if (c12 > 0) {
1014 // v0 <= v2 < v1
1015 var tmp = v1;
1016 v1 = v2;
1017 v2 = tmp;
1018 }
1019 }
1020 // v0 <= v1 <= v2
1021 a[from] = v0;
1022 a[to - 1] = v2;
1023 var pivot = v1;
1024 var low_end = from + 1; // Upper bound of elements lower than pivot.
1025 var high_start = to - 1; // Lower bound of elements greater than pivot.
1026 a[third_index] = a[low_end];
1027 a[low_end] = pivot;
1028
1029 // From low_end to i are elements equal to pivot.
1030 // From i to high_start are elements that haven't been compared yet.
1031 partition: for (var i = low_end + 1; i < high_start; i++) {
1032 var element = a[i];
1033 var order = comparefn(element, pivot);
1034 if (order < 0) {
1035 a[i] = a[low_end];
1036 a[low_end] = element;
1037 low_end++;
1038 } else if (order > 0) {
1039 do {
1040 high_start--;
1041 if (high_start == i) break partition;
1042 var top_elem = a[high_start];
1043 order = comparefn(top_elem, pivot);
1044 } while (order > 0);
1045 a[i] = a[high_start];
1046 a[high_start] = element;
1047 if (order < 0) {
1048 element = a[i];
1049 a[i] = a[low_end];
1050 a[low_end] = element;
1051 low_end++;
1052 }
1053 }
1054 }
1055 if (to - high_start < low_end - from) {
1056 QuickSort(a, high_start, to);
1057 to = low_end;
1058 } else {
1059 QuickSort(a, from, low_end);
1060 from = high_start;
1061 }
1062 }
1063 };
1064
1065 // Copy elements in the range 0..length from obj's prototype chain
1066 // to obj itself, if obj has holes. Return one more than the maximal index
1067 // of a prototype property.
1068 var CopyFromPrototype = function CopyFromPrototype(obj, length) {
1069 var max = 0;
1070 for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
1071 var indices = %GetArrayKeys(proto, length);
1072 if (IS_NUMBER(indices)) {
1073 // It's an interval.
1074 var proto_length = indices;
1075 for (var i = 0; i < proto_length; i++) {
1076 if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
1077 obj[i] = proto[i];
1078 if (i >= max) { max = i + 1; }
1079 }
1080 }
1081 } else {
1082 for (var i = 0; i < indices.length; i++) {
1083 var index = indices[i];
1084 if (!IS_UNDEFINED(index) && !HAS_OWN_PROPERTY(obj, index)
1085 && HAS_OWN_PROPERTY(proto, index)) {
1086 obj[index] = proto[index];
1087 if (index >= max) { max = index + 1; }
1088 }
1089 }
1090 }
1091 }
1092 return max;
1093 };
1094
1095 // Set a value of "undefined" on all indices in the range from..to
1096 // where a prototype of obj has an element. I.e., shadow all prototype
1097 // elements in that range.
1098 var ShadowPrototypeElements = function(obj, from, to) {
1099 for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
1100 var indices = %GetArrayKeys(proto, to);
1101 if (IS_NUMBER(indices)) {
1102 // It's an interval.
1103 var proto_length = indices;
1104 for (var i = from; i < proto_length; i++) {
1105 if (HAS_OWN_PROPERTY(proto, i)) {
1106 obj[i] = UNDEFINED;
1107 }
1108 }
1109 } else {
1110 for (var i = 0; i < indices.length; i++) {
1111 var index = indices[i];
1112 if (!IS_UNDEFINED(index) && from <= index &&
1113 HAS_OWN_PROPERTY(proto, index)) {
1114 obj[index] = UNDEFINED;
1115 }
1116 }
1117 }
1118 }
1119 };
1120
1121 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
1122 // Copy defined elements from the end to fill in all holes and undefineds
1123 // in the beginning of the array. Write undefineds and holes at the end
1124 // after loop is finished.
1125 var first_undefined = 0;
1126 var last_defined = length - 1;
1127 var num_holes = 0;
1128 while (first_undefined < last_defined) {
1129 // Find first undefined element.
1130 while (first_undefined < last_defined &&
1131 !IS_UNDEFINED(obj[first_undefined])) {
1132 first_undefined++;
1133 }
1134 // Maintain the invariant num_holes = the number of holes in the original
1135 // array with indices <= first_undefined or > last_defined.
1136 if (!HAS_OWN_PROPERTY(obj, first_undefined)) {
1137 num_holes++;
1138 }
1139
1140 // Find last defined element.
1141 while (first_undefined < last_defined &&
1142 IS_UNDEFINED(obj[last_defined])) {
1143 if (!HAS_OWN_PROPERTY(obj, last_defined)) {
1144 num_holes++;
1145 }
1146 last_defined--;
1147 }
1148 if (first_undefined < last_defined) {
1149 // Fill in hole or undefined.
1150 obj[first_undefined] = obj[last_defined];
1151 obj[last_defined] = UNDEFINED;
1152 }
1153 }
1154 // If there were any undefineds in the entire array, first_undefined
1155 // points to one past the last defined element. Make this true if
1156 // there were no undefineds, as well, so that first_undefined == number
1157 // of defined elements.
1158 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
1159 // Fill in the undefineds and the holes. There may be a hole where
1160 // an undefined should be and vice versa.
1161 var i;
1162 for (i = first_undefined; i < length - num_holes; i++) {
1163 obj[i] = UNDEFINED;
1164 }
1165 for (i = length - num_holes; i < length; i++) {
1166 // For compatability with Webkit, do not expose elements in the prototype.
1167 if (i in %_GetPrototype(obj)) {
1168 obj[i] = UNDEFINED;
1169 } else {
1170 delete obj[i];
1171 }
1172 }
1173
1174 // Return the number of defined elements.
1175 return first_undefined;
1176 };
1177
1178 if (length < 2) return array;
1179
1180 var is_array = IS_ARRAY(array);
1181 var max_prototype_element;
1182 if (!is_array) {
1183 // For compatibility with JSC, we also sort elements inherited from
1184 // the prototype chain on non-Array objects.
1185 // We do this by copying them to this object and sorting only
1186 // own elements. This is not very efficient, but sorting with
1187 // inherited elements happens very, very rarely, if at all.
1188 // The specification allows "implementation dependent" behavior
1189 // if an element on the prototype chain has an element that
1190 // might interact with sorting.
1191 max_prototype_element = CopyFromPrototype(array, length);
1192 }
1193
1194 // %RemoveArrayHoles returns -1 if fast removal is not supported.
1195 var num_non_undefined = %RemoveArrayHoles(array, length);
1196
1197 if (num_non_undefined == -1) {
1198 // The array is observed, or there were indexed accessors in the array.
1199 // Move array holes and undefineds to the end using a Javascript function
1200 // that is safe in the presence of accessors and is observable.
1201 num_non_undefined = SafeRemoveArrayHoles(array);
1202 }
1203
1204 QuickSort(array, 0, num_non_undefined);
1205
1206 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
1207 // For compatibility with JSC, we shadow any elements in the prototype
1208 // chain that has become exposed by sort moving a hole to its position.
1209 ShadowPrototypeElements(array, num_non_undefined, max_prototype_element);
1210 }
1211
1212 return array;
1213}
1214
1215
1216function ArraySort(comparefn) {
1217 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
1218
1219 var array = TO_OBJECT(this);
1220 var length = TO_LENGTH_OR_UINT32(array.length);
1221 return InnerArraySort(array, length, comparefn);
1222}
1223
1224
1225// The following functions cannot be made efficient on sparse arrays while
1226// preserving the semantics, since the calls to the receiver function can add
1227// or delete elements from the array.
1228function InnerArrayFilter(f, receiver, array, length, result) {
1229 var result_length = 0;
1230 var is_array = IS_ARRAY(array);
1231 for (var i = 0; i < length; i++) {
1232 if (HAS_INDEX(array, i, is_array)) {
1233 var element = array[i];
1234 if (%_Call(f, receiver, element, i, array)) {
1235 DefineIndexedProperty(result, result_length, element);
1236 result_length++;
1237 }
1238 }
1239 }
1240 return result;
1241}
1242
1243
1244
1245function ArrayFilter(f, receiver) {
1246 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
1247
1248 // Pull out the length so that modifications to the length in the
1249 // loop will not affect the looping and side effects are visible.
1250 var array = TO_OBJECT(this);
1251 var length = TO_LENGTH_OR_UINT32(array.length);
1252 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1253 var result = ArraySpeciesCreate(array, 0);
1254 return InnerArrayFilter(f, receiver, array, length, result);
1255}
1256
1257
1258function InnerArrayForEach(f, receiver, array, length) {
1259 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1260
1261 var is_array = IS_ARRAY(array);
1262 for (var i = 0; i < length; i++) {
1263 if (HAS_INDEX(array, i, is_array)) {
1264 var element = array[i];
1265 %_Call(f, receiver, element, i, array);
1266 }
1267 }
1268}
1269
1270
1271function ArrayForEach(f, receiver) {
1272 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
1273
1274 // Pull out the length so that modifications to the length in the
1275 // loop will not affect the looping and side effects are visible.
1276 var array = TO_OBJECT(this);
1277 var length = TO_LENGTH_OR_UINT32(array.length);
1278 InnerArrayForEach(f, receiver, array, length);
1279}
1280
1281
1282function InnerArraySome(f, receiver, array, length) {
1283 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1284
1285 var is_array = IS_ARRAY(array);
1286 for (var i = 0; i < length; i++) {
1287 if (HAS_INDEX(array, i, is_array)) {
1288 var element = array[i];
1289 if (%_Call(f, receiver, element, i, array)) return true;
1290 }
1291 }
1292 return false;
1293}
1294
1295
1296// Executes the function once for each element present in the
1297// array until it finds one where callback returns true.
1298function ArraySome(f, receiver) {
1299 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
1300
1301 // Pull out the length so that modifications to the length in the
1302 // loop will not affect the looping and side effects are visible.
1303 var array = TO_OBJECT(this);
1304 var length = TO_LENGTH_OR_UINT32(array.length);
1305 return InnerArraySome(f, receiver, array, length);
1306}
1307
1308
1309function InnerArrayEvery(f, receiver, array, length) {
1310 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1311
1312 var is_array = IS_ARRAY(array);
1313 for (var i = 0; i < length; i++) {
1314 if (HAS_INDEX(array, i, is_array)) {
1315 var element = array[i];
1316 if (!%_Call(f, receiver, element, i, array)) return false;
1317 }
1318 }
1319 return true;
1320}
1321
1322function ArrayEvery(f, receiver) {
1323 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
1324
1325 // Pull out the length so that modifications to the length in the
1326 // loop will not affect the looping and side effects are visible.
1327 var array = TO_OBJECT(this);
1328 var length = TO_LENGTH_OR_UINT32(array.length);
1329 return InnerArrayEvery(f, receiver, array, length);
1330}
1331
1332
1333function ArrayMap(f, receiver) {
1334 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
1335
1336 // Pull out the length so that modifications to the length in the
1337 // loop will not affect the looping and side effects are visible.
1338 var array = TO_OBJECT(this);
1339 var length = TO_LENGTH_OR_UINT32(array.length);
1340 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1341 var result = ArraySpeciesCreate(array, length);
1342 var is_array = IS_ARRAY(array);
1343 for (var i = 0; i < length; i++) {
1344 if (HAS_INDEX(array, i, is_array)) {
1345 var element = array[i];
1346 DefineIndexedProperty(result, i, %_Call(f, receiver, element, i, array));
1347 }
1348 }
1349 return result;
1350}
1351
1352
1353// For .indexOf, we don't need to pass in the number of arguments
1354// at the callsite since ToInteger(undefined) == 0; however, for
1355// .lastIndexOf, we need to pass it, since the behavior for passing
1356// undefined is 0 but for not including the argument is length-1.
1357function InnerArrayIndexOf(array, element, index, length) {
1358 if (length == 0) return -1;
1359 if (IS_UNDEFINED(index)) {
1360 index = 0;
1361 } else {
1362 index = TO_INTEGER(index);
1363 // If index is negative, index from the end of the array.
1364 if (index < 0) {
1365 index = length + index;
1366 // If index is still negative, search the entire array.
1367 if (index < 0) index = 0;
1368 }
1369 }
1370 var min = index;
1371 var max = length;
1372 if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) {
1373 %NormalizeElements(array);
1374 var indices = %GetArrayKeys(array, length);
1375 if (IS_NUMBER(indices)) {
1376 // It's an interval.
1377 max = indices; // Capped by length already.
1378 // Fall through to loop below.
1379 } else {
1380 if (indices.length == 0) return -1;
1381 // Get all the keys in sorted order.
1382 var sortedKeys = GetSortedArrayKeys(array, indices);
1383 var n = sortedKeys.length;
1384 var i = 0;
1385 while (i < n && sortedKeys[i] < index) i++;
1386 while (i < n) {
1387 var key = sortedKeys[i];
1388 if (!IS_UNDEFINED(key) && array[key] === element) return key;
1389 i++;
1390 }
1391 return -1;
1392 }
1393 }
1394 // Lookup through the array.
1395 if (!IS_UNDEFINED(element)) {
1396 for (var i = min; i < max; i++) {
1397 if (array[i] === element) return i;
1398 }
1399 return -1;
1400 }
1401 // Lookup through the array.
1402 for (var i = min; i < max; i++) {
1403 if (IS_UNDEFINED(array[i]) && i in array) {
1404 return i;
1405 }
1406 }
1407 return -1;
1408}
1409
1410
1411function ArrayIndexOf(element, index) {
1412 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
1413
1414 var length = TO_LENGTH_OR_UINT32(this.length);
1415 return InnerArrayIndexOf(this, element, index, length);
1416}
1417
1418
1419function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) {
1420 if (length == 0) return -1;
1421 if (argumentsLength < 2) {
1422 index = length - 1;
1423 } else {
1424 index = TO_INTEGER(index);
1425 // If index is negative, index from end of the array.
1426 if (index < 0) index += length;
1427 // If index is still negative, do not search the array.
1428 if (index < 0) return -1;
1429 else if (index >= length) index = length - 1;
1430 }
1431 var min = 0;
1432 var max = index;
1433 if (UseSparseVariant(array, length, IS_ARRAY(array), index)) {
1434 %NormalizeElements(array);
1435 var indices = %GetArrayKeys(array, index + 1);
1436 if (IS_NUMBER(indices)) {
1437 // It's an interval.
1438 max = indices; // Capped by index already.
1439 // Fall through to loop below.
1440 } else {
1441 if (indices.length == 0) return -1;
1442 // Get all the keys in sorted order.
1443 var sortedKeys = GetSortedArrayKeys(array, indices);
1444 var i = sortedKeys.length - 1;
1445 while (i >= 0) {
1446 var key = sortedKeys[i];
1447 if (!IS_UNDEFINED(key) && array[key] === element) return key;
1448 i--;
1449 }
1450 return -1;
1451 }
1452 }
1453 // Lookup through the array.
1454 if (!IS_UNDEFINED(element)) {
1455 for (var i = max; i >= min; i--) {
1456 if (array[i] === element) return i;
1457 }
1458 return -1;
1459 }
1460 for (var i = max; i >= min; i--) {
1461 if (IS_UNDEFINED(array[i]) && i in array) {
1462 return i;
1463 }
1464 }
1465 return -1;
1466}
1467
1468
1469function ArrayLastIndexOf(element, index) {
1470 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
1471
1472 var length = TO_LENGTH_OR_UINT32(this.length);
1473 return InnerArrayLastIndexOf(this, element, index, length,
1474 %_ArgumentsLength());
1475}
1476
1477
1478function InnerArrayReduce(callback, current, array, length, argumentsLength) {
1479 if (!IS_CALLABLE(callback)) {
1480 throw MakeTypeError(kCalledNonCallable, callback);
1481 }
1482
1483 var is_array = IS_ARRAY(array);
1484 var i = 0;
1485 find_initial: if (argumentsLength < 2) {
1486 for (; i < length; i++) {
1487 if (HAS_INDEX(array, i, is_array)) {
1488 current = array[i++];
1489 break find_initial;
1490 }
1491 }
1492 throw MakeTypeError(kReduceNoInitial);
1493 }
1494
1495 for (; i < length; i++) {
1496 if (HAS_INDEX(array, i, is_array)) {
1497 var element = array[i];
1498 current = callback(current, element, i, array);
1499 }
1500 }
1501 return current;
1502}
1503
1504
1505function ArrayReduce(callback, current) {
1506 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
1507
1508 // Pull out the length so that modifications to the length in the
1509 // loop will not affect the looping and side effects are visible.
1510 var array = TO_OBJECT(this);
1511 var length = TO_LENGTH_OR_UINT32(array.length);
1512 return InnerArrayReduce(callback, current, array, length,
1513 %_ArgumentsLength());
1514}
1515
1516
1517function InnerArrayReduceRight(callback, current, array, length,
1518 argumentsLength) {
1519 if (!IS_CALLABLE(callback)) {
1520 throw MakeTypeError(kCalledNonCallable, callback);
1521 }
1522
1523 var is_array = IS_ARRAY(array);
1524 var i = length - 1;
1525 find_initial: if (argumentsLength < 2) {
1526 for (; i >= 0; i--) {
1527 if (HAS_INDEX(array, i, is_array)) {
1528 current = array[i--];
1529 break find_initial;
1530 }
1531 }
1532 throw MakeTypeError(kReduceNoInitial);
1533 }
1534
1535 for (; i >= 0; i--) {
1536 if (HAS_INDEX(array, i, is_array)) {
1537 var element = array[i];
1538 current = callback(current, element, i, array);
1539 }
1540 }
1541 return current;
1542}
1543
1544
1545function ArrayReduceRight(callback, current) {
1546 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
1547
1548 // Pull out the length so that side effects are visible before the
1549 // callback function is checked.
1550 var array = TO_OBJECT(this);
1551 var length = TO_LENGTH_OR_UINT32(array.length);
1552 return InnerArrayReduceRight(callback, current, array, length,
1553 %_ArgumentsLength());
1554}
1555
1556
1557function InnerArrayCopyWithin(target, start, end, array, length) {
1558 target = TO_INTEGER(target);
1559 var to;
1560 if (target < 0) {
1561 to = MaxSimple(length + target, 0);
1562 } else {
1563 to = MinSimple(target, length);
1564 }
1565
1566 start = TO_INTEGER(start);
1567 var from;
1568 if (start < 0) {
1569 from = MaxSimple(length + start, 0);
1570 } else {
1571 from = MinSimple(start, length);
1572 }
1573
1574 end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
1575 var final;
1576 if (end < 0) {
1577 final = MaxSimple(length + end, 0);
1578 } else {
1579 final = MinSimple(end, length);
1580 }
1581
1582 var count = MinSimple(final - from, length - to);
1583 var direction = 1;
1584 if (from < to && to < (from + count)) {
1585 direction = -1;
1586 from = from + count - 1;
1587 to = to + count - 1;
1588 }
1589
1590 while (count > 0) {
1591 if (from in array) {
1592 array[to] = array[from];
1593 } else {
1594 delete array[to];
1595 }
1596 from = from + direction;
1597 to = to + direction;
1598 count--;
1599 }
1600
1601 return array;
1602}
1603
1604
1605// ES6 draft 03-17-15, section 22.1.3.3
1606function ArrayCopyWithin(target, start, end) {
1607 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
1608
1609 var array = TO_OBJECT(this);
1610 var length = TO_LENGTH(array.length);
1611
1612 return InnerArrayCopyWithin(target, start, end, array, length);
1613}
1614
1615
1616function InnerArrayFind(predicate, thisArg, array, length) {
1617 if (!IS_CALLABLE(predicate)) {
1618 throw MakeTypeError(kCalledNonCallable, predicate);
1619 }
1620
1621 for (var i = 0; i < length; i++) {
1622 var element = array[i];
1623 if (%_Call(predicate, thisArg, element, i, array)) {
1624 return element;
1625 }
1626 }
1627
1628 return;
1629}
1630
1631
1632// ES6 draft 07-15-13, section 15.4.3.23
1633function ArrayFind(predicate, thisArg) {
1634 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
1635
1636 var array = TO_OBJECT(this);
1637 var length = TO_INTEGER(array.length);
1638
1639 return InnerArrayFind(predicate, thisArg, array, length);
1640}
1641
1642
1643function InnerArrayFindIndex(predicate, thisArg, array, length) {
1644 if (!IS_CALLABLE(predicate)) {
1645 throw MakeTypeError(kCalledNonCallable, predicate);
1646 }
1647
1648 for (var i = 0; i < length; i++) {
1649 var element = array[i];
1650 if (%_Call(predicate, thisArg, element, i, array)) {
1651 return i;
1652 }
1653 }
1654
1655 return -1;
1656}
1657
1658
1659// ES6 draft 07-15-13, section 15.4.3.24
1660function ArrayFindIndex(predicate, thisArg) {
1661 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex");
1662
1663 var array = TO_OBJECT(this);
1664 var length = TO_INTEGER(array.length);
1665
1666 return InnerArrayFindIndex(predicate, thisArg, array, length);
1667}
1668
1669
1670// ES6, draft 04-05-14, section 22.1.3.6
1671function InnerArrayFill(value, start, end, array, length) {
1672 var i = IS_UNDEFINED(start) ? 0 : TO_INTEGER(start);
1673 var end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
1674
1675 if (i < 0) {
1676 i += length;
1677 if (i < 0) i = 0;
1678 } else {
1679 if (i > length) i = length;
1680 }
1681
1682 if (end < 0) {
1683 end += length;
1684 if (end < 0) end = 0;
1685 } else {
1686 if (end > length) end = length;
1687 }
1688
1689 if ((end - i) > 0 && %object_is_frozen(array)) {
1690 throw MakeTypeError(kArrayFunctionsOnFrozen);
1691 }
1692
1693 for (; i < end; i++)
1694 array[i] = value;
1695 return array;
1696}
1697
1698
1699// ES6, draft 04-05-14, section 22.1.3.6
1700function ArrayFill(value, start, end) {
1701 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
1702
1703 var array = TO_OBJECT(this);
1704 var length = TO_LENGTH_OR_UINT32(array.length);
1705
1706 return InnerArrayFill(value, start, end, array, length);
1707}
1708
1709
1710function InnerArrayIncludes(searchElement, fromIndex, array, length) {
1711 if (length === 0) {
1712 return false;
1713 }
1714
1715 var n = TO_INTEGER(fromIndex);
1716
1717 var k;
1718 if (n >= 0) {
1719 k = n;
1720 } else {
1721 k = length + n;
1722 if (k < 0) {
1723 k = 0;
1724 }
1725 }
1726
1727 while (k < length) {
1728 var elementK = array[k];
1729 if (SameValueZero(searchElement, elementK)) {
1730 return true;
1731 }
1732
1733 ++k;
1734 }
1735
1736 return false;
1737}
1738
1739
1740// ES2016 draft, section 22.1.3.11
1741function ArrayIncludes(searchElement, fromIndex) {
1742 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.includes");
1743
1744 var array = TO_OBJECT(this);
1745 var length = TO_LENGTH(array.length);
1746
1747 return InnerArrayIncludes(searchElement, fromIndex, array, length);
1748}
1749
1750
1751function AddArrayElement(constructor, array, i, value) {
1752 if (constructor === GlobalArray) {
1753 AddIndexedProperty(array, i, value);
1754 } else {
1755 ObjectDefineProperty(array, i, {
1756 value: value, writable: true, configurable: true, enumerable: true
1757 });
1758 }
1759}
1760
1761
1762// ES6, draft 10-14-14, section 22.1.2.1
1763function ArrayFrom(arrayLike, mapfn, receiver) {
1764 var items = TO_OBJECT(arrayLike);
1765 var mapping = !IS_UNDEFINED(mapfn);
1766
1767 if (mapping) {
1768 if (!IS_CALLABLE(mapfn)) {
1769 throw MakeTypeError(kCalledNonCallable, mapfn);
1770 }
1771 }
1772
1773 var iterable = GetMethod(items, iteratorSymbol);
1774 var k;
1775 var result;
1776 var mappedValue;
1777 var nextValue;
1778
1779 if (!IS_UNDEFINED(iterable)) {
1780 result = %IsConstructor(this) ? new this() : [];
1781
1782 var iterator = GetIterator(items, iterable);
1783
1784 k = 0;
1785 while (true) {
1786 var next = iterator.next();
1787
1788 if (!IS_RECEIVER(next)) {
1789 throw MakeTypeError(kIteratorResultNotAnObject, next);
1790 }
1791
1792 if (next.done) {
1793 result.length = k;
1794 return result;
1795 }
1796
1797 nextValue = next.value;
1798 if (mapping) {
1799 mappedValue = %_Call(mapfn, receiver, nextValue, k);
1800 } else {
1801 mappedValue = nextValue;
1802 }
1803 AddArrayElement(this, result, k, mappedValue);
1804 k++;
1805 }
1806 } else {
1807 var len = TO_LENGTH(items.length);
1808 result = %IsConstructor(this) ? new this(len) : new GlobalArray(len);
1809
1810 for (k = 0; k < len; ++k) {
1811 nextValue = items[k];
1812 if (mapping) {
1813 mappedValue = %_Call(mapfn, receiver, nextValue, k);
1814 } else {
1815 mappedValue = nextValue;
1816 }
1817 AddArrayElement(this, result, k, mappedValue);
1818 }
1819
1820 result.length = k;
1821 return result;
1822 }
1823}
1824
1825
1826// ES6, draft 05-22-14, section 22.1.2.3
1827function ArrayOf() {
1828 var length = %_ArgumentsLength();
1829 var constructor = this;
1830 // TODO: Implement IsConstructor (ES6 section 7.2.5)
1831 var array = %IsConstructor(constructor) ? new constructor(length) : [];
1832 for (var i = 0; i < length; i++) {
1833 AddArrayElement(constructor, array, i, %_Arguments(i));
1834 }
1835 array.length = length;
1836 return array;
1837}
1838
1839// -------------------------------------------------------------------
1840
1841// Set up non-enumerable constructor property on the Array.prototype
1842// object.
1843%AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray,
1844 DONT_ENUM);
1845
1846// Set up unscopable properties on the Array.prototype object.
1847var unscopables = {
1848 __proto__: null,
1849 copyWithin: true,
1850 entries: true,
1851 fill: true,
1852 find: true,
1853 findIndex: true,
1854 keys: true,
1855};
1856
1857%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
1858 DONT_ENUM | READ_ONLY);
1859
1860%FunctionSetLength(ArrayFrom, 1);
1861
1862// Set up non-enumerable functions on the Array object.
1863utils.InstallFunctions(GlobalArray, DONT_ENUM, [
1864 "from", ArrayFrom,
1865 "of", ArrayOf
1866]);
1867
1868var specialFunctions = %SpecialArrayFunctions();
1869
1870var getFunction = function(name, jsBuiltin, len) {
1871 var f = jsBuiltin;
1872 if (specialFunctions.hasOwnProperty(name)) {
1873 f = specialFunctions[name];
1874 }
1875 if (!IS_UNDEFINED(len)) {
1876 %FunctionSetLength(f, len);
1877 }
1878 return f;
1879};
1880
1881// Set up non-enumerable functions of the Array.prototype object and
1882// set their names.
1883// Manipulate the length of some of the functions to meet
1884// expectations set by ECMA-262 or Mozilla.
1885utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
1886 "toString", getFunction("toString", ArrayToString),
1887 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1888 "join", getFunction("join", ArrayJoin),
1889 "pop", getFunction("pop", ArrayPop),
1890 "push", getFunction("push", ArrayPush, 1),
1891 "reverse", getFunction("reverse", ArrayReverse),
1892 "shift", getFunction("shift", ArrayShift),
1893 "unshift", getFunction("unshift", ArrayUnshift, 1),
1894 "slice", getFunction("slice", ArraySlice, 2),
1895 "splice", getFunction("splice", ArraySplice, 2),
1896 "sort", getFunction("sort", ArraySort),
1897 "filter", getFunction("filter", ArrayFilter, 1),
1898 "forEach", getFunction("forEach", ArrayForEach, 1),
1899 "some", getFunction("some", ArraySome, 1),
1900 "every", getFunction("every", ArrayEvery, 1),
1901 "map", getFunction("map", ArrayMap, 1),
1902 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1903 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1904 "reduce", getFunction("reduce", ArrayReduce, 1),
1905 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1),
1906 "copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2),
1907 "find", getFunction("find", ArrayFind, 1),
1908 "findIndex", getFunction("findIndex", ArrayFindIndex, 1),
1909 "fill", getFunction("fill", ArrayFill, 1),
1910 "includes", getFunction("includes", ArrayIncludes, 1),
1911]);
1912
1913%FinishArrayPrototypeSetup(GlobalArray.prototype);
1914
1915// The internal Array prototype doesn't need to be fancy, since it's never
1916// exposed to user code.
1917// Adding only the functions that are actually used.
1918utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
1919 "indexOf", getFunction("indexOf", ArrayIndexOf),
1920 "join", getFunction("join", ArrayJoin),
1921 "pop", getFunction("pop", ArrayPop),
1922 "push", getFunction("push", ArrayPush),
1923 "shift", getFunction("shift", ArrayShift),
1924 "sort", getFunction("sort", ArraySort),
1925 "splice", getFunction("splice", ArraySplice)
1926]);
1927
1928utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [
1929 "join", getFunction("join", ArrayJoin),
1930 "pop", getFunction("pop", ArrayPop),
1931 "push", getFunction("push", ArrayPush),
1932 "shift", getFunction("shift", ArrayShift)
1933]);
1934
1935// V8 extras get a separate copy of InternalPackedArray. We give them the basic
1936// manipulation methods.
1937utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
1938 "push", getFunction("push", ArrayPush),
1939 "pop", getFunction("pop", ArrayPop),
1940 "shift", getFunction("shift", ArrayShift),
1941 "unshift", getFunction("unshift", ArrayUnshift),
1942 "splice", getFunction("splice", ArraySplice),
1943 "slice", getFunction("slice", ArraySlice)
1944]);
1945
1946// -------------------------------------------------------------------
1947// Exports
1948
1949utils.Export(function(to) {
1950 to.ArrayFrom = ArrayFrom;
1951 to.ArrayIndexOf = ArrayIndexOf;
1952 to.ArrayJoin = ArrayJoin;
1953 to.ArrayPush = ArrayPush;
1954 to.ArrayToString = ArrayToString;
1955 to.InnerArrayCopyWithin = InnerArrayCopyWithin;
1956 to.InnerArrayEvery = InnerArrayEvery;
1957 to.InnerArrayFill = InnerArrayFill;
1958 to.InnerArrayFilter = InnerArrayFilter;
1959 to.InnerArrayFind = InnerArrayFind;
1960 to.InnerArrayFindIndex = InnerArrayFindIndex;
1961 to.InnerArrayForEach = InnerArrayForEach;
1962 to.InnerArrayIncludes = InnerArrayIncludes;
1963 to.InnerArrayIndexOf = InnerArrayIndexOf;
1964 to.InnerArrayJoin = InnerArrayJoin;
1965 to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
1966 to.InnerArrayReduce = InnerArrayReduce;
1967 to.InnerArrayReduceRight = InnerArrayReduceRight;
1968 to.InnerArraySome = InnerArraySome;
1969 to.InnerArraySort = InnerArraySort;
1970 to.InnerArrayToLocaleString = InnerArrayToLocaleString;
1971 to.PackedArrayReverse = PackedArrayReverse;
1972});
1973
1974%InstallToContext([
1975 "array_pop", ArrayPop,
1976 "array_push", ArrayPush,
1977 "array_shift", ArrayShift,
1978 "array_splice", ArraySplice,
1979 "array_slice", ArraySlice,
1980 "array_unshift", ArrayUnshift,
1981]);
1982
1983});