blob: 1298434d5975cec4cc3deed9a989765938ae49b1 [file] [log] [blame]
whesse@chromium.org023421e2010-12-21 12:19:12 +00001// Copyright 2010 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000034// Global list of arrays visited during toString, toLocaleString and
35// join invocations.
36var visited_arrays = new $Array();
37
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
70// Optimized for sparse arrays if separator is ''.
71function SparseJoin(array, len, convert) {
72 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000073 var last_key = -1;
74 var keys_length = keys.length;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000075
76 var elements = new $Array(keys_length);
77 var elements_length = 0;
78
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000079 for (var i = 0; i < keys_length; i++) {
80 var key = keys[i];
81 if (key != last_key) {
82 var e = array[key];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000083 if (!IS_STRING(e)) e = convert(e);
84 elements[elements_length++] = e;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000085 last_key = key;
86 }
87 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000088 return %StringBuilderConcat(elements, elements_length, '');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000089}
90
91
92function UseSparseVariant(object, length, is_array) {
93 return is_array &&
94 length > 1000 &&
95 (!%_IsSmi(length) ||
96 %EstimateNumberOfElements(object) < (length >> 2));
97}
98
99
100function Join(array, length, separator, convert) {
101 if (length == 0) return '';
102
103 var is_array = IS_ARRAY(array);
104
105 if (is_array) {
106 // If the array is cyclic, return the empty string for already
107 // visited arrays.
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000108 if (!%PushIfAbsent(visited_arrays, array)) return '';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000109 }
110
111 // Attempt to convert the elements.
112 try {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000113 if (UseSparseVariant(array, length, is_array) && (separator.length == 0)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000114 return SparseJoin(array, length, convert);
115 }
116
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000117 // Fast case for one-element arrays.
118 if (length == 1) {
119 var e = array[0];
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000120 if (IS_STRING(e)) return e;
121 return convert(e);
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000122 }
123
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000124 // Construct an array for the elements.
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000125 var elements = new $Array(length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000126
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000127 // We pull the empty separator check outside the loop for speed!
128 if (separator.length == 0) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000129 var elements_length = 0;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000130 for (var i = 0; i < length; i++) {
131 var e = array[i];
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000132 if (!IS_UNDEFINED(e)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000133 if (!IS_STRING(e)) e = convert(e);
134 elements[elements_length++] = e;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000135 }
136 }
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000137 elements.length = elements_length;
138 var result = %_FastAsciiArrayJoin(elements, '');
139 if (!IS_UNDEFINED(result)) return result;
140 return %StringBuilderConcat(elements, elements_length, '');
141 }
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000142 // Non-empty separator case.
143 // If the first element is a number then use the heuristic that the
144 // remaining elements are also likely to be numbers.
145 if (!IS_NUMBER(array[0])) {
146 for (var i = 0; i < length; i++) {
147 var e = array[i];
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000148 if (!IS_STRING(e)) e = convert(e);
149 elements[i] = e;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000150 }
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000151 } else {
152 for (var i = 0; i < length; i++) {
153 var e = array[i];
154 if (IS_NUMBER(e)) elements[i] = %_NumberToString(e);
155 else {
156 if (!IS_STRING(e)) e = convert(e);
157 elements[i] = e;
158 }
159 }
160 }
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000161 var result = %_FastAsciiArrayJoin(elements, separator);
162 if (!IS_UNDEFINED(result)) return result;
163
164 var length2 = (length << 1) - 1;
165 var j = length2;
166 var i = length;
167 elements[--j] = elements[--i];
168 while (i > 0) {
169 elements[--j] = separator;
170 elements[--j] = elements[--i];
171 }
172 return %StringBuilderConcat(elements, length2, '');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000173 } finally {
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000174 // Make sure to remove the last element of the visited array no
175 // matter what happens.
176 if (is_array) visited_arrays.length = visited_arrays.length - 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000177 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000178}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000179
180
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000181function ConvertToString(x) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000182 // Assumes x is a non-string.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000183 if (IS_NUMBER(x)) return %_NumberToString(x);
184 if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
185 return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000186}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000187
188
189function ConvertToLocaleString(e) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000190 if (e == null) {
191 return '';
192 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000193 // e_obj's toLocaleString might be overwritten, check if it is a function.
194 // Call ToString if toLocaleString is not a function.
195 // See issue 877615.
196 var e_obj = ToObject(e);
197 if (IS_FUNCTION(e_obj.toLocaleString))
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000198 return ToString(e_obj.toLocaleString());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000199 else
200 return ToString(e);
201 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000202}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000203
204
205// This function implements the optimized splice implementation that can use
206// special array operations to handle sparse arrays in a sensible fashion.
207function SmartSlice(array, start_i, del_count, len, deleted_elements) {
208 // Move deleted elements to a new array (the return value from splice).
209 // Intervals array can contain keys and intervals. See comment in Concat.
210 var intervals = %GetArrayKeys(array, start_i + del_count);
211 var length = intervals.length;
212 for (var k = 0; k < length; k++) {
213 var key = intervals[k];
214 if (key < 0) {
215 var j = -1 - key;
216 var interval_limit = j + intervals[++k];
217 if (j < start_i) {
218 j = start_i;
219 }
220 for (; j < interval_limit; j++) {
221 // ECMA-262 15.4.4.12 line 10. The spec could also be
222 // interpreted such that %HasLocalProperty would be the
223 // appropriate test. We follow KJS in consulting the
224 // prototype.
225 var current = array[j];
226 if (!IS_UNDEFINED(current) || j in array) {
227 deleted_elements[j - start_i] = current;
228 }
229 }
230 } else {
231 if (!IS_UNDEFINED(key)) {
232 if (key >= start_i) {
233 // ECMA-262 15.4.4.12 line 10. The spec could also be
234 // interpreted such that %HasLocalProperty would be the
235 // appropriate test. We follow KJS in consulting the
236 // prototype.
237 var current = array[key];
238 if (!IS_UNDEFINED(current) || key in array) {
239 deleted_elements[key - start_i] = current;
240 }
241 }
242 }
243 }
244 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000245}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000246
247
248// This function implements the optimized splice implementation that can use
249// special array operations to handle sparse arrays in a sensible fashion.
250function SmartMove(array, start_i, del_count, len, num_additional_args) {
251 // Move data to new array.
252 var new_array = new $Array(len - del_count + num_additional_args);
253 var intervals = %GetArrayKeys(array, len);
254 var length = intervals.length;
255 for (var k = 0; k < length; k++) {
256 var key = intervals[k];
257 if (key < 0) {
258 var j = -1 - key;
259 var interval_limit = j + intervals[++k];
260 while (j < start_i && j < interval_limit) {
261 // The spec could also be interpreted such that
262 // %HasLocalProperty would be the appropriate test. We follow
263 // KJS in consulting the prototype.
264 var current = array[j];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000265 if (!IS_UNDEFINED(current) || j in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266 new_array[j] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000267 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000268 j++;
269 }
270 j = start_i + del_count;
271 while (j < interval_limit) {
272 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also be
273 // interpreted such that %HasLocalProperty would be the
274 // appropriate test. We follow KJS in consulting the
275 // prototype.
276 var current = array[j];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000277 if (!IS_UNDEFINED(current) || j in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000278 new_array[j - del_count + num_additional_args] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000279 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000280 j++;
281 }
282 } else {
283 if (!IS_UNDEFINED(key)) {
284 if (key < start_i) {
285 // The spec could also be interpreted such that
286 // %HasLocalProperty would be the appropriate test. We follow
287 // KJS in consulting the prototype.
288 var current = array[key];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000289 if (!IS_UNDEFINED(current) || key in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290 new_array[key] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000291 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000292 } else if (key >= start_i + del_count) {
293 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also
294 // be interpreted such that %HasLocalProperty would be the
295 // appropriate test. We follow KJS in consulting the
296 // prototype.
297 var current = array[key];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000298 if (!IS_UNDEFINED(current) || key in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000299 new_array[key - del_count + num_additional_args] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000300 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 }
302 }
303 }
304 }
305 // Move contents of new_array into this array
306 %MoveArrayContents(new_array, array);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000307}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000308
309
310// This is part of the old simple-minded splice. We are using it either
311// because the receiver is not an array (so we have no choice) or because we
312// know we are not deleting or moving a lot of elements.
313function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
314 for (var i = 0; i < del_count; i++) {
315 var index = start_i + i;
316 // The spec could also be interpreted such that %HasLocalProperty
317 // would be the appropriate test. We follow KJS in consulting the
318 // prototype.
319 var current = array[index];
320 if (!IS_UNDEFINED(current) || index in array)
321 deleted_elements[i] = current;
322 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000323}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000324
325
326function SimpleMove(array, start_i, del_count, len, num_additional_args) {
327 if (num_additional_args !== del_count) {
328 // Move the existing elements after the elements to be deleted
329 // to the right position in the resulting array.
330 if (num_additional_args > del_count) {
331 for (var i = len - del_count; i > start_i; i--) {
332 var from_index = i + del_count - 1;
333 var to_index = i + num_additional_args - 1;
334 // The spec could also be interpreted such that
335 // %HasLocalProperty would be the appropriate test. We follow
336 // KJS in consulting the prototype.
337 var current = array[from_index];
338 if (!IS_UNDEFINED(current) || from_index in array) {
339 array[to_index] = current;
340 } else {
341 delete array[to_index];
342 }
343 }
344 } else {
345 for (var i = start_i; i < len - del_count; i++) {
346 var from_index = i + del_count;
347 var to_index = i + num_additional_args;
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 for (var i = len; i > len - del_count + num_additional_args; i--) {
359 delete array[i - 1];
360 }
361 }
362 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000363}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000364
365
366// -------------------------------------------------------------------
367
368
369function ArrayToString() {
370 if (!IS_ARRAY(this)) {
371 throw new $TypeError('Array.prototype.toString is not generic');
372 }
373 return Join(this, this.length, ',', ConvertToString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000374}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000375
376
377function ArrayToLocaleString() {
378 if (!IS_ARRAY(this)) {
379 throw new $TypeError('Array.prototype.toString is not generic');
380 }
381 return Join(this, this.length, ',', ConvertToLocaleString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000382}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000383
384
385function ArrayJoin(separator) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000386 if (IS_UNDEFINED(separator)) {
387 separator = ',';
388 } else if (!IS_STRING(separator)) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000389 separator = NonStringToString(separator);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000390 }
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000391
392 var result = %_FastAsciiArrayJoin(this, separator);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000393 if (!IS_UNDEFINED(result)) return result;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000394
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000395 return Join(this, TO_UINT32(this.length), separator, ConvertToString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000396}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000397
398
399// Removes the last element from the array and returns it. See
400// ECMA-262, section 15.4.4.6.
401function ArrayPop() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000402 var n = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000403 if (n == 0) {
404 this.length = n;
405 return;
406 }
407 n--;
408 var value = this[n];
409 this.length = n;
410 delete this[n];
411 return value;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000412}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000413
414
415// Appends the arguments to the end of the array and returns the new
416// length of the array. See ECMA-262, section 15.4.4.7.
417function ArrayPush() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000418 var n = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000419 var m = %_ArgumentsLength();
420 for (var i = 0; i < m; i++) {
421 this[i+n] = %_Arguments(i);
422 }
423 this.length = n + m;
424 return this.length;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000425}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000426
427
428function ArrayConcat(arg1) { // length == 1
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000429 // TODO: can we just use arguments?
430 var arg_count = %_ArgumentsLength();
431 var arrays = new $Array(1 + arg_count);
432 arrays[0] = this;
433 for (var i = 0; i < arg_count; i++) {
434 arrays[i + 1] = %_Arguments(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000435 }
436
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000437 return %ArrayConcat(arrays);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000438}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000439
440
441// For implementing reverse() on large, sparse arrays.
442function SparseReverse(array, len) {
443 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
444 var high_counter = keys.length - 1;
445 var low_counter = 0;
446 while (low_counter <= high_counter) {
447 var i = keys[low_counter];
448 var j = keys[high_counter];
449
450 var j_complement = len - j - 1;
451 var low, high;
452
453 if (j_complement <= i) {
454 high = j;
455 while (keys[--high_counter] == j);
456 low = j_complement;
457 }
458 if (j_complement >= i) {
459 low = i;
460 while (keys[++low_counter] == i);
461 high = len - i - 1;
462 }
463
464 var current_i = array[low];
465 if (!IS_UNDEFINED(current_i) || low in array) {
466 var current_j = array[high];
467 if (!IS_UNDEFINED(current_j) || high in array) {
468 array[low] = current_j;
469 array[high] = current_i;
470 } else {
471 array[high] = current_i;
472 delete array[low];
473 }
474 } else {
475 var current_j = array[high];
476 if (!IS_UNDEFINED(current_j) || high in array) {
477 array[low] = current_j;
478 delete array[high];
479 }
480 }
481 }
482}
483
484
485function ArrayReverse() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000486 var j = TO_UINT32(this.length) - 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487
488 if (UseSparseVariant(this, j, IS_ARRAY(this))) {
489 SparseReverse(this, j+1);
490 return this;
491 }
492
493 for (var i = 0; i < j; i++, j--) {
494 var current_i = this[i];
495 if (!IS_UNDEFINED(current_i) || i in this) {
496 var current_j = this[j];
497 if (!IS_UNDEFINED(current_j) || j in this) {
498 this[i] = current_j;
499 this[j] = current_i;
500 } else {
501 this[j] = current_i;
502 delete this[i];
503 }
504 } else {
505 var current_j = this[j];
506 if (!IS_UNDEFINED(current_j) || j in this) {
507 this[i] = current_j;
508 delete this[j];
509 }
510 }
511 }
512 return this;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000513}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000514
515
516function ArrayShift() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000517 var len = TO_UINT32(this.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000518
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000519 if (len === 0) {
520 this.length = 0;
521 return;
522 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000523
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000524 var first = this[0];
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000525
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000526 if (IS_ARRAY(this))
527 SmartMove(this, 0, 1, len, 0);
528 else
529 SimpleMove(this, 0, 1, len, 0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000530
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531 this.length = len - 1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000532
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000533 return first;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000534}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000535
536
537function ArrayUnshift(arg1) { // length == 1
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000538 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000539 var num_arguments = %_ArgumentsLength();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000540
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000541 if (IS_ARRAY(this))
542 SmartMove(this, 0, 0, len, num_arguments);
543 else
544 SimpleMove(this, 0, 0, len, num_arguments);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000545
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000546 for (var i = 0; i < num_arguments; i++) {
547 this[i] = %_Arguments(i);
548 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000550 this.length = len + num_arguments;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000551
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000552 return len + num_arguments;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000553}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000554
555
556function ArraySlice(start, end) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000557 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558 var start_i = TO_INTEGER(start);
559 var end_i = len;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000560
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561 if (end !== void 0) end_i = TO_INTEGER(end);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000562
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000563 if (start_i < 0) {
564 start_i += len;
565 if (start_i < 0) start_i = 0;
566 } else {
567 if (start_i > len) start_i = len;
568 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000569
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000570 if (end_i < 0) {
571 end_i += len;
572 if (end_i < 0) end_i = 0;
573 } else {
574 if (end_i > len) end_i = len;
575 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000576
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000577 var result = [];
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000578
579 if (end_i < start_i) return result;
580
581 if (IS_ARRAY(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000582 SmartSlice(this, start_i, end_i - start_i, len, result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000583 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584 SimpleSlice(this, start_i, end_i - start_i, len, result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000585 }
586
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000587 result.length = end_i - start_i;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000588
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000589 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000590}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000591
592
593function ArraySplice(start, delete_count) {
594 var num_arguments = %_ArgumentsLength();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000595
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000596 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000597 var start_i = TO_INTEGER(start);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000598
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000599 if (start_i < 0) {
600 start_i += len;
601 if (start_i < 0) start_i = 0;
602 } else {
603 if (start_i > len) start_i = len;
604 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000605
ager@chromium.org5c838252010-02-19 08:53:10 +0000606 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000607 // given as a request to delete all the elements from the start.
608 // And it differs from the case of undefined delete count.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000609 // This does not follow ECMA-262, but we do the same for
610 // compatibility.
611 var del_count = 0;
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000612 if (num_arguments == 1) {
613 del_count = len - start_i;
614 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000615 del_count = TO_INTEGER(delete_count);
616 if (del_count < 0) del_count = 0;
617 if (del_count > len - start_i) del_count = len - start_i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000618 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000619
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000620 var deleted_elements = [];
621 deleted_elements.length = del_count;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000623 // Number of elements to add.
624 var num_additional_args = 0;
625 if (num_arguments > 2) {
626 num_additional_args = num_arguments - 2;
627 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000628
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000629 var use_simple_splice = true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000630
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000631 if (IS_ARRAY(this) && num_additional_args !== del_count) {
632 // If we are only deleting/moving a few things near the end of the
633 // array then the simple version is going to be faster, because it
634 // doesn't touch most of the array.
635 var estimated_non_hole_elements = %EstimateNumberOfElements(this);
636 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
637 use_simple_splice = false;
638 }
639 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000640
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641 if (use_simple_splice) {
642 SimpleSlice(this, start_i, del_count, len, deleted_elements);
643 SimpleMove(this, start_i, del_count, len, num_additional_args);
644 } else {
645 SmartSlice(this, start_i, del_count, len, deleted_elements);
646 SmartMove(this, start_i, del_count, len, num_additional_args);
647 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000648
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000649 // Insert the arguments into the resulting array in
650 // place of the deleted elements.
651 var i = start_i;
652 var arguments_index = 2;
653 var arguments_length = %_ArgumentsLength();
654 while (arguments_index < arguments_length) {
655 this[i++] = %_Arguments(arguments_index++);
656 }
657 this.length = len - del_count + num_additional_args;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000658
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000659 // Return the deleted elements.
660 return deleted_elements;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000661}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000662
663
664function ArraySort(comparefn) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000665 // In-place QuickSort algorithm.
666 // For short (length <= 22) arrays, insertion sort is used for efficiency.
667
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000668 if (!IS_FUNCTION(comparefn)) {
669 comparefn = function (x, y) {
670 if (x === y) return 0;
671 if (%_IsSmi(x) && %_IsSmi(y)) {
672 return %SmiLexicographicCompare(x, y);
ager@chromium.org357bf652010-04-12 11:30:10 +0000673 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000674 x = ToString(x);
675 y = ToString(y);
676 if (x == y) return 0;
677 else return x < y ? -1 : 1;
678 };
ager@chromium.org357bf652010-04-12 11:30:10 +0000679 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000680 var global_receiver = %GetGlobalReceiver();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000681
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000682 function InsertionSort(a, from, to) {
683 for (var i = from + 1; i < to; i++) {
684 var element = a[i];
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000685 for (var j = i - 1; j >= from; j--) {
686 var tmp = a[j];
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000687 var order = %_CallFunction(global_receiver, tmp, element, comparefn);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000688 if (order > 0) {
689 a[j + 1] = tmp;
690 } else {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000691 break;
692 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000693 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000694 a[j + 1] = element;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000695 }
696 }
697
698 function QuickSort(a, from, to) {
699 // Insertion sort is faster for short arrays.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000700 if (to - from <= 10) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000701 InsertionSort(a, from, to);
702 return;
703 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000704 // Find a pivot as the median of first, last and middle element.
705 var v0 = a[from];
706 var v1 = a[to - 1];
707 var middle_index = from + ((to - from) >> 1);
708 var v2 = a[middle_index];
709 var c01 = %_CallFunction(global_receiver, v0, v1, comparefn);
710 if (c01 > 0) {
711 // v1 < v0, so swap them.
712 var tmp = v0;
713 v0 = v1;
714 v1 = tmp;
715 } // v0 <= v1.
716 var c02 = %_CallFunction(global_receiver, v0, v2, comparefn);
717 if (c02 >= 0) {
718 // v2 <= v0 <= v1.
719 var tmp = v0;
720 v0 = v2;
721 v2 = v1;
722 v1 = tmp;
723 } else {
724 // v0 <= v1 && v0 < v2
725 var c12 = %_CallFunction(global_receiver, v1, v2, comparefn);
726 if (c12 > 0) {
727 // v0 <= v2 < v1
728 var tmp = v1;
729 v1 = v2;
730 v2 = tmp;
731 }
732 }
733 // v0 <= v1 <= v2
734 a[from] = v0;
735 a[to - 1] = v2;
736 var pivot = v1;
737 var low_end = from + 1; // Upper bound of elements lower than pivot.
738 var high_start = to - 1; // Lower bound of elements greater than pivot.
739 a[middle_index] = a[low_end];
740 a[low_end] = pivot;
741
ager@chromium.org381abbb2009-02-25 13:23:22 +0000742 // From low_end to i are elements equal to pivot.
743 // From i to high_start are elements that haven't been compared yet.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000744 partition: for (var i = low_end + 1; i < high_start; i++) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000745 var element = a[i];
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000746 var order = %_CallFunction(global_receiver, element, pivot, comparefn);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000747 if (order < 0) {
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000748 %_SwapElements(a, i, low_end);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000749 low_end++;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000750 } else if (order > 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000751 do {
752 high_start--;
753 if (high_start == i) break partition;
754 var top_elem = a[high_start];
755 order = %_CallFunction(global_receiver, top_elem, pivot, comparefn);
756 } while (order > 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000757 %_SwapElements(a, i, high_start);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000758 if (order < 0) {
759 %_SwapElements(a, i, low_end);
760 low_end++;
761 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000762 }
763 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000764 QuickSort(a, from, low_end);
765 QuickSort(a, high_start, to);
766 }
767
whesse@chromium.org023421e2010-12-21 12:19:12 +0000768 // Copy elements in the range 0..length from obj's prototype chain
769 // to obj itself, if obj has holes. Return one more than the maximal index
ager@chromium.org5ec48922009-05-05 07:25:34 +0000770 // of a prototype property.
771 function CopyFromPrototype(obj, length) {
772 var max = 0;
773 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
774 var indices = %GetArrayKeys(proto, length);
775 if (indices.length > 0) {
776 if (indices[0] == -1) {
777 // It's an interval.
778 var proto_length = indices[1];
779 for (var i = 0; i < proto_length; i++) {
780 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
781 obj[i] = proto[i];
782 if (i >= max) { max = i + 1; }
783 }
784 }
785 } else {
786 for (var i = 0; i < indices.length; i++) {
787 var index = indices[i];
788 if (!IS_UNDEFINED(index) &&
789 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
790 obj[index] = proto[index];
791 if (index >= max) { max = index + 1; }
792 }
793 }
794 }
795 }
796 }
797 return max;
798 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000799
ager@chromium.org5ec48922009-05-05 07:25:34 +0000800 // Set a value of "undefined" on all indices in the range from..to
801 // where a prototype of obj has an element. I.e., shadow all prototype
802 // elements in that range.
803 function ShadowPrototypeElements(obj, from, to) {
804 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
805 var indices = %GetArrayKeys(proto, to);
806 if (indices.length > 0) {
807 if (indices[0] == -1) {
808 // It's an interval.
809 var proto_length = indices[1];
810 for (var i = from; i < proto_length; i++) {
811 if (proto.hasOwnProperty(i)) {
812 obj[i] = void 0;
813 }
814 }
815 } else {
816 for (var i = 0; i < indices.length; i++) {
817 var index = indices[i];
818 if (!IS_UNDEFINED(index) && from <= index &&
819 proto.hasOwnProperty(index)) {
820 obj[index] = void 0;
821 }
822 }
823 }
824 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000825 }
826 }
827
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000828 function SafeRemoveArrayHoles(obj) {
829 // Copy defined elements from the end to fill in all holes and undefineds
830 // in the beginning of the array. Write undefineds and holes at the end
831 // after loop is finished.
832 var first_undefined = 0;
833 var last_defined = length - 1;
834 var num_holes = 0;
835 while (first_undefined < last_defined) {
836 // Find first undefined element.
837 while (first_undefined < last_defined &&
838 !IS_UNDEFINED(obj[first_undefined])) {
839 first_undefined++;
840 }
841 // Maintain the invariant num_holes = the number of holes in the original
842 // array with indices <= first_undefined or > last_defined.
843 if (!obj.hasOwnProperty(first_undefined)) {
844 num_holes++;
845 }
846
847 // Find last defined element.
848 while (first_undefined < last_defined &&
849 IS_UNDEFINED(obj[last_defined])) {
850 if (!obj.hasOwnProperty(last_defined)) {
851 num_holes++;
852 }
853 last_defined--;
854 }
855 if (first_undefined < last_defined) {
856 // Fill in hole or undefined.
857 obj[first_undefined] = obj[last_defined];
858 obj[last_defined] = void 0;
859 }
860 }
861 // If there were any undefineds in the entire array, first_undefined
862 // points to one past the last defined element. Make this true if
863 // there were no undefineds, as well, so that first_undefined == number
864 // of defined elements.
865 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
866 // Fill in the undefineds and the holes. There may be a hole where
867 // an undefined should be and vice versa.
868 var i;
869 for (i = first_undefined; i < length - num_holes; i++) {
870 obj[i] = void 0;
871 }
872 for (i = length - num_holes; i < length; i++) {
873 // For compatability with Webkit, do not expose elements in the prototype.
874 if (i in obj.__proto__) {
875 obj[i] = void 0;
876 } else {
877 delete obj[i];
878 }
879 }
880
881 // Return the number of defined elements.
882 return first_undefined;
883 }
884
ager@chromium.org357bf652010-04-12 11:30:10 +0000885 var length = TO_UINT32(this.length);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000886 if (length < 2) return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887
ager@chromium.org5ec48922009-05-05 07:25:34 +0000888 var is_array = IS_ARRAY(this);
889 var max_prototype_element;
890 if (!is_array) {
891 // For compatibility with JSC, we also sort elements inherited from
892 // the prototype chain on non-Array objects.
893 // We do this by copying them to this object and sorting only
894 // local elements. This is not very efficient, but sorting with
895 // inherited elements happens very, very rarely, if at all.
896 // The specification allows "implementation dependent" behavior
897 // if an element on the prototype chain has an element that
898 // might interact with sorting.
899 max_prototype_element = CopyFromPrototype(this, length);
900 }
901
902 var num_non_undefined = %RemoveArrayHoles(this, length);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000903 if (num_non_undefined == -1) {
904 // There were indexed accessors in the array. Move array holes and
905 // undefineds to the end using a Javascript function that is safe
906 // in the presence of accessors.
907 num_non_undefined = SafeRemoveArrayHoles(this);
908 }
ager@chromium.org5ec48922009-05-05 07:25:34 +0000909
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000910 QuickSort(this, 0, num_non_undefined);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000911
912 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
913 // For compatibility with JSC, we shadow any elements in the prototype
914 // chain that has become exposed by sort moving a hole to its position.
915 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000916 }
917
918 return this;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000919}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000920
921
922// The following functions cannot be made efficient on sparse arrays while
923// preserving the semantics, since the calls to the receiver function can add
924// or delete elements from the array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000925function ArrayFilter(f, receiver) {
926 if (!IS_FUNCTION(f)) {
927 throw MakeTypeError('called_non_callable', [ f ]);
928 }
929 // Pull out the length so that modifications to the length in the
930 // loop will not affect the looping.
931 var length = this.length;
932 var result = [];
ager@chromium.org381abbb2009-02-25 13:23:22 +0000933 var result_length = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000934 for (var i = 0; i < length; i++) {
935 var current = this[i];
936 if (!IS_UNDEFINED(current) || i in this) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000937 if (f.call(receiver, current, i, this)) result[result_length++] = current;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000938 }
939 }
940 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000941}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000942
943
944function ArrayForEach(f, receiver) {
945 if (!IS_FUNCTION(f)) {
946 throw MakeTypeError('called_non_callable', [ f ]);
947 }
948 // Pull out the length so that modifications to the length in the
949 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000950 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000951 for (var i = 0; i < length; i++) {
952 var current = this[i];
953 if (!IS_UNDEFINED(current) || i in this) {
954 f.call(receiver, current, i, this);
955 }
956 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000957}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000958
959
960// Executes the function once for each element present in the
961// array until it finds one where callback returns true.
962function ArraySome(f, receiver) {
963 if (!IS_FUNCTION(f)) {
964 throw MakeTypeError('called_non_callable', [ f ]);
965 }
966 // Pull out the length so that modifications to the length in the
967 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000968 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000969 for (var i = 0; i < length; i++) {
970 var current = this[i];
971 if (!IS_UNDEFINED(current) || i in this) {
972 if (f.call(receiver, current, i, this)) return true;
973 }
974 }
975 return false;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000976}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000977
978
979function ArrayEvery(f, receiver) {
980 if (!IS_FUNCTION(f)) {
981 throw MakeTypeError('called_non_callable', [ f ]);
982 }
983 // Pull out the length so that modifications to the length in the
984 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000985 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000986 for (var i = 0; i < length; i++) {
987 var current = this[i];
988 if (!IS_UNDEFINED(current) || i in this) {
989 if (!f.call(receiver, current, i, this)) return false;
990 }
991 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000992 return true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000993}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000994
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000995function ArrayMap(f, receiver) {
996 if (!IS_FUNCTION(f)) {
997 throw MakeTypeError('called_non_callable', [ f ]);
998 }
999 // Pull out the length so that modifications to the length in the
1000 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001001 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001002 var result = new $Array(length);
1003 for (var i = 0; i < length; i++) {
1004 var current = this[i];
1005 if (!IS_UNDEFINED(current) || i in this) {
1006 result[i] = f.call(receiver, current, i, this);
1007 }
1008 }
1009 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001010}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001011
1012
1013function ArrayIndexOf(element, index) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00001014 var length = TO_UINT32(this.length);
1015 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001016 if (IS_UNDEFINED(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001017 index = 0;
1018 } else {
1019 index = TO_INTEGER(index);
1020 // If index is negative, index from the end of the array.
ricow@chromium.org83aa5492011-02-07 12:42:56 +00001021 if (index < 0) {
1022 index = length + index;
1023 // If index is still negative, search the entire array.
1024 if (index < 0) index = 0;
1025 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001026 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001027 var min = index;
1028 var max = length;
1029 if (UseSparseVariant(this, length, true)) {
1030 var intervals = %GetArrayKeys(this, length);
1031 if (intervals.length == 2 && intervals[0] < 0) {
1032 // A single interval.
1033 var intervalMin = -(intervals[0] + 1);
1034 var intervalMax = intervalMin + intervals[1];
1035 min = MAX(min, intervalMin);
1036 max = intervalMax; // Capped by length already.
1037 // Fall through to loop below.
1038 } else {
1039 if (intervals.length == 0) return -1;
1040 // Get all the keys in sorted order.
1041 var sortedKeys = GetSortedArrayKeys(this, intervals);
1042 var n = sortedKeys.length;
1043 var i = 0;
1044 while (i < n && sortedKeys[i] < index) i++;
1045 while (i < n) {
1046 var key = sortedKeys[i];
1047 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1048 i++;
1049 }
1050 return -1;
1051 }
1052 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00001053 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001054 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001055 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001056 if (this[i] === element) return i;
1057 }
1058 return -1;
1059 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001060 // Lookup through the array.
1061 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001062 if (IS_UNDEFINED(this[i]) && i in this) {
1063 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001064 }
1065 }
1066 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001067}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001068
1069
1070function ArrayLastIndexOf(element, index) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00001071 var length = TO_UINT32(this.length);
1072 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001073 if (%_ArgumentsLength() < 2) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001074 index = length - 1;
1075 } else {
1076 index = TO_INTEGER(index);
1077 // If index is negative, index from end of the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001078 if (index < 0) index += length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001079 // If index is still negative, do not search the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001080 if (index < 0) return -1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001081 else if (index >= length) index = length - 1;
1082 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001083 var min = 0;
1084 var max = index;
1085 if (UseSparseVariant(this, length, true)) {
1086 var intervals = %GetArrayKeys(this, index + 1);
1087 if (intervals.length == 2 && intervals[0] < 0) {
1088 // A single interval.
1089 var intervalMin = -(intervals[0] + 1);
1090 var intervalMax = intervalMin + intervals[1];
1091 min = MAX(min, intervalMin);
1092 max = intervalMax; // Capped by index already.
1093 // Fall through to loop below.
1094 } else {
1095 if (intervals.length == 0) return -1;
1096 // Get all the keys in sorted order.
1097 var sortedKeys = GetSortedArrayKeys(this, intervals);
1098 var i = sortedKeys.length - 1;
1099 while (i >= 0) {
1100 var key = sortedKeys[i];
1101 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1102 i--;
1103 }
1104 return -1;
1105 }
1106 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001107 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001108 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001109 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001110 if (this[i] === element) return i;
1111 }
1112 return -1;
1113 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001114 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001115 if (IS_UNDEFINED(this[i]) && i in this) {
1116 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001117 }
1118 }
1119 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001120}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001121
1122
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001123function ArrayReduce(callback, current) {
1124 if (!IS_FUNCTION(callback)) {
1125 throw MakeTypeError('called_non_callable', [callback]);
1126 }
1127 // Pull out the length so that modifications to the length in the
1128 // loop will not affect the looping.
1129 var length = this.length;
1130 var i = 0;
1131
1132 find_initial: if (%_ArgumentsLength() < 2) {
1133 for (; i < length; i++) {
1134 current = this[i];
1135 if (!IS_UNDEFINED(current) || i in this) {
1136 i++;
1137 break find_initial;
1138 }
1139 }
1140 throw MakeTypeError('reduce_no_initial', []);
1141 }
1142
1143 for (; i < length; i++) {
1144 var element = this[i];
1145 if (!IS_UNDEFINED(element) || i in this) {
1146 current = callback.call(null, current, element, i, this);
1147 }
1148 }
1149 return current;
1150}
1151
1152function ArrayReduceRight(callback, current) {
1153 if (!IS_FUNCTION(callback)) {
1154 throw MakeTypeError('called_non_callable', [callback]);
1155 }
1156 var i = this.length - 1;
1157
1158 find_initial: if (%_ArgumentsLength() < 2) {
1159 for (; i >= 0; i--) {
1160 current = this[i];
1161 if (!IS_UNDEFINED(current) || i in this) {
1162 i--;
1163 break find_initial;
1164 }
1165 }
1166 throw MakeTypeError('reduce_no_initial', []);
1167 }
1168
1169 for (; i >= 0; i--) {
1170 var element = this[i];
1171 if (!IS_UNDEFINED(element) || i in this) {
1172 current = callback.call(null, current, element, i, this);
1173 }
1174 }
1175 return current;
1176}
1177
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001178// ES5, 15.4.3.2
1179function ArrayIsArray(obj) {
1180 return IS_ARRAY(obj);
1181}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001182
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001183
1184// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001185function SetupArray() {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001186 // Setup non-enumerable constructor property on the Array.prototype
1187 // object.
1188 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1189
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001190 // Setup non-enumerable functions on the Array object.
1191 InstallFunctions($Array, DONT_ENUM, $Array(
1192 "isArray", ArrayIsArray
1193 ));
1194
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001195 var specialFunctions = %SpecialArrayFunctions({});
1196
1197 function getFunction(name, jsBuiltin, len) {
1198 var f = jsBuiltin;
1199 if (specialFunctions.hasOwnProperty(name)) {
1200 f = specialFunctions[name];
1201 }
1202 if (!IS_UNDEFINED(len)) {
1203 %FunctionSetLength(f, len);
1204 }
1205 return f;
1206 }
1207
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001208 // Setup non-enumerable functions of the Array.prototype object and
1209 // set their names.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001210 // Manipulate the length of some of the functions to meet
1211 // expectations set by ECMA-262 or Mozilla.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001212 InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
1213 "toString", getFunction("toString", ArrayToString),
1214 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1215 "join", getFunction("join", ArrayJoin),
1216 "pop", getFunction("pop", ArrayPop),
1217 "push", getFunction("push", ArrayPush, 1),
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001218 "concat", getFunction("concat", ArrayConcat, 1),
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001219 "reverse", getFunction("reverse", ArrayReverse),
1220 "shift", getFunction("shift", ArrayShift),
1221 "unshift", getFunction("unshift", ArrayUnshift, 1),
1222 "slice", getFunction("slice", ArraySlice, 2),
1223 "splice", getFunction("splice", ArraySplice, 2),
1224 "sort", getFunction("sort", ArraySort),
1225 "filter", getFunction("filter", ArrayFilter, 1),
1226 "forEach", getFunction("forEach", ArrayForEach, 1),
1227 "some", getFunction("some", ArraySome, 1),
1228 "every", getFunction("every", ArrayEvery, 1),
1229 "map", getFunction("map", ArrayMap, 1),
1230 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1231 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1232 "reduce", getFunction("reduce", ArrayReduce, 1),
1233 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1234 ));
lrn@chromium.org25156de2010-04-06 13:10:27 +00001235
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001236 %FinishArrayPrototypeSetup($Array.prototype);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001237}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001238
1239
1240SetupArray();