blob: c29b8f7256ad1d2c568f5a5103d4c666f4aaf485 [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 GetIterator;
15var GetMethod;
16var GlobalArray = global.Array;
17var InternalArray = utils.InternalArray;
18var InternalPackedArray = utils.InternalPackedArray;
19var MakeTypeError;
20var MaxSimple;
21var MinSimple;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000022var ObjectHasOwnProperty;
23var ObjectToString = utils.ImportNow("object_to_string");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000024var iteratorSymbol = utils.ImportNow("iterator_symbol");
Ben Murdoch61f157c2016-09-16 13:49:30 +010025var speciesSymbol = utils.ImportNow("species_symbol");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000026var 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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000037// -------------------------------------------------------------------
38
39
40function ArraySpeciesCreate(array, length) {
Ben Murdochc5610432016-08-08 18:44:38 +010041 length = INVERT_NEG_ZERO(length);
Ben Murdoch61f157c2016-09-16 13:49:30 +010042 var constructor = %ArraySpeciesConstructor(array);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000043 return new constructor(length);
44}
45
46
Ben Murdochda12d292016-06-02 14:46:10 +010047function KeySortCompare(a, b) {
48 return a - b;
49}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000050
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000051function GetSortedArrayKeys(array, indices) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000052 if (IS_NUMBER(indices)) {
Ben Murdochda12d292016-06-02 14:46:10 +010053 var keys = new InternalArray();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000054 // It's an interval
55 var limit = indices;
56 for (var i = 0; i < limit; ++i) {
57 var e = array[i];
58 if (!IS_UNDEFINED(e) || i in array) {
59 keys.push(i);
60 }
61 }
Ben Murdochda12d292016-06-02 14:46:10 +010062 return keys;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000063 }
Ben Murdochda12d292016-06-02 14:46:10 +010064 return InnerArraySort(indices, indices.length, KeySortCompare);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000065}
66
67
Ben Murdochda12d292016-06-02 14:46:10 +010068function SparseJoinWithSeparatorJS(array, keys, length, convert, separator) {
69 var keys_length = keys.length;
70 var elements = new InternalArray(keys_length * 2);
71 for (var i = 0; i < keys_length; i++) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000072 var key = keys[i];
Ben Murdochda12d292016-06-02 14:46:10 +010073 var e = array[key];
74 elements[i * 2] = key;
75 elements[i * 2 + 1] = IS_STRING(e) ? e : convert(e);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000076 }
Ben Murdochda12d292016-06-02 14:46:10 +010077 return %SparseJoinWithSeparator(elements, length, separator);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000078}
79
80
81// Optimized for sparse arrays if separator is ''.
Ben Murdochda12d292016-06-02 14:46:10 +010082function SparseJoin(array, keys, convert) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000083 var keys_length = keys.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000084 var elements = new InternalArray(keys_length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000085 for (var i = 0; i < keys_length; i++) {
Ben Murdochda12d292016-06-02 14:46:10 +010086 var e = array[keys[i]];
87 elements[i] = IS_STRING(e) ? e : convert(e);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000088 }
Ben Murdochda12d292016-06-02 14:46:10 +010089 return %StringBuilderConcat(elements, keys_length, '');
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000090}
91
92
93function UseSparseVariant(array, length, is_array, touched) {
94 // Only use the sparse variant on arrays that are likely to be sparse and the
95 // number of elements touched in the operation is relatively small compared to
96 // the overall size of the array.
Ben Murdochc5610432016-08-08 18:44:38 +010097 if (!is_array || length < 1000 || %HasComplexElements(array)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000098 return false;
99 }
100 if (!%_IsSmi(length)) {
101 return true;
102 }
103 var elements_threshold = length >> 2; // No more than 75% holes
104 var estimated_elements = %EstimateNumberOfElements(array);
105 return (estimated_elements < elements_threshold) &&
106 (touched > estimated_elements * 4);
107}
108
Ben Murdochda12d292016-06-02 14:46:10 +0100109function Stack() {
110 this.length = 0;
111 this.values = new InternalArray();
112}
113
114// Predeclare the instance variables on the prototype. Otherwise setting them in
115// the constructor will leak the instance through settings on Object.prototype.
116Stack.prototype.length = null;
117Stack.prototype.values = null;
118
119function StackPush(stack, value) {
120 stack.values[stack.length++] = value;
121}
122
123function StackPop(stack) {
124 stack.values[--stack.length] = null
125}
126
127function StackHas(stack, v) {
128 var length = stack.length;
129 var values = stack.values;
130 for (var i = 0; i < length; i++) {
131 if (values[i] === v) return true;
132 }
133 return false;
134}
135
136// Global list of arrays visited during toString, toLocaleString and
137// join invocations.
138var visited_arrays = new Stack();
139
140function DoJoin(array, length, is_array, separator, convert) {
141 if (UseSparseVariant(array, length, is_array, length)) {
142 %NormalizeElements(array);
143 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, length));
144 if (separator === '') {
145 if (keys.length === 0) return '';
146 return SparseJoin(array, keys, convert);
147 } else {
148 return SparseJoinWithSeparatorJS(array, keys, length, convert, separator);
149 }
150 }
151
152 // Fast case for one-element arrays.
153 if (length === 1) {
154 var e = array[0];
155 return IS_STRING(e) ? e : convert(e);
156 }
157
158 // Construct an array for the elements.
159 var elements = new InternalArray(length);
160
161 // We pull the empty separator check outside the loop for speed!
162 if (separator === '') {
163 for (var i = 0; i < length; i++) {
164 var e = array[i];
165 elements[i] = IS_STRING(e) ? e : convert(e);
166 }
167 return %StringBuilderConcat(elements, length, '');
168 }
169 // Non-empty separator case.
170 // If the first element is a number then use the heuristic that the
171 // remaining elements are also likely to be numbers.
172 var e = array[0];
173 if (IS_NUMBER(e)) {
174 elements[0] = %_NumberToString(e);
175 for (var i = 1; i < length; i++) {
176 e = array[i];
177 if (IS_NUMBER(e)) {
178 elements[i] = %_NumberToString(e);
179 } else {
180 elements[i] = IS_STRING(e) ? e : convert(e);
181 }
182 }
183 } else {
184 elements[0] = IS_STRING(e) ? e : convert(e);
185 for (var i = 1; i < length; i++) {
186 e = array[i];
187 elements[i] = IS_STRING(e) ? e : convert(e);
188 }
189 }
190 return %StringBuilderJoin(elements, length, separator);
191}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000192
193function Join(array, length, separator, convert) {
Ben Murdochda12d292016-06-02 14:46:10 +0100194 if (length === 0) return '';
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000195
196 var is_array = IS_ARRAY(array);
197
198 if (is_array) {
199 // If the array is cyclic, return the empty string for already
200 // visited arrays.
Ben Murdochda12d292016-06-02 14:46:10 +0100201 if (StackHas(visited_arrays, array)) return '';
202 StackPush(visited_arrays, array);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000203 }
204
205 // Attempt to convert the elements.
206 try {
Ben Murdochda12d292016-06-02 14:46:10 +0100207 return DoJoin(array, length, is_array, separator, convert);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000208 } finally {
209 // Make sure to remove the last element of the visited array no
210 // matter what happens.
Ben Murdochda12d292016-06-02 14:46:10 +0100211 if (is_array) StackPop(visited_arrays);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000212 }
213}
214
215
216function ConvertToString(x) {
Ben Murdochda12d292016-06-02 14:46:10 +0100217 if (IS_NULL_OR_UNDEFINED(x)) return '';
218 return TO_STRING(x);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000219}
220
221
222function ConvertToLocaleString(e) {
Ben Murdochda12d292016-06-02 14:46:10 +0100223 if (IS_NULL_OR_UNDEFINED(e)) return '';
224 return TO_STRING(e.toLocaleString());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000225}
226
227
228// This function implements the optimized splice implementation that can use
229// special array operations to handle sparse arrays in a sensible fashion.
230function SparseSlice(array, start_i, del_count, len, deleted_elements) {
231 // Move deleted elements to a new array (the return value from splice).
232 var indices = %GetArrayKeys(array, start_i + del_count);
233 if (IS_NUMBER(indices)) {
234 var limit = indices;
235 for (var i = start_i; i < limit; ++i) {
236 var current = array[i];
237 if (!IS_UNDEFINED(current) || i in array) {
Ben Murdochc5610432016-08-08 18:44:38 +0100238 %CreateDataProperty(deleted_elements, i - start_i, current);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000239 }
240 }
241 } else {
242 var length = indices.length;
243 for (var k = 0; k < length; ++k) {
244 var key = indices[k];
Ben Murdochda12d292016-06-02 14:46:10 +0100245 if (key >= start_i) {
246 var current = array[key];
247 if (!IS_UNDEFINED(current) || key in array) {
Ben Murdochc5610432016-08-08 18:44:38 +0100248 %CreateDataProperty(deleted_elements, key - start_i, current);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000249 }
250 }
251 }
252 }
253}
254
255
256// This function implements the optimized splice implementation that can use
257// special array operations to handle sparse arrays in a sensible fashion.
258function SparseMove(array, start_i, del_count, len, num_additional_args) {
259 // Bail out if no moving is necessary.
260 if (num_additional_args === del_count) return;
261 // Move data to new array.
262 var new_array = new InternalArray(
263 // Clamp array length to 2^32-1 to avoid early RangeError.
264 MinSimple(len - del_count + num_additional_args, 0xffffffff));
265 var big_indices;
266 var indices = %GetArrayKeys(array, len);
267 if (IS_NUMBER(indices)) {
268 var limit = indices;
269 for (var i = 0; i < start_i && i < limit; ++i) {
270 var current = array[i];
271 if (!IS_UNDEFINED(current) || i in array) {
272 new_array[i] = current;
273 }
274 }
275 for (var i = start_i + del_count; i < limit; ++i) {
276 var current = array[i];
277 if (!IS_UNDEFINED(current) || i in array) {
278 new_array[i - del_count + num_additional_args] = current;
279 }
280 }
281 } else {
282 var length = indices.length;
283 for (var k = 0; k < length; ++k) {
284 var key = indices[k];
Ben Murdochda12d292016-06-02 14:46:10 +0100285 if (key < start_i) {
286 var current = array[key];
287 if (!IS_UNDEFINED(current) || key in array) {
288 new_array[key] = current;
289 }
290 } else if (key >= start_i + del_count) {
291 var current = array[key];
292 if (!IS_UNDEFINED(current) || key in array) {
293 var new_key = key - del_count + num_additional_args;
294 new_array[new_key] = current;
295 if (new_key > 0xfffffffe) {
296 big_indices = big_indices || new InternalArray();
297 big_indices.push(new_key);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000298 }
299 }
300 }
301 }
302 }
303 // Move contents of new_array into this array
304 %MoveArrayContents(new_array, array);
305 // Add any moved values that aren't elements anymore.
306 if (!IS_UNDEFINED(big_indices)) {
307 var length = big_indices.length;
308 for (var i = 0; i < length; ++i) {
309 var key = big_indices[i];
310 array[key] = new_array[key];
311 }
312 }
313}
314
315
316// This is part of the old simple-minded splice. We are using it either
317// because the receiver is not an array (so we have no choice) or because we
318// know we are not deleting or moving a lot of elements.
319function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000320 for (var i = 0; i < del_count; i++) {
321 var index = start_i + i;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100322 if (index in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000323 var current = array[index];
Ben Murdochc5610432016-08-08 18:44:38 +0100324 %CreateDataProperty(deleted_elements, i, current);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000325 }
326 }
327}
328
329
330function SimpleMove(array, start_i, del_count, len, num_additional_args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000331 if (num_additional_args !== del_count) {
332 // Move the existing elements after the elements to be deleted
333 // to the right position in the resulting array.
334 if (num_additional_args > del_count) {
335 for (var i = len - del_count; i > start_i; i--) {
336 var from_index = i + del_count - 1;
337 var to_index = i + num_additional_args - 1;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100338 if (from_index in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000339 array[to_index] = array[from_index];
340 } else {
341 delete array[to_index];
342 }
343 }
344 } else {
345 for (var i = start_i; i < len - del_count; i++) {
346 var from_index = i + del_count;
347 var to_index = i + num_additional_args;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100348 if (from_index in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000349 array[to_index] = array[from_index];
350 } else {
351 delete array[to_index];
352 }
353 }
354 for (var i = len; i > len - del_count + num_additional_args; i--) {
355 delete array[i - 1];
356 }
357 }
358 }
359}
360
361
362// -------------------------------------------------------------------
363
364
365function ArrayToString() {
366 var array;
367 var func;
368 if (IS_ARRAY(this)) {
369 func = this.join;
370 if (func === ArrayJoin) {
371 return Join(this, this.length, ',', ConvertToString);
372 }
373 array = this;
374 } else {
375 array = TO_OBJECT(this);
376 func = array.join;
377 }
378 if (!IS_CALLABLE(func)) {
379 return %_Call(ObjectToString, array);
380 }
381 return %_Call(func, array);
382}
383
384
385function InnerArrayToLocaleString(array, length) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100386 var len = TO_LENGTH(length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000387 if (len === 0) return "";
388 return Join(array, len, ',', ConvertToLocaleString);
389}
390
391
392function ArrayToLocaleString() {
393 var array = TO_OBJECT(this);
394 var arrayLen = array.length;
395 return InnerArrayToLocaleString(array, arrayLen);
396}
397
398
399function InnerArrayJoin(separator, array, length) {
400 if (IS_UNDEFINED(separator)) {
401 separator = ',';
402 } else {
403 separator = TO_STRING(separator);
404 }
405
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000406 // Fast case for one-element arrays.
407 if (length === 1) {
408 var e = array[0];
409 if (IS_NULL_OR_UNDEFINED(e)) return '';
410 return TO_STRING(e);
411 }
412
413 return Join(array, length, separator, ConvertToString);
414}
415
416
417function ArrayJoin(separator) {
418 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
419
420 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100421 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000422
423 return InnerArrayJoin(separator, array, length);
424}
425
426
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000427// Removes the last element from the array and returns it. See
428// ECMA-262, section 15.4.4.6.
429function ArrayPop() {
430 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
431
432 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100433 var n = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000434 if (n == 0) {
435 array.length = n;
436 return;
437 }
438
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000439 n--;
440 var value = array[n];
441 %DeleteProperty_Strict(array, n);
442 array.length = n;
443 return value;
444}
445
446
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000447// Appends the arguments to the end of the array and returns the new
448// length of the array. See ECMA-262, section 15.4.4.7.
449function ArrayPush() {
450 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
451
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000452 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100453 var n = TO_LENGTH(array.length);
454 var m = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000455
Ben Murdochc5610432016-08-08 18:44:38 +0100456 // Subtract n from kMaxSafeInteger rather than testing m + n >
457 // kMaxSafeInteger. n may already be kMaxSafeInteger. In that case adding
458 // e.g., 1 would not be safe.
459 if (m > kMaxSafeInteger - n) throw MakeTypeError(kPushPastSafeLength, m, n);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000460
461 for (var i = 0; i < m; i++) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100462 array[i+n] = arguments[i];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000463 }
464
465 var new_length = n + m;
466 array.length = new_length;
467 return new_length;
468}
469
470
471// For implementing reverse() on large, sparse arrays.
472function SparseReverse(array, len) {
473 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
474 var high_counter = keys.length - 1;
475 var low_counter = 0;
476 while (low_counter <= high_counter) {
477 var i = keys[low_counter];
478 var j = keys[high_counter];
479
480 var j_complement = len - j - 1;
481 var low, high;
482
483 if (j_complement <= i) {
484 high = j;
485 while (keys[--high_counter] == j) { }
486 low = j_complement;
487 }
488 if (j_complement >= i) {
489 low = i;
490 while (keys[++low_counter] == i) { }
491 high = len - i - 1;
492 }
493
494 var current_i = array[low];
495 if (!IS_UNDEFINED(current_i) || low in array) {
496 var current_j = array[high];
497 if (!IS_UNDEFINED(current_j) || high in array) {
498 array[low] = current_j;
499 array[high] = current_i;
500 } else {
501 array[high] = current_i;
502 delete array[low];
503 }
504 } else {
505 var current_j = array[high];
506 if (!IS_UNDEFINED(current_j) || high in array) {
507 array[low] = current_j;
508 delete array[high];
509 }
510 }
511 }
512}
513
514function PackedArrayReverse(array, len) {
515 var j = len - 1;
516 for (var i = 0; i < j; i++, j--) {
517 var current_i = array[i];
518 var current_j = array[j];
519 array[i] = current_j;
520 array[j] = current_i;
521 }
522 return array;
523}
524
525
526function GenericArrayReverse(array, len) {
527 var j = len - 1;
528 for (var i = 0; i < j; i++, j--) {
529 if (i in array) {
530 var current_i = array[i];
531 if (j in array) {
532 var current_j = array[j];
533 array[i] = current_j;
534 array[j] = current_i;
535 } else {
536 array[j] = current_i;
537 delete array[i];
538 }
539 } else {
540 if (j in array) {
541 var current_j = array[j];
542 array[i] = current_j;
543 delete array[j];
544 }
545 }
546 }
547 return array;
548}
549
550
551function ArrayReverse() {
552 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
553
554 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100555 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000556 var isArray = IS_ARRAY(array);
557
558 if (UseSparseVariant(array, len, isArray, len)) {
559 %NormalizeElements(array);
560 SparseReverse(array, len);
561 return array;
562 } else if (isArray && %_HasFastPackedElements(array)) {
563 return PackedArrayReverse(array, len);
564 } else {
565 return GenericArrayReverse(array, len);
566 }
567}
568
569
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000570function ArrayShift() {
571 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
572
573 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100574 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000575
576 if (len === 0) {
577 array.length = 0;
578 return;
579 }
580
581 if (%object_is_sealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed);
582
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000583 var first = array[0];
584
585 if (UseSparseVariant(array, len, IS_ARRAY(array), len)) {
586 SparseMove(array, 0, 1, len, 0);
587 } else {
588 SimpleMove(array, 0, 1, len, 0);
589 }
590
591 array.length = len - 1;
592
593 return first;
594}
595
596
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000597function ArrayUnshift(arg1) { // length == 1
598 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
599
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000600 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100601 var len = TO_LENGTH(array.length);
602 var num_arguments = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000603
604 if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) &&
605 !%object_is_sealed(array)) {
606 SparseMove(array, 0, 0, len, num_arguments);
607 } else {
608 SimpleMove(array, 0, 0, len, num_arguments);
609 }
610
611 for (var i = 0; i < num_arguments; i++) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100612 array[i] = arguments[i];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000613 }
614
615 var new_length = len + num_arguments;
616 array.length = new_length;
617 return new_length;
618}
619
620
621function ArraySlice(start, end) {
622 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
623
624 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100625 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000626 var start_i = TO_INTEGER(start);
627 var end_i = len;
628
629 if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
630
631 if (start_i < 0) {
632 start_i += len;
633 if (start_i < 0) start_i = 0;
634 } else {
635 if (start_i > len) start_i = len;
636 }
637
638 if (end_i < 0) {
639 end_i += len;
640 if (end_i < 0) end_i = 0;
641 } else {
642 if (end_i > len) end_i = len;
643 }
644
645 var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
646
647 if (end_i < start_i) return result;
648
649 if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
650 %NormalizeElements(array);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100651 if (IS_ARRAY(result)) %NormalizeElements(result);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000652 SparseSlice(array, start_i, end_i - start_i, len, result);
653 } else {
654 SimpleSlice(array, start_i, end_i - start_i, len, result);
655 }
656
657 result.length = end_i - start_i;
658
659 return result;
660}
661
662
663function ComputeSpliceStartIndex(start_i, len) {
664 if (start_i < 0) {
665 start_i += len;
666 return start_i < 0 ? 0 : start_i;
667 }
668
669 return start_i > len ? len : start_i;
670}
671
672
673function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
674 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
675 // given as a request to delete all the elements from the start.
676 // And it differs from the case of undefined delete count.
677 // This does not follow ECMA-262, but we do the same for
678 // compatibility.
679 var del_count = 0;
680 if (num_arguments == 1)
681 return len - start_i;
682
683 del_count = TO_INTEGER(delete_count);
684 if (del_count < 0)
685 return 0;
686
687 if (del_count > len - start_i)
688 return len - start_i;
689
690 return del_count;
691}
692
693
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000694function ArraySplice(start, delete_count) {
695 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
696
Ben Murdoch097c5b22016-05-18 11:27:45 +0100697 var num_arguments = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000698 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100699 var len = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000700 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
701 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
702 start_i);
703 var deleted_elements = ArraySpeciesCreate(array, del_count);
704 deleted_elements.length = del_count;
705 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
706
707 if (del_count != num_elements_to_add && %object_is_sealed(array)) {
708 throw MakeTypeError(kArrayFunctionsOnSealed);
709 } else if (del_count > 0 && %object_is_frozen(array)) {
710 throw MakeTypeError(kArrayFunctionsOnFrozen);
711 }
712
713 var changed_elements = del_count;
714 if (num_elements_to_add != del_count) {
715 // If the slice needs to do a actually move elements after the insertion
716 // point, then include those in the estimate of changed elements.
717 changed_elements += len - start_i - del_count;
718 }
719 if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
720 %NormalizeElements(array);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100721 if (IS_ARRAY(deleted_elements)) %NormalizeElements(deleted_elements);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000722 SparseSlice(array, start_i, del_count, len, deleted_elements);
723 SparseMove(array, start_i, del_count, len, num_elements_to_add);
724 } else {
725 SimpleSlice(array, start_i, del_count, len, deleted_elements);
726 SimpleMove(array, start_i, del_count, len, num_elements_to_add);
727 }
728
729 // Insert the arguments into the resulting array in
730 // place of the deleted elements.
731 var i = start_i;
732 var arguments_index = 2;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100733 var arguments_length = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000734 while (arguments_index < arguments_length) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100735 array[i++] = arguments[arguments_index++];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000736 }
737 array.length = len - del_count + num_elements_to_add;
738
739 // Return the deleted elements.
740 return deleted_elements;
741}
742
743
744function InnerArraySort(array, length, comparefn) {
745 // In-place QuickSort algorithm.
746 // For short (length <= 22) arrays, insertion sort is used for efficiency.
747
748 if (!IS_CALLABLE(comparefn)) {
749 comparefn = function (x, y) {
750 if (x === y) return 0;
751 if (%_IsSmi(x) && %_IsSmi(y)) {
752 return %SmiLexicographicCompare(x, y);
753 }
754 x = TO_STRING(x);
755 y = TO_STRING(y);
756 if (x == y) return 0;
757 else return x < y ? -1 : 1;
758 };
759 }
760 var InsertionSort = function InsertionSort(a, from, to) {
761 for (var i = from + 1; i < to; i++) {
762 var element = a[i];
763 for (var j = i - 1; j >= from; j--) {
764 var tmp = a[j];
765 var order = comparefn(tmp, element);
766 if (order > 0) {
767 a[j + 1] = tmp;
768 } else {
769 break;
770 }
771 }
772 a[j + 1] = element;
773 }
774 };
775
776 var GetThirdIndex = function(a, from, to) {
777 var t_array = new InternalArray();
778 // Use both 'from' and 'to' to determine the pivot candidates.
779 var increment = 200 + ((to - from) & 15);
780 var j = 0;
781 from += 1;
782 to -= 1;
783 for (var i = from; i < to; i += increment) {
784 t_array[j] = [i, a[i]];
785 j++;
786 }
787 t_array.sort(function(a, b) {
788 return comparefn(a[1], b[1]);
789 });
790 var third_index = t_array[t_array.length >> 1][0];
791 return third_index;
792 }
793
794 var QuickSort = function QuickSort(a, from, to) {
795 var third_index = 0;
796 while (true) {
797 // Insertion sort is faster for short arrays.
798 if (to - from <= 10) {
799 InsertionSort(a, from, to);
800 return;
801 }
802 if (to - from > 1000) {
803 third_index = GetThirdIndex(a, from, to);
804 } else {
805 third_index = from + ((to - from) >> 1);
806 }
807 // Find a pivot as the median of first, last and middle element.
808 var v0 = a[from];
809 var v1 = a[to - 1];
810 var v2 = a[third_index];
811 var c01 = comparefn(v0, v1);
812 if (c01 > 0) {
813 // v1 < v0, so swap them.
814 var tmp = v0;
815 v0 = v1;
816 v1 = tmp;
817 } // v0 <= v1.
818 var c02 = comparefn(v0, v2);
819 if (c02 >= 0) {
820 // v2 <= v0 <= v1.
821 var tmp = v0;
822 v0 = v2;
823 v2 = v1;
824 v1 = tmp;
825 } else {
826 // v0 <= v1 && v0 < v2
827 var c12 = comparefn(v1, v2);
828 if (c12 > 0) {
829 // v0 <= v2 < v1
830 var tmp = v1;
831 v1 = v2;
832 v2 = tmp;
833 }
834 }
835 // v0 <= v1 <= v2
836 a[from] = v0;
837 a[to - 1] = v2;
838 var pivot = v1;
839 var low_end = from + 1; // Upper bound of elements lower than pivot.
840 var high_start = to - 1; // Lower bound of elements greater than pivot.
841 a[third_index] = a[low_end];
842 a[low_end] = pivot;
843
844 // From low_end to i are elements equal to pivot.
845 // From i to high_start are elements that haven't been compared yet.
846 partition: for (var i = low_end + 1; i < high_start; i++) {
847 var element = a[i];
848 var order = comparefn(element, pivot);
849 if (order < 0) {
850 a[i] = a[low_end];
851 a[low_end] = element;
852 low_end++;
853 } else if (order > 0) {
854 do {
855 high_start--;
856 if (high_start == i) break partition;
857 var top_elem = a[high_start];
858 order = comparefn(top_elem, pivot);
859 } while (order > 0);
860 a[i] = a[high_start];
861 a[high_start] = element;
862 if (order < 0) {
863 element = a[i];
864 a[i] = a[low_end];
865 a[low_end] = element;
866 low_end++;
867 }
868 }
869 }
870 if (to - high_start < low_end - from) {
871 QuickSort(a, high_start, to);
872 to = low_end;
873 } else {
874 QuickSort(a, from, low_end);
875 from = high_start;
876 }
877 }
878 };
879
880 // Copy elements in the range 0..length from obj's prototype chain
881 // to obj itself, if obj has holes. Return one more than the maximal index
882 // of a prototype property.
883 var CopyFromPrototype = function CopyFromPrototype(obj, length) {
884 var max = 0;
Ben Murdochc5610432016-08-08 18:44:38 +0100885 for (var proto = %object_get_prototype_of(obj); proto;
886 proto = %object_get_prototype_of(proto)) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100887 var indices = IS_PROXY(proto) ? length : %GetArrayKeys(proto, length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000888 if (IS_NUMBER(indices)) {
889 // It's an interval.
890 var proto_length = indices;
891 for (var i = 0; i < proto_length; i++) {
892 if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
893 obj[i] = proto[i];
894 if (i >= max) { max = i + 1; }
895 }
896 }
897 } else {
898 for (var i = 0; i < indices.length; i++) {
899 var index = indices[i];
Ben Murdochda12d292016-06-02 14:46:10 +0100900 if (!HAS_OWN_PROPERTY(obj, index) && HAS_OWN_PROPERTY(proto, index)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000901 obj[index] = proto[index];
902 if (index >= max) { max = index + 1; }
903 }
904 }
905 }
906 }
907 return max;
908 };
909
910 // Set a value of "undefined" on all indices in the range from..to
911 // where a prototype of obj has an element. I.e., shadow all prototype
912 // elements in that range.
913 var ShadowPrototypeElements = function(obj, from, to) {
Ben Murdochc5610432016-08-08 18:44:38 +0100914 for (var proto = %object_get_prototype_of(obj); proto;
915 proto = %object_get_prototype_of(proto)) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100916 var indices = IS_PROXY(proto) ? to : %GetArrayKeys(proto, to);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000917 if (IS_NUMBER(indices)) {
918 // It's an interval.
919 var proto_length = indices;
920 for (var i = from; i < proto_length; i++) {
921 if (HAS_OWN_PROPERTY(proto, i)) {
922 obj[i] = UNDEFINED;
923 }
924 }
925 } else {
926 for (var i = 0; i < indices.length; i++) {
927 var index = indices[i];
Ben Murdochda12d292016-06-02 14:46:10 +0100928 if (from <= index && HAS_OWN_PROPERTY(proto, index)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000929 obj[index] = UNDEFINED;
930 }
931 }
932 }
933 }
934 };
935
936 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
937 // Copy defined elements from the end to fill in all holes and undefineds
938 // in the beginning of the array. Write undefineds and holes at the end
939 // after loop is finished.
940 var first_undefined = 0;
941 var last_defined = length - 1;
942 var num_holes = 0;
943 while (first_undefined < last_defined) {
944 // Find first undefined element.
945 while (first_undefined < last_defined &&
946 !IS_UNDEFINED(obj[first_undefined])) {
947 first_undefined++;
948 }
949 // Maintain the invariant num_holes = the number of holes in the original
950 // array with indices <= first_undefined or > last_defined.
951 if (!HAS_OWN_PROPERTY(obj, first_undefined)) {
952 num_holes++;
953 }
954
955 // Find last defined element.
956 while (first_undefined < last_defined &&
957 IS_UNDEFINED(obj[last_defined])) {
958 if (!HAS_OWN_PROPERTY(obj, last_defined)) {
959 num_holes++;
960 }
961 last_defined--;
962 }
963 if (first_undefined < last_defined) {
964 // Fill in hole or undefined.
965 obj[first_undefined] = obj[last_defined];
966 obj[last_defined] = UNDEFINED;
967 }
968 }
969 // If there were any undefineds in the entire array, first_undefined
970 // points to one past the last defined element. Make this true if
971 // there were no undefineds, as well, so that first_undefined == number
972 // of defined elements.
973 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
974 // Fill in the undefineds and the holes. There may be a hole where
975 // an undefined should be and vice versa.
976 var i;
977 for (i = first_undefined; i < length - num_holes; i++) {
978 obj[i] = UNDEFINED;
979 }
980 for (i = length - num_holes; i < length; i++) {
981 // For compatability with Webkit, do not expose elements in the prototype.
Ben Murdochc5610432016-08-08 18:44:38 +0100982 if (i in %object_get_prototype_of(obj)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000983 obj[i] = UNDEFINED;
984 } else {
985 delete obj[i];
986 }
987 }
988
989 // Return the number of defined elements.
990 return first_undefined;
991 };
992
993 if (length < 2) return array;
994
995 var is_array = IS_ARRAY(array);
996 var max_prototype_element;
997 if (!is_array) {
998 // For compatibility with JSC, we also sort elements inherited from
999 // the prototype chain on non-Array objects.
1000 // We do this by copying them to this object and sorting only
1001 // own elements. This is not very efficient, but sorting with
1002 // inherited elements happens very, very rarely, if at all.
1003 // The specification allows "implementation dependent" behavior
1004 // if an element on the prototype chain has an element that
1005 // might interact with sorting.
1006 max_prototype_element = CopyFromPrototype(array, length);
1007 }
1008
1009 // %RemoveArrayHoles returns -1 if fast removal is not supported.
1010 var num_non_undefined = %RemoveArrayHoles(array, length);
1011
1012 if (num_non_undefined == -1) {
Ben Murdochc5610432016-08-08 18:44:38 +01001013 // There were indexed accessors in the array.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001014 // Move array holes and undefineds to the end using a Javascript function
Ben Murdochc5610432016-08-08 18:44:38 +01001015 // that is safe in the presence of accessors.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001016 num_non_undefined = SafeRemoveArrayHoles(array);
1017 }
1018
1019 QuickSort(array, 0, num_non_undefined);
1020
1021 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
1022 // For compatibility with JSC, we shadow any elements in the prototype
1023 // chain that has become exposed by sort moving a hole to its position.
1024 ShadowPrototypeElements(array, num_non_undefined, max_prototype_element);
1025 }
1026
1027 return array;
1028}
1029
1030
1031function ArraySort(comparefn) {
1032 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
1033
1034 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001035 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001036 return InnerArraySort(array, length, comparefn);
1037}
1038
1039
1040// The following functions cannot be made efficient on sparse arrays while
1041// preserving the semantics, since the calls to the receiver function can add
1042// or delete elements from the array.
1043function InnerArrayFilter(f, receiver, array, length, result) {
1044 var result_length = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001045 for (var i = 0; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001046 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001047 var element = array[i];
1048 if (%_Call(f, receiver, element, i, array)) {
Ben Murdochc5610432016-08-08 18:44:38 +01001049 %CreateDataProperty(result, result_length, element);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001050 result_length++;
1051 }
1052 }
1053 }
1054 return result;
1055}
1056
1057
1058
1059function ArrayFilter(f, receiver) {
1060 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
1061
1062 // Pull out the length so that modifications to the length in the
1063 // loop will not affect the looping and side effects are visible.
1064 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001065 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001066 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1067 var result = ArraySpeciesCreate(array, 0);
1068 return InnerArrayFilter(f, receiver, array, length, result);
1069}
1070
1071
1072function InnerArrayForEach(f, receiver, array, length) {
1073 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1074
Ben Murdochda12d292016-06-02 14:46:10 +01001075 if (IS_UNDEFINED(receiver)) {
1076 for (var i = 0; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001077 if (i in array) {
Ben Murdochda12d292016-06-02 14:46:10 +01001078 var element = array[i];
1079 f(element, i, array);
1080 }
1081 }
1082 } else {
1083 for (var i = 0; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001084 if (i in array) {
Ben Murdochda12d292016-06-02 14:46:10 +01001085 var element = array[i];
1086 %_Call(f, receiver, element, i, array);
1087 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001088 }
1089 }
1090}
1091
1092
1093function ArrayForEach(f, receiver) {
1094 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
1095
1096 // Pull out the length so that modifications to the length in the
1097 // loop will not affect the looping and side effects are visible.
1098 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001099 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001100 InnerArrayForEach(f, receiver, array, length);
1101}
1102
1103
1104function InnerArraySome(f, receiver, array, length) {
1105 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1106
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001107 for (var i = 0; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001108 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001109 var element = array[i];
1110 if (%_Call(f, receiver, element, i, array)) return true;
1111 }
1112 }
1113 return false;
1114}
1115
1116
1117// Executes the function once for each element present in the
1118// array until it finds one where callback returns true.
1119function ArraySome(f, receiver) {
1120 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
1121
1122 // Pull out the length so that modifications to the length in the
1123 // loop will not affect the looping and side effects are visible.
1124 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001125 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001126 return InnerArraySome(f, receiver, array, length);
1127}
1128
1129
1130function InnerArrayEvery(f, receiver, array, length) {
1131 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1132
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001133 for (var i = 0; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001134 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001135 var element = array[i];
1136 if (!%_Call(f, receiver, element, i, array)) return false;
1137 }
1138 }
1139 return true;
1140}
1141
1142function ArrayEvery(f, receiver) {
1143 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
1144
1145 // Pull out the length so that modifications to the length in the
1146 // loop will not affect the looping and side effects are visible.
1147 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001148 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001149 return InnerArrayEvery(f, receiver, array, length);
1150}
1151
1152
1153function ArrayMap(f, receiver) {
1154 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
1155
1156 // Pull out the length so that modifications to the length in the
1157 // loop will not affect the looping and side effects are visible.
1158 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001159 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001160 if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
1161 var result = ArraySpeciesCreate(array, length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001162 for (var i = 0; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001163 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001164 var element = array[i];
Ben Murdochc5610432016-08-08 18:44:38 +01001165 %CreateDataProperty(result, i, %_Call(f, receiver, element, i, array));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001166 }
1167 }
1168 return result;
1169}
1170
1171
1172// For .indexOf, we don't need to pass in the number of arguments
1173// at the callsite since ToInteger(undefined) == 0; however, for
1174// .lastIndexOf, we need to pass it, since the behavior for passing
1175// undefined is 0 but for not including the argument is length-1.
1176function InnerArrayIndexOf(array, element, index, length) {
1177 if (length == 0) return -1;
1178 if (IS_UNDEFINED(index)) {
1179 index = 0;
1180 } else {
Ben Murdochc5610432016-08-08 18:44:38 +01001181 index = INVERT_NEG_ZERO(TO_INTEGER(index));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001182 // If index is negative, index from the end of the array.
1183 if (index < 0) {
1184 index = length + index;
1185 // If index is still negative, search the entire array.
1186 if (index < 0) index = 0;
1187 }
1188 }
1189 var min = index;
1190 var max = length;
1191 if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) {
1192 %NormalizeElements(array);
1193 var indices = %GetArrayKeys(array, length);
1194 if (IS_NUMBER(indices)) {
1195 // It's an interval.
1196 max = indices; // Capped by length already.
1197 // Fall through to loop below.
1198 } else {
1199 if (indices.length == 0) return -1;
1200 // Get all the keys in sorted order.
1201 var sortedKeys = GetSortedArrayKeys(array, indices);
1202 var n = sortedKeys.length;
1203 var i = 0;
1204 while (i < n && sortedKeys[i] < index) i++;
1205 while (i < n) {
1206 var key = sortedKeys[i];
Ben Murdochda12d292016-06-02 14:46:10 +01001207 if (array[key] === element) return key;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001208 i++;
1209 }
1210 return -1;
1211 }
1212 }
1213 // Lookup through the array.
1214 if (!IS_UNDEFINED(element)) {
1215 for (var i = min; i < max; i++) {
1216 if (array[i] === element) return i;
1217 }
1218 return -1;
1219 }
1220 // Lookup through the array.
1221 for (var i = min; i < max; i++) {
1222 if (IS_UNDEFINED(array[i]) && i in array) {
1223 return i;
1224 }
1225 }
1226 return -1;
1227}
1228
1229
1230function ArrayIndexOf(element, index) {
1231 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
1232
Ben Murdoch097c5b22016-05-18 11:27:45 +01001233 var length = TO_LENGTH(this.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001234 return InnerArrayIndexOf(this, element, index, length);
1235}
1236
1237
1238function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) {
1239 if (length == 0) return -1;
1240 if (argumentsLength < 2) {
1241 index = length - 1;
1242 } else {
Ben Murdochc5610432016-08-08 18:44:38 +01001243 index = INVERT_NEG_ZERO(TO_INTEGER(index));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001244 // If index is negative, index from end of the array.
1245 if (index < 0) index += length;
1246 // If index is still negative, do not search the array.
1247 if (index < 0) return -1;
1248 else if (index >= length) index = length - 1;
1249 }
1250 var min = 0;
1251 var max = index;
1252 if (UseSparseVariant(array, length, IS_ARRAY(array), index)) {
1253 %NormalizeElements(array);
1254 var indices = %GetArrayKeys(array, index + 1);
1255 if (IS_NUMBER(indices)) {
1256 // It's an interval.
1257 max = indices; // Capped by index already.
1258 // Fall through to loop below.
1259 } else {
1260 if (indices.length == 0) return -1;
1261 // Get all the keys in sorted order.
1262 var sortedKeys = GetSortedArrayKeys(array, indices);
1263 var i = sortedKeys.length - 1;
1264 while (i >= 0) {
1265 var key = sortedKeys[i];
Ben Murdochda12d292016-06-02 14:46:10 +01001266 if (array[key] === element) return key;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001267 i--;
1268 }
1269 return -1;
1270 }
1271 }
1272 // Lookup through the array.
1273 if (!IS_UNDEFINED(element)) {
1274 for (var i = max; i >= min; i--) {
1275 if (array[i] === element) return i;
1276 }
1277 return -1;
1278 }
1279 for (var i = max; i >= min; i--) {
1280 if (IS_UNDEFINED(array[i]) && i in array) {
1281 return i;
1282 }
1283 }
1284 return -1;
1285}
1286
1287
1288function ArrayLastIndexOf(element, index) {
1289 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
1290
Ben Murdoch097c5b22016-05-18 11:27:45 +01001291 var length = TO_LENGTH(this.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001292 return InnerArrayLastIndexOf(this, element, index, length,
Ben Murdoch097c5b22016-05-18 11:27:45 +01001293 arguments.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001294}
1295
1296
1297function InnerArrayReduce(callback, current, array, length, argumentsLength) {
1298 if (!IS_CALLABLE(callback)) {
1299 throw MakeTypeError(kCalledNonCallable, callback);
1300 }
1301
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001302 var i = 0;
1303 find_initial: if (argumentsLength < 2) {
1304 for (; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001305 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001306 current = array[i++];
1307 break find_initial;
1308 }
1309 }
1310 throw MakeTypeError(kReduceNoInitial);
1311 }
1312
1313 for (; i < length; i++) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001314 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001315 var element = array[i];
1316 current = callback(current, element, i, array);
1317 }
1318 }
1319 return current;
1320}
1321
1322
1323function ArrayReduce(callback, current) {
1324 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
1325
1326 // Pull out the length so that modifications to the length in the
1327 // loop will not affect the looping and side effects are visible.
1328 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001329 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001330 return InnerArrayReduce(callback, current, array, length,
Ben Murdoch097c5b22016-05-18 11:27:45 +01001331 arguments.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001332}
1333
1334
1335function InnerArrayReduceRight(callback, current, array, length,
1336 argumentsLength) {
1337 if (!IS_CALLABLE(callback)) {
1338 throw MakeTypeError(kCalledNonCallable, callback);
1339 }
1340
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001341 var i = length - 1;
1342 find_initial: if (argumentsLength < 2) {
1343 for (; i >= 0; i--) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001344 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001345 current = array[i--];
1346 break find_initial;
1347 }
1348 }
1349 throw MakeTypeError(kReduceNoInitial);
1350 }
1351
1352 for (; i >= 0; i--) {
Ben Murdoch61f157c2016-09-16 13:49:30 +01001353 if (i in array) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001354 var element = array[i];
1355 current = callback(current, element, i, array);
1356 }
1357 }
1358 return current;
1359}
1360
1361
1362function ArrayReduceRight(callback, current) {
1363 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
1364
1365 // Pull out the length so that side effects are visible before the
1366 // callback function is checked.
1367 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001368 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001369 return InnerArrayReduceRight(callback, current, array, length,
Ben Murdoch097c5b22016-05-18 11:27:45 +01001370 arguments.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001371}
1372
1373
1374function InnerArrayCopyWithin(target, start, end, array, length) {
1375 target = TO_INTEGER(target);
1376 var to;
1377 if (target < 0) {
1378 to = MaxSimple(length + target, 0);
1379 } else {
1380 to = MinSimple(target, length);
1381 }
1382
1383 start = TO_INTEGER(start);
1384 var from;
1385 if (start < 0) {
1386 from = MaxSimple(length + start, 0);
1387 } else {
1388 from = MinSimple(start, length);
1389 }
1390
1391 end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
1392 var final;
1393 if (end < 0) {
1394 final = MaxSimple(length + end, 0);
1395 } else {
1396 final = MinSimple(end, length);
1397 }
1398
1399 var count = MinSimple(final - from, length - to);
1400 var direction = 1;
1401 if (from < to && to < (from + count)) {
1402 direction = -1;
1403 from = from + count - 1;
1404 to = to + count - 1;
1405 }
1406
1407 while (count > 0) {
1408 if (from in array) {
1409 array[to] = array[from];
1410 } else {
1411 delete array[to];
1412 }
1413 from = from + direction;
1414 to = to + direction;
1415 count--;
1416 }
1417
1418 return array;
1419}
1420
1421
1422// ES6 draft 03-17-15, section 22.1.3.3
1423function ArrayCopyWithin(target, start, end) {
1424 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
1425
1426 var array = TO_OBJECT(this);
1427 var length = TO_LENGTH(array.length);
1428
1429 return InnerArrayCopyWithin(target, start, end, array, length);
1430}
1431
1432
1433function InnerArrayFind(predicate, thisArg, array, length) {
1434 if (!IS_CALLABLE(predicate)) {
1435 throw MakeTypeError(kCalledNonCallable, predicate);
1436 }
1437
1438 for (var i = 0; i < length; i++) {
1439 var element = array[i];
1440 if (%_Call(predicate, thisArg, element, i, array)) {
1441 return element;
1442 }
1443 }
1444
1445 return;
1446}
1447
1448
1449// ES6 draft 07-15-13, section 15.4.3.23
1450function ArrayFind(predicate, thisArg) {
1451 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
1452
1453 var array = TO_OBJECT(this);
1454 var length = TO_INTEGER(array.length);
1455
1456 return InnerArrayFind(predicate, thisArg, array, length);
1457}
1458
1459
1460function InnerArrayFindIndex(predicate, thisArg, array, length) {
1461 if (!IS_CALLABLE(predicate)) {
1462 throw MakeTypeError(kCalledNonCallable, predicate);
1463 }
1464
1465 for (var i = 0; i < length; i++) {
1466 var element = array[i];
1467 if (%_Call(predicate, thisArg, element, i, array)) {
1468 return i;
1469 }
1470 }
1471
1472 return -1;
1473}
1474
1475
1476// ES6 draft 07-15-13, section 15.4.3.24
1477function ArrayFindIndex(predicate, thisArg) {
1478 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex");
1479
1480 var array = TO_OBJECT(this);
1481 var length = TO_INTEGER(array.length);
1482
1483 return InnerArrayFindIndex(predicate, thisArg, array, length);
1484}
1485
1486
1487// ES6, draft 04-05-14, section 22.1.3.6
1488function InnerArrayFill(value, start, end, array, length) {
1489 var i = IS_UNDEFINED(start) ? 0 : TO_INTEGER(start);
1490 var end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
1491
1492 if (i < 0) {
1493 i += length;
1494 if (i < 0) i = 0;
1495 } else {
1496 if (i > length) i = length;
1497 }
1498
1499 if (end < 0) {
1500 end += length;
1501 if (end < 0) end = 0;
1502 } else {
1503 if (end > length) end = length;
1504 }
1505
1506 if ((end - i) > 0 && %object_is_frozen(array)) {
1507 throw MakeTypeError(kArrayFunctionsOnFrozen);
1508 }
1509
1510 for (; i < end; i++)
1511 array[i] = value;
1512 return array;
1513}
1514
1515
1516// ES6, draft 04-05-14, section 22.1.3.6
1517function ArrayFill(value, start, end) {
1518 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
1519
1520 var array = TO_OBJECT(this);
Ben Murdoch097c5b22016-05-18 11:27:45 +01001521 var length = TO_LENGTH(array.length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001522
1523 return InnerArrayFill(value, start, end, array, length);
1524}
1525
1526
1527function InnerArrayIncludes(searchElement, fromIndex, array, length) {
1528 if (length === 0) {
1529 return false;
1530 }
1531
1532 var n = TO_INTEGER(fromIndex);
1533
1534 var k;
1535 if (n >= 0) {
1536 k = n;
1537 } else {
1538 k = length + n;
1539 if (k < 0) {
1540 k = 0;
1541 }
1542 }
1543
1544 while (k < length) {
1545 var elementK = array[k];
Ben Murdoch097c5b22016-05-18 11:27:45 +01001546 if (%SameValueZero(searchElement, elementK)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001547 return true;
1548 }
1549
1550 ++k;
1551 }
1552
1553 return false;
1554}
1555
1556
1557// ES2016 draft, section 22.1.3.11
1558function ArrayIncludes(searchElement, fromIndex) {
1559 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.includes");
1560
1561 var array = TO_OBJECT(this);
1562 var length = TO_LENGTH(array.length);
1563
1564 return InnerArrayIncludes(searchElement, fromIndex, array, length);
1565}
1566
1567
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001568// ES6, draft 10-14-14, section 22.1.2.1
1569function ArrayFrom(arrayLike, mapfn, receiver) {
1570 var items = TO_OBJECT(arrayLike);
1571 var mapping = !IS_UNDEFINED(mapfn);
1572
1573 if (mapping) {
1574 if (!IS_CALLABLE(mapfn)) {
1575 throw MakeTypeError(kCalledNonCallable, mapfn);
1576 }
1577 }
1578
1579 var iterable = GetMethod(items, iteratorSymbol);
1580 var k;
1581 var result;
1582 var mappedValue;
1583 var nextValue;
1584
1585 if (!IS_UNDEFINED(iterable)) {
1586 result = %IsConstructor(this) ? new this() : [];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001587 k = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001588
Ben Murdoch097c5b22016-05-18 11:27:45 +01001589 for (nextValue of
1590 { [iteratorSymbol]() { return GetIterator(items, iterable) } }) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001591 if (mapping) {
1592 mappedValue = %_Call(mapfn, receiver, nextValue, k);
1593 } else {
1594 mappedValue = nextValue;
1595 }
Ben Murdochc5610432016-08-08 18:44:38 +01001596 %CreateDataProperty(result, k, mappedValue);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001597 k++;
1598 }
Ben Murdoch097c5b22016-05-18 11:27:45 +01001599 result.length = k;
1600 return result;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001601 } else {
1602 var len = TO_LENGTH(items.length);
1603 result = %IsConstructor(this) ? new this(len) : new GlobalArray(len);
1604
1605 for (k = 0; k < len; ++k) {
1606 nextValue = items[k];
1607 if (mapping) {
1608 mappedValue = %_Call(mapfn, receiver, nextValue, k);
1609 } else {
1610 mappedValue = nextValue;
1611 }
Ben Murdochc5610432016-08-08 18:44:38 +01001612 %CreateDataProperty(result, k, mappedValue);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001613 }
1614
1615 result.length = k;
1616 return result;
1617 }
1618}
1619
1620
1621// ES6, draft 05-22-14, section 22.1.2.3
Ben Murdoch097c5b22016-05-18 11:27:45 +01001622function ArrayOf(...args) {
1623 var length = args.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001624 var constructor = this;
1625 // TODO: Implement IsConstructor (ES6 section 7.2.5)
1626 var array = %IsConstructor(constructor) ? new constructor(length) : [];
1627 for (var i = 0; i < length; i++) {
Ben Murdochc5610432016-08-08 18:44:38 +01001628 %CreateDataProperty(array, i, args[i]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001629 }
1630 array.length = length;
1631 return array;
1632}
1633
Ben Murdoch61f157c2016-09-16 13:49:30 +01001634
1635function ArraySpecies() {
1636 return this;
1637}
1638
1639
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001640// -------------------------------------------------------------------
1641
1642// Set up non-enumerable constructor property on the Array.prototype
1643// object.
1644%AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray,
1645 DONT_ENUM);
1646
1647// Set up unscopable properties on the Array.prototype object.
1648var unscopables = {
1649 __proto__: null,
1650 copyWithin: true,
1651 entries: true,
1652 fill: true,
1653 find: true,
1654 findIndex: true,
Ben Murdoch61f157c2016-09-16 13:49:30 +01001655 includes: true,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001656 keys: true,
1657};
1658
1659%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
1660 DONT_ENUM | READ_ONLY);
1661
1662%FunctionSetLength(ArrayFrom, 1);
1663
1664// Set up non-enumerable functions on the Array object.
1665utils.InstallFunctions(GlobalArray, DONT_ENUM, [
1666 "from", ArrayFrom,
1667 "of", ArrayOf
1668]);
1669
1670var specialFunctions = %SpecialArrayFunctions();
1671
1672var getFunction = function(name, jsBuiltin, len) {
1673 var f = jsBuiltin;
1674 if (specialFunctions.hasOwnProperty(name)) {
1675 f = specialFunctions[name];
1676 }
1677 if (!IS_UNDEFINED(len)) {
1678 %FunctionSetLength(f, len);
1679 }
1680 return f;
1681};
1682
1683// Set up non-enumerable functions of the Array.prototype object and
1684// set their names.
1685// Manipulate the length of some of the functions to meet
1686// expectations set by ECMA-262 or Mozilla.
1687utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
1688 "toString", getFunction("toString", ArrayToString),
1689 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1690 "join", getFunction("join", ArrayJoin),
1691 "pop", getFunction("pop", ArrayPop),
1692 "push", getFunction("push", ArrayPush, 1),
1693 "reverse", getFunction("reverse", ArrayReverse),
1694 "shift", getFunction("shift", ArrayShift),
1695 "unshift", getFunction("unshift", ArrayUnshift, 1),
1696 "slice", getFunction("slice", ArraySlice, 2),
1697 "splice", getFunction("splice", ArraySplice, 2),
1698 "sort", getFunction("sort", ArraySort),
1699 "filter", getFunction("filter", ArrayFilter, 1),
1700 "forEach", getFunction("forEach", ArrayForEach, 1),
1701 "some", getFunction("some", ArraySome, 1),
1702 "every", getFunction("every", ArrayEvery, 1),
1703 "map", getFunction("map", ArrayMap, 1),
1704 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1705 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1706 "reduce", getFunction("reduce", ArrayReduce, 1),
1707 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1),
1708 "copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2),
1709 "find", getFunction("find", ArrayFind, 1),
1710 "findIndex", getFunction("findIndex", ArrayFindIndex, 1),
1711 "fill", getFunction("fill", ArrayFill, 1),
1712 "includes", getFunction("includes", ArrayIncludes, 1),
1713]);
1714
Ben Murdoch61f157c2016-09-16 13:49:30 +01001715utils.InstallGetter(GlobalArray, speciesSymbol, ArraySpecies);
1716
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001717%FinishArrayPrototypeSetup(GlobalArray.prototype);
1718
1719// The internal Array prototype doesn't need to be fancy, since it's never
1720// exposed to user code.
1721// Adding only the functions that are actually used.
1722utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
1723 "indexOf", getFunction("indexOf", ArrayIndexOf),
1724 "join", getFunction("join", ArrayJoin),
1725 "pop", getFunction("pop", ArrayPop),
1726 "push", getFunction("push", ArrayPush),
1727 "shift", getFunction("shift", ArrayShift),
1728 "sort", getFunction("sort", ArraySort),
1729 "splice", getFunction("splice", ArraySplice)
1730]);
1731
1732utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [
1733 "join", getFunction("join", ArrayJoin),
1734 "pop", getFunction("pop", ArrayPop),
1735 "push", getFunction("push", ArrayPush),
1736 "shift", getFunction("shift", ArrayShift)
1737]);
1738
1739// V8 extras get a separate copy of InternalPackedArray. We give them the basic
1740// manipulation methods.
1741utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
1742 "push", getFunction("push", ArrayPush),
1743 "pop", getFunction("pop", ArrayPop),
1744 "shift", getFunction("shift", ArrayShift),
1745 "unshift", getFunction("unshift", ArrayUnshift),
1746 "splice", getFunction("splice", ArraySplice),
1747 "slice", getFunction("slice", ArraySlice)
1748]);
1749
1750// -------------------------------------------------------------------
1751// Exports
1752
1753utils.Export(function(to) {
1754 to.ArrayFrom = ArrayFrom;
1755 to.ArrayIndexOf = ArrayIndexOf;
1756 to.ArrayJoin = ArrayJoin;
1757 to.ArrayPush = ArrayPush;
1758 to.ArrayToString = ArrayToString;
1759 to.InnerArrayCopyWithin = InnerArrayCopyWithin;
1760 to.InnerArrayEvery = InnerArrayEvery;
1761 to.InnerArrayFill = InnerArrayFill;
1762 to.InnerArrayFilter = InnerArrayFilter;
1763 to.InnerArrayFind = InnerArrayFind;
1764 to.InnerArrayFindIndex = InnerArrayFindIndex;
1765 to.InnerArrayForEach = InnerArrayForEach;
1766 to.InnerArrayIncludes = InnerArrayIncludes;
1767 to.InnerArrayIndexOf = InnerArrayIndexOf;
1768 to.InnerArrayJoin = InnerArrayJoin;
1769 to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
1770 to.InnerArrayReduce = InnerArrayReduce;
1771 to.InnerArrayReduceRight = InnerArrayReduceRight;
1772 to.InnerArraySome = InnerArraySome;
1773 to.InnerArraySort = InnerArraySort;
1774 to.InnerArrayToLocaleString = InnerArrayToLocaleString;
1775 to.PackedArrayReverse = PackedArrayReverse;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001776});
1777
1778%InstallToContext([
1779 "array_pop", ArrayPop,
1780 "array_push", ArrayPush,
1781 "array_shift", ArrayShift,
1782 "array_splice", ArraySplice,
1783 "array_slice", ArraySlice,
1784 "array_unshift", ArrayUnshift,
1785]);
1786
1787});