blob: df080a76992925fd7961de1d241d11c47995828f [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:
30// const $Array = global.Array;
31
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 Murdoch257744e2011-11-30 15:57:28 +0000175 if (IS_NUMBER(e)) {
176 e = %_NumberToString(e);
177 } else if (!IS_STRING(e)) {
178 e = convert(e);
179 }
Ben Murdochb8e0da22011-05-16 14:20:40 +0100180 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) {
Leon Clarkee46be812010-01-19 14:06:41 +0000204 if (e == null) {
205 return '';
206 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000207 // 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.
210 var e_obj = ToObject(e);
211 if (IS_FUNCTION(e_obj.toLocaleString))
Steve Blockd0582a62009-12-15 09:54:21 +0000212 return ToString(e_obj.toLocaleString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000213 else
214 return ToString(e);
215 }
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];
334 if (!IS_UNDEFINED(current) || index in array)
335 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() {
384 if (!IS_ARRAY(this)) {
385 throw new $TypeError('Array.prototype.toString is not generic');
386 }
387 return Join(this, this.length, ',', ConvertToString);
388}
389
390
391function ArrayToLocaleString() {
392 if (!IS_ARRAY(this)) {
393 throw new $TypeError('Array.prototype.toString is not generic');
394 }
395 return Join(this, this.length, ',', ConvertToLocaleString);
396}
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;
488 while (keys[--high_counter] == j);
489 low = j_complement;
490 }
491 if (j_complement >= i) {
492 low = i;
493 while (keys[++low_counter] == i);
494 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
569 if (IS_ARRAY(this))
570 SmartMove(this, 0, 1, len, 0);
571 else
572 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
589 if (IS_ARRAY(this))
590 SmartMove(this, 0, 0, len, num_arguments);
591 else
592 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
634 if (IS_ARRAY(this)) {
635 SmartSlice(this, start_i, end_i - start_i, len, result);
636 } else {
637 SimpleSlice(this, start_i, end_i - start_i, len, result);
638 }
639
640 result.length = end_i - start_i;
641
642 return result;
643}
644
645
646function ArraySplice(start, delete_count) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000647 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
648 throw MakeTypeError("called_on_null_or_undefined",
649 ["Array.prototype.splice"]);
650 }
651
Steve Blocka7e24c12009-10-30 11:49:00 +0000652 var num_arguments = %_ArgumentsLength();
653
Leon Clarkee46be812010-01-19 14:06:41 +0000654 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000655 var start_i = TO_INTEGER(start);
656
657 if (start_i < 0) {
658 start_i += len;
659 if (start_i < 0) start_i = 0;
660 } else {
661 if (start_i > len) start_i = len;
662 }
663
Andrei Popescu402d9372010-02-26 13:31:12 +0000664 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
Steve Block1e0659c2011-05-24 12:43:12 +0100665 // given as a request to delete all the elements from the start.
666 // And it differs from the case of undefined delete count.
Steve Blocka7e24c12009-10-30 11:49:00 +0000667 // This does not follow ECMA-262, but we do the same for
668 // compatibility.
669 var del_count = 0;
Steve Block1e0659c2011-05-24 12:43:12 +0100670 if (num_arguments == 1) {
671 del_count = len - start_i;
672 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000673 del_count = TO_INTEGER(delete_count);
674 if (del_count < 0) del_count = 0;
675 if (del_count > len - start_i) del_count = len - start_i;
Steve Blocka7e24c12009-10-30 11:49:00 +0000676 }
677
678 var deleted_elements = [];
679 deleted_elements.length = del_count;
680
681 // Number of elements to add.
682 var num_additional_args = 0;
683 if (num_arguments > 2) {
684 num_additional_args = num_arguments - 2;
685 }
686
687 var use_simple_splice = true;
688
689 if (IS_ARRAY(this) && num_additional_args !== del_count) {
690 // If we are only deleting/moving a few things near the end of the
691 // array then the simple version is going to be faster, because it
692 // doesn't touch most of the array.
693 var estimated_non_hole_elements = %EstimateNumberOfElements(this);
694 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
695 use_simple_splice = false;
696 }
697 }
698
699 if (use_simple_splice) {
700 SimpleSlice(this, start_i, del_count, len, deleted_elements);
701 SimpleMove(this, start_i, del_count, len, num_additional_args);
702 } else {
703 SmartSlice(this, start_i, del_count, len, deleted_elements);
704 SmartMove(this, start_i, del_count, len, num_additional_args);
705 }
706
707 // Insert the arguments into the resulting array in
708 // place of the deleted elements.
709 var i = start_i;
710 var arguments_index = 2;
711 var arguments_length = %_ArgumentsLength();
712 while (arguments_index < arguments_length) {
713 this[i++] = %_Arguments(arguments_index++);
714 }
715 this.length = len - del_count + num_additional_args;
716
717 // Return the deleted elements.
718 return deleted_elements;
719}
720
721
722function ArraySort(comparefn) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000723 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
724 throw MakeTypeError("called_on_null_or_undefined",
725 ["Array.prototype.sort"]);
726 }
727
Steve Blocka7e24c12009-10-30 11:49:00 +0000728 // In-place QuickSort algorithm.
729 // For short (length <= 22) arrays, insertion sort is used for efficiency.
730
Steve Block6ded16b2010-05-10 14:33:55 +0100731 if (!IS_FUNCTION(comparefn)) {
732 comparefn = function (x, y) {
733 if (x === y) return 0;
734 if (%_IsSmi(x) && %_IsSmi(y)) {
735 return %SmiLexicographicCompare(x, y);
736 }
737 x = ToString(x);
738 y = ToString(y);
739 if (x == y) return 0;
740 else return x < y ? -1 : 1;
741 };
742 }
743 var global_receiver = %GetGlobalReceiver();
Steve Blocka7e24c12009-10-30 11:49:00 +0000744
745 function InsertionSort(a, from, to) {
746 for (var i = from + 1; i < to; i++) {
747 var element = a[i];
Steve Block6ded16b2010-05-10 14:33:55 +0100748 for (var j = i - 1; j >= from; j--) {
749 var tmp = a[j];
750 var order = %_CallFunction(global_receiver, tmp, element, comparefn);
751 if (order > 0) {
752 a[j + 1] = tmp;
753 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000754 break;
755 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000756 }
Steve Block6ded16b2010-05-10 14:33:55 +0100757 a[j + 1] = element;
Steve Blocka7e24c12009-10-30 11:49:00 +0000758 }
759 }
760
761 function QuickSort(a, from, to) {
762 // Insertion sort is faster for short arrays.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100763 if (to - from <= 10) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000764 InsertionSort(a, from, to);
765 return;
766 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100767 // Find a pivot as the median of first, last and middle element.
768 var v0 = a[from];
769 var v1 = a[to - 1];
770 var middle_index = from + ((to - from) >> 1);
771 var v2 = a[middle_index];
772 var c01 = %_CallFunction(global_receiver, v0, v1, comparefn);
773 if (c01 > 0) {
774 // v1 < v0, so swap them.
775 var tmp = v0;
776 v0 = v1;
777 v1 = tmp;
778 } // v0 <= v1.
779 var c02 = %_CallFunction(global_receiver, v0, v2, comparefn);
780 if (c02 >= 0) {
781 // v2 <= v0 <= v1.
782 var tmp = v0;
783 v0 = v2;
784 v2 = v1;
785 v1 = tmp;
786 } else {
787 // v0 <= v1 && v0 < v2
788 var c12 = %_CallFunction(global_receiver, v1, v2, comparefn);
789 if (c12 > 0) {
790 // v0 <= v2 < v1
791 var tmp = v1;
792 v1 = v2;
793 v2 = tmp;
794 }
795 }
796 // v0 <= v1 <= v2
797 a[from] = v0;
798 a[to - 1] = v2;
799 var pivot = v1;
800 var low_end = from + 1; // Upper bound of elements lower than pivot.
801 var high_start = to - 1; // Lower bound of elements greater than pivot.
802 a[middle_index] = a[low_end];
803 a[low_end] = pivot;
804
Steve Blocka7e24c12009-10-30 11:49:00 +0000805 // From low_end to i are elements equal to pivot.
806 // From i to high_start are elements that haven't been compared yet.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100807 partition: for (var i = low_end + 1; i < high_start; i++) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000808 var element = a[i];
Steve Block6ded16b2010-05-10 14:33:55 +0100809 var order = %_CallFunction(global_receiver, element, pivot, comparefn);
Steve Blocka7e24c12009-10-30 11:49:00 +0000810 if (order < 0) {
Steve Block6ded16b2010-05-10 14:33:55 +0100811 %_SwapElements(a, i, low_end);
Steve Blocka7e24c12009-10-30 11:49:00 +0000812 low_end++;
813 } else if (order > 0) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100814 do {
815 high_start--;
816 if (high_start == i) break partition;
817 var top_elem = a[high_start];
818 order = %_CallFunction(global_receiver, top_elem, pivot, comparefn);
819 } while (order > 0);
Steve Block6ded16b2010-05-10 14:33:55 +0100820 %_SwapElements(a, i, high_start);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100821 if (order < 0) {
822 %_SwapElements(a, i, low_end);
823 low_end++;
824 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000825 }
826 }
827 QuickSort(a, from, low_end);
828 QuickSort(a, high_start, to);
829 }
830
Ben Murdochb0fe1622011-05-05 13:52:32 +0100831 // Copy elements in the range 0..length from obj's prototype chain
832 // to obj itself, if obj has holes. Return one more than the maximal index
Steve Blocka7e24c12009-10-30 11:49:00 +0000833 // of a prototype property.
834 function CopyFromPrototype(obj, length) {
835 var max = 0;
836 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
837 var indices = %GetArrayKeys(proto, length);
838 if (indices.length > 0) {
839 if (indices[0] == -1) {
840 // It's an interval.
841 var proto_length = indices[1];
842 for (var i = 0; i < proto_length; i++) {
843 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
844 obj[i] = proto[i];
845 if (i >= max) { max = i + 1; }
846 }
847 }
848 } else {
849 for (var i = 0; i < indices.length; i++) {
850 var index = indices[i];
851 if (!IS_UNDEFINED(index) &&
852 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
853 obj[index] = proto[index];
854 if (index >= max) { max = index + 1; }
855 }
856 }
857 }
858 }
859 }
860 return max;
861 }
862
863 // Set a value of "undefined" on all indices in the range from..to
864 // where a prototype of obj has an element. I.e., shadow all prototype
865 // elements in that range.
866 function ShadowPrototypeElements(obj, from, to) {
867 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
868 var indices = %GetArrayKeys(proto, to);
869 if (indices.length > 0) {
870 if (indices[0] == -1) {
871 // It's an interval.
872 var proto_length = indices[1];
873 for (var i = from; i < proto_length; i++) {
874 if (proto.hasOwnProperty(i)) {
875 obj[i] = void 0;
876 }
877 }
878 } else {
879 for (var i = 0; i < indices.length; i++) {
880 var index = indices[i];
881 if (!IS_UNDEFINED(index) && from <= index &&
882 proto.hasOwnProperty(index)) {
883 obj[index] = void 0;
884 }
885 }
886 }
887 }
888 }
889 }
890
891 function SafeRemoveArrayHoles(obj) {
892 // Copy defined elements from the end to fill in all holes and undefineds
893 // in the beginning of the array. Write undefineds and holes at the end
894 // after loop is finished.
895 var first_undefined = 0;
896 var last_defined = length - 1;
897 var num_holes = 0;
898 while (first_undefined < last_defined) {
899 // Find first undefined element.
900 while (first_undefined < last_defined &&
901 !IS_UNDEFINED(obj[first_undefined])) {
902 first_undefined++;
903 }
904 // Maintain the invariant num_holes = the number of holes in the original
905 // array with indices <= first_undefined or > last_defined.
906 if (!obj.hasOwnProperty(first_undefined)) {
907 num_holes++;
908 }
909
910 // Find last defined element.
911 while (first_undefined < last_defined &&
912 IS_UNDEFINED(obj[last_defined])) {
913 if (!obj.hasOwnProperty(last_defined)) {
914 num_holes++;
915 }
916 last_defined--;
917 }
918 if (first_undefined < last_defined) {
919 // Fill in hole or undefined.
920 obj[first_undefined] = obj[last_defined];
921 obj[last_defined] = void 0;
922 }
923 }
924 // If there were any undefineds in the entire array, first_undefined
925 // points to one past the last defined element. Make this true if
926 // there were no undefineds, as well, so that first_undefined == number
927 // of defined elements.
928 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
929 // Fill in the undefineds and the holes. There may be a hole where
930 // an undefined should be and vice versa.
931 var i;
932 for (i = first_undefined; i < length - num_holes; i++) {
933 obj[i] = void 0;
934 }
935 for (i = length - num_holes; i < length; i++) {
936 // For compatability with Webkit, do not expose elements in the prototype.
937 if (i in obj.__proto__) {
938 obj[i] = void 0;
939 } else {
940 delete obj[i];
941 }
942 }
943
944 // Return the number of defined elements.
945 return first_undefined;
946 }
947
Steve Block6ded16b2010-05-10 14:33:55 +0100948 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000949 if (length < 2) return this;
950
951 var is_array = IS_ARRAY(this);
952 var max_prototype_element;
953 if (!is_array) {
954 // For compatibility with JSC, we also sort elements inherited from
955 // the prototype chain on non-Array objects.
956 // We do this by copying them to this object and sorting only
957 // local elements. This is not very efficient, but sorting with
958 // inherited elements happens very, very rarely, if at all.
959 // The specification allows "implementation dependent" behavior
960 // if an element on the prototype chain has an element that
961 // might interact with sorting.
962 max_prototype_element = CopyFromPrototype(this, length);
963 }
964
965 var num_non_undefined = %RemoveArrayHoles(this, length);
966 if (num_non_undefined == -1) {
967 // There were indexed accessors in the array. Move array holes and
968 // undefineds to the end using a Javascript function that is safe
969 // in the presence of accessors.
970 num_non_undefined = SafeRemoveArrayHoles(this);
971 }
972
973 QuickSort(this, 0, num_non_undefined);
974
975 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
976 // For compatibility with JSC, we shadow any elements in the prototype
977 // chain that has become exposed by sort moving a hole to its position.
978 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
979 }
980
981 return this;
982}
983
984
985// The following functions cannot be made efficient on sparse arrays while
986// preserving the semantics, since the calls to the receiver function can add
987// or delete elements from the array.
988function ArrayFilter(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000989 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
990 throw MakeTypeError("called_on_null_or_undefined",
991 ["Array.prototype.filter"]);
992 }
993
Steve Blocka7e24c12009-10-30 11:49:00 +0000994 if (!IS_FUNCTION(f)) {
995 throw MakeTypeError('called_non_callable', [ f ]);
996 }
997 // Pull out the length so that modifications to the length in the
998 // loop will not affect the looping.
999 var length = this.length;
1000 var result = [];
1001 var result_length = 0;
1002 for (var i = 0; i < length; i++) {
1003 var current = this[i];
1004 if (!IS_UNDEFINED(current) || i in this) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001005 if (f.call(receiver, current, i, this)) {
1006 result[result_length++] = current;
1007 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001008 }
1009 }
1010 return result;
1011}
1012
1013
1014function ArrayForEach(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001015 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1016 throw MakeTypeError("called_on_null_or_undefined",
1017 ["Array.prototype.forEach"]);
1018 }
1019
Steve Blocka7e24c12009-10-30 11:49:00 +00001020 if (!IS_FUNCTION(f)) {
1021 throw MakeTypeError('called_non_callable', [ f ]);
1022 }
1023 // Pull out the length so that modifications to the length in the
1024 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +00001025 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001026 for (var i = 0; i < length; i++) {
1027 var current = this[i];
1028 if (!IS_UNDEFINED(current) || i in this) {
1029 f.call(receiver, current, i, this);
1030 }
1031 }
1032}
1033
1034
1035// Executes the function once for each element present in the
1036// array until it finds one where callback returns true.
1037function ArraySome(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001038 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1039 throw MakeTypeError("called_on_null_or_undefined",
1040 ["Array.prototype.some"]);
1041 }
1042
Steve Blocka7e24c12009-10-30 11:49:00 +00001043 if (!IS_FUNCTION(f)) {
1044 throw MakeTypeError('called_non_callable', [ f ]);
1045 }
1046 // Pull out the length so that modifications to the length in the
1047 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +00001048 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001049 for (var i = 0; i < length; i++) {
1050 var current = this[i];
1051 if (!IS_UNDEFINED(current) || i in this) {
1052 if (f.call(receiver, current, i, this)) return true;
1053 }
1054 }
1055 return false;
1056}
1057
1058
1059function ArrayEvery(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001060 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1061 throw MakeTypeError("called_on_null_or_undefined",
1062 ["Array.prototype.every"]);
1063 }
1064
Steve Blocka7e24c12009-10-30 11:49:00 +00001065 if (!IS_FUNCTION(f)) {
1066 throw MakeTypeError('called_non_callable', [ f ]);
1067 }
1068 // Pull out the length so that modifications to the length in the
1069 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +00001070 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001071 for (var i = 0; i < length; i++) {
1072 var current = this[i];
1073 if (!IS_UNDEFINED(current) || i in this) {
1074 if (!f.call(receiver, current, i, this)) return false;
1075 }
1076 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001077 return true;
1078}
1079
Steve Blocka7e24c12009-10-30 11:49:00 +00001080function ArrayMap(f, receiver) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001081 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1082 throw MakeTypeError("called_on_null_or_undefined",
1083 ["Array.prototype.map"]);
1084 }
1085
Steve Blocka7e24c12009-10-30 11:49:00 +00001086 if (!IS_FUNCTION(f)) {
1087 throw MakeTypeError('called_non_callable', [ f ]);
1088 }
1089 // Pull out the length so that modifications to the length in the
1090 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +00001091 var length = TO_UINT32(this.length);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001092 var result = new $Array();
1093 var accumulator = new InternalArray(length);
Steve Blocka7e24c12009-10-30 11:49:00 +00001094 for (var i = 0; i < length; i++) {
1095 var current = this[i];
1096 if (!IS_UNDEFINED(current) || i in this) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001097 accumulator[i] = f.call(receiver, current, i, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00001098 }
1099 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001100 %MoveArrayContents(accumulator, result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001101 return result;
1102}
1103
1104
1105function ArrayIndexOf(element, index) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001106 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1107 throw MakeTypeError("called_on_null_or_undefined",
1108 ["Array.prototype.indexOf"]);
1109 }
1110
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001111 var length = TO_UINT32(this.length);
1112 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001113 if (IS_UNDEFINED(index)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001114 index = 0;
1115 } else {
1116 index = TO_INTEGER(index);
1117 // If index is negative, index from the end of the array.
Steve Block1e0659c2011-05-24 12:43:12 +01001118 if (index < 0) {
1119 index = length + index;
1120 // If index is still negative, search the entire array.
1121 if (index < 0) index = 0;
1122 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001123 }
Steve Block59151502010-09-22 15:07:15 +01001124 var min = index;
1125 var max = length;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001126 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001127 var intervals = %GetArrayKeys(this, length);
1128 if (intervals.length == 2 && intervals[0] < 0) {
1129 // A single interval.
1130 var intervalMin = -(intervals[0] + 1);
1131 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001132 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001133 max = intervalMax; // Capped by length already.
1134 // Fall through to loop below.
1135 } else {
1136 if (intervals.length == 0) return -1;
1137 // Get all the keys in sorted order.
1138 var sortedKeys = GetSortedArrayKeys(this, intervals);
1139 var n = sortedKeys.length;
1140 var i = 0;
1141 while (i < n && sortedKeys[i] < index) i++;
1142 while (i < n) {
1143 var key = sortedKeys[i];
1144 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1145 i++;
1146 }
1147 return -1;
1148 }
1149 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001150 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001151 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001152 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001153 if (this[i] === element) return i;
1154 }
1155 return -1;
1156 }
Steve Block59151502010-09-22 15:07:15 +01001157 // Lookup through the array.
1158 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001159 if (IS_UNDEFINED(this[i]) && i in this) {
1160 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001161 }
1162 }
1163 return -1;
1164}
1165
1166
1167function ArrayLastIndexOf(element, index) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001168 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1169 throw MakeTypeError("called_on_null_or_undefined",
1170 ["Array.prototype.lastIndexOf"]);
1171 }
1172
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001173 var length = TO_UINT32(this.length);
1174 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001175 if (%_ArgumentsLength() < 2) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001176 index = length - 1;
1177 } else {
1178 index = TO_INTEGER(index);
1179 // If index is negative, index from end of the array.
Steve Block59151502010-09-22 15:07:15 +01001180 if (index < 0) index += length;
Steve Blocka7e24c12009-10-30 11:49:00 +00001181 // If index is still negative, do not search the array.
Steve Block59151502010-09-22 15:07:15 +01001182 if (index < 0) return -1;
Steve Blocka7e24c12009-10-30 11:49:00 +00001183 else if (index >= length) index = length - 1;
1184 }
Steve Block59151502010-09-22 15:07:15 +01001185 var min = 0;
1186 var max = index;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001187 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001188 var intervals = %GetArrayKeys(this, index + 1);
1189 if (intervals.length == 2 && intervals[0] < 0) {
1190 // A single interval.
1191 var intervalMin = -(intervals[0] + 1);
1192 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001193 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001194 max = intervalMax; // Capped by index already.
1195 // Fall through to loop below.
1196 } else {
1197 if (intervals.length == 0) return -1;
1198 // Get all the keys in sorted order.
1199 var sortedKeys = GetSortedArrayKeys(this, intervals);
1200 var i = sortedKeys.length - 1;
1201 while (i >= 0) {
1202 var key = sortedKeys[i];
1203 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1204 i--;
1205 }
1206 return -1;
1207 }
1208 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001209 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001210 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001211 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001212 if (this[i] === element) return i;
1213 }
1214 return -1;
1215 }
Steve Block59151502010-09-22 15:07:15 +01001216 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001217 if (IS_UNDEFINED(this[i]) && i in this) {
1218 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001219 }
1220 }
1221 return -1;
1222}
1223
1224
1225function ArrayReduce(callback, current) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001226 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1227 throw MakeTypeError("called_on_null_or_undefined",
1228 ["Array.prototype.reduce"]);
1229 }
1230
Steve Blocka7e24c12009-10-30 11:49:00 +00001231 if (!IS_FUNCTION(callback)) {
1232 throw MakeTypeError('called_non_callable', [callback]);
1233 }
1234 // Pull out the length so that modifications to the length in the
1235 // loop will not affect the looping.
1236 var length = this.length;
1237 var i = 0;
1238
1239 find_initial: if (%_ArgumentsLength() < 2) {
1240 for (; i < length; i++) {
1241 current = this[i];
1242 if (!IS_UNDEFINED(current) || i in this) {
1243 i++;
1244 break find_initial;
1245 }
1246 }
1247 throw MakeTypeError('reduce_no_initial', []);
1248 }
1249
1250 for (; i < length; i++) {
1251 var element = this[i];
1252 if (!IS_UNDEFINED(element) || i in this) {
1253 current = callback.call(null, current, element, i, this);
1254 }
1255 }
1256 return current;
1257}
1258
1259function ArrayReduceRight(callback, current) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001260 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1261 throw MakeTypeError("called_on_null_or_undefined",
1262 ["Array.prototype.reduceRight"]);
1263 }
1264
Steve Blocka7e24c12009-10-30 11:49:00 +00001265 if (!IS_FUNCTION(callback)) {
1266 throw MakeTypeError('called_non_callable', [callback]);
1267 }
1268 var i = this.length - 1;
1269
1270 find_initial: if (%_ArgumentsLength() < 2) {
1271 for (; i >= 0; i--) {
1272 current = this[i];
1273 if (!IS_UNDEFINED(current) || i in this) {
1274 i--;
1275 break find_initial;
1276 }
1277 }
1278 throw MakeTypeError('reduce_no_initial', []);
1279 }
1280
1281 for (; i >= 0; i--) {
1282 var element = this[i];
1283 if (!IS_UNDEFINED(element) || i in this) {
1284 current = callback.call(null, current, element, i, this);
1285 }
1286 }
1287 return current;
1288}
1289
Steve Block3ce2e202009-11-05 08:53:23 +00001290// ES5, 15.4.3.2
1291function ArrayIsArray(obj) {
1292 return IS_ARRAY(obj);
1293}
Steve Blocka7e24c12009-10-30 11:49:00 +00001294
Steve Blocka7e24c12009-10-30 11:49:00 +00001295
1296// -------------------------------------------------------------------
1297function SetupArray() {
1298 // Setup non-enumerable constructor property on the Array.prototype
1299 // object.
1300 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1301
Steve Block3ce2e202009-11-05 08:53:23 +00001302 // Setup non-enumerable functions on the Array object.
1303 InstallFunctions($Array, DONT_ENUM, $Array(
1304 "isArray", ArrayIsArray
1305 ));
1306
Steve Block6ded16b2010-05-10 14:33:55 +01001307 var specialFunctions = %SpecialArrayFunctions({});
1308
1309 function getFunction(name, jsBuiltin, len) {
1310 var f = jsBuiltin;
1311 if (specialFunctions.hasOwnProperty(name)) {
1312 f = specialFunctions[name];
1313 }
1314 if (!IS_UNDEFINED(len)) {
1315 %FunctionSetLength(f, len);
1316 }
1317 return f;
1318 }
1319
Steve Blocka7e24c12009-10-30 11:49:00 +00001320 // Setup non-enumerable functions of the Array.prototype object and
1321 // set their names.
Steve Blocka7e24c12009-10-30 11:49:00 +00001322 // Manipulate the length of some of the functions to meet
1323 // expectations set by ECMA-262 or Mozilla.
Steve Block6ded16b2010-05-10 14:33:55 +01001324 InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
1325 "toString", getFunction("toString", ArrayToString),
1326 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1327 "join", getFunction("join", ArrayJoin),
1328 "pop", getFunction("pop", ArrayPop),
1329 "push", getFunction("push", ArrayPush, 1),
1330 "concat", getFunction("concat", ArrayConcat, 1),
1331 "reverse", getFunction("reverse", ArrayReverse),
1332 "shift", getFunction("shift", ArrayShift),
1333 "unshift", getFunction("unshift", ArrayUnshift, 1),
1334 "slice", getFunction("slice", ArraySlice, 2),
1335 "splice", getFunction("splice", ArraySplice, 2),
1336 "sort", getFunction("sort", ArraySort),
1337 "filter", getFunction("filter", ArrayFilter, 1),
1338 "forEach", getFunction("forEach", ArrayForEach, 1),
1339 "some", getFunction("some", ArraySome, 1),
1340 "every", getFunction("every", ArrayEvery, 1),
1341 "map", getFunction("map", ArrayMap, 1),
1342 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1343 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1344 "reduce", getFunction("reduce", ArrayReduce, 1),
1345 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1346 ));
1347
1348 %FinishArrayPrototypeSetup($Array.prototype);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001349
1350 // The internal Array prototype doesn't need to be fancy, since it's never
1351 // exposed to user code, so no hidden prototypes or DONT_ENUM attributes
1352 // are necessary.
1353 // The null __proto__ ensures that we never inherit any user created
1354 // getters or setters from, e.g., Object.prototype.
1355 InternalArray.prototype.__proto__ = null;
1356 // Adding only the functions that are actually used, and a toString.
1357 InternalArray.prototype.join = getFunction("join", ArrayJoin);
1358 InternalArray.prototype.pop = getFunction("pop", ArrayPop);
1359 InternalArray.prototype.push = getFunction("push", ArrayPush);
1360 InternalArray.prototype.toString = function() {
1361 return "Internal Array, length " + this.length;
1362 };
Steve Blocka7e24c12009-10-30 11:49:00 +00001363}
1364
1365
1366SetupArray();