blob: 98fe3ac7b14e5b4861cf11d4581ed2a9e67ff367 [file] [log] [blame]
Ben Murdochb0fe1622011-05-05 13:52:32 +01001// Copyright 2010 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// This file relies on the fact that the following declarations have been made
29// in runtime.js:
Ben Murdoch85b71792012-04-11 18:30:58 +010030// const $Array = global.Array;
Steve Blocka7e24c12009-10-30 11:49:00 +000031
32// -------------------------------------------------------------------
33
34// Global list of arrays visited during toString, toLocaleString and
35// join invocations.
Ben Murdoche0cee9b2011-05-25 10:26:03 +010036var visited_arrays = new InternalArray();
Steve Blocka7e24c12009-10-30 11:49:00 +000037
38
39// Gets a sorted array of array keys. Useful for operations on sparse
40// arrays. Dupes have not been removed.
41function GetSortedArrayKeys(array, intervals) {
42 var length = intervals.length;
43 var keys = [];
44 for (var k = 0; k < length; k++) {
45 var key = intervals[k];
46 if (key < 0) {
47 var j = -1 - key;
48 var limit = j + intervals[++k];
49 for (; j < limit; j++) {
50 var e = array[j];
51 if (!IS_UNDEFINED(e) || j in array) {
52 keys.push(j);
53 }
54 }
55 } else {
56 // The case where key is undefined also ends here.
57 if (!IS_UNDEFINED(key)) {
58 var e = array[key];
59 if (!IS_UNDEFINED(e) || key in array) {
60 keys.push(key);
61 }
62 }
63 }
64 }
65 keys.sort(function(a, b) { return a - b; });
66 return keys;
67}
68
69
Ben Murdoch257744e2011-11-30 15:57:28 +000070function SparseJoinWithSeparator(array, len, convert, separator) {
71 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
72 var totalLength = 0;
73 var elements = new InternalArray(keys.length * 2);
74 var previousKey = -1;
75 for (var i = 0; i < keys.length; i++) {
76 var key = keys[i];
77 if (key != previousKey) { // keys may contain duplicates.
78 var e = array[key];
79 if (!IS_STRING(e)) e = convert(e);
80 elements[i * 2] = key;
81 elements[i * 2 + 1] = e;
82 previousKey = key;
83 }
84 }
85 return %SparseJoinWithSeparator(elements, len, separator);
86}
87
88
Steve Blocka7e24c12009-10-30 11:49:00 +000089// Optimized for sparse arrays if separator is ''.
90function SparseJoin(array, len, convert) {
91 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
Steve Blocka7e24c12009-10-30 11:49:00 +000092 var last_key = -1;
93 var keys_length = keys.length;
Leon Clarkee46be812010-01-19 14:06:41 +000094
Ben Murdoche0cee9b2011-05-25 10:26:03 +010095 var elements = new InternalArray(keys_length);
Leon Clarkee46be812010-01-19 14:06:41 +000096 var elements_length = 0;
97
Steve Blocka7e24c12009-10-30 11:49:00 +000098 for (var i = 0; i < keys_length; i++) {
99 var key = keys[i];
100 if (key != last_key) {
101 var e = array[key];
Leon Clarkee46be812010-01-19 14:06:41 +0000102 if (!IS_STRING(e)) e = convert(e);
103 elements[elements_length++] = e;
Steve Blocka7e24c12009-10-30 11:49:00 +0000104 last_key = key;
105 }
106 }
Leon Clarkee46be812010-01-19 14:06:41 +0000107 return %StringBuilderConcat(elements, elements_length, '');
Steve Blocka7e24c12009-10-30 11:49:00 +0000108}
109
110
111function UseSparseVariant(object, length, is_array) {
112 return is_array &&
113 length > 1000 &&
114 (!%_IsSmi(length) ||
115 %EstimateNumberOfElements(object) < (length >> 2));
116}
117
118
119function Join(array, length, separator, convert) {
120 if (length == 0) return '';
121
122 var is_array = IS_ARRAY(array);
123
124 if (is_array) {
125 // If the array is cyclic, return the empty string for already
126 // visited arrays.
127 if (!%PushIfAbsent(visited_arrays, array)) return '';
128 }
129
130 // Attempt to convert the elements.
131 try {
Ben Murdoch257744e2011-11-30 15:57:28 +0000132 if (UseSparseVariant(array, length, is_array)) {
133 if (separator.length == 0) {
134 return SparseJoin(array, length, convert);
135 } else {
136 return SparseJoinWithSeparator(array, length, convert, separator);
137 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000138 }
139
140 // Fast case for one-element arrays.
141 if (length == 1) {
142 var e = array[0];
Ben Murdochb8e0da22011-05-16 14:20:40 +0100143 if (IS_STRING(e)) return e;
144 return convert(e);
Steve Blocka7e24c12009-10-30 11:49:00 +0000145 }
146
Leon Clarkee46be812010-01-19 14:06:41 +0000147 // Construct an array for the elements.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100148 var elements = new InternalArray(length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000149
Steve Blockd0582a62009-12-15 09:54:21 +0000150 // We pull the empty separator check outside the loop for speed!
151 if (separator.length == 0) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100152 var elements_length = 0;
Steve Blockd0582a62009-12-15 09:54:21 +0000153 for (var i = 0; i < length; i++) {
154 var e = array[i];
Ben Murdoch257744e2011-11-30 15:57:28 +0000155 if (!IS_STRING(e)) e = convert(e);
156 elements[elements_length++] = e;
Steve Blockd0582a62009-12-15 09:54:21 +0000157 }
Ben Murdoch086aeea2011-05-13 15:57:08 +0100158 elements.length = elements_length;
159 var result = %_FastAsciiArrayJoin(elements, '');
160 if (!IS_UNDEFINED(result)) return result;
161 return %StringBuilderConcat(elements, elements_length, '');
162 }
Ben Murdochb8e0da22011-05-16 14:20:40 +0100163 // Non-empty separator case.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100164 // If the first element is a number then use the heuristic that the
Ben Murdochb8e0da22011-05-16 14:20:40 +0100165 // remaining elements are also likely to be numbers.
166 if (!IS_NUMBER(array[0])) {
167 for (var i = 0; i < length; i++) {
168 var e = array[i];
Ben Murdoch086aeea2011-05-13 15:57:08 +0100169 if (!IS_STRING(e)) e = convert(e);
170 elements[i] = e;
Steve Blocka7e24c12009-10-30 11:49:00 +0000171 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100172 } else {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100173 for (var i = 0; i < length; i++) {
174 var e = array[i];
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000175 if (IS_NUMBER(e)) {
176 e = %_NumberToString(e);
177 } else if (!IS_STRING(e)) {
178 e = convert(e);
179 }
180 elements[i] = e;
Ben Murdochb8e0da22011-05-16 14:20:40 +0100181 }
Ben Murdoch086aeea2011-05-13 15:57:08 +0100182 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100183 var result = %_FastAsciiArrayJoin(elements, separator);
184 if (!IS_UNDEFINED(result)) return result;
185
186 return %StringBuilderJoin(elements, length, separator);
Steve Blocka7e24c12009-10-30 11:49:00 +0000187 } finally {
Steve Block1e0659c2011-05-24 12:43:12 +0100188 // Make sure to remove the last element of the visited array no
189 // matter what happens.
190 if (is_array) visited_arrays.length = visited_arrays.length - 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000191 }
192}
193
194
Ben Murdochb0fe1622011-05-05 13:52:32 +0100195function ConvertToString(x) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100196 // Assumes x is a non-string.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100197 if (IS_NUMBER(x)) return %_NumberToString(x);
198 if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
199 return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
Steve Blocka7e24c12009-10-30 11:49:00 +0000200}
201
202
203function ConvertToLocaleString(e) {
Ben Murdoch85b71792012-04-11 18:30:58 +0100204 if (e == null) {
Leon Clarkee46be812010-01-19 14:06:41 +0000205 return '';
206 } else {
Ben Murdoch85b71792012-04-11 18:30:58 +0100207 // e_obj's toLocaleString might be overwritten, check if it is a function.
208 // Call ToString if toLocaleString is not a function.
209 // See issue 877615.
Steve Blocka7e24c12009-10-30 11:49:00 +0000210 var e_obj = ToObject(e);
Ben Murdoch85b71792012-04-11 18:30:58 +0100211 if (IS_SPEC_FUNCTION(e_obj.toLocaleString))
212 return ToString(e_obj.toLocaleString());
213 else
214 return ToString(e);
Steve Blocka7e24c12009-10-30 11:49:00 +0000215 }
216}
217
218
219// This function implements the optimized splice implementation that can use
220// special array operations to handle sparse arrays in a sensible fashion.
221function SmartSlice(array, start_i, del_count, len, deleted_elements) {
222 // Move deleted elements to a new array (the return value from splice).
223 // Intervals array can contain keys and intervals. See comment in Concat.
224 var intervals = %GetArrayKeys(array, start_i + del_count);
225 var length = intervals.length;
226 for (var k = 0; k < length; k++) {
227 var key = intervals[k];
228 if (key < 0) {
229 var j = -1 - key;
230 var interval_limit = j + intervals[++k];
231 if (j < start_i) {
232 j = start_i;
233 }
234 for (; j < interval_limit; j++) {
235 // ECMA-262 15.4.4.12 line 10. The spec could also be
236 // interpreted such that %HasLocalProperty would be the
237 // appropriate test. We follow KJS in consulting the
238 // prototype.
239 var current = array[j];
240 if (!IS_UNDEFINED(current) || j in array) {
241 deleted_elements[j - start_i] = current;
242 }
243 }
244 } else {
245 if (!IS_UNDEFINED(key)) {
246 if (key >= start_i) {
247 // ECMA-262 15.4.4.12 line 10. The spec could also be
248 // interpreted such that %HasLocalProperty would be the
249 // appropriate test. We follow KJS in consulting the
250 // prototype.
251 var current = array[key];
252 if (!IS_UNDEFINED(current) || key in array) {
253 deleted_elements[key - start_i] = current;
254 }
255 }
256 }
257 }
258 }
259}
260
261
262// This function implements the optimized splice implementation that can use
263// special array operations to handle sparse arrays in a sensible fashion.
264function SmartMove(array, start_i, del_count, len, num_additional_args) {
265 // Move data to new array.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100266 var new_array = new InternalArray(len - del_count + num_additional_args);
Steve Blocka7e24c12009-10-30 11:49:00 +0000267 var intervals = %GetArrayKeys(array, len);
268 var length = intervals.length;
269 for (var k = 0; k < length; k++) {
270 var key = intervals[k];
271 if (key < 0) {
272 var j = -1 - key;
273 var interval_limit = j + intervals[++k];
274 while (j < start_i && j < interval_limit) {
275 // The spec could also be interpreted such that
276 // %HasLocalProperty would be the appropriate test. We follow
277 // KJS in consulting the prototype.
278 var current = array[j];
279 if (!IS_UNDEFINED(current) || j in array) {
280 new_array[j] = current;
281 }
282 j++;
283 }
284 j = start_i + del_count;
285 while (j < interval_limit) {
286 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also be
287 // interpreted such that %HasLocalProperty would be the
288 // appropriate test. We follow KJS in consulting the
289 // prototype.
290 var current = array[j];
291 if (!IS_UNDEFINED(current) || j in array) {
292 new_array[j - del_count + num_additional_args] = current;
293 }
294 j++;
295 }
296 } else {
297 if (!IS_UNDEFINED(key)) {
298 if (key < start_i) {
299 // The spec could also be interpreted such that
300 // %HasLocalProperty would be the appropriate test. We follow
301 // KJS in consulting the prototype.
302 var current = array[key];
303 if (!IS_UNDEFINED(current) || key in array) {
304 new_array[key] = current;
305 }
306 } else if (key >= start_i + del_count) {
307 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also
308 // be interpreted such that %HasLocalProperty would be the
309 // appropriate test. We follow KJS in consulting the
310 // prototype.
311 var current = array[key];
312 if (!IS_UNDEFINED(current) || key in array) {
313 new_array[key - del_count + num_additional_args] = current;
314 }
315 }
316 }
317 }
318 }
319 // Move contents of new_array into this array
320 %MoveArrayContents(new_array, array);
321}
322
323
324// This is part of the old simple-minded splice. We are using it either
325// because the receiver is not an array (so we have no choice) or because we
326// know we are not deleting or moving a lot of elements.
327function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
328 for (var i = 0; i < del_count; i++) {
329 var index = start_i + i;
330 // The spec could also be interpreted such that %HasLocalProperty
331 // would be the appropriate test. We follow KJS in consulting the
332 // prototype.
333 var current = array[index];
Ben Murdoch85b71792012-04-11 18:30:58 +0100334 if (!IS_UNDEFINED(current) || index in array)
Steve Blocka7e24c12009-10-30 11:49:00 +0000335 deleted_elements[i] = current;
336 }
337}
338
339
340function SimpleMove(array, start_i, del_count, len, num_additional_args) {
341 if (num_additional_args !== del_count) {
342 // Move the existing elements after the elements to be deleted
343 // to the right position in the resulting array.
344 if (num_additional_args > del_count) {
345 for (var i = len - del_count; i > start_i; i--) {
346 var from_index = i + del_count - 1;
347 var to_index = i + num_additional_args - 1;
348 // The spec could also be interpreted such that
349 // %HasLocalProperty would be the appropriate test. We follow
350 // KJS in consulting the prototype.
351 var current = array[from_index];
352 if (!IS_UNDEFINED(current) || from_index in array) {
353 array[to_index] = current;
354 } else {
355 delete array[to_index];
356 }
357 }
358 } else {
359 for (var i = start_i; i < len - del_count; i++) {
360 var from_index = i + del_count;
361 var to_index = i + num_additional_args;
362 // The spec could also be interpreted such that
363 // %HasLocalProperty would be the appropriate test. We follow
364 // KJS in consulting the prototype.
365 var current = array[from_index];
366 if (!IS_UNDEFINED(current) || from_index in array) {
367 array[to_index] = current;
368 } else {
369 delete array[to_index];
370 }
371 }
372 for (var i = len; i > len - del_count + num_additional_args; i--) {
373 delete array[i - 1];
374 }
375 }
376 }
377}
378
379
380// -------------------------------------------------------------------
381
382
383function ArrayToString() {
Ben Murdoch85b71792012-04-11 18:30:58 +0100384 if (!IS_ARRAY(this)) {
385 throw new $TypeError('Array.prototype.toString is not generic');
Steve Blocka7e24c12009-10-30 11:49:00 +0000386 }
Ben Murdoch85b71792012-04-11 18:30:58 +0100387 return Join(this, this.length, ',', ConvertToString);
Steve Blocka7e24c12009-10-30 11:49:00 +0000388}
389
390
391function ArrayToLocaleString() {
Ben Murdoch85b71792012-04-11 18:30:58 +0100392 if (!IS_ARRAY(this)) {
393 throw new $TypeError('Array.prototype.toString is not generic');
394 }
395 return Join(this, this.length, ',', ConvertToLocaleString);
Steve Blocka7e24c12009-10-30 11:49:00 +0000396}
397
398
399function ArrayJoin(separator) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000400 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
401 throw MakeTypeError("called_on_null_or_undefined",
402 ["Array.prototype.join"]);
403 }
404
Leon Clarkee46be812010-01-19 14:06:41 +0000405 if (IS_UNDEFINED(separator)) {
406 separator = ',';
407 } else if (!IS_STRING(separator)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100408 separator = NonStringToString(separator);
Leon Clarkee46be812010-01-19 14:06:41 +0000409 }
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800410
411 var result = %_FastAsciiArrayJoin(this, separator);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100412 if (!IS_UNDEFINED(result)) return result;
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800413
Ben Murdochb0fe1622011-05-05 13:52:32 +0100414 return Join(this, TO_UINT32(this.length), separator, ConvertToString);
Steve Blocka7e24c12009-10-30 11:49:00 +0000415}
416
417
418// Removes the last element from the array and returns it. See
419// ECMA-262, section 15.4.4.6.
420function ArrayPop() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000421 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
422 throw MakeTypeError("called_on_null_or_undefined",
423 ["Array.prototype.pop"]);
424 }
425
Leon Clarkee46be812010-01-19 14:06:41 +0000426 var n = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000427 if (n == 0) {
428 this.length = n;
429 return;
430 }
431 n--;
432 var value = this[n];
433 this.length = n;
434 delete this[n];
435 return value;
436}
437
438
439// Appends the arguments to the end of the array and returns the new
440// length of the array. See ECMA-262, section 15.4.4.7.
441function ArrayPush() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000442 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
443 throw MakeTypeError("called_on_null_or_undefined",
444 ["Array.prototype.push"]);
445 }
446
Leon Clarkee46be812010-01-19 14:06:41 +0000447 var n = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000448 var m = %_ArgumentsLength();
449 for (var i = 0; i < m; i++) {
450 this[i+n] = %_Arguments(i);
451 }
452 this.length = n + m;
453 return this.length;
454}
455
456
457function ArrayConcat(arg1) { // length == 1
Ben Murdoch257744e2011-11-30 15:57:28 +0000458 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
459 throw MakeTypeError("called_on_null_or_undefined",
460 ["Array.prototype.concat"]);
461 }
462
Steve Blocka7e24c12009-10-30 11:49:00 +0000463 var arg_count = %_ArgumentsLength();
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100464 var arrays = new InternalArray(1 + arg_count);
Steve Blocka7e24c12009-10-30 11:49:00 +0000465 arrays[0] = this;
466 for (var i = 0; i < arg_count; i++) {
467 arrays[i + 1] = %_Arguments(i);
468 }
469
470 return %ArrayConcat(arrays);
471}
472
473
474// For implementing reverse() on large, sparse arrays.
475function SparseReverse(array, len) {
476 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
477 var high_counter = keys.length - 1;
478 var low_counter = 0;
479 while (low_counter <= high_counter) {
480 var i = keys[low_counter];
481 var j = keys[high_counter];
482
483 var j_complement = len - j - 1;
484 var low, high;
485
486 if (j_complement <= i) {
487 high = j;
Ben Murdoch85b71792012-04-11 18:30:58 +0100488 while (keys[--high_counter] == j);
Steve Blocka7e24c12009-10-30 11:49:00 +0000489 low = j_complement;
490 }
491 if (j_complement >= i) {
492 low = i;
Ben Murdoch85b71792012-04-11 18:30:58 +0100493 while (keys[++low_counter] == i);
Steve Blocka7e24c12009-10-30 11:49:00 +0000494 high = len - i - 1;
495 }
496
497 var current_i = array[low];
498 if (!IS_UNDEFINED(current_i) || low in array) {
499 var current_j = array[high];
500 if (!IS_UNDEFINED(current_j) || high in array) {
501 array[low] = current_j;
502 array[high] = current_i;
503 } else {
504 array[high] = current_i;
505 delete array[low];
506 }
507 } else {
508 var current_j = array[high];
509 if (!IS_UNDEFINED(current_j) || high in array) {
510 array[low] = current_j;
511 delete array[high];
512 }
513 }
514 }
515}
516
517
518function ArrayReverse() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000519 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
520 throw MakeTypeError("called_on_null_or_undefined",
521 ["Array.prototype.reverse"]);
522 }
523
Leon Clarkee46be812010-01-19 14:06:41 +0000524 var j = TO_UINT32(this.length) - 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000525
526 if (UseSparseVariant(this, j, IS_ARRAY(this))) {
527 SparseReverse(this, j+1);
528 return this;
529 }
530
531 for (var i = 0; i < j; i++, j--) {
532 var current_i = this[i];
533 if (!IS_UNDEFINED(current_i) || i in this) {
534 var current_j = this[j];
535 if (!IS_UNDEFINED(current_j) || j in this) {
536 this[i] = current_j;
537 this[j] = current_i;
538 } else {
539 this[j] = current_i;
540 delete this[i];
541 }
542 } else {
543 var current_j = this[j];
544 if (!IS_UNDEFINED(current_j) || j in this) {
545 this[i] = current_j;
546 delete this[j];
547 }
548 }
549 }
550 return this;
551}
552
553
554function ArrayShift() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000555 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
556 throw MakeTypeError("called_on_null_or_undefined",
557 ["Array.prototype.shift"]);
558 }
559
Leon Clarkee46be812010-01-19 14:06:41 +0000560 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000561
562 if (len === 0) {
563 this.length = 0;
564 return;
565 }
566
567 var first = this[0];
568
Ben Murdoch85b71792012-04-11 18:30:58 +0100569 if (IS_ARRAY(this))
Steve Blocka7e24c12009-10-30 11:49:00 +0000570 SmartMove(this, 0, 1, len, 0);
Ben Murdoch85b71792012-04-11 18:30:58 +0100571 else
Steve Blocka7e24c12009-10-30 11:49:00 +0000572 SimpleMove(this, 0, 1, len, 0);
573
574 this.length = len - 1;
575
576 return first;
577}
578
579
580function ArrayUnshift(arg1) { // length == 1
Ben Murdoch257744e2011-11-30 15:57:28 +0000581 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
582 throw MakeTypeError("called_on_null_or_undefined",
583 ["Array.prototype.unshift"]);
584 }
585
Leon Clarkee46be812010-01-19 14:06:41 +0000586 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000587 var num_arguments = %_ArgumentsLength();
588
Ben Murdoch85b71792012-04-11 18:30:58 +0100589 if (IS_ARRAY(this))
Steve Blocka7e24c12009-10-30 11:49:00 +0000590 SmartMove(this, 0, 0, len, num_arguments);
Ben Murdoch85b71792012-04-11 18:30:58 +0100591 else
Steve Blocka7e24c12009-10-30 11:49:00 +0000592 SimpleMove(this, 0, 0, len, num_arguments);
593
594 for (var i = 0; i < num_arguments; i++) {
595 this[i] = %_Arguments(i);
596 }
597
598 this.length = len + num_arguments;
599
600 return len + num_arguments;
601}
602
603
604function ArraySlice(start, end) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000605 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
606 throw MakeTypeError("called_on_null_or_undefined",
607 ["Array.prototype.slice"]);
608 }
609
Leon Clarkee46be812010-01-19 14:06:41 +0000610 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000611 var start_i = TO_INTEGER(start);
612 var end_i = len;
613
614 if (end !== void 0) end_i = TO_INTEGER(end);
615
616 if (start_i < 0) {
617 start_i += len;
618 if (start_i < 0) start_i = 0;
619 } else {
620 if (start_i > len) start_i = len;
621 }
622
623 if (end_i < 0) {
624 end_i += len;
625 if (end_i < 0) end_i = 0;
626 } else {
627 if (end_i > len) end_i = len;
628 }
629
630 var result = [];
631
632 if (end_i < start_i) return result;
633
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000634 if (IS_ARRAY(this) &&
635 (end_i > 1000) &&
636 (%EstimateNumberOfElements(this) < end_i)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000637 SmartSlice(this, start_i, end_i - start_i, len, result);
638 } else {
639 SimpleSlice(this, start_i, end_i - start_i, len, result);
640 }
641
642 result.length = end_i - start_i;
643
644 return result;
645}
646
647
648function ArraySplice(start, delete_count) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000649 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
650 throw MakeTypeError("called_on_null_or_undefined",
651 ["Array.prototype.splice"]);
652 }
653
Steve Blocka7e24c12009-10-30 11:49:00 +0000654 var num_arguments = %_ArgumentsLength();
655
Leon Clarkee46be812010-01-19 14:06:41 +0000656 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000657 var start_i = TO_INTEGER(start);
658
659 if (start_i < 0) {
660 start_i += len;
661 if (start_i < 0) start_i = 0;
662 } else {
663 if (start_i > len) start_i = len;
664 }
665
Andrei Popescu402d9372010-02-26 13:31:12 +0000666 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
Steve Block1e0659c2011-05-24 12:43:12 +0100667 // given as a request to delete all the elements from the start.
668 // And it differs from the case of undefined delete count.
Steve Blocka7e24c12009-10-30 11:49:00 +0000669 // This does not follow ECMA-262, but we do the same for
670 // compatibility.
671 var del_count = 0;
Steve Block1e0659c2011-05-24 12:43:12 +0100672 if (num_arguments == 1) {
673 del_count = len - start_i;
674 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000675 del_count = TO_INTEGER(delete_count);
676 if (del_count < 0) del_count = 0;
677 if (del_count > len - start_i) del_count = len - start_i;
Steve Blocka7e24c12009-10-30 11:49:00 +0000678 }
679
680 var deleted_elements = [];
681 deleted_elements.length = del_count;
682
683 // Number of elements to add.
684 var num_additional_args = 0;
685 if (num_arguments > 2) {
686 num_additional_args = num_arguments - 2;
687 }
688
689 var use_simple_splice = true;
690
691 if (IS_ARRAY(this) && num_additional_args !== del_count) {
692 // If we are only deleting/moving a few things near the end of the
693 // array then the simple version is going to be faster, because it
694 // doesn't touch most of the array.
695 var estimated_non_hole_elements = %EstimateNumberOfElements(this);
696 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
697 use_simple_splice = false;
698 }
699 }
700
701 if (use_simple_splice) {
702 SimpleSlice(this, start_i, del_count, len, deleted_elements);
703 SimpleMove(this, start_i, del_count, len, num_additional_args);
704 } else {
705 SmartSlice(this, start_i, del_count, len, deleted_elements);
706 SmartMove(this, start_i, del_count, len, num_additional_args);
707 }
708
709 // Insert the arguments into the resulting array in
710 // place of the deleted elements.
711 var i = start_i;
712 var arguments_index = 2;
713 var arguments_length = %_ArgumentsLength();
714 while (arguments_index < arguments_length) {
715 this[i++] = %_Arguments(arguments_index++);
716 }
717 this.length = len - del_count + num_additional_args;
718
719 // Return the deleted elements.
720 return deleted_elements;
721}
722
723
724function ArraySort(comparefn) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000725 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
726 throw MakeTypeError("called_on_null_or_undefined",
727 ["Array.prototype.sort"]);
728 }
729
Steve Blocka7e24c12009-10-30 11:49:00 +0000730 // In-place QuickSort algorithm.
731 // For short (length <= 22) arrays, insertion sort is used for efficiency.
732
Ben Murdoch589d6972011-11-30 16:04:58 +0000733 if (!IS_SPEC_FUNCTION(comparefn)) {
Steve Block6ded16b2010-05-10 14:33:55 +0100734 comparefn = function (x, y) {
735 if (x === y) return 0;
736 if (%_IsSmi(x) && %_IsSmi(y)) {
737 return %SmiLexicographicCompare(x, y);
738 }
739 x = ToString(x);
740 y = ToString(y);
741 if (x == y) return 0;
742 else return x < y ? -1 : 1;
743 };
744 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000745 var receiver = %GetDefaultReceiver(comparefn);
Steve Blocka7e24c12009-10-30 11:49:00 +0000746
Ben Murdoch85b71792012-04-11 18:30:58 +0100747 function InsertionSort(a, from, to) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000748 for (var i = from + 1; i < to; i++) {
749 var element = a[i];
Steve Block6ded16b2010-05-10 14:33:55 +0100750 for (var j = i - 1; j >= from; j--) {
751 var tmp = a[j];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000752 var order = %_CallFunction(receiver, tmp, element, comparefn);
Steve Block6ded16b2010-05-10 14:33:55 +0100753 if (order > 0) {
754 a[j + 1] = tmp;
755 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000756 break;
757 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000758 }
Steve Block6ded16b2010-05-10 14:33:55 +0100759 a[j + 1] = element;
Steve Blocka7e24c12009-10-30 11:49:00 +0000760 }
Ben Murdoch85b71792012-04-11 18:30:58 +0100761 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000762
Ben Murdoch85b71792012-04-11 18:30:58 +0100763 function QuickSort(a, from, to) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000764 // Insertion sort is faster for short arrays.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100765 if (to - from <= 10) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000766 InsertionSort(a, from, to);
767 return;
768 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100769 // Find a pivot as the median of first, last and middle element.
770 var v0 = a[from];
771 var v1 = a[to - 1];
772 var middle_index = from + ((to - from) >> 1);
773 var v2 = a[middle_index];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000774 var c01 = %_CallFunction(receiver, v0, v1, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100775 if (c01 > 0) {
776 // v1 < v0, so swap them.
777 var tmp = v0;
778 v0 = v1;
779 v1 = tmp;
780 } // v0 <= v1.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000781 var c02 = %_CallFunction(receiver, v0, v2, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100782 if (c02 >= 0) {
783 // v2 <= v0 <= v1.
784 var tmp = v0;
785 v0 = v2;
786 v2 = v1;
787 v1 = tmp;
788 } else {
789 // v0 <= v1 && v0 < v2
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000790 var c12 = %_CallFunction(receiver, v1, v2, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100791 if (c12 > 0) {
792 // v0 <= v2 < v1
793 var tmp = v1;
794 v1 = v2;
795 v2 = tmp;
796 }
797 }
798 // v0 <= v1 <= v2
799 a[from] = v0;
800 a[to - 1] = v2;
801 var pivot = v1;
802 var low_end = from + 1; // Upper bound of elements lower than pivot.
803 var high_start = to - 1; // Lower bound of elements greater than pivot.
804 a[middle_index] = a[low_end];
805 a[low_end] = pivot;
806
Steve Blocka7e24c12009-10-30 11:49:00 +0000807 // From low_end to i are elements equal to pivot.
808 // From i to high_start are elements that haven't been compared yet.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100809 partition: for (var i = low_end + 1; i < high_start; i++) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000810 var element = a[i];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000811 var order = %_CallFunction(receiver, element, pivot, comparefn);
Steve Blocka7e24c12009-10-30 11:49:00 +0000812 if (order < 0) {
Steve Block6ded16b2010-05-10 14:33:55 +0100813 %_SwapElements(a, i, low_end);
Steve Blocka7e24c12009-10-30 11:49:00 +0000814 low_end++;
815 } else if (order > 0) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100816 do {
817 high_start--;
818 if (high_start == i) break partition;
819 var top_elem = a[high_start];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000820 order = %_CallFunction(receiver, top_elem, pivot, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100821 } while (order > 0);
Steve Block6ded16b2010-05-10 14:33:55 +0100822 %_SwapElements(a, i, high_start);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100823 if (order < 0) {
824 %_SwapElements(a, i, low_end);
825 low_end++;
826 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000827 }
828 }
829 QuickSort(a, from, low_end);
830 QuickSort(a, high_start, to);
Ben Murdoch85b71792012-04-11 18:30:58 +0100831 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000832
Ben Murdochb0fe1622011-05-05 13:52:32 +0100833 // Copy elements in the range 0..length from obj's prototype chain
834 // to obj itself, if obj has holes. Return one more than the maximal index
Steve Blocka7e24c12009-10-30 11:49:00 +0000835 // of a prototype property.
Ben Murdoch85b71792012-04-11 18:30:58 +0100836 function CopyFromPrototype(obj, length) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000837 var max = 0;
838 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
839 var indices = %GetArrayKeys(proto, length);
840 if (indices.length > 0) {
841 if (indices[0] == -1) {
842 // It's an interval.
843 var proto_length = indices[1];
844 for (var i = 0; i < proto_length; i++) {
845 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
846 obj[i] = proto[i];
847 if (i >= max) { max = i + 1; }
848 }
849 }
850 } else {
851 for (var i = 0; i < indices.length; i++) {
852 var index = indices[i];
853 if (!IS_UNDEFINED(index) &&
854 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
855 obj[index] = proto[index];
856 if (index >= max) { max = index + 1; }
857 }
858 }
859 }
860 }
861 }
862 return max;
Ben Murdoch85b71792012-04-11 18:30:58 +0100863 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000864
865 // Set a value of "undefined" on all indices in the range from..to
866 // where a prototype of obj has an element. I.e., shadow all prototype
867 // elements in that range.
Ben Murdoch85b71792012-04-11 18:30:58 +0100868 function ShadowPrototypeElements(obj, from, to) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000869 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
870 var indices = %GetArrayKeys(proto, to);
871 if (indices.length > 0) {
872 if (indices[0] == -1) {
873 // It's an interval.
874 var proto_length = indices[1];
875 for (var i = from; i < proto_length; i++) {
876 if (proto.hasOwnProperty(i)) {
877 obj[i] = void 0;
878 }
879 }
880 } else {
881 for (var i = 0; i < indices.length; i++) {
882 var index = indices[i];
883 if (!IS_UNDEFINED(index) && from <= index &&
884 proto.hasOwnProperty(index)) {
885 obj[index] = void 0;
886 }
887 }
888 }
889 }
890 }
Ben Murdoch85b71792012-04-11 18:30:58 +0100891 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000892
Ben Murdoch85b71792012-04-11 18:30:58 +0100893 function SafeRemoveArrayHoles(obj) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000894 // Copy defined elements from the end to fill in all holes and undefineds
895 // in the beginning of the array. Write undefineds and holes at the end
896 // after loop is finished.
897 var first_undefined = 0;
898 var last_defined = length - 1;
899 var num_holes = 0;
900 while (first_undefined < last_defined) {
901 // Find first undefined element.
902 while (first_undefined < last_defined &&
903 !IS_UNDEFINED(obj[first_undefined])) {
904 first_undefined++;
905 }
906 // Maintain the invariant num_holes = the number of holes in the original
907 // array with indices <= first_undefined or > last_defined.
908 if (!obj.hasOwnProperty(first_undefined)) {
909 num_holes++;
910 }
911
912 // Find last defined element.
913 while (first_undefined < last_defined &&
914 IS_UNDEFINED(obj[last_defined])) {
915 if (!obj.hasOwnProperty(last_defined)) {
916 num_holes++;
917 }
918 last_defined--;
919 }
920 if (first_undefined < last_defined) {
921 // Fill in hole or undefined.
922 obj[first_undefined] = obj[last_defined];
923 obj[last_defined] = void 0;
924 }
925 }
926 // If there were any undefineds in the entire array, first_undefined
927 // points to one past the last defined element. Make this true if
928 // there were no undefineds, as well, so that first_undefined == number
929 // of defined elements.
930 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
931 // Fill in the undefineds and the holes. There may be a hole where
932 // an undefined should be and vice versa.
933 var i;
934 for (i = first_undefined; i < length - num_holes; i++) {
935 obj[i] = void 0;
936 }
937 for (i = length - num_holes; i < length; i++) {
938 // For compatability with Webkit, do not expose elements in the prototype.
939 if (i in obj.__proto__) {
940 obj[i] = void 0;
941 } else {
942 delete obj[i];
943 }
944 }
945
946 // Return the number of defined elements.
947 return first_undefined;
Ben Murdoch85b71792012-04-11 18:30:58 +0100948 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000949
Steve Block6ded16b2010-05-10 14:33:55 +0100950 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000951 if (length < 2) return this;
952
953 var is_array = IS_ARRAY(this);
954 var max_prototype_element;
955 if (!is_array) {
956 // For compatibility with JSC, we also sort elements inherited from
957 // the prototype chain on non-Array objects.
958 // We do this by copying them to this object and sorting only
959 // local elements. This is not very efficient, but sorting with
960 // inherited elements happens very, very rarely, if at all.
961 // The specification allows "implementation dependent" behavior
962 // if an element on the prototype chain has an element that
963 // might interact with sorting.
964 max_prototype_element = CopyFromPrototype(this, length);
965 }
966
967 var num_non_undefined = %RemoveArrayHoles(this, length);
968 if (num_non_undefined == -1) {
969 // There were indexed accessors in the array. Move array holes and
970 // undefineds to the end using a Javascript function that is safe
971 // in the presence of accessors.
972 num_non_undefined = SafeRemoveArrayHoles(this);
973 }
974
975 QuickSort(this, 0, num_non_undefined);
976
977 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
978 // For compatibility with JSC, we shadow any elements in the prototype
979 // chain that has become exposed by sort moving a hole to its position.
980 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
981 }
982
983 return this;
984}
985
986
987// The following functions cannot be made efficient on sparse arrays while
988// preserving the semantics, since the calls to the receiver function can add
989// or delete elements from the array.
990function ArrayFilter(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000991 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
992 throw MakeTypeError("called_on_null_or_undefined",
993 ["Array.prototype.filter"]);
994 }
995
Ben Murdoch589d6972011-11-30 16:04:58 +0000996 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000997 throw MakeTypeError('called_non_callable', [ f ]);
998 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000999 if (IS_NULL_OR_UNDEFINED(receiver)) {
1000 receiver = %GetDefaultReceiver(f) || receiver;
1001 }
Ben Murdoch85b71792012-04-11 18:30:58 +01001002 // Pull out the length so that modifications to the length in the
1003 // loop will not affect the looping.
1004 var length = ToUint32(this.length);
1005 var result = [];
1006 var result_length = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00001007 for (var i = 0; i < length; i++) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001008 var current = this[i];
1009 if (!IS_UNDEFINED(current) || i in this) {
1010 if (%_CallFunction(receiver, current, i, this, f)) {
1011 result[result_length++] = current;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001012 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001013 }
1014 }
1015 return result;
1016}
1017
1018
1019function ArrayForEach(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001020 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1021 throw MakeTypeError("called_on_null_or_undefined",
1022 ["Array.prototype.forEach"]);
1023 }
1024
Ben Murdoch589d6972011-11-30 16:04:58 +00001025 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001026 throw MakeTypeError('called_non_callable', [ f ]);
1027 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001028 if (IS_NULL_OR_UNDEFINED(receiver)) {
1029 receiver = %GetDefaultReceiver(f) || receiver;
1030 }
Ben Murdoch85b71792012-04-11 18:30:58 +01001031 // Pull out the length so that modifications to the length in the
1032 // loop will not affect the looping.
1033 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001034 for (var i = 0; i < length; i++) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001035 var current = this[i];
1036 if (!IS_UNDEFINED(current) || i in this) {
1037 %_CallFunction(receiver, current, i, this, f);
Steve Blocka7e24c12009-10-30 11:49:00 +00001038 }
1039 }
1040}
1041
1042
1043// Executes the function once for each element present in the
1044// array until it finds one where callback returns true.
1045function ArraySome(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001046 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1047 throw MakeTypeError("called_on_null_or_undefined",
1048 ["Array.prototype.some"]);
1049 }
1050
Ben Murdoch589d6972011-11-30 16:04:58 +00001051 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001052 throw MakeTypeError('called_non_callable', [ f ]);
1053 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001054 if (IS_NULL_OR_UNDEFINED(receiver)) {
1055 receiver = %GetDefaultReceiver(f) || receiver;
1056 }
Ben Murdoch85b71792012-04-11 18:30:58 +01001057 // Pull out the length so that modifications to the length in the
1058 // loop will not affect the looping.
1059 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001060 for (var i = 0; i < length; i++) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001061 var current = this[i];
1062 if (!IS_UNDEFINED(current) || i in this) {
1063 if (%_CallFunction(receiver, current, i, this, f)) return true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001064 }
1065 }
1066 return false;
1067}
1068
1069
1070function ArrayEvery(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001071 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1072 throw MakeTypeError("called_on_null_or_undefined",
1073 ["Array.prototype.every"]);
1074 }
1075
Ben Murdoch589d6972011-11-30 16:04:58 +00001076 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001077 throw MakeTypeError('called_non_callable', [ f ]);
1078 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001079 if (IS_NULL_OR_UNDEFINED(receiver)) {
1080 receiver = %GetDefaultReceiver(f) || receiver;
1081 }
Ben Murdoch85b71792012-04-11 18:30:58 +01001082 // Pull out the length so that modifications to the length in the
1083 // loop will not affect the looping.
1084 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001085 for (var i = 0; i < length; i++) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001086 var current = this[i];
1087 if (!IS_UNDEFINED(current) || i in this) {
1088 if (!%_CallFunction(receiver, current, i, this, f)) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001089 }
1090 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001091 return true;
1092}
1093
Steve Blocka7e24c12009-10-30 11:49:00 +00001094function ArrayMap(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001095 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1096 throw MakeTypeError("called_on_null_or_undefined",
1097 ["Array.prototype.map"]);
1098 }
1099
Ben Murdoch589d6972011-11-30 16:04:58 +00001100 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001101 throw MakeTypeError('called_non_callable', [ f ]);
1102 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001103 if (IS_NULL_OR_UNDEFINED(receiver)) {
1104 receiver = %GetDefaultReceiver(f) || receiver;
1105 }
Ben Murdoch85b71792012-04-11 18:30:58 +01001106 // Pull out the length so that modifications to the length in the
1107 // loop will not affect the looping.
1108 var length = TO_UINT32(this.length);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001109 var result = new $Array();
1110 var accumulator = new InternalArray(length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001111 for (var i = 0; i < length; i++) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001112 var current = this[i];
1113 if (!IS_UNDEFINED(current) || i in this) {
1114 accumulator[i] = %_CallFunction(receiver, current, i, this, f);
Steve Blocka7e24c12009-10-30 11:49:00 +00001115 }
1116 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001117 %MoveArrayContents(accumulator, result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001118 return result;
1119}
1120
1121
1122function ArrayIndexOf(element, index) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001123 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1124 throw MakeTypeError("called_on_null_or_undefined",
1125 ["Array.prototype.indexOf"]);
1126 }
1127
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001128 var length = TO_UINT32(this.length);
1129 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001130 if (IS_UNDEFINED(index)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001131 index = 0;
1132 } else {
1133 index = TO_INTEGER(index);
1134 // If index is negative, index from the end of the array.
Steve Block1e0659c2011-05-24 12:43:12 +01001135 if (index < 0) {
1136 index = length + index;
1137 // If index is still negative, search the entire array.
1138 if (index < 0) index = 0;
1139 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001140 }
Steve Block59151502010-09-22 15:07:15 +01001141 var min = index;
1142 var max = length;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001143 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001144 var intervals = %GetArrayKeys(this, length);
1145 if (intervals.length == 2 && intervals[0] < 0) {
1146 // A single interval.
1147 var intervalMin = -(intervals[0] + 1);
1148 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001149 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001150 max = intervalMax; // Capped by length already.
1151 // Fall through to loop below.
1152 } else {
1153 if (intervals.length == 0) return -1;
1154 // Get all the keys in sorted order.
1155 var sortedKeys = GetSortedArrayKeys(this, intervals);
1156 var n = sortedKeys.length;
1157 var i = 0;
1158 while (i < n && sortedKeys[i] < index) i++;
1159 while (i < n) {
1160 var key = sortedKeys[i];
1161 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1162 i++;
1163 }
1164 return -1;
1165 }
1166 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001167 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001168 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001169 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001170 if (this[i] === element) return i;
1171 }
1172 return -1;
1173 }
Steve Block59151502010-09-22 15:07:15 +01001174 // Lookup through the array.
1175 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001176 if (IS_UNDEFINED(this[i]) && i in this) {
1177 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001178 }
1179 }
1180 return -1;
1181}
1182
1183
1184function ArrayLastIndexOf(element, index) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001185 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1186 throw MakeTypeError("called_on_null_or_undefined",
1187 ["Array.prototype.lastIndexOf"]);
1188 }
1189
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001190 var length = TO_UINT32(this.length);
1191 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001192 if (%_ArgumentsLength() < 2) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001193 index = length - 1;
1194 } else {
1195 index = TO_INTEGER(index);
1196 // If index is negative, index from end of the array.
Steve Block59151502010-09-22 15:07:15 +01001197 if (index < 0) index += length;
Steve Blocka7e24c12009-10-30 11:49:00 +00001198 // If index is still negative, do not search the array.
Steve Block59151502010-09-22 15:07:15 +01001199 if (index < 0) return -1;
Steve Blocka7e24c12009-10-30 11:49:00 +00001200 else if (index >= length) index = length - 1;
1201 }
Steve Block59151502010-09-22 15:07:15 +01001202 var min = 0;
1203 var max = index;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001204 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001205 var intervals = %GetArrayKeys(this, index + 1);
1206 if (intervals.length == 2 && intervals[0] < 0) {
1207 // A single interval.
1208 var intervalMin = -(intervals[0] + 1);
1209 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001210 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001211 max = intervalMax; // Capped by index already.
1212 // Fall through to loop below.
1213 } else {
1214 if (intervals.length == 0) return -1;
1215 // Get all the keys in sorted order.
1216 var sortedKeys = GetSortedArrayKeys(this, intervals);
1217 var i = sortedKeys.length - 1;
1218 while (i >= 0) {
1219 var key = sortedKeys[i];
1220 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1221 i--;
1222 }
1223 return -1;
1224 }
1225 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001226 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001227 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001228 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001229 if (this[i] === element) return i;
1230 }
1231 return -1;
1232 }
Steve Block59151502010-09-22 15:07:15 +01001233 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001234 if (IS_UNDEFINED(this[i]) && i in this) {
1235 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001236 }
1237 }
1238 return -1;
1239}
1240
1241
1242function ArrayReduce(callback, current) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001243 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1244 throw MakeTypeError("called_on_null_or_undefined",
1245 ["Array.prototype.reduce"]);
1246 }
1247
Ben Murdoch589d6972011-11-30 16:04:58 +00001248 if (!IS_SPEC_FUNCTION(callback)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001249 throw MakeTypeError('called_non_callable', [callback]);
1250 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001251
Ben Murdoch85b71792012-04-11 18:30:58 +01001252 // Pull out the length so that modifications to the length in the
1253 // loop will not affect the looping.
1254 var length = ToUint32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001255 var i = 0;
Ben Murdoch85b71792012-04-11 18:30:58 +01001256
Steve Blocka7e24c12009-10-30 11:49:00 +00001257 find_initial: if (%_ArgumentsLength() < 2) {
1258 for (; i < length; i++) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001259 current = this[i];
1260 if (!IS_UNDEFINED(current) || i in this) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001261 i++;
1262 break find_initial;
1263 }
1264 }
1265 throw MakeTypeError('reduce_no_initial', []);
1266 }
1267
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001268 var receiver = %GetDefaultReceiver(callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001269 for (; i < length; i++) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001270 var element = this[i];
1271 if (!IS_UNDEFINED(element) || i in this) {
1272 current = %_CallFunction(receiver, current, element, i, this, callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001273 }
1274 }
1275 return current;
1276}
1277
1278function ArrayReduceRight(callback, current) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001279 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1280 throw MakeTypeError("called_on_null_or_undefined",
1281 ["Array.prototype.reduceRight"]);
1282 }
1283
Ben Murdoch589d6972011-11-30 16:04:58 +00001284 if (!IS_SPEC_FUNCTION(callback)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001285 throw MakeTypeError('called_non_callable', [callback]);
1286 }
Ben Murdoch85b71792012-04-11 18:30:58 +01001287 var i = ToUint32(this.length) - 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00001288
1289 find_initial: if (%_ArgumentsLength() < 2) {
1290 for (; i >= 0; i--) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001291 current = this[i];
1292 if (!IS_UNDEFINED(current) || i in this) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001293 i--;
1294 break find_initial;
1295 }
1296 }
1297 throw MakeTypeError('reduce_no_initial', []);
1298 }
1299
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001300 var receiver = %GetDefaultReceiver(callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001301 for (; i >= 0; i--) {
Ben Murdoch85b71792012-04-11 18:30:58 +01001302 var element = this[i];
1303 if (!IS_UNDEFINED(element) || i in this) {
1304 current = %_CallFunction(receiver, current, element, i, this, callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001305 }
1306 }
1307 return current;
1308}
1309
Steve Block3ce2e202009-11-05 08:53:23 +00001310// ES5, 15.4.3.2
1311function ArrayIsArray(obj) {
1312 return IS_ARRAY(obj);
1313}
Steve Blocka7e24c12009-10-30 11:49:00 +00001314
Steve Blocka7e24c12009-10-30 11:49:00 +00001315
1316// -------------------------------------------------------------------
Ben Murdoch589d6972011-11-30 16:04:58 +00001317function SetUpArray() {
1318 %CheckIsBootstrapping();
1319 // Set up non-enumerable constructor property on the Array.prototype
Steve Blocka7e24c12009-10-30 11:49:00 +00001320 // object.
1321 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1322
Ben Murdoch589d6972011-11-30 16:04:58 +00001323 // Set up non-enumerable functions on the Array object.
Steve Block3ce2e202009-11-05 08:53:23 +00001324 InstallFunctions($Array, DONT_ENUM, $Array(
1325 "isArray", ArrayIsArray
1326 ));
1327
Steve Block6ded16b2010-05-10 14:33:55 +01001328 var specialFunctions = %SpecialArrayFunctions({});
1329
Ben Murdoch85b71792012-04-11 18:30:58 +01001330 function getFunction(name, jsBuiltin, len) {
Steve Block6ded16b2010-05-10 14:33:55 +01001331 var f = jsBuiltin;
1332 if (specialFunctions.hasOwnProperty(name)) {
1333 f = specialFunctions[name];
1334 }
1335 if (!IS_UNDEFINED(len)) {
1336 %FunctionSetLength(f, len);
1337 }
1338 return f;
Ben Murdoch85b71792012-04-11 18:30:58 +01001339 }
Steve Block6ded16b2010-05-10 14:33:55 +01001340
Ben Murdoch589d6972011-11-30 16:04:58 +00001341 // Set up non-enumerable functions of the Array.prototype object and
Steve Blocka7e24c12009-10-30 11:49:00 +00001342 // set their names.
Steve Blocka7e24c12009-10-30 11:49:00 +00001343 // Manipulate the length of some of the functions to meet
1344 // expectations set by ECMA-262 or Mozilla.
Ben Murdoch85b71792012-04-11 18:30:58 +01001345 InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
Steve Block6ded16b2010-05-10 14:33:55 +01001346 "toString", getFunction("toString", ArrayToString),
1347 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1348 "join", getFunction("join", ArrayJoin),
1349 "pop", getFunction("pop", ArrayPop),
1350 "push", getFunction("push", ArrayPush, 1),
1351 "concat", getFunction("concat", ArrayConcat, 1),
1352 "reverse", getFunction("reverse", ArrayReverse),
1353 "shift", getFunction("shift", ArrayShift),
1354 "unshift", getFunction("unshift", ArrayUnshift, 1),
1355 "slice", getFunction("slice", ArraySlice, 2),
1356 "splice", getFunction("splice", ArraySplice, 2),
1357 "sort", getFunction("sort", ArraySort),
1358 "filter", getFunction("filter", ArrayFilter, 1),
1359 "forEach", getFunction("forEach", ArrayForEach, 1),
1360 "some", getFunction("some", ArraySome, 1),
1361 "every", getFunction("every", ArrayEvery, 1),
1362 "map", getFunction("map", ArrayMap, 1),
1363 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1364 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1365 "reduce", getFunction("reduce", ArrayReduce, 1),
1366 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1367 ));
1368
1369 %FinishArrayPrototypeSetup($Array.prototype);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001370
1371 // The internal Array prototype doesn't need to be fancy, since it's never
Ben Murdoch589d6972011-11-30 16:04:58 +00001372 // exposed to user code.
1373 // Adding only the functions that are actually used.
1374 SetUpLockedPrototype(InternalArray, $Array(), $Array(
1375 "join", getFunction("join", ArrayJoin),
1376 "pop", getFunction("pop", ArrayPop),
1377 "push", getFunction("push", ArrayPush)
1378 ));
Steve Blocka7e24c12009-10-30 11:49:00 +00001379}
1380
Ben Murdoch589d6972011-11-30 16:04:58 +00001381SetUpArray();