blob: 0a77b23ca356b69dceb9ae1acb9f1cbbf2ffa369 [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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000014var FLAG_harmony_species;
15var GetIterator;
16var GetMethod;
17var GlobalArray = global.Array;
18var InternalArray = utils.InternalArray;
19var InternalPackedArray = utils.InternalPackedArray;
20var MakeTypeError;
21var MaxSimple;
22var MinSimple;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000023var ObjectHasOwnProperty;
24var ObjectToString = utils.ImportNow("object_to_string");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000025var iteratorSymbol = utils.ImportNow("iterator_symbol");
26var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
27
28utils.Import(function(from) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000029 GetIterator = from.GetIterator;
30 GetMethod = from.GetMethod;
31 MakeTypeError = from.MakeTypeError;
32 MaxSimple = from.MaxSimple;
33 MinSimple = from.MinSimple;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000034 ObjectHasOwnProperty = from.ObjectHasOwnProperty;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000035});
36
37utils.ImportFromExperimental(function(from) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000038 FLAG_harmony_species = from.FLAG_harmony_species;
39});
40
41// -------------------------------------------------------------------
42
43
44function ArraySpeciesCreate(array, length) {
45 var constructor;
Ben Murdochc5610432016-08-08 18:44:38 +010046
47 length = INVERT_NEG_ZERO(length);
48
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000049 if (FLAG_harmony_species) {
50 constructor = %ArraySpeciesConstructor(array);
51 } else {
52 constructor = GlobalArray;
53 }
54 return new constructor(length);
55}
56
57
Ben Murdochda12d292016-06-02 14:46:10 +010058function KeySortCompare(a, b) {
59 return a - b;
60}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000061
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000062function GetSortedArrayKeys(array, indices) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000063 if (IS_NUMBER(indices)) {
Ben Murdochda12d292016-06-02 14:46:10 +010064 var keys = new InternalArray();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000065 // It's an interval
66 var limit = indices;
67 for (var i = 0; i < limit; ++i) {
68 var e = array[i];
69 if (!IS_UNDEFINED(e) || i in array) {
70 keys.push(i);
71 }
72 }
Ben Murdochda12d292016-06-02 14:46:10 +010073 return keys;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000074 }
Ben Murdochda12d292016-06-02 14:46:10 +010075 return InnerArraySort(indices, indices.length, KeySortCompare);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000076}
77
78
Ben Murdochda12d292016-06-02 14:46:10 +010079function SparseJoinWithSeparatorJS(array, keys, length, convert, separator) {
80 var keys_length = keys.length;
81 var elements = new InternalArray(keys_length * 2);
82 for (var i = 0; i < keys_length; i++) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000083 var key = keys[i];
Ben Murdochda12d292016-06-02 14:46:10 +010084 var e = array[key];
85 elements[i * 2] = key;
86 elements[i * 2 + 1] = IS_STRING(e) ? e : convert(e);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000087 }
Ben Murdochda12d292016-06-02 14:46:10 +010088 return %SparseJoinWithSeparator(elements, length, separator);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000089}
90
91
92// Optimized for sparse arrays if separator is ''.
Ben Murdochda12d292016-06-02 14:46:10 +010093function SparseJoin(array, keys, convert) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000094 var keys_length = keys.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000095 var elements = new InternalArray(keys_length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000096 for (var i = 0; i < keys_length; i++) {
Ben Murdochda12d292016-06-02 14:46:10 +010097 var e = array[keys[i]];
98 elements[i] = IS_STRING(e) ? e : convert(e);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000099 }
Ben Murdochda12d292016-06-02 14:46:10 +0100100 return %StringBuilderConcat(elements, keys_length, '');
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000101}
102
103
104function UseSparseVariant(array, length, is_array, touched) {
105 // Only use the sparse variant on arrays that are likely to be sparse and the
106 // number of elements touched in the operation is relatively small compared to
107 // the overall size of the array.
Ben Murdochc5610432016-08-08 18:44:38 +0100108 if (!is_array || length < 1000 || %HasComplexElements(array)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109 return false;
110 }
111 if (!%_IsSmi(length)) {
112 return true;
113 }
114 var elements_threshold = length >> 2; // No more than 75% holes
115 var estimated_elements = %EstimateNumberOfElements(array);
116 return (estimated_elements < elements_threshold) &&
117 (touched > estimated_elements * 4);
118}
119
Ben Murdochda12d292016-06-02 14:46:10 +0100120function Stack() {
121 this.length = 0;
122 this.values = new InternalArray();
123}
124
125// Predeclare the instance variables on the prototype. Otherwise setting them in
126// the constructor will leak the instance through settings on Object.prototype.
127Stack.prototype.length = null;
128Stack.prototype.values = null;
129
130function StackPush(stack, value) {
131 stack.values[stack.length++] = value;
132}
133
134function StackPop(stack) {
135 stack.values[--stack.length] = null
136}
137
138function StackHas(stack, v) {
139 var length = stack.length;
140 var values = stack.values;
141 for (var i = 0; i < length; i++) {
142 if (values[i] === v) return true;
143 }
144 return false;
145}
146
147// Global list of arrays visited during toString, toLocaleString and
148// join invocations.
149var visited_arrays = new Stack();
150
151function DoJoin(array, length, is_array, separator, convert) {
152 if (UseSparseVariant(array, length, is_array, length)) {
153 %NormalizeElements(array);
154 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, length));
155 if (separator === '') {
156 if (keys.length === 0) return '';
157 return SparseJoin(array, keys, convert);
158 } else {
159 return SparseJoinWithSeparatorJS(array, keys, length, convert, separator);
160 }
161 }
162
163 // Fast case for one-element arrays.
164 if (length === 1) {
165 var e = array[0];
166 return IS_STRING(e) ? e : convert(e);
167 }
168
169 // Construct an array for the elements.
170 var elements = new InternalArray(length);
171
172 // We pull the empty separator check outside the loop for speed!
173 if (separator === '') {
174 for (var i = 0; i < length; i++) {
175 var e = array[i];
176 elements[i] = IS_STRING(e) ? e : convert(e);
177 }
178 return %StringBuilderConcat(elements, length, '');
179 }
180 // Non-empty separator case.
181 // If the first element is a number then use the heuristic that the
182 // remaining elements are also likely to be numbers.
183 var e = array[0];
184 if (IS_NUMBER(e)) {
185 elements[0] = %_NumberToString(e);
186 for (var i = 1; i < length; i++) {
187 e = array[i];
188 if (IS_NUMBER(e)) {
189 elements[i] = %_NumberToString(e);
190 } else {
191 elements[i] = IS_STRING(e) ? e : convert(e);
192 }
193 }
194 } else {
195 elements[0] = IS_STRING(e) ? e : convert(e);
196 for (var i = 1; i < length; i++) {
197 e = array[i];
198 elements[i] = IS_STRING(e) ? e : convert(e);
199 }
200 }
201 return %StringBuilderJoin(elements, length, separator);
202}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000203
204function Join(array, length, separator, convert) {
Ben Murdochda12d292016-06-02 14:46:10 +0100205 if (length === 0) return '';
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000206
207 var is_array = IS_ARRAY(array);
208
209 if (is_array) {
210 // If the array is cyclic, return the empty string for already
211 // visited arrays.
Ben Murdochda12d292016-06-02 14:46:10 +0100212 if (StackHas(visited_arrays, array)) return '';
213 StackPush(visited_arrays, array);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000214 }
215
216 // Attempt to convert the elements.
217 try {
Ben Murdochda12d292016-06-02 14:46:10 +0100218 return DoJoin(array, length, is_array, separator, convert);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000219 } finally {
220 // Make sure to remove the last element of the visited array no
221 // matter what happens.
Ben Murdochda12d292016-06-02 14:46:10 +0100222 if (is_array) StackPop(visited_arrays);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000223 }
224}
225
226
227function ConvertToString(x) {
Ben Murdochda12d292016-06-02 14:46:10 +0100228 if (IS_NULL_OR_UNDEFINED(x)) return '';
229 return TO_STRING(x);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000230}
231
232
233function ConvertToLocaleString(e) {
Ben Murdochda12d292016-06-02 14:46:10 +0100234 if (IS_NULL_OR_UNDEFINED(e)) return '';
235 return TO_STRING(e.toLocaleString());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000236}
237
238
239// This function implements the optimized splice implementation that can use
240// special array operations to handle sparse arrays in a sensible fashion.
241function SparseSlice(array, start_i, del_count, len, deleted_elements) {
242 // Move deleted elements to a new array (the return value from splice).
243 var indices = %GetArrayKeys(array, start_i + del_count);
244 if (IS_NUMBER(indices)) {
245 var limit = indices;
246 for (var i = start_i; i < limit; ++i) {
247 var current = array[i];
248 if (!IS_UNDEFINED(current) || i in array) {
Ben Murdochc5610432016-08-08 18:44:38 +0100249 %CreateDataProperty(deleted_elements, i - start_i, current);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000250 }
251 }
252 } else {
253 var length = indices.length;
254 for (var k = 0; k < length; ++k) {
255 var key = indices[k];
Ben Murdochda12d292016-06-02 14:46:10 +0100256 if (key >= start_i) {
257 var current = array[key];
258 if (!IS_UNDEFINED(current) || key in array) {
Ben Murdochc5610432016-08-08 18:44:38 +0100259 %CreateDataProperty(deleted_elements, key - start_i, current);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000260 }
261 }
262 }
263 }
264}
265
266
267// This function implements the optimized splice implementation that can use
268// special array operations to handle sparse arrays in a sensible fashion.
269function SparseMove(array, start_i, del_count, len, num_additional_args) {
270 // Bail out if no moving is necessary.
271 if (num_additional_args === del_count) return;
272 // Move data to new array.
273 var new_array = new InternalArray(
274 // Clamp array length to 2^32-1 to avoid early RangeError.
275 MinSimple(len - del_count + num_additional_args, 0xffffffff));
276 var big_indices;
277 var indices = %GetArrayKeys(array, len);
278 if (IS_NUMBER(indices)) {
279 var limit = indices;
280 for (var i = 0; i < start_i && i < limit; ++i) {
281 var current = array[i];
282 if (!IS_UNDEFINED(current) || i in array) {
283 new_array[i] = current;
284 }
285 }
286 for (var i = start_i + del_count; i < limit; ++i) {
287 var current = array[i];
288 if (!IS_UNDEFINED(current) || i in array) {
289 new_array[i - del_count + num_additional_args] = current;
290 }
291 }
292 } else {
293 var length = indices.length;
294 for (var k = 0; k < length; ++k) {
295 var key = indices[k];
Ben Murdochda12d292016-06-02 14:46:10 +0100296 if (key < start_i) {
297 var current = array[key];
298 if (!IS_UNDEFINED(current) || key in array) {
299 new_array[key] = current;
300 }
301 } else if (key >= start_i + del_count) {
302 var current = array[key];
303 if (!IS_UNDEFINED(current) || key in array) {
304 var new_key = key - del_count + num_additional_args;
305 new_array[new_key] = current;
306 if (new_key > 0xfffffffe) {
307 big_indices = big_indices || new InternalArray();
308 big_indices.push(new_key);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000309 }
310 }
311 }
312 }
313 }
314 // Move contents of new_array into this array
315 %MoveArrayContents(new_array, array);
316 // Add any moved values that aren't elements anymore.
317 if (!IS_UNDEFINED(big_indices)) {
318 var length = big_indices.length;
319 for (var i = 0; i < length; ++i) {
320 var key = big_indices[i];
321 array[key] = new_array[key];
322 }
323 }
324}
325
326
327// This is part of the old simple-minded splice. We are using it either
328// because the receiver is not an array (so we have no choice) or because we
329// know we are not deleting or moving a lot of elements.
330function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
331 var is_array = IS_ARRAY(array);
332 for (var i = 0; i < del_count; i++) {
333 var index = start_i + i;
334 if (HAS_INDEX(array, index, is_array)) {
335 var current = array[index];
Ben Murdochc5610432016-08-08 18:44:38 +0100336 %CreateDataProperty(deleted_elements, i, current);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000337 }
338 }
339}
340
341
342function SimpleMove(array, start_i, del_count, len, num_additional_args) {
343 var is_array = IS_ARRAY(array);
344 if (num_additional_args !== del_count) {
345 // Move the existing elements after the elements to be deleted
346 // to the right position in the resulting array.
347 if (num_additional_args > del_count) {
348 for (var i = len - del_count; i > start_i; i--) {
349 var from_index = i + del_count - 1;
350 var to_index = i + num_additional_args - 1;
351 if (HAS_INDEX(array, from_index, is_array)) {
352 array[to_index] = array[from_index];
353 } else {
354 delete array[to_index];
355 }
356 }
357 } else {
358 for (var i = start_i; i < len - del_count; i++) {
359 var from_index = i + del_count;
360 var to_index = i + num_additional_args;
361 if (HAS_INDEX(array, from_index, is_array)) {
362 array[to_index] = array[from_index];
363 } else {
364 delete array[to_index];
365 }
366 }
367 for (var i = len; i > len - del_count + num_additional_args; i--) {
368 delete array[i - 1];
369 }
370 }
371 }
372}
373
374
375// -------------------------------------------------------------------
376
377
378function ArrayToString() {
379 var array;
380 var func;
381 if (IS_ARRAY(this)) {
382 func = this.join;
383 if (func === ArrayJoin) {
384 return Join(this, this.length, ',', ConvertToString);
385 }
386 array = this;
387 } else {
388 array = TO_OBJECT(this);
389 func = array.join;
390 }
391 if (!IS_CALLABLE(func)) {
392 return %_Call(ObjectToString, array);
393 }
394 return %_Call(func, array);
395}
396
397
398function InnerArrayToLocaleString(array, length) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100399 var len = TO_LENGTH(length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000400 if (len === 0) return "";
401 return Join(array, len, ',', ConvertToLocaleString);
402}
403
404
405function ArrayToLocaleString() {
406 var array = TO_OBJECT(this);
407 var arrayLen = array.length;
408 return InnerArrayToLocaleString(array, arrayLen);
409}
410
411
412function InnerArrayJoin(separator, array, length) {
413 if (IS_UNDEFINED(separator)) {
414 separator = ',';
415 } else {
416 separator = TO_STRING(separator);
417 }
418
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000419 // Fast case for one-element arrays.
420 if (length === 1) {
421 var e = array[0];
422 if (IS_NULL_OR_UNDEFINED(e)) return '';
423 return TO_STRING(e);
424 }
425
426 return Join(array, length, separator, ConvertToString);
427}
428
429
430function ArrayJoin(separator) {
431 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
432
433 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100434 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000435
436 return InnerArrayJoin(separator, array, length);
437}
438
439
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000440// Removes the last element from the array and returns it. See
441// ECMA-262, section 15.4.4.6.
442function ArrayPop() {
443 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
444
445 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100446 var n = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000447 if (n == 0) {
448 array.length = n;
449 return;
450 }
451
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000452 n--;
453 var value = array[n];
454 %DeleteProperty_Strict(array, n);
455 array.length = n;
456 return value;
457}
458
459
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000460// Appends the arguments to the end of the array and returns the new
461// length of the array. See ECMA-262, section 15.4.4.7.
462function ArrayPush() {
463 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
464
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000465 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100466 var n = TO_LENGTH(array.length);
467 var m = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000468
Ben Murdochc5610432016-08-08 18:44:38 +0100469 // Subtract n from kMaxSafeInteger rather than testing m + n >
470 // kMaxSafeInteger. n may already be kMaxSafeInteger. In that case adding
471 // e.g., 1 would not be safe.
472 if (m > kMaxSafeInteger - n) throw MakeTypeError(kPushPastSafeLength, m, n);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000473
474 for (var i = 0; i < m; i++) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100475 array[i+n] = arguments[i];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000476 }
477
478 var new_length = n + m;
479 array.length = new_length;
480 return new_length;
481}
482
483
484// For implementing reverse() on large, sparse arrays.
485function SparseReverse(array, len) {
486 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
487 var high_counter = keys.length - 1;
488 var low_counter = 0;
489 while (low_counter <= high_counter) {
490 var i = keys[low_counter];
491 var j = keys[high_counter];
492
493 var j_complement = len - j - 1;
494 var low, high;
495
496 if (j_complement <= i) {
497 high = j;
498 while (keys[--high_counter] == j) { }
499 low = j_complement;
500 }
501 if (j_complement >= i) {
502 low = i;
503 while (keys[++low_counter] == i) { }
504 high = len - i - 1;
505 }
506
507 var current_i = array[low];
508 if (!IS_UNDEFINED(current_i) || low in array) {
509 var current_j = array[high];
510 if (!IS_UNDEFINED(current_j) || high in array) {
511 array[low] = current_j;
512 array[high] = current_i;
513 } else {
514 array[high] = current_i;
515 delete array[low];
516 }
517 } else {
518 var current_j = array[high];
519 if (!IS_UNDEFINED(current_j) || high in array) {
520 array[low] = current_j;
521 delete array[high];
522 }
523 }
524 }
525}
526
527function PackedArrayReverse(array, len) {
528 var j = len - 1;
529 for (var i = 0; i < j; i++, j--) {
530 var current_i = array[i];
531 var current_j = array[j];
532 array[i] = current_j;
533 array[j] = current_i;
534 }
535 return array;
536}
537
538
539function GenericArrayReverse(array, len) {
540 var j = len - 1;
541 for (var i = 0; i < j; i++, j--) {
542 if (i in array) {
543 var current_i = array[i];
544 if (j in array) {
545 var current_j = array[j];
546 array[i] = current_j;
547 array[j] = current_i;
548 } else {
549 array[j] = current_i;
550 delete array[i];
551 }
552 } else {
553 if (j in array) {
554 var current_j = array[j];
555 array[i] = current_j;
556 delete array[j];
557 }
558 }
559 }
560 return array;
561}
562
563
564function ArrayReverse() {
565 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
566
567 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100568 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000569 var isArray = IS_ARRAY(array);
570
571 if (UseSparseVariant(array, len, isArray, len)) {
572 %NormalizeElements(array);
573 SparseReverse(array, len);
574 return array;
575 } else if (isArray && %_HasFastPackedElements(array)) {
576 return PackedArrayReverse(array, len);
577 } else {
578 return GenericArrayReverse(array, len);
579 }
580}
581
582
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000583function ArrayShift() {
584 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
585
586 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100587 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000588
589 if (len === 0) {
590 array.length = 0;
591 return;
592 }
593
594 if (%object_is_sealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed);
595
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000596 var first = array[0];
597
598 if (UseSparseVariant(array, len, IS_ARRAY(array), len)) {
599 SparseMove(array, 0, 1, len, 0);
600 } else {
601 SimpleMove(array, 0, 1, len, 0);
602 }
603
604 array.length = len - 1;
605
606 return first;
607}
608
609
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000610function ArrayUnshift(arg1) { // length == 1
611 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
612
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000613 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100614 var len = TO_LENGTH(array.length);
615 var num_arguments = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000616
617 if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) &&
618 !%object_is_sealed(array)) {
619 SparseMove(array, 0, 0, len, num_arguments);
620 } else {
621 SimpleMove(array, 0, 0, len, num_arguments);
622 }
623
624 for (var i = 0; i < num_arguments; i++) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100625 array[i] = arguments[i];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000626 }
627
628 var new_length = len + num_arguments;
629 array.length = new_length;
630 return new_length;
631}
632
633
634function ArraySlice(start, end) {
635 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
636
637 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100638 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000639 var start_i = TO_INTEGER(start);
640 var end_i = len;
641
642 if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
643
644 if (start_i < 0) {
645 start_i += len;
646 if (start_i < 0) start_i = 0;
647 } else {
648 if (start_i > len) start_i = len;
649 }
650
651 if (end_i < 0) {
652 end_i += len;
653 if (end_i < 0) end_i = 0;
654 } else {
655 if (end_i > len) end_i = len;
656 }
657
658 var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
659
660 if (end_i < start_i) return result;
661
662 if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
663 %NormalizeElements(array);
664 %NormalizeElements(result);
665 SparseSlice(array, start_i, end_i - start_i, len, result);
666 } else {
667 SimpleSlice(array, start_i, end_i - start_i, len, result);
668 }
669
670 result.length = end_i - start_i;
671
672 return result;
673}
674
675
676function ComputeSpliceStartIndex(start_i, len) {
677 if (start_i < 0) {
678 start_i += len;
679 return start_i < 0 ? 0 : start_i;
680 }
681
682 return start_i > len ? len : start_i;
683}
684
685
686function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
687 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
688 // given as a request to delete all the elements from the start.
689 // And it differs from the case of undefined delete count.
690 // This does not follow ECMA-262, but we do the same for
691 // compatibility.
692 var del_count = 0;
693 if (num_arguments == 1)
694 return len - start_i;
695
696 del_count = TO_INTEGER(delete_count);
697 if (del_count < 0)
698 return 0;
699
700 if (del_count > len - start_i)
701 return len - start_i;
702
703 return del_count;
704}
705
706
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000707function ArraySplice(start, delete_count) {
708 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
709
Ben Murdoch097c5b22016-05-18 11:27:45 +0100710 var num_arguments = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000711 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100712 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000713 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
714 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
715 start_i);
716 var deleted_elements = ArraySpeciesCreate(array, del_count);
717 deleted_elements.length = del_count;
718 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
719
720 if (del_count != num_elements_to_add && %object_is_sealed(array)) {
721 throw MakeTypeError(kArrayFunctionsOnSealed);
722 } else if (del_count > 0 && %object_is_frozen(array)) {
723 throw MakeTypeError(kArrayFunctionsOnFrozen);
724 }
725
726 var changed_elements = del_count;
727 if (num_elements_to_add != del_count) {
728 // If the slice needs to do a actually move elements after the insertion
729 // point, then include those in the estimate of changed elements.
730 changed_elements += len - start_i - del_count;
731 }
732 if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
733 %NormalizeElements(array);
734 %NormalizeElements(deleted_elements);
735 SparseSlice(array, start_i, del_count, len, deleted_elements);
736 SparseMove(array, start_i, del_count, len, num_elements_to_add);
737 } else {
738 SimpleSlice(array, start_i, del_count, len, deleted_elements);
739 SimpleMove(array, start_i, del_count, len, num_elements_to_add);
740 }
741
742 // Insert the arguments into the resulting array in
743 // place of the deleted elements.
744 var i = start_i;
745 var arguments_index = 2;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100746 var arguments_length = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000747 while (arguments_index < arguments_length) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100748 array[i++] = arguments[arguments_index++];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000749 }
750 array.length = len - del_count + num_elements_to_add;
751
752 // Return the deleted elements.
753 return deleted_elements;
754}
755
756
757function InnerArraySort(array, length, comparefn) {
758 // In-place QuickSort algorithm.
759 // For short (length <= 22) arrays, insertion sort is used for efficiency.
760
761 if (!IS_CALLABLE(comparefn)) {
762 comparefn = function (x, y) {
763 if (x === y) return 0;
764 if (%_IsSmi(x) && %_IsSmi(y)) {
765 return %SmiLexicographicCompare(x, y);
766 }
767 x = TO_STRING(x);
768 y = TO_STRING(y);
769 if (x == y) return 0;
770 else return x < y ? -1 : 1;
771 };
772 }
773 var InsertionSort = function InsertionSort(a, from, to) {
774 for (var i = from + 1; i < to; i++) {
775 var element = a[i];
776 for (var j = i - 1; j >= from; j--) {
777 var tmp = a[j];
778 var order = comparefn(tmp, element);
779 if (order > 0) {
780 a[j + 1] = tmp;
781 } else {
782 break;
783 }
784 }
785 a[j + 1] = element;
786 }
787 };
788
789 var GetThirdIndex = function(a, from, to) {
790 var t_array = new InternalArray();
791 // Use both 'from' and 'to' to determine the pivot candidates.
792 var increment = 200 + ((to - from) & 15);
793 var j = 0;
794 from += 1;
795 to -= 1;
796 for (var i = from; i < to; i += increment) {
797 t_array[j] = [i, a[i]];
798 j++;
799 }
800 t_array.sort(function(a, b) {
801 return comparefn(a[1], b[1]);
802 });
803 var third_index = t_array[t_array.length >> 1][0];
804 return third_index;
805 }
806
807 var QuickSort = function QuickSort(a, from, to) {
808 var third_index = 0;
809 while (true) {
810 // Insertion sort is faster for short arrays.
811 if (to - from <= 10) {
812 InsertionSort(a, from, to);
813 return;
814 }
815 if (to - from > 1000) {
816 third_index = GetThirdIndex(a, from, to);
817 } else {
818 third_index = from + ((to - from) >> 1);
819 }
820 // Find a pivot as the median of first, last and middle element.
821 var v0 = a[from];
822 var v1 = a[to - 1];
823 var v2 = a[third_index];
824 var c01 = comparefn(v0, v1);
825 if (c01 > 0) {
826 // v1 < v0, so swap them.
827 var tmp = v0;
828 v0 = v1;
829 v1 = tmp;
830 } // v0 <= v1.
831 var c02 = comparefn(v0, v2);
832 if (c02 >= 0) {
833 // v2 <= v0 <= v1.
834 var tmp = v0;
835 v0 = v2;
836 v2 = v1;
837 v1 = tmp;
838 } else {
839 // v0 <= v1 && v0 < v2
840 var c12 = comparefn(v1, v2);
841 if (c12 > 0) {
842 // v0 <= v2 < v1
843 var tmp = v1;
844 v1 = v2;
845 v2 = tmp;
846 }
847 }
848 // v0 <= v1 <= v2
849 a[from] = v0;
850 a[to - 1] = v2;
851 var pivot = v1;
852 var low_end = from + 1; // Upper bound of elements lower than pivot.
853 var high_start = to - 1; // Lower bound of elements greater than pivot.
854 a[third_index] = a[low_end];
855 a[low_end] = pivot;
856
857 // From low_end to i are elements equal to pivot.
858 // From i to high_start are elements that haven't been compared yet.
859 partition: for (var i = low_end + 1; i < high_start; i++) {
860 var element = a[i];
861 var order = comparefn(element, pivot);
862 if (order < 0) {
863 a[i] = a[low_end];
864 a[low_end] = element;
865 low_end++;
866 } else if (order > 0) {
867 do {
868 high_start--;
869 if (high_start == i) break partition;
870 var top_elem = a[high_start];
871 order = comparefn(top_elem, pivot);
872 } while (order > 0);
873 a[i] = a[high_start];
874 a[high_start] = element;
875 if (order < 0) {
876 element = a[i];
877 a[i] = a[low_end];
878 a[low_end] = element;
879 low_end++;
880 }
881 }
882 }
883 if (to - high_start < low_end - from) {
884 QuickSort(a, high_start, to);
885 to = low_end;
886 } else {
887 QuickSort(a, from, low_end);
888 from = high_start;
889 }
890 }
891 };
892
893 // Copy elements in the range 0..length from obj's prototype chain
894 // to obj itself, if obj has holes. Return one more than the maximal index
895 // of a prototype property.
896 var CopyFromPrototype = function CopyFromPrototype(obj, length) {
897 var max = 0;
Ben Murdochc5610432016-08-08 18:44:38 +0100898 for (var proto = %object_get_prototype_of(obj); proto;
899 proto = %object_get_prototype_of(proto)) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100900 var indices = IS_PROXY(proto) ? length : %GetArrayKeys(proto, length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000901 if (IS_NUMBER(indices)) {
902 // It's an interval.
903 var proto_length = indices;
904 for (var i = 0; i < proto_length; i++) {
905 if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
906 obj[i] = proto[i];
907 if (i >= max) { max = i + 1; }
908 }
909 }
910 } else {
911 for (var i = 0; i < indices.length; i++) {
912 var index = indices[i];
Ben Murdochda12d292016-06-02 14:46:10 +0100913 if (!HAS_OWN_PROPERTY(obj, index) && HAS_OWN_PROPERTY(proto, index)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000914 obj[index] = proto[index];
915 if (index >= max) { max = index + 1; }
916 }
917 }
918 }
919 }
920 return max;
921 };
922
923 // Set a value of "undefined" on all indices in the range from..to
924 // where a prototype of obj has an element. I.e., shadow all prototype
925 // elements in that range.
926 var ShadowPrototypeElements = function(obj, from, to) {
Ben Murdochc5610432016-08-08 18:44:38 +0100927 for (var proto = %object_get_prototype_of(obj); proto;
928 proto = %object_get_prototype_of(proto)) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100929 var indices = IS_PROXY(proto) ? to : %GetArrayKeys(proto, to);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000930 if (IS_NUMBER(indices)) {
931 // It's an interval.
932 var proto_length = indices;
933 for (var i = from; i < proto_length; i++) {
934 if (HAS_OWN_PROPERTY(proto, i)) {
935 obj[i] = UNDEFINED;
936 }
937 }
938 } else {
939 for (var i = 0; i < indices.length; i++) {
940 var index = indices[i];
Ben Murdochda12d292016-06-02 14:46:10 +0100941 if (from <= index && HAS_OWN_PROPERTY(proto, index)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000942 obj[index] = UNDEFINED;
943 }
944 }
945 }
946 }
947 };
948
949 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
950 // Copy defined elements from the end to fill in all holes and undefineds
951 // in the beginning of the array. Write undefineds and holes at the end
952 // after loop is finished.
953 var first_undefined = 0;
954 var last_defined = length - 1;
955 var num_holes = 0;
956 while (first_undefined < last_defined) {
957 // Find first undefined element.
958 while (first_undefined < last_defined &&
959 !IS_UNDEFINED(obj[first_undefined])) {
960 first_undefined++;
961 }
962 // Maintain the invariant num_holes = the number of holes in the original
963 // array with indices <= first_undefined or > last_defined.
964 if (!HAS_OWN_PROPERTY(obj, first_undefined)) {
965 num_holes++;
966 }
967
968 // Find last defined element.
969 while (first_undefined < last_defined &&
970 IS_UNDEFINED(obj[last_defined])) {
971 if (!HAS_OWN_PROPERTY(obj, last_defined)) {
972 num_holes++;
973 }
974 last_defined--;
975 }
976 if (first_undefined < last_defined) {
977 // Fill in hole or undefined.
978 obj[first_undefined] = obj[last_defined];
979 obj[last_defined] = UNDEFINED;
980 }
981 }
982 // If there were any undefineds in the entire array, first_undefined
983 // points to one past the last defined element. Make this true if
984 // there were no undefineds, as well, so that first_undefined == number
985 // of defined elements.
986 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
987 // Fill in the undefineds and the holes. There may be a hole where
988 // an undefined should be and vice versa.
989 var i;
990 for (i = first_undefined; i < length - num_holes; i++) {
991 obj[i] = UNDEFINED;
992 }
993 for (i = length - num_holes; i < length; i++) {
994 // For compatability with Webkit, do not expose elements in the prototype.
Ben Murdochc5610432016-08-08 18:44:38 +0100995 if (i in %object_get_prototype_of(obj)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000996 obj[i] = UNDEFINED;
997 } else {
998 delete obj[i];
999 }
1000 }
1001
1002 // Return the number of defined elements.
1003 return first_undefined;
1004 };
1005
1006 if (length < 2) return array;
1007
1008 var is_array = IS_ARRAY(array);
1009 var max_prototype_element;
1010 if (!is_array) {
1011 // For compatibility with JSC, we also sort elements inherited from
1012 // the prototype chain on non-Array objects.
1013 // We do this by copying them to this object and sorting only
1014 // own elements. This is not very efficient, but sorting with
1015 // inherited elements happens very, very rarely, if at all.
1016 // The specification allows "implementation dependent" behavior
1017 // if an element on the prototype chain has an element that
1018 // might interact with sorting.
1019 max_prototype_element = CopyFromPrototype(array, length);
1020 }
1021
1022 // %RemoveArrayHoles returns -1 if fast removal is not supported.
1023 var num_non_undefined = %RemoveArrayHoles(array, length);
1024
1025 if (num_non_undefined == -1) {
Ben Murdochc5610432016-08-08 18:44:38 +01001026 // There were indexed accessors in the array.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001027 // Move array holes and undefineds to the end using a Javascript function
Ben Murdochc5610432016-08-08 18:44:38 +01001028 // that is safe in the presence of accessors.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001029 num_non_undefined = SafeRemoveArrayHoles(array);
1030 }
1031
1032 QuickSort(array, 0, num_non_undefined);
1033
1034 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
1035 // For compatibility with JSC, we shadow any elements in the prototype
1036 // chain that has become exposed by sort moving a hole to its position.
1037 ShadowPrototypeElements(array, num_non_undefined, max_prototype_element);
1038 }
1039
1040 return array;
1041}
1042
1043
1044function ArraySort(comparefn) {
1045 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
1046
1047 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001048 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001049 return InnerArraySort(array, length, comparefn);
1050}
1051
1052
1053// The following functions cannot be made efficient on sparse arrays while
1054// preserving the semantics, since the calls to the receiver function can add
1055// or delete elements from the array.
1056function InnerArrayFilter(f, receiver, array, length, result) {
1057 var result_length = 0;
1058 var is_array = IS_ARRAY(array);
1059 for (var i = 0; i < length; i++) {
1060 if (HAS_INDEX(array, i, is_array)) {
1061 var element = array[i];
1062 if (%_Call(f, receiver, element, i, array)) {
Ben Murdochc5610432016-08-08 18:44:38 +01001063 %CreateDataProperty(result, result_length, element);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001064 result_length++;
1065 }
1066 }
1067 }
1068 return result;
1069}
1070
1071
1072
1073function ArrayFilter(f, receiver) {
1074 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
1075
1076 // Pull out the length so that modifications to the length in the
1077 // loop will not affect the looping and side effects are visible.
1078 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001079 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001080 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1081 var result = ArraySpeciesCreate(array, 0);
1082 return InnerArrayFilter(f, receiver, array, length, result);
1083}
1084
1085
1086function InnerArrayForEach(f, receiver, array, length) {
1087 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1088
1089 var is_array = IS_ARRAY(array);
Ben Murdochda12d292016-06-02 14:46:10 +01001090 if (IS_UNDEFINED(receiver)) {
1091 for (var i = 0; i < length; i++) {
1092 if (HAS_INDEX(array, i, is_array)) {
1093 var element = array[i];
1094 f(element, i, array);
1095 }
1096 }
1097 } else {
1098 for (var i = 0; i < length; i++) {
1099 if (HAS_INDEX(array, i, is_array)) {
1100 var element = array[i];
1101 %_Call(f, receiver, element, i, array);
1102 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001103 }
1104 }
1105}
1106
1107
1108function ArrayForEach(f, receiver) {
1109 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
1110
1111 // Pull out the length so that modifications to the length in the
1112 // loop will not affect the looping and side effects are visible.
1113 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001114 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001115 InnerArrayForEach(f, receiver, array, length);
1116}
1117
1118
1119function InnerArraySome(f, receiver, array, length) {
1120 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1121
1122 var is_array = IS_ARRAY(array);
1123 for (var i = 0; i < length; i++) {
1124 if (HAS_INDEX(array, i, is_array)) {
1125 var element = array[i];
1126 if (%_Call(f, receiver, element, i, array)) return true;
1127 }
1128 }
1129 return false;
1130}
1131
1132
1133// Executes the function once for each element present in the
1134// array until it finds one where callback returns true.
1135function ArraySome(f, receiver) {
1136 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
1137
1138 // Pull out the length so that modifications to the length in the
1139 // loop will not affect the looping and side effects are visible.
1140 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001141 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001142 return InnerArraySome(f, receiver, array, length);
1143}
1144
1145
1146function InnerArrayEvery(f, receiver, array, length) {
1147 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1148
1149 var is_array = IS_ARRAY(array);
1150 for (var i = 0; i < length; i++) {
1151 if (HAS_INDEX(array, i, is_array)) {
1152 var element = array[i];
1153 if (!%_Call(f, receiver, element, i, array)) return false;
1154 }
1155 }
1156 return true;
1157}
1158
1159function ArrayEvery(f, receiver) {
1160 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
1161
1162 // Pull out the length so that modifications to the length in the
1163 // loop will not affect the looping and side effects are visible.
1164 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001165 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001166 return InnerArrayEvery(f, receiver, array, length);
1167}
1168
1169
1170function ArrayMap(f, receiver) {
1171 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
1172
1173 // Pull out the length so that modifications to the length in the
1174 // loop will not affect the looping and side effects are visible.
1175 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001176 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001177 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1178 var result = ArraySpeciesCreate(array, length);
1179 var is_array = IS_ARRAY(array);
1180 for (var i = 0; i < length; i++) {
1181 if (HAS_INDEX(array, i, is_array)) {
1182 var element = array[i];
Ben Murdochc5610432016-08-08 18:44:38 +01001183 %CreateDataProperty(result, i, %_Call(f, receiver, element, i, array));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001184 }
1185 }
1186 return result;
1187}
1188
1189
1190// For .indexOf, we don't need to pass in the number of arguments
1191// at the callsite since ToInteger(undefined) == 0; however, for
1192// .lastIndexOf, we need to pass it, since the behavior for passing
1193// undefined is 0 but for not including the argument is length-1.
1194function InnerArrayIndexOf(array, element, index, length) {
1195 if (length == 0) return -1;
1196 if (IS_UNDEFINED(index)) {
1197 index = 0;
1198 } else {
Ben Murdochc5610432016-08-08 18:44:38 +01001199 index = INVERT_NEG_ZERO(TO_INTEGER(index));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001200 // If index is negative, index from the end of the array.
1201 if (index < 0) {
1202 index = length + index;
1203 // If index is still negative, search the entire array.
1204 if (index < 0) index = 0;
1205 }
1206 }
1207 var min = index;
1208 var max = length;
1209 if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) {
1210 %NormalizeElements(array);
1211 var indices = %GetArrayKeys(array, length);
1212 if (IS_NUMBER(indices)) {
1213 // It's an interval.
1214 max = indices; // Capped by length already.
1215 // Fall through to loop below.
1216 } else {
1217 if (indices.length == 0) return -1;
1218 // Get all the keys in sorted order.
1219 var sortedKeys = GetSortedArrayKeys(array, indices);
1220 var n = sortedKeys.length;
1221 var i = 0;
1222 while (i < n && sortedKeys[i] < index) i++;
1223 while (i < n) {
1224 var key = sortedKeys[i];
Ben Murdochda12d292016-06-02 14:46:10 +01001225 if (array[key] === element) return key;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001226 i++;
1227 }
1228 return -1;
1229 }
1230 }
1231 // Lookup through the array.
1232 if (!IS_UNDEFINED(element)) {
1233 for (var i = min; i < max; i++) {
1234 if (array[i] === element) return i;
1235 }
1236 return -1;
1237 }
1238 // Lookup through the array.
1239 for (var i = min; i < max; i++) {
1240 if (IS_UNDEFINED(array[i]) && i in array) {
1241 return i;
1242 }
1243 }
1244 return -1;
1245}
1246
1247
1248function ArrayIndexOf(element, index) {
1249 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
1250
Ben Murdoch097c5b22016-05-18 11:27:45 +01001251 var length = TO_LENGTH(this.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001252 return InnerArrayIndexOf(this, element, index, length);
1253}
1254
1255
1256function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) {
1257 if (length == 0) return -1;
1258 if (argumentsLength < 2) {
1259 index = length - 1;
1260 } else {
Ben Murdochc5610432016-08-08 18:44:38 +01001261 index = INVERT_NEG_ZERO(TO_INTEGER(index));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001262 // If index is negative, index from end of the array.
1263 if (index < 0) index += length;
1264 // If index is still negative, do not search the array.
1265 if (index < 0) return -1;
1266 else if (index >= length) index = length - 1;
1267 }
1268 var min = 0;
1269 var max = index;
1270 if (UseSparseVariant(array, length, IS_ARRAY(array), index)) {
1271 %NormalizeElements(array);
1272 var indices = %GetArrayKeys(array, index + 1);
1273 if (IS_NUMBER(indices)) {
1274 // It's an interval.
1275 max = indices; // Capped by index already.
1276 // Fall through to loop below.
1277 } else {
1278 if (indices.length == 0) return -1;
1279 // Get all the keys in sorted order.
1280 var sortedKeys = GetSortedArrayKeys(array, indices);
1281 var i = sortedKeys.length - 1;
1282 while (i >= 0) {
1283 var key = sortedKeys[i];
Ben Murdochda12d292016-06-02 14:46:10 +01001284 if (array[key] === element) return key;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001285 i--;
1286 }
1287 return -1;
1288 }
1289 }
1290 // Lookup through the array.
1291 if (!IS_UNDEFINED(element)) {
1292 for (var i = max; i >= min; i--) {
1293 if (array[i] === element) return i;
1294 }
1295 return -1;
1296 }
1297 for (var i = max; i >= min; i--) {
1298 if (IS_UNDEFINED(array[i]) && i in array) {
1299 return i;
1300 }
1301 }
1302 return -1;
1303}
1304
1305
1306function ArrayLastIndexOf(element, index) {
1307 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
1308
Ben Murdoch097c5b22016-05-18 11:27:45 +01001309 var length = TO_LENGTH(this.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001310 return InnerArrayLastIndexOf(this, element, index, length,
Ben Murdoch097c5b22016-05-18 11:27:45 +01001311 arguments.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001312}
1313
1314
1315function InnerArrayReduce(callback, current, array, length, argumentsLength) {
1316 if (!IS_CALLABLE(callback)) {
1317 throw MakeTypeError(kCalledNonCallable, callback);
1318 }
1319
1320 var is_array = IS_ARRAY(array);
1321 var i = 0;
1322 find_initial: if (argumentsLength < 2) {
1323 for (; i < length; i++) {
1324 if (HAS_INDEX(array, i, is_array)) {
1325 current = array[i++];
1326 break find_initial;
1327 }
1328 }
1329 throw MakeTypeError(kReduceNoInitial);
1330 }
1331
1332 for (; i < length; i++) {
1333 if (HAS_INDEX(array, i, is_array)) {
1334 var element = array[i];
1335 current = callback(current, element, i, array);
1336 }
1337 }
1338 return current;
1339}
1340
1341
1342function ArrayReduce(callback, current) {
1343 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
1344
1345 // Pull out the length so that modifications to the length in the
1346 // loop will not affect the looping and side effects are visible.
1347 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001348 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001349 return InnerArrayReduce(callback, current, array, length,
Ben Murdoch097c5b22016-05-18 11:27:45 +01001350 arguments.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001351}
1352
1353
1354function InnerArrayReduceRight(callback, current, array, length,
1355 argumentsLength) {
1356 if (!IS_CALLABLE(callback)) {
1357 throw MakeTypeError(kCalledNonCallable, callback);
1358 }
1359
1360 var is_array = IS_ARRAY(array);
1361 var i = length - 1;
1362 find_initial: if (argumentsLength < 2) {
1363 for (; i >= 0; i--) {
1364 if (HAS_INDEX(array, i, is_array)) {
1365 current = array[i--];
1366 break find_initial;
1367 }
1368 }
1369 throw MakeTypeError(kReduceNoInitial);
1370 }
1371
1372 for (; i >= 0; i--) {
1373 if (HAS_INDEX(array, i, is_array)) {
1374 var element = array[i];
1375 current = callback(current, element, i, array);
1376 }
1377 }
1378 return current;
1379}
1380
1381
1382function ArrayReduceRight(callback, current) {
1383 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
1384
1385 // Pull out the length so that side effects are visible before the
1386 // callback function is checked.
1387 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001388 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001389 return InnerArrayReduceRight(callback, current, array, length,
Ben Murdoch097c5b22016-05-18 11:27:45 +01001390 arguments.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001391}
1392
1393
1394function InnerArrayCopyWithin(target, start, end, array, length) {
1395 target = TO_INTEGER(target);
1396 var to;
1397 if (target < 0) {
1398 to = MaxSimple(length + target, 0);
1399 } else {
1400 to = MinSimple(target, length);
1401 }
1402
1403 start = TO_INTEGER(start);
1404 var from;
1405 if (start < 0) {
1406 from = MaxSimple(length + start, 0);
1407 } else {
1408 from = MinSimple(start, length);
1409 }
1410
1411 end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
1412 var final;
1413 if (end < 0) {
1414 final = MaxSimple(length + end, 0);
1415 } else {
1416 final = MinSimple(end, length);
1417 }
1418
1419 var count = MinSimple(final - from, length - to);
1420 var direction = 1;
1421 if (from < to && to < (from + count)) {
1422 direction = -1;
1423 from = from + count - 1;
1424 to = to + count - 1;
1425 }
1426
1427 while (count > 0) {
1428 if (from in array) {
1429 array[to] = array[from];
1430 } else {
1431 delete array[to];
1432 }
1433 from = from + direction;
1434 to = to + direction;
1435 count--;
1436 }
1437
1438 return array;
1439}
1440
1441
1442// ES6 draft 03-17-15, section 22.1.3.3
1443function ArrayCopyWithin(target, start, end) {
1444 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
1445
1446 var array = TO_OBJECT(this);
1447 var length = TO_LENGTH(array.length);
1448
1449 return InnerArrayCopyWithin(target, start, end, array, length);
1450}
1451
1452
1453function InnerArrayFind(predicate, thisArg, array, length) {
1454 if (!IS_CALLABLE(predicate)) {
1455 throw MakeTypeError(kCalledNonCallable, predicate);
1456 }
1457
1458 for (var i = 0; i < length; i++) {
1459 var element = array[i];
1460 if (%_Call(predicate, thisArg, element, i, array)) {
1461 return element;
1462 }
1463 }
1464
1465 return;
1466}
1467
1468
1469// ES6 draft 07-15-13, section 15.4.3.23
1470function ArrayFind(predicate, thisArg) {
1471 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
1472
1473 var array = TO_OBJECT(this);
1474 var length = TO_INTEGER(array.length);
1475
1476 return InnerArrayFind(predicate, thisArg, array, length);
1477}
1478
1479
1480function InnerArrayFindIndex(predicate, thisArg, array, length) {
1481 if (!IS_CALLABLE(predicate)) {
1482 throw MakeTypeError(kCalledNonCallable, predicate);
1483 }
1484
1485 for (var i = 0; i < length; i++) {
1486 var element = array[i];
1487 if (%_Call(predicate, thisArg, element, i, array)) {
1488 return i;
1489 }
1490 }
1491
1492 return -1;
1493}
1494
1495
1496// ES6 draft 07-15-13, section 15.4.3.24
1497function ArrayFindIndex(predicate, thisArg) {
1498 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex");
1499
1500 var array = TO_OBJECT(this);
1501 var length = TO_INTEGER(array.length);
1502
1503 return InnerArrayFindIndex(predicate, thisArg, array, length);
1504}
1505
1506
1507// ES6, draft 04-05-14, section 22.1.3.6
1508function InnerArrayFill(value, start, end, array, length) {
1509 var i = IS_UNDEFINED(start) ? 0 : TO_INTEGER(start);
1510 var end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
1511
1512 if (i < 0) {
1513 i += length;
1514 if (i < 0) i = 0;
1515 } else {
1516 if (i > length) i = length;
1517 }
1518
1519 if (end < 0) {
1520 end += length;
1521 if (end < 0) end = 0;
1522 } else {
1523 if (end > length) end = length;
1524 }
1525
1526 if ((end - i) > 0 && %object_is_frozen(array)) {
1527 throw MakeTypeError(kArrayFunctionsOnFrozen);
1528 }
1529
1530 for (; i < end; i++)
1531 array[i] = value;
1532 return array;
1533}
1534
1535
1536// ES6, draft 04-05-14, section 22.1.3.6
1537function ArrayFill(value, start, end) {
1538 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
1539
1540 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001541 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001542
1543 return InnerArrayFill(value, start, end, array, length);
1544}
1545
1546
1547function InnerArrayIncludes(searchElement, fromIndex, array, length) {
1548 if (length === 0) {
1549 return false;
1550 }
1551
1552 var n = TO_INTEGER(fromIndex);
1553
1554 var k;
1555 if (n >= 0) {
1556 k = n;
1557 } else {
1558 k = length + n;
1559 if (k < 0) {
1560 k = 0;
1561 }
1562 }
1563
1564 while (k < length) {
1565 var elementK = array[k];
Ben Murdoch097c5b22016-05-18 11:27:45 +01001566 if (%SameValueZero(searchElement, elementK)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001567 return true;
1568 }
1569
1570 ++k;
1571 }
1572
1573 return false;
1574}
1575
1576
1577// ES2016 draft, section 22.1.3.11
1578function ArrayIncludes(searchElement, fromIndex) {
1579 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.includes");
1580
1581 var array = TO_OBJECT(this);
1582 var length = TO_LENGTH(array.length);
1583
1584 return InnerArrayIncludes(searchElement, fromIndex, array, length);
1585}
1586
1587
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001588// ES6, draft 10-14-14, section 22.1.2.1
1589function ArrayFrom(arrayLike, mapfn, receiver) {
1590 var items = TO_OBJECT(arrayLike);
1591 var mapping = !IS_UNDEFINED(mapfn);
1592
1593 if (mapping) {
1594 if (!IS_CALLABLE(mapfn)) {
1595 throw MakeTypeError(kCalledNonCallable, mapfn);
1596 }
1597 }
1598
1599 var iterable = GetMethod(items, iteratorSymbol);
1600 var k;
1601 var result;
1602 var mappedValue;
1603 var nextValue;
1604
1605 if (!IS_UNDEFINED(iterable)) {
1606 result = %IsConstructor(this) ? new this() : [];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001607 k = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001608
Ben Murdoch097c5b22016-05-18 11:27:45 +01001609 for (nextValue of
1610 { [iteratorSymbol]() { return GetIterator(items, iterable) } }) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001611 if (mapping) {
1612 mappedValue = %_Call(mapfn, receiver, nextValue, k);
1613 } else {
1614 mappedValue = nextValue;
1615 }
Ben Murdochc5610432016-08-08 18:44:38 +01001616 %CreateDataProperty(result, k, mappedValue);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001617 k++;
1618 }
Ben Murdoch097c5b22016-05-18 11:27:45 +01001619 result.length = k;
1620 return result;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001621 } else {
1622 var len = TO_LENGTH(items.length);
1623 result = %IsConstructor(this) ? new this(len) : new GlobalArray(len);
1624
1625 for (k = 0; k < len; ++k) {
1626 nextValue = items[k];
1627 if (mapping) {
1628 mappedValue = %_Call(mapfn, receiver, nextValue, k);
1629 } else {
1630 mappedValue = nextValue;
1631 }
Ben Murdochc5610432016-08-08 18:44:38 +01001632 %CreateDataProperty(result, k, mappedValue);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001633 }
1634
1635 result.length = k;
1636 return result;
1637 }
1638}
1639
1640
1641// ES6, draft 05-22-14, section 22.1.2.3
Ben Murdoch097c5b22016-05-18 11:27:45 +01001642function ArrayOf(...args) {
1643 var length = args.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001644 var constructor = this;
1645 // TODO: Implement IsConstructor (ES6 section 7.2.5)
1646 var array = %IsConstructor(constructor) ? new constructor(length) : [];
1647 for (var i = 0; i < length; i++) {
Ben Murdochc5610432016-08-08 18:44:38 +01001648 %CreateDataProperty(array, i, args[i]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001649 }
1650 array.length = length;
1651 return array;
1652}
1653
1654// -------------------------------------------------------------------
1655
1656// Set up non-enumerable constructor property on the Array.prototype
1657// object.
1658%AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray,
1659 DONT_ENUM);
1660
1661// Set up unscopable properties on the Array.prototype object.
1662var unscopables = {
1663 __proto__: null,
1664 copyWithin: true,
1665 entries: true,
1666 fill: true,
1667 find: true,
1668 findIndex: true,
1669 keys: true,
1670};
1671
1672%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
1673 DONT_ENUM | READ_ONLY);
1674
1675%FunctionSetLength(ArrayFrom, 1);
1676
1677// Set up non-enumerable functions on the Array object.
1678utils.InstallFunctions(GlobalArray, DONT_ENUM, [
1679 "from", ArrayFrom,
1680 "of", ArrayOf
1681]);
1682
1683var specialFunctions = %SpecialArrayFunctions();
1684
1685var getFunction = function(name, jsBuiltin, len) {
1686 var f = jsBuiltin;
1687 if (specialFunctions.hasOwnProperty(name)) {
1688 f = specialFunctions[name];
1689 }
1690 if (!IS_UNDEFINED(len)) {
1691 %FunctionSetLength(f, len);
1692 }
1693 return f;
1694};
1695
1696// Set up non-enumerable functions of the Array.prototype object and
1697// set their names.
1698// Manipulate the length of some of the functions to meet
1699// expectations set by ECMA-262 or Mozilla.
1700utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
1701 "toString", getFunction("toString", ArrayToString),
1702 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1703 "join", getFunction("join", ArrayJoin),
1704 "pop", getFunction("pop", ArrayPop),
1705 "push", getFunction("push", ArrayPush, 1),
1706 "reverse", getFunction("reverse", ArrayReverse),
1707 "shift", getFunction("shift", ArrayShift),
1708 "unshift", getFunction("unshift", ArrayUnshift, 1),
1709 "slice", getFunction("slice", ArraySlice, 2),
1710 "splice", getFunction("splice", ArraySplice, 2),
1711 "sort", getFunction("sort", ArraySort),
1712 "filter", getFunction("filter", ArrayFilter, 1),
1713 "forEach", getFunction("forEach", ArrayForEach, 1),
1714 "some", getFunction("some", ArraySome, 1),
1715 "every", getFunction("every", ArrayEvery, 1),
1716 "map", getFunction("map", ArrayMap, 1),
1717 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1718 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1719 "reduce", getFunction("reduce", ArrayReduce, 1),
1720 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1),
1721 "copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2),
1722 "find", getFunction("find", ArrayFind, 1),
1723 "findIndex", getFunction("findIndex", ArrayFindIndex, 1),
1724 "fill", getFunction("fill", ArrayFill, 1),
1725 "includes", getFunction("includes", ArrayIncludes, 1),
1726]);
1727
1728%FinishArrayPrototypeSetup(GlobalArray.prototype);
1729
1730// The internal Array prototype doesn't need to be fancy, since it's never
1731// exposed to user code.
1732// Adding only the functions that are actually used.
1733utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
1734 "indexOf", getFunction("indexOf", ArrayIndexOf),
1735 "join", getFunction("join", ArrayJoin),
1736 "pop", getFunction("pop", ArrayPop),
1737 "push", getFunction("push", ArrayPush),
1738 "shift", getFunction("shift", ArrayShift),
1739 "sort", getFunction("sort", ArraySort),
1740 "splice", getFunction("splice", ArraySplice)
1741]);
1742
1743utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [
1744 "join", getFunction("join", ArrayJoin),
1745 "pop", getFunction("pop", ArrayPop),
1746 "push", getFunction("push", ArrayPush),
1747 "shift", getFunction("shift", ArrayShift)
1748]);
1749
1750// V8 extras get a separate copy of InternalPackedArray. We give them the basic
1751// manipulation methods.
1752utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
1753 "push", getFunction("push", ArrayPush),
1754 "pop", getFunction("pop", ArrayPop),
1755 "shift", getFunction("shift", ArrayShift),
1756 "unshift", getFunction("unshift", ArrayUnshift),
1757 "splice", getFunction("splice", ArraySplice),
1758 "slice", getFunction("slice", ArraySlice)
1759]);
1760
1761// -------------------------------------------------------------------
1762// Exports
1763
1764utils.Export(function(to) {
1765 to.ArrayFrom = ArrayFrom;
1766 to.ArrayIndexOf = ArrayIndexOf;
1767 to.ArrayJoin = ArrayJoin;
1768 to.ArrayPush = ArrayPush;
1769 to.ArrayToString = ArrayToString;
1770 to.InnerArrayCopyWithin = InnerArrayCopyWithin;
1771 to.InnerArrayEvery = InnerArrayEvery;
1772 to.InnerArrayFill = InnerArrayFill;
1773 to.InnerArrayFilter = InnerArrayFilter;
1774 to.InnerArrayFind = InnerArrayFind;
1775 to.InnerArrayFindIndex = InnerArrayFindIndex;
1776 to.InnerArrayForEach = InnerArrayForEach;
1777 to.InnerArrayIncludes = InnerArrayIncludes;
1778 to.InnerArrayIndexOf = InnerArrayIndexOf;
1779 to.InnerArrayJoin = InnerArrayJoin;
1780 to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
1781 to.InnerArrayReduce = InnerArrayReduce;
1782 to.InnerArrayReduceRight = InnerArrayReduceRight;
1783 to.InnerArraySome = InnerArraySome;
1784 to.InnerArraySort = InnerArraySort;
1785 to.InnerArrayToLocaleString = InnerArrayToLocaleString;
1786 to.PackedArrayReverse = PackedArrayReverse;
Ben Murdochda12d292016-06-02 14:46:10 +01001787 to.Stack = Stack;
1788 to.StackHas = StackHas;
1789 to.StackPush = StackPush;
1790 to.StackPop = StackPop;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001791});
1792
1793%InstallToContext([
1794 "array_pop", ArrayPop,
1795 "array_push", ArrayPush,
1796 "array_shift", ArrayShift,
1797 "array_splice", ArraySplice,
1798 "array_slice", ArraySlice,
1799 "array_unshift", ArrayUnshift,
1800]);
1801
1802});