blob: daa75d5753c1bdb0c1659ae5436978501adc7291 [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 Murdoch3ef787d2012-04-12 10:51:47 +010030// var $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 Murdoch3ef787d2012-04-12 10:51:47 +0100204 if (IS_NULL_OR_UNDEFINED(e)) {
Leon Clarkee46be812010-01-19 14:06:41 +0000205 return '';
206 } else {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100207 // According to ES5, section 15.4.4.3, the toLocaleString conversion
208 // must throw a TypeError if ToObject(e).toLocaleString isn't
209 // callable.
Steve Blocka7e24c12009-10-30 11:49:00 +0000210 var e_obj = ToObject(e);
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100211 return %ToString(e_obj.toLocaleString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000212 }
213}
214
215
216// This function implements the optimized splice implementation that can use
217// special array operations to handle sparse arrays in a sensible fashion.
218function SmartSlice(array, start_i, del_count, len, deleted_elements) {
219 // Move deleted elements to a new array (the return value from splice).
220 // Intervals array can contain keys and intervals. See comment in Concat.
221 var intervals = %GetArrayKeys(array, start_i + del_count);
222 var length = intervals.length;
223 for (var k = 0; k < length; k++) {
224 var key = intervals[k];
225 if (key < 0) {
226 var j = -1 - key;
227 var interval_limit = j + intervals[++k];
228 if (j < start_i) {
229 j = start_i;
230 }
231 for (; j < interval_limit; j++) {
232 // ECMA-262 15.4.4.12 line 10. The spec could also be
233 // interpreted such that %HasLocalProperty would be the
234 // appropriate test. We follow KJS in consulting the
235 // prototype.
236 var current = array[j];
237 if (!IS_UNDEFINED(current) || j in array) {
238 deleted_elements[j - start_i] = current;
239 }
240 }
241 } else {
242 if (!IS_UNDEFINED(key)) {
243 if (key >= start_i) {
244 // ECMA-262 15.4.4.12 line 10. The spec could also be
245 // interpreted such that %HasLocalProperty would be the
246 // appropriate test. We follow KJS in consulting the
247 // prototype.
248 var current = array[key];
249 if (!IS_UNDEFINED(current) || key in array) {
250 deleted_elements[key - start_i] = current;
251 }
252 }
253 }
254 }
255 }
256}
257
258
259// This function implements the optimized splice implementation that can use
260// special array operations to handle sparse arrays in a sensible fashion.
261function SmartMove(array, start_i, del_count, len, num_additional_args) {
262 // Move data to new array.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100263 var new_array = new InternalArray(len - del_count + num_additional_args);
Steve Blocka7e24c12009-10-30 11:49:00 +0000264 var intervals = %GetArrayKeys(array, len);
265 var length = intervals.length;
266 for (var k = 0; k < length; k++) {
267 var key = intervals[k];
268 if (key < 0) {
269 var j = -1 - key;
270 var interval_limit = j + intervals[++k];
271 while (j < start_i && j < interval_limit) {
272 // The spec could also be interpreted such that
273 // %HasLocalProperty would be the appropriate test. We follow
274 // KJS in consulting the prototype.
275 var current = array[j];
276 if (!IS_UNDEFINED(current) || j in array) {
277 new_array[j] = current;
278 }
279 j++;
280 }
281 j = start_i + del_count;
282 while (j < interval_limit) {
283 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also be
284 // interpreted such that %HasLocalProperty would be the
285 // appropriate test. We follow KJS in consulting the
286 // prototype.
287 var current = array[j];
288 if (!IS_UNDEFINED(current) || j in array) {
289 new_array[j - del_count + num_additional_args] = current;
290 }
291 j++;
292 }
293 } else {
294 if (!IS_UNDEFINED(key)) {
295 if (key < start_i) {
296 // The spec could also be interpreted such that
297 // %HasLocalProperty would be the appropriate test. We follow
298 // KJS in consulting the prototype.
299 var current = array[key];
300 if (!IS_UNDEFINED(current) || key in array) {
301 new_array[key] = current;
302 }
303 } else if (key >= start_i + del_count) {
304 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also
305 // be interpreted such that %HasLocalProperty would be the
306 // appropriate test. We follow KJS in consulting the
307 // prototype.
308 var current = array[key];
309 if (!IS_UNDEFINED(current) || key in array) {
310 new_array[key - del_count + num_additional_args] = current;
311 }
312 }
313 }
314 }
315 }
316 // Move contents of new_array into this array
317 %MoveArrayContents(new_array, array);
318}
319
320
321// This is part of the old simple-minded splice. We are using it either
322// because the receiver is not an array (so we have no choice) or because we
323// know we are not deleting or moving a lot of elements.
324function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
325 for (var i = 0; i < del_count; i++) {
326 var index = start_i + i;
327 // The spec could also be interpreted such that %HasLocalProperty
328 // would be the appropriate test. We follow KJS in consulting the
329 // prototype.
330 var current = array[index];
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100331 if (!IS_UNDEFINED(current) || index in array) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000332 deleted_elements[i] = current;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100333 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000334 }
335}
336
337
338function SimpleMove(array, start_i, del_count, len, num_additional_args) {
339 if (num_additional_args !== del_count) {
340 // Move the existing elements after the elements to be deleted
341 // to the right position in the resulting array.
342 if (num_additional_args > del_count) {
343 for (var i = len - del_count; i > start_i; i--) {
344 var from_index = i + del_count - 1;
345 var to_index = i + num_additional_args - 1;
346 // The spec could also be interpreted such that
347 // %HasLocalProperty would be the appropriate test. We follow
348 // KJS in consulting the prototype.
349 var current = array[from_index];
350 if (!IS_UNDEFINED(current) || from_index in array) {
351 array[to_index] = current;
352 } else {
353 delete array[to_index];
354 }
355 }
356 } else {
357 for (var i = start_i; i < len - del_count; i++) {
358 var from_index = i + del_count;
359 var to_index = i + num_additional_args;
360 // The spec could also be interpreted such that
361 // %HasLocalProperty would be the appropriate test. We follow
362 // KJS in consulting the prototype.
363 var current = array[from_index];
364 if (!IS_UNDEFINED(current) || from_index in array) {
365 array[to_index] = current;
366 } else {
367 delete array[to_index];
368 }
369 }
370 for (var i = len; i > len - del_count + num_additional_args; i--) {
371 delete array[i - 1];
372 }
373 }
374 }
375}
376
377
378// -------------------------------------------------------------------
379
380
381function ArrayToString() {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100382 var array;
383 var func;
384 if (IS_ARRAY(this)) {
385 func = this.join;
386 if (func === ArrayJoin) {
387 return Join(this, this.length, ',', ConvertToString);
388 }
389 array = this;
390 } else {
391 array = ToObject(this);
392 func = array.join;
Steve Blocka7e24c12009-10-30 11:49:00 +0000393 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100394 if (!IS_SPEC_FUNCTION(func)) {
395 return %_CallFunction(array, ObjectToString);
396 }
397 return %_CallFunction(array, func);
Steve Blocka7e24c12009-10-30 11:49:00 +0000398}
399
400
401function ArrayToLocaleString() {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100402 var array = ToObject(this);
403 var arrayLen = array.length;
404 var len = TO_UINT32(arrayLen);
405 if (len === 0) return "";
406 return Join(array, len, ',', ConvertToLocaleString);
Steve Blocka7e24c12009-10-30 11:49:00 +0000407}
408
409
410function ArrayJoin(separator) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000411 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
412 throw MakeTypeError("called_on_null_or_undefined",
413 ["Array.prototype.join"]);
414 }
415
Leon Clarkee46be812010-01-19 14:06:41 +0000416 if (IS_UNDEFINED(separator)) {
417 separator = ',';
418 } else if (!IS_STRING(separator)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100419 separator = NonStringToString(separator);
Leon Clarkee46be812010-01-19 14:06:41 +0000420 }
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800421
422 var result = %_FastAsciiArrayJoin(this, separator);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100423 if (!IS_UNDEFINED(result)) return result;
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800424
Ben Murdochb0fe1622011-05-05 13:52:32 +0100425 return Join(this, TO_UINT32(this.length), separator, ConvertToString);
Steve Blocka7e24c12009-10-30 11:49:00 +0000426}
427
428
429// Removes the last element from the array and returns it. See
430// ECMA-262, section 15.4.4.6.
431function ArrayPop() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000432 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
433 throw MakeTypeError("called_on_null_or_undefined",
434 ["Array.prototype.pop"]);
435 }
436
Leon Clarkee46be812010-01-19 14:06:41 +0000437 var n = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000438 if (n == 0) {
439 this.length = n;
440 return;
441 }
442 n--;
443 var value = this[n];
444 this.length = n;
445 delete this[n];
446 return value;
447}
448
449
450// Appends the arguments to the end of the array and returns the new
451// length of the array. See ECMA-262, section 15.4.4.7.
452function ArrayPush() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000453 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
454 throw MakeTypeError("called_on_null_or_undefined",
455 ["Array.prototype.push"]);
456 }
457
Leon Clarkee46be812010-01-19 14:06:41 +0000458 var n = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000459 var m = %_ArgumentsLength();
460 for (var i = 0; i < m; i++) {
461 this[i+n] = %_Arguments(i);
462 }
463 this.length = n + m;
464 return this.length;
465}
466
467
468function ArrayConcat(arg1) { // length == 1
Ben Murdoch257744e2011-11-30 15:57:28 +0000469 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
470 throw MakeTypeError("called_on_null_or_undefined",
471 ["Array.prototype.concat"]);
472 }
473
Steve Blocka7e24c12009-10-30 11:49:00 +0000474 var arg_count = %_ArgumentsLength();
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100475 var arrays = new InternalArray(1 + arg_count);
Steve Blocka7e24c12009-10-30 11:49:00 +0000476 arrays[0] = this;
477 for (var i = 0; i < arg_count; i++) {
478 arrays[i + 1] = %_Arguments(i);
479 }
480
481 return %ArrayConcat(arrays);
482}
483
484
485// For implementing reverse() on large, sparse arrays.
486function SparseReverse(array, len) {
487 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
488 var high_counter = keys.length - 1;
489 var low_counter = 0;
490 while (low_counter <= high_counter) {
491 var i = keys[low_counter];
492 var j = keys[high_counter];
493
494 var j_complement = len - j - 1;
495 var low, high;
496
497 if (j_complement <= i) {
498 high = j;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100499 while (keys[--high_counter] == j) { }
Steve Blocka7e24c12009-10-30 11:49:00 +0000500 low = j_complement;
501 }
502 if (j_complement >= i) {
503 low = i;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100504 while (keys[++low_counter] == i) { }
Steve Blocka7e24c12009-10-30 11:49:00 +0000505 high = len - i - 1;
506 }
507
508 var current_i = array[low];
509 if (!IS_UNDEFINED(current_i) || low in array) {
510 var current_j = array[high];
511 if (!IS_UNDEFINED(current_j) || high in array) {
512 array[low] = current_j;
513 array[high] = current_i;
514 } else {
515 array[high] = current_i;
516 delete array[low];
517 }
518 } else {
519 var current_j = array[high];
520 if (!IS_UNDEFINED(current_j) || high in array) {
521 array[low] = current_j;
522 delete array[high];
523 }
524 }
525 }
526}
527
528
529function ArrayReverse() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000530 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
531 throw MakeTypeError("called_on_null_or_undefined",
532 ["Array.prototype.reverse"]);
533 }
534
Leon Clarkee46be812010-01-19 14:06:41 +0000535 var j = TO_UINT32(this.length) - 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000536
537 if (UseSparseVariant(this, j, IS_ARRAY(this))) {
538 SparseReverse(this, j+1);
539 return this;
540 }
541
542 for (var i = 0; i < j; i++, j--) {
543 var current_i = this[i];
544 if (!IS_UNDEFINED(current_i) || i in this) {
545 var current_j = this[j];
546 if (!IS_UNDEFINED(current_j) || j in this) {
547 this[i] = current_j;
548 this[j] = current_i;
549 } else {
550 this[j] = current_i;
551 delete this[i];
552 }
553 } else {
554 var current_j = this[j];
555 if (!IS_UNDEFINED(current_j) || j in this) {
556 this[i] = current_j;
557 delete this[j];
558 }
559 }
560 }
561 return this;
562}
563
564
565function ArrayShift() {
Ben Murdoch257744e2011-11-30 15:57:28 +0000566 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
567 throw MakeTypeError("called_on_null_or_undefined",
568 ["Array.prototype.shift"]);
569 }
570
Leon Clarkee46be812010-01-19 14:06:41 +0000571 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000572
573 if (len === 0) {
574 this.length = 0;
575 return;
576 }
577
578 var first = this[0];
579
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100580 if (IS_ARRAY(this)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000581 SmartMove(this, 0, 1, len, 0);
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100582 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000583 SimpleMove(this, 0, 1, len, 0);
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100584 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000585
586 this.length = len - 1;
587
588 return first;
589}
590
591
592function ArrayUnshift(arg1) { // length == 1
Ben Murdoch257744e2011-11-30 15:57:28 +0000593 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
594 throw MakeTypeError("called_on_null_or_undefined",
595 ["Array.prototype.unshift"]);
596 }
597
Leon Clarkee46be812010-01-19 14:06:41 +0000598 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000599 var num_arguments = %_ArgumentsLength();
600
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100601 if (IS_ARRAY(this)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000602 SmartMove(this, 0, 0, len, num_arguments);
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100603 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000604 SimpleMove(this, 0, 0, len, num_arguments);
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100605 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000606
607 for (var i = 0; i < num_arguments; i++) {
608 this[i] = %_Arguments(i);
609 }
610
611 this.length = len + num_arguments;
612
613 return len + num_arguments;
614}
615
616
617function ArraySlice(start, end) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000618 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
619 throw MakeTypeError("called_on_null_or_undefined",
620 ["Array.prototype.slice"]);
621 }
622
Leon Clarkee46be812010-01-19 14:06:41 +0000623 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000624 var start_i = TO_INTEGER(start);
625 var end_i = len;
626
627 if (end !== void 0) end_i = TO_INTEGER(end);
628
629 if (start_i < 0) {
630 start_i += len;
631 if (start_i < 0) start_i = 0;
632 } else {
633 if (start_i > len) start_i = len;
634 }
635
636 if (end_i < 0) {
637 end_i += len;
638 if (end_i < 0) end_i = 0;
639 } else {
640 if (end_i > len) end_i = len;
641 }
642
643 var result = [];
644
645 if (end_i < start_i) return result;
646
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000647 if (IS_ARRAY(this) &&
648 (end_i > 1000) &&
649 (%EstimateNumberOfElements(this) < end_i)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000650 SmartSlice(this, start_i, end_i - start_i, len, result);
651 } else {
652 SimpleSlice(this, start_i, end_i - start_i, len, result);
653 }
654
655 result.length = end_i - start_i;
656
657 return result;
658}
659
660
661function ArraySplice(start, delete_count) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000662 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
663 throw MakeTypeError("called_on_null_or_undefined",
664 ["Array.prototype.splice"]);
665 }
666
Steve Blocka7e24c12009-10-30 11:49:00 +0000667 var num_arguments = %_ArgumentsLength();
668
Leon Clarkee46be812010-01-19 14:06:41 +0000669 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000670 var start_i = TO_INTEGER(start);
671
672 if (start_i < 0) {
673 start_i += len;
674 if (start_i < 0) start_i = 0;
675 } else {
676 if (start_i > len) start_i = len;
677 }
678
Andrei Popescu402d9372010-02-26 13:31:12 +0000679 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
Steve Block1e0659c2011-05-24 12:43:12 +0100680 // given as a request to delete all the elements from the start.
681 // And it differs from the case of undefined delete count.
Steve Blocka7e24c12009-10-30 11:49:00 +0000682 // This does not follow ECMA-262, but we do the same for
683 // compatibility.
684 var del_count = 0;
Steve Block1e0659c2011-05-24 12:43:12 +0100685 if (num_arguments == 1) {
686 del_count = len - start_i;
687 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000688 del_count = TO_INTEGER(delete_count);
689 if (del_count < 0) del_count = 0;
690 if (del_count > len - start_i) del_count = len - start_i;
Steve Blocka7e24c12009-10-30 11:49:00 +0000691 }
692
693 var deleted_elements = [];
694 deleted_elements.length = del_count;
695
696 // Number of elements to add.
697 var num_additional_args = 0;
698 if (num_arguments > 2) {
699 num_additional_args = num_arguments - 2;
700 }
701
702 var use_simple_splice = true;
703
704 if (IS_ARRAY(this) && num_additional_args !== del_count) {
705 // If we are only deleting/moving a few things near the end of the
706 // array then the simple version is going to be faster, because it
707 // doesn't touch most of the array.
708 var estimated_non_hole_elements = %EstimateNumberOfElements(this);
709 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
710 use_simple_splice = false;
711 }
712 }
713
714 if (use_simple_splice) {
715 SimpleSlice(this, start_i, del_count, len, deleted_elements);
716 SimpleMove(this, start_i, del_count, len, num_additional_args);
717 } else {
718 SmartSlice(this, start_i, del_count, len, deleted_elements);
719 SmartMove(this, start_i, del_count, len, num_additional_args);
720 }
721
722 // Insert the arguments into the resulting array in
723 // place of the deleted elements.
724 var i = start_i;
725 var arguments_index = 2;
726 var arguments_length = %_ArgumentsLength();
727 while (arguments_index < arguments_length) {
728 this[i++] = %_Arguments(arguments_index++);
729 }
730 this.length = len - del_count + num_additional_args;
731
732 // Return the deleted elements.
733 return deleted_elements;
734}
735
736
737function ArraySort(comparefn) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000738 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
739 throw MakeTypeError("called_on_null_or_undefined",
740 ["Array.prototype.sort"]);
741 }
742
Steve Blocka7e24c12009-10-30 11:49:00 +0000743 // In-place QuickSort algorithm.
744 // For short (length <= 22) arrays, insertion sort is used for efficiency.
745
Ben Murdoch589d6972011-11-30 16:04:58 +0000746 if (!IS_SPEC_FUNCTION(comparefn)) {
Steve Block6ded16b2010-05-10 14:33:55 +0100747 comparefn = function (x, y) {
748 if (x === y) return 0;
749 if (%_IsSmi(x) && %_IsSmi(y)) {
750 return %SmiLexicographicCompare(x, y);
751 }
752 x = ToString(x);
753 y = ToString(y);
754 if (x == y) return 0;
755 else return x < y ? -1 : 1;
756 };
757 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000758 var receiver = %GetDefaultReceiver(comparefn);
Steve Blocka7e24c12009-10-30 11:49:00 +0000759
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100760 var InsertionSort = function InsertionSort(a, from, to) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000761 for (var i = from + 1; i < to; i++) {
762 var element = a[i];
Steve Block6ded16b2010-05-10 14:33:55 +0100763 for (var j = i - 1; j >= from; j--) {
764 var tmp = a[j];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000765 var order = %_CallFunction(receiver, tmp, element, comparefn);
Steve Block6ded16b2010-05-10 14:33:55 +0100766 if (order > 0) {
767 a[j + 1] = tmp;
768 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000769 break;
770 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000771 }
Steve Block6ded16b2010-05-10 14:33:55 +0100772 a[j + 1] = element;
Steve Blocka7e24c12009-10-30 11:49:00 +0000773 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100774 };
Steve Blocka7e24c12009-10-30 11:49:00 +0000775
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100776 var QuickSort = function QuickSort(a, from, to) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000777 // Insertion sort is faster for short arrays.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100778 if (to - from <= 10) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000779 InsertionSort(a, from, to);
780 return;
781 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100782 // Find a pivot as the median of first, last and middle element.
783 var v0 = a[from];
784 var v1 = a[to - 1];
785 var middle_index = from + ((to - from) >> 1);
786 var v2 = a[middle_index];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000787 var c01 = %_CallFunction(receiver, v0, v1, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100788 if (c01 > 0) {
789 // v1 < v0, so swap them.
790 var tmp = v0;
791 v0 = v1;
792 v1 = tmp;
793 } // v0 <= v1.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000794 var c02 = %_CallFunction(receiver, v0, v2, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100795 if (c02 >= 0) {
796 // v2 <= v0 <= v1.
797 var tmp = v0;
798 v0 = v2;
799 v2 = v1;
800 v1 = tmp;
801 } else {
802 // v0 <= v1 && v0 < v2
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000803 var c12 = %_CallFunction(receiver, v1, v2, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100804 if (c12 > 0) {
805 // v0 <= v2 < v1
806 var tmp = v1;
807 v1 = v2;
808 v2 = tmp;
809 }
810 }
811 // v0 <= v1 <= v2
812 a[from] = v0;
813 a[to - 1] = v2;
814 var pivot = v1;
815 var low_end = from + 1; // Upper bound of elements lower than pivot.
816 var high_start = to - 1; // Lower bound of elements greater than pivot.
817 a[middle_index] = a[low_end];
818 a[low_end] = pivot;
819
Steve Blocka7e24c12009-10-30 11:49:00 +0000820 // From low_end to i are elements equal to pivot.
821 // From i to high_start are elements that haven't been compared yet.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100822 partition: for (var i = low_end + 1; i < high_start; i++) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000823 var element = a[i];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000824 var order = %_CallFunction(receiver, element, pivot, comparefn);
Steve Blocka7e24c12009-10-30 11:49:00 +0000825 if (order < 0) {
Steve Block6ded16b2010-05-10 14:33:55 +0100826 %_SwapElements(a, i, low_end);
Steve Blocka7e24c12009-10-30 11:49:00 +0000827 low_end++;
828 } else if (order > 0) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100829 do {
830 high_start--;
831 if (high_start == i) break partition;
832 var top_elem = a[high_start];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000833 order = %_CallFunction(receiver, top_elem, pivot, comparefn);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100834 } while (order > 0);
Steve Block6ded16b2010-05-10 14:33:55 +0100835 %_SwapElements(a, i, high_start);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100836 if (order < 0) {
837 %_SwapElements(a, i, low_end);
838 low_end++;
839 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000840 }
841 }
842 QuickSort(a, from, low_end);
843 QuickSort(a, high_start, to);
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100844 };
Steve Blocka7e24c12009-10-30 11:49:00 +0000845
Ben Murdochb0fe1622011-05-05 13:52:32 +0100846 // Copy elements in the range 0..length from obj's prototype chain
847 // to obj itself, if obj has holes. Return one more than the maximal index
Steve Blocka7e24c12009-10-30 11:49:00 +0000848 // of a prototype property.
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100849 var CopyFromPrototype = function CopyFromPrototype(obj, length) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000850 var max = 0;
851 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
852 var indices = %GetArrayKeys(proto, length);
853 if (indices.length > 0) {
854 if (indices[0] == -1) {
855 // It's an interval.
856 var proto_length = indices[1];
857 for (var i = 0; i < proto_length; i++) {
858 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
859 obj[i] = proto[i];
860 if (i >= max) { max = i + 1; }
861 }
862 }
863 } else {
864 for (var i = 0; i < indices.length; i++) {
865 var index = indices[i];
866 if (!IS_UNDEFINED(index) &&
867 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
868 obj[index] = proto[index];
869 if (index >= max) { max = index + 1; }
870 }
871 }
872 }
873 }
874 }
875 return max;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100876 };
Steve Blocka7e24c12009-10-30 11:49:00 +0000877
878 // Set a value of "undefined" on all indices in the range from..to
879 // where a prototype of obj has an element. I.e., shadow all prototype
880 // elements in that range.
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100881 var ShadowPrototypeElements = function(obj, from, to) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000882 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
883 var indices = %GetArrayKeys(proto, to);
884 if (indices.length > 0) {
885 if (indices[0] == -1) {
886 // It's an interval.
887 var proto_length = indices[1];
888 for (var i = from; i < proto_length; i++) {
889 if (proto.hasOwnProperty(i)) {
890 obj[i] = void 0;
891 }
892 }
893 } else {
894 for (var i = 0; i < indices.length; i++) {
895 var index = indices[i];
896 if (!IS_UNDEFINED(index) && from <= index &&
897 proto.hasOwnProperty(index)) {
898 obj[index] = void 0;
899 }
900 }
901 }
902 }
903 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100904 };
Steve Blocka7e24c12009-10-30 11:49:00 +0000905
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100906 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000907 // Copy defined elements from the end to fill in all holes and undefineds
908 // in the beginning of the array. Write undefineds and holes at the end
909 // after loop is finished.
910 var first_undefined = 0;
911 var last_defined = length - 1;
912 var num_holes = 0;
913 while (first_undefined < last_defined) {
914 // Find first undefined element.
915 while (first_undefined < last_defined &&
916 !IS_UNDEFINED(obj[first_undefined])) {
917 first_undefined++;
918 }
919 // Maintain the invariant num_holes = the number of holes in the original
920 // array with indices <= first_undefined or > last_defined.
921 if (!obj.hasOwnProperty(first_undefined)) {
922 num_holes++;
923 }
924
925 // Find last defined element.
926 while (first_undefined < last_defined &&
927 IS_UNDEFINED(obj[last_defined])) {
928 if (!obj.hasOwnProperty(last_defined)) {
929 num_holes++;
930 }
931 last_defined--;
932 }
933 if (first_undefined < last_defined) {
934 // Fill in hole or undefined.
935 obj[first_undefined] = obj[last_defined];
936 obj[last_defined] = void 0;
937 }
938 }
939 // If there were any undefineds in the entire array, first_undefined
940 // points to one past the last defined element. Make this true if
941 // there were no undefineds, as well, so that first_undefined == number
942 // of defined elements.
943 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
944 // Fill in the undefineds and the holes. There may be a hole where
945 // an undefined should be and vice versa.
946 var i;
947 for (i = first_undefined; i < length - num_holes; i++) {
948 obj[i] = void 0;
949 }
950 for (i = length - num_holes; i < length; i++) {
951 // For compatability with Webkit, do not expose elements in the prototype.
952 if (i in obj.__proto__) {
953 obj[i] = void 0;
954 } else {
955 delete obj[i];
956 }
957 }
958
959 // Return the number of defined elements.
960 return first_undefined;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100961 };
Steve Blocka7e24c12009-10-30 11:49:00 +0000962
Steve Block6ded16b2010-05-10 14:33:55 +0100963 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000964 if (length < 2) return this;
965
966 var is_array = IS_ARRAY(this);
967 var max_prototype_element;
968 if (!is_array) {
969 // For compatibility with JSC, we also sort elements inherited from
970 // the prototype chain on non-Array objects.
971 // We do this by copying them to this object and sorting only
972 // local elements. This is not very efficient, but sorting with
973 // inherited elements happens very, very rarely, if at all.
974 // The specification allows "implementation dependent" behavior
975 // if an element on the prototype chain has an element that
976 // might interact with sorting.
977 max_prototype_element = CopyFromPrototype(this, length);
978 }
979
980 var num_non_undefined = %RemoveArrayHoles(this, length);
981 if (num_non_undefined == -1) {
982 // There were indexed accessors in the array. Move array holes and
983 // undefineds to the end using a Javascript function that is safe
984 // in the presence of accessors.
985 num_non_undefined = SafeRemoveArrayHoles(this);
986 }
987
988 QuickSort(this, 0, num_non_undefined);
989
990 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
991 // For compatibility with JSC, we shadow any elements in the prototype
992 // chain that has become exposed by sort moving a hole to its position.
993 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
994 }
995
996 return this;
997}
998
999
1000// The following functions cannot be made efficient on sparse arrays while
1001// preserving the semantics, since the calls to the receiver function can add
1002// or delete elements from the array.
1003function ArrayFilter(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001004 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1005 throw MakeTypeError("called_on_null_or_undefined",
1006 ["Array.prototype.filter"]);
1007 }
1008
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001009 // Pull out the length so that modifications to the length in the
1010 // loop will not affect the looping and side effects are visible.
1011 var array = ToObject(this);
1012 var length = ToUint32(array.length);
1013
Ben Murdoch589d6972011-11-30 16:04:58 +00001014 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001015 throw MakeTypeError('called_non_callable', [ f ]);
1016 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001017 if (IS_NULL_OR_UNDEFINED(receiver)) {
1018 receiver = %GetDefaultReceiver(f) || receiver;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001019 } else if (!IS_SPEC_OBJECT(receiver)) {
1020 receiver = ToObject(receiver);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001021 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001022
1023 var result = new $Array();
1024 var accumulator = new InternalArray();
1025 var accumulator_length = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00001026 for (var i = 0; i < length; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001027 if (i in array) {
1028 var element = array[i];
1029 if (%_CallFunction(receiver, element, i, array, f)) {
1030 accumulator[accumulator_length++] = element;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001031 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001032 }
1033 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001034 %MoveArrayContents(accumulator, result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001035 return result;
1036}
1037
1038
1039function ArrayForEach(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001040 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1041 throw MakeTypeError("called_on_null_or_undefined",
1042 ["Array.prototype.forEach"]);
1043 }
1044
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001045 // Pull out the length so that modifications to the length in the
1046 // loop will not affect the looping and side effects are visible.
1047 var array = ToObject(this);
1048 var length = TO_UINT32(array.length);
1049
Ben Murdoch589d6972011-11-30 16:04:58 +00001050 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001051 throw MakeTypeError('called_non_callable', [ f ]);
1052 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001053 if (IS_NULL_OR_UNDEFINED(receiver)) {
1054 receiver = %GetDefaultReceiver(f) || receiver;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001055 } else if (!IS_SPEC_OBJECT(receiver)) {
1056 receiver = ToObject(receiver);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001057 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001058
Steve Blocka7e24c12009-10-30 11:49:00 +00001059 for (var i = 0; i < length; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001060 if (i in array) {
1061 var element = array[i];
1062 %_CallFunction(receiver, element, i, array, f);
Steve Blocka7e24c12009-10-30 11:49:00 +00001063 }
1064 }
1065}
1066
1067
1068// Executes the function once for each element present in the
1069// array until it finds one where callback returns true.
1070function ArraySome(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.some"]);
1074 }
1075
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001076 // Pull out the length so that modifications to the length in the
1077 // loop will not affect the looping and side effects are visible.
1078 var array = ToObject(this);
1079 var length = TO_UINT32(array.length);
1080
Ben Murdoch589d6972011-11-30 16:04:58 +00001081 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001082 throw MakeTypeError('called_non_callable', [ f ]);
1083 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001084 if (IS_NULL_OR_UNDEFINED(receiver)) {
1085 receiver = %GetDefaultReceiver(f) || receiver;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001086 } else if (!IS_SPEC_OBJECT(receiver)) {
1087 receiver = ToObject(receiver);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001088 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001089
Steve Blocka7e24c12009-10-30 11:49:00 +00001090 for (var i = 0; i < length; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001091 if (i in array) {
1092 var element = array[i];
1093 if (%_CallFunction(receiver, element, i, array, f)) return true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001094 }
1095 }
1096 return false;
1097}
1098
1099
1100function ArrayEvery(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001101 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1102 throw MakeTypeError("called_on_null_or_undefined",
1103 ["Array.prototype.every"]);
1104 }
1105
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001106 // Pull out the length so that modifications to the length in the
1107 // loop will not affect the looping and side effects are visible.
1108 var array = ToObject(this);
1109 var length = TO_UINT32(array.length);
1110
Ben Murdoch589d6972011-11-30 16:04:58 +00001111 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001112 throw MakeTypeError('called_non_callable', [ f ]);
1113 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001114 if (IS_NULL_OR_UNDEFINED(receiver)) {
1115 receiver = %GetDefaultReceiver(f) || receiver;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001116 } else if (!IS_SPEC_OBJECT(receiver)) {
1117 receiver = ToObject(receiver);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001118 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001119
Steve Blocka7e24c12009-10-30 11:49:00 +00001120 for (var i = 0; i < length; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001121 if (i in array) {
1122 var element = array[i];
1123 if (!%_CallFunction(receiver, element, i, array, f)) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001124 }
1125 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001126 return true;
1127}
1128
Steve Blocka7e24c12009-10-30 11:49:00 +00001129function ArrayMap(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001130 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1131 throw MakeTypeError("called_on_null_or_undefined",
1132 ["Array.prototype.map"]);
1133 }
1134
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001135 // Pull out the length so that modifications to the length in the
1136 // loop will not affect the looping and side effects are visible.
1137 var array = ToObject(this);
1138 var length = TO_UINT32(array.length);
1139
Ben Murdoch589d6972011-11-30 16:04:58 +00001140 if (!IS_SPEC_FUNCTION(f)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001141 throw MakeTypeError('called_non_callable', [ f ]);
1142 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001143 if (IS_NULL_OR_UNDEFINED(receiver)) {
1144 receiver = %GetDefaultReceiver(f) || receiver;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001145 } else if (!IS_SPEC_OBJECT(receiver)) {
1146 receiver = ToObject(receiver);
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001147 }
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001148
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001149 var result = new $Array();
1150 var accumulator = new InternalArray(length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001151 for (var i = 0; i < length; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001152 if (i in array) {
1153 var element = array[i];
1154 accumulator[i] = %_CallFunction(receiver, element, i, array, f);
Steve Blocka7e24c12009-10-30 11:49:00 +00001155 }
1156 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001157 %MoveArrayContents(accumulator, result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001158 return result;
1159}
1160
1161
1162function ArrayIndexOf(element, index) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001163 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1164 throw MakeTypeError("called_on_null_or_undefined",
1165 ["Array.prototype.indexOf"]);
1166 }
1167
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001168 var length = TO_UINT32(this.length);
1169 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001170 if (IS_UNDEFINED(index)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001171 index = 0;
1172 } else {
1173 index = TO_INTEGER(index);
1174 // If index is negative, index from the end of the array.
Steve Block1e0659c2011-05-24 12:43:12 +01001175 if (index < 0) {
1176 index = length + index;
1177 // If index is still negative, search the entire array.
1178 if (index < 0) index = 0;
1179 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001180 }
Steve Block59151502010-09-22 15:07:15 +01001181 var min = index;
1182 var max = length;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001183 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001184 var intervals = %GetArrayKeys(this, length);
1185 if (intervals.length == 2 && intervals[0] < 0) {
1186 // A single interval.
1187 var intervalMin = -(intervals[0] + 1);
1188 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001189 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001190 max = intervalMax; // Capped by length already.
1191 // Fall through to loop below.
1192 } else {
1193 if (intervals.length == 0) return -1;
1194 // Get all the keys in sorted order.
1195 var sortedKeys = GetSortedArrayKeys(this, intervals);
1196 var n = sortedKeys.length;
1197 var i = 0;
1198 while (i < n && sortedKeys[i] < index) i++;
1199 while (i < n) {
1200 var key = sortedKeys[i];
1201 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1202 i++;
1203 }
1204 return -1;
1205 }
1206 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001207 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001208 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001209 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001210 if (this[i] === element) return i;
1211 }
1212 return -1;
1213 }
Steve Block59151502010-09-22 15:07:15 +01001214 // Lookup through the array.
1215 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001216 if (IS_UNDEFINED(this[i]) && i in this) {
1217 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001218 }
1219 }
1220 return -1;
1221}
1222
1223
1224function ArrayLastIndexOf(element, index) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001225 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1226 throw MakeTypeError("called_on_null_or_undefined",
1227 ["Array.prototype.lastIndexOf"]);
1228 }
1229
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001230 var length = TO_UINT32(this.length);
1231 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001232 if (%_ArgumentsLength() < 2) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001233 index = length - 1;
1234 } else {
1235 index = TO_INTEGER(index);
1236 // If index is negative, index from end of the array.
Steve Block59151502010-09-22 15:07:15 +01001237 if (index < 0) index += length;
Steve Blocka7e24c12009-10-30 11:49:00 +00001238 // If index is still negative, do not search the array.
Steve Block59151502010-09-22 15:07:15 +01001239 if (index < 0) return -1;
Steve Blocka7e24c12009-10-30 11:49:00 +00001240 else if (index >= length) index = length - 1;
1241 }
Steve Block59151502010-09-22 15:07:15 +01001242 var min = 0;
1243 var max = index;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001244 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001245 var intervals = %GetArrayKeys(this, index + 1);
1246 if (intervals.length == 2 && intervals[0] < 0) {
1247 // A single interval.
1248 var intervalMin = -(intervals[0] + 1);
1249 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001250 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001251 max = intervalMax; // Capped by index already.
1252 // Fall through to loop below.
1253 } else {
1254 if (intervals.length == 0) return -1;
1255 // Get all the keys in sorted order.
1256 var sortedKeys = GetSortedArrayKeys(this, intervals);
1257 var i = sortedKeys.length - 1;
1258 while (i >= 0) {
1259 var key = sortedKeys[i];
1260 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1261 i--;
1262 }
1263 return -1;
1264 }
1265 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001266 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001267 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001268 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001269 if (this[i] === element) return i;
1270 }
1271 return -1;
1272 }
Steve Block59151502010-09-22 15:07:15 +01001273 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001274 if (IS_UNDEFINED(this[i]) && i in this) {
1275 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001276 }
1277 }
1278 return -1;
1279}
1280
1281
1282function ArrayReduce(callback, current) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001283 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1284 throw MakeTypeError("called_on_null_or_undefined",
1285 ["Array.prototype.reduce"]);
1286 }
1287
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001288 // Pull out the length so that modifications to the length in the
1289 // loop will not affect the looping and side effects are visible.
1290 var array = ToObject(this);
1291 var length = ToUint32(array.length);
1292
Ben Murdoch589d6972011-11-30 16:04:58 +00001293 if (!IS_SPEC_FUNCTION(callback)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001294 throw MakeTypeError('called_non_callable', [callback]);
1295 }
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001296
Steve Blocka7e24c12009-10-30 11:49:00 +00001297 var i = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +00001298 find_initial: if (%_ArgumentsLength() < 2) {
1299 for (; i < length; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001300 current = array[i];
1301 if (!IS_UNDEFINED(current) || i in array) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001302 i++;
1303 break find_initial;
1304 }
1305 }
1306 throw MakeTypeError('reduce_no_initial', []);
1307 }
1308
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001309 var receiver = %GetDefaultReceiver(callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001310 for (; i < length; i++) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001311 if (i in array) {
1312 var element = array[i];
1313 current = %_CallFunction(receiver, current, element, i, array, callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001314 }
1315 }
1316 return current;
1317}
1318
1319function ArrayReduceRight(callback, current) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001320 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1321 throw MakeTypeError("called_on_null_or_undefined",
1322 ["Array.prototype.reduceRight"]);
1323 }
1324
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001325 // Pull out the length so that side effects are visible before the
1326 // callback function is checked.
1327 var array = ToObject(this);
1328 var length = ToUint32(array.length);
1329
Ben Murdoch589d6972011-11-30 16:04:58 +00001330 if (!IS_SPEC_FUNCTION(callback)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001331 throw MakeTypeError('called_non_callable', [callback]);
1332 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001333
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001334 var i = length - 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00001335 find_initial: if (%_ArgumentsLength() < 2) {
1336 for (; i >= 0; i--) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001337 current = array[i];
1338 if (!IS_UNDEFINED(current) || i in array) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001339 i--;
1340 break find_initial;
1341 }
1342 }
1343 throw MakeTypeError('reduce_no_initial', []);
1344 }
1345
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001346 var receiver = %GetDefaultReceiver(callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001347 for (; i >= 0; i--) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001348 if (i in array) {
1349 var element = array[i];
1350 current = %_CallFunction(receiver, current, element, i, array, callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001351 }
1352 }
1353 return current;
1354}
1355
Steve Block3ce2e202009-11-05 08:53:23 +00001356// ES5, 15.4.3.2
1357function ArrayIsArray(obj) {
1358 return IS_ARRAY(obj);
1359}
Steve Blocka7e24c12009-10-30 11:49:00 +00001360
Steve Blocka7e24c12009-10-30 11:49:00 +00001361
1362// -------------------------------------------------------------------
Ben Murdoch589d6972011-11-30 16:04:58 +00001363function SetUpArray() {
1364 %CheckIsBootstrapping();
1365 // Set up non-enumerable constructor property on the Array.prototype
Steve Blocka7e24c12009-10-30 11:49:00 +00001366 // object.
1367 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1368
Ben Murdoch589d6972011-11-30 16:04:58 +00001369 // Set up non-enumerable functions on the Array object.
Steve Block3ce2e202009-11-05 08:53:23 +00001370 InstallFunctions($Array, DONT_ENUM, $Array(
1371 "isArray", ArrayIsArray
1372 ));
1373
Steve Block6ded16b2010-05-10 14:33:55 +01001374 var specialFunctions = %SpecialArrayFunctions({});
1375
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001376 var getFunction = function(name, jsBuiltin, len) {
Steve Block6ded16b2010-05-10 14:33:55 +01001377 var f = jsBuiltin;
1378 if (specialFunctions.hasOwnProperty(name)) {
1379 f = specialFunctions[name];
1380 }
1381 if (!IS_UNDEFINED(len)) {
1382 %FunctionSetLength(f, len);
1383 }
1384 return f;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001385 };
Steve Block6ded16b2010-05-10 14:33:55 +01001386
Ben Murdoch589d6972011-11-30 16:04:58 +00001387 // Set up non-enumerable functions of the Array.prototype object and
Steve Blocka7e24c12009-10-30 11:49:00 +00001388 // set their names.
Steve Blocka7e24c12009-10-30 11:49:00 +00001389 // Manipulate the length of some of the functions to meet
1390 // expectations set by ECMA-262 or Mozilla.
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001391 InstallFunctions($Array.prototype, DONT_ENUM, $Array(
Steve Block6ded16b2010-05-10 14:33:55 +01001392 "toString", getFunction("toString", ArrayToString),
1393 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1394 "join", getFunction("join", ArrayJoin),
1395 "pop", getFunction("pop", ArrayPop),
1396 "push", getFunction("push", ArrayPush, 1),
1397 "concat", getFunction("concat", ArrayConcat, 1),
1398 "reverse", getFunction("reverse", ArrayReverse),
1399 "shift", getFunction("shift", ArrayShift),
1400 "unshift", getFunction("unshift", ArrayUnshift, 1),
1401 "slice", getFunction("slice", ArraySlice, 2),
1402 "splice", getFunction("splice", ArraySplice, 2),
1403 "sort", getFunction("sort", ArraySort),
1404 "filter", getFunction("filter", ArrayFilter, 1),
1405 "forEach", getFunction("forEach", ArrayForEach, 1),
1406 "some", getFunction("some", ArraySome, 1),
1407 "every", getFunction("every", ArrayEvery, 1),
1408 "map", getFunction("map", ArrayMap, 1),
1409 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1410 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1411 "reduce", getFunction("reduce", ArrayReduce, 1),
1412 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1413 ));
1414
1415 %FinishArrayPrototypeSetup($Array.prototype);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001416
1417 // The internal Array prototype doesn't need to be fancy, since it's never
Ben Murdoch589d6972011-11-30 16:04:58 +00001418 // exposed to user code.
1419 // Adding only the functions that are actually used.
1420 SetUpLockedPrototype(InternalArray, $Array(), $Array(
1421 "join", getFunction("join", ArrayJoin),
1422 "pop", getFunction("pop", ArrayPop),
1423 "push", getFunction("push", ArrayPush)
1424 ));
Steve Blocka7e24c12009-10-30 11:49:00 +00001425}
1426
Ben Murdoch589d6972011-11-30 16:04:58 +00001427SetUpArray();