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