blob: b2ebece9c19296bc62c6307ddfbacfa56be3d236 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 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];
120 if (!IS_UNDEFINED(e) || (0 in array)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000121 if (IS_STRING(e)) return e;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000122 return convert(e);
123 }
124 }
125
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000126 // Construct an array for the elements.
127 var elements;
128 var elements_length = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000129
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000130 // We pull the empty separator check outside the loop for speed!
131 if (separator.length == 0) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000132 elements = new $Array(length);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000133 for (var i = 0; i < length; i++) {
134 var e = array[i];
135 if (!IS_UNDEFINED(e) || (i in array)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000136 if (!IS_STRING(e)) e = convert(e);
137 elements[elements_length++] = e;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000138 }
139 }
140 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000141 elements = new $Array(length << 1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000142 for (var i = 0; i < length; i++) {
143 var e = array[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000144 if (i != 0) elements[elements_length++] = separator;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000145 if (!IS_UNDEFINED(e) || (i in array)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000146 if (!IS_STRING(e)) e = convert(e);
147 elements[elements_length++] = e;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000148 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000149 }
150 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000151 return %StringBuilderConcat(elements, elements_length, '');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000152 } finally {
153 // Make sure to pop the visited array no matter what happens.
154 if (is_array) visited_arrays.pop();
155 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000156}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000157
158
159function ConvertToString(e) {
160 if (e == null) return '';
161 else return ToString(e);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000162}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000163
164
165function ConvertToLocaleString(e) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000166 if (e == null) {
167 return '';
168 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000169 // e_obj's toLocaleString might be overwritten, check if it is a function.
170 // Call ToString if toLocaleString is not a function.
171 // See issue 877615.
172 var e_obj = ToObject(e);
173 if (IS_FUNCTION(e_obj.toLocaleString))
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000174 return ToString(e_obj.toLocaleString());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000175 else
176 return ToString(e);
177 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000178}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000179
180
181// This function implements the optimized splice implementation that can use
182// special array operations to handle sparse arrays in a sensible fashion.
183function SmartSlice(array, start_i, del_count, len, deleted_elements) {
184 // Move deleted elements to a new array (the return value from splice).
185 // Intervals array can contain keys and intervals. See comment in Concat.
186 var intervals = %GetArrayKeys(array, start_i + del_count);
187 var length = intervals.length;
188 for (var k = 0; k < length; k++) {
189 var key = intervals[k];
190 if (key < 0) {
191 var j = -1 - key;
192 var interval_limit = j + intervals[++k];
193 if (j < start_i) {
194 j = start_i;
195 }
196 for (; j < interval_limit; j++) {
197 // ECMA-262 15.4.4.12 line 10. The spec could also be
198 // interpreted such that %HasLocalProperty would be the
199 // appropriate test. We follow KJS in consulting the
200 // prototype.
201 var current = array[j];
202 if (!IS_UNDEFINED(current) || j in array) {
203 deleted_elements[j - start_i] = current;
204 }
205 }
206 } else {
207 if (!IS_UNDEFINED(key)) {
208 if (key >= start_i) {
209 // ECMA-262 15.4.4.12 line 10. The spec could also be
210 // interpreted such that %HasLocalProperty would be the
211 // appropriate test. We follow KJS in consulting the
212 // prototype.
213 var current = array[key];
214 if (!IS_UNDEFINED(current) || key in array) {
215 deleted_elements[key - start_i] = current;
216 }
217 }
218 }
219 }
220 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000221}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000222
223
224// This function implements the optimized splice implementation that can use
225// special array operations to handle sparse arrays in a sensible fashion.
226function SmartMove(array, start_i, del_count, len, num_additional_args) {
227 // Move data to new array.
228 var new_array = new $Array(len - del_count + num_additional_args);
229 var intervals = %GetArrayKeys(array, len);
230 var length = intervals.length;
231 for (var k = 0; k < length; k++) {
232 var key = intervals[k];
233 if (key < 0) {
234 var j = -1 - key;
235 var interval_limit = j + intervals[++k];
236 while (j < start_i && j < interval_limit) {
237 // The spec could also be interpreted such that
238 // %HasLocalProperty would be the appropriate test. We follow
239 // KJS in consulting the prototype.
240 var current = array[j];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000241 if (!IS_UNDEFINED(current) || j in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000242 new_array[j] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000243 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000244 j++;
245 }
246 j = start_i + del_count;
247 while (j < interval_limit) {
248 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also be
249 // interpreted such that %HasLocalProperty would be the
250 // appropriate test. We follow KJS in consulting the
251 // prototype.
252 var current = array[j];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000253 if (!IS_UNDEFINED(current) || j in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000254 new_array[j - del_count + num_additional_args] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000255 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000256 j++;
257 }
258 } else {
259 if (!IS_UNDEFINED(key)) {
260 if (key < start_i) {
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[key];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000265 if (!IS_UNDEFINED(current) || key in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266 new_array[key] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000267 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000268 } else if (key >= start_i + del_count) {
269 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also
270 // be interpreted such that %HasLocalProperty would be the
271 // appropriate test. We follow KJS in consulting the
272 // prototype.
273 var current = array[key];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000274 if (!IS_UNDEFINED(current) || key in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000275 new_array[key - del_count + num_additional_args] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000276 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 }
278 }
279 }
280 }
281 // Move contents of new_array into this array
282 %MoveArrayContents(new_array, array);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000283}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284
285
286// This is part of the old simple-minded splice. We are using it either
287// because the receiver is not an array (so we have no choice) or because we
288// know we are not deleting or moving a lot of elements.
289function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
290 for (var i = 0; i < del_count; i++) {
291 var index = start_i + i;
292 // The spec could also be interpreted such that %HasLocalProperty
293 // would be the appropriate test. We follow KJS in consulting the
294 // prototype.
295 var current = array[index];
296 if (!IS_UNDEFINED(current) || index in array)
297 deleted_elements[i] = current;
298 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000299}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000300
301
302function SimpleMove(array, start_i, del_count, len, num_additional_args) {
303 if (num_additional_args !== del_count) {
304 // Move the existing elements after the elements to be deleted
305 // to the right position in the resulting array.
306 if (num_additional_args > del_count) {
307 for (var i = len - del_count; i > start_i; i--) {
308 var from_index = i + del_count - 1;
309 var to_index = i + num_additional_args - 1;
310 // The spec could also be interpreted such that
311 // %HasLocalProperty would be the appropriate test. We follow
312 // KJS in consulting the prototype.
313 var current = array[from_index];
314 if (!IS_UNDEFINED(current) || from_index in array) {
315 array[to_index] = current;
316 } else {
317 delete array[to_index];
318 }
319 }
320 } else {
321 for (var i = start_i; i < len - del_count; i++) {
322 var from_index = i + del_count;
323 var to_index = i + num_additional_args;
324 // The spec could also be interpreted such that
325 // %HasLocalProperty would be the appropriate test. We follow
326 // KJS in consulting the prototype.
327 var current = array[from_index];
328 if (!IS_UNDEFINED(current) || from_index in array) {
329 array[to_index] = current;
330 } else {
331 delete array[to_index];
332 }
333 }
334 for (var i = len; i > len - del_count + num_additional_args; i--) {
335 delete array[i - 1];
336 }
337 }
338 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000339}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340
341
342// -------------------------------------------------------------------
343
344
345function ArrayToString() {
346 if (!IS_ARRAY(this)) {
347 throw new $TypeError('Array.prototype.toString is not generic');
348 }
349 return Join(this, this.length, ',', ConvertToString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000350}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000351
352
353function ArrayToLocaleString() {
354 if (!IS_ARRAY(this)) {
355 throw new $TypeError('Array.prototype.toString is not generic');
356 }
357 return Join(this, this.length, ',', ConvertToLocaleString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000358}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000359
360
361function ArrayJoin(separator) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000362 if (IS_UNDEFINED(separator)) {
363 separator = ',';
364 } else if (!IS_STRING(separator)) {
365 separator = ToString(separator);
366 }
367 var length = TO_UINT32(this.length);
368 return Join(this, length, separator, ConvertToString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000369}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000370
371
372// Removes the last element from the array and returns it. See
373// ECMA-262, section 15.4.4.6.
374function ArrayPop() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000375 var n = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376 if (n == 0) {
377 this.length = n;
378 return;
379 }
380 n--;
381 var value = this[n];
382 this.length = n;
383 delete this[n];
384 return value;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000385}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000386
387
388// Appends the arguments to the end of the array and returns the new
389// length of the array. See ECMA-262, section 15.4.4.7.
390function ArrayPush() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000391 var n = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000392 var m = %_ArgumentsLength();
393 for (var i = 0; i < m; i++) {
394 this[i+n] = %_Arguments(i);
395 }
396 this.length = n + m;
397 return this.length;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000398}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399
400
401function ArrayConcat(arg1) { // length == 1
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000402 // TODO: can we just use arguments?
403 var arg_count = %_ArgumentsLength();
404 var arrays = new $Array(1 + arg_count);
405 arrays[0] = this;
406 for (var i = 0; i < arg_count; i++) {
407 arrays[i + 1] = %_Arguments(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408 }
409
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000410 return %ArrayConcat(arrays);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000411}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000412
413
414// For implementing reverse() on large, sparse arrays.
415function SparseReverse(array, len) {
416 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
417 var high_counter = keys.length - 1;
418 var low_counter = 0;
419 while (low_counter <= high_counter) {
420 var i = keys[low_counter];
421 var j = keys[high_counter];
422
423 var j_complement = len - j - 1;
424 var low, high;
425
426 if (j_complement <= i) {
427 high = j;
428 while (keys[--high_counter] == j);
429 low = j_complement;
430 }
431 if (j_complement >= i) {
432 low = i;
433 while (keys[++low_counter] == i);
434 high = len - i - 1;
435 }
436
437 var current_i = array[low];
438 if (!IS_UNDEFINED(current_i) || low in array) {
439 var current_j = array[high];
440 if (!IS_UNDEFINED(current_j) || high in array) {
441 array[low] = current_j;
442 array[high] = current_i;
443 } else {
444 array[high] = current_i;
445 delete array[low];
446 }
447 } else {
448 var current_j = array[high];
449 if (!IS_UNDEFINED(current_j) || high in array) {
450 array[low] = current_j;
451 delete array[high];
452 }
453 }
454 }
455}
456
457
458function ArrayReverse() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000459 var j = TO_UINT32(this.length) - 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000460
461 if (UseSparseVariant(this, j, IS_ARRAY(this))) {
462 SparseReverse(this, j+1);
463 return this;
464 }
465
466 for (var i = 0; i < j; i++, j--) {
467 var current_i = this[i];
468 if (!IS_UNDEFINED(current_i) || i in this) {
469 var current_j = this[j];
470 if (!IS_UNDEFINED(current_j) || j in this) {
471 this[i] = current_j;
472 this[j] = current_i;
473 } else {
474 this[j] = current_i;
475 delete this[i];
476 }
477 } else {
478 var current_j = this[j];
479 if (!IS_UNDEFINED(current_j) || j in this) {
480 this[i] = current_j;
481 delete this[j];
482 }
483 }
484 }
485 return this;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000486}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487
488
489function ArrayShift() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000490 var len = TO_UINT32(this.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000491
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000492 if (len === 0) {
493 this.length = 0;
494 return;
495 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497 var first = this[0];
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000499 if (IS_ARRAY(this))
500 SmartMove(this, 0, 1, len, 0);
501 else
502 SimpleMove(this, 0, 1, len, 0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000503
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000504 this.length = len - 1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000505
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000506 return first;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000507}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000508
509
510function ArrayUnshift(arg1) { // length == 1
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000511 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000512 var num_arguments = %_ArgumentsLength();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000513
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000514 if (IS_ARRAY(this))
515 SmartMove(this, 0, 0, len, num_arguments);
516 else
517 SimpleMove(this, 0, 0, len, num_arguments);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000518
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000519 for (var i = 0; i < num_arguments; i++) {
520 this[i] = %_Arguments(i);
521 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000522
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000523 this.length = len + num_arguments;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000524
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525 return len + num_arguments;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000526}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000527
528
529function ArraySlice(start, end) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000530 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531 var start_i = TO_INTEGER(start);
532 var end_i = len;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000533
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000534 if (end !== void 0) end_i = TO_INTEGER(end);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000535
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000536 if (start_i < 0) {
537 start_i += len;
538 if (start_i < 0) start_i = 0;
539 } else {
540 if (start_i > len) start_i = len;
541 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000542
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000543 if (end_i < 0) {
544 end_i += len;
545 if (end_i < 0) end_i = 0;
546 } else {
547 if (end_i > len) end_i = len;
548 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000550 var result = [];
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000551
552 if (end_i < start_i) return result;
553
554 if (IS_ARRAY(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555 SmartSlice(this, start_i, end_i - start_i, len, result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000556 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000557 SimpleSlice(this, start_i, end_i - start_i, len, result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000558 }
559
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000560 result.length = end_i - start_i;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000561
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000562 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000563}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000564
565
566function ArraySplice(start, delete_count) {
567 var num_arguments = %_ArgumentsLength();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000568
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000569 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000570 var start_i = TO_INTEGER(start);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000571
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000572 if (start_i < 0) {
573 start_i += len;
574 if (start_i < 0) start_i = 0;
575 } else {
576 if (start_i > len) start_i = len;
577 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000578
ager@chromium.org5c838252010-02-19 08:53:10 +0000579 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000580 // given differently from when an undefined delete count is given.
581 // This does not follow ECMA-262, but we do the same for
582 // compatibility.
583 var del_count = 0;
584 if (num_arguments > 1) {
585 del_count = TO_INTEGER(delete_count);
586 if (del_count < 0) del_count = 0;
587 if (del_count > len - start_i) del_count = len - start_i;
588 } else {
589 del_count = len - start_i;
590 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000592 var deleted_elements = [];
593 deleted_elements.length = del_count;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000594
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000595 // Number of elements to add.
596 var num_additional_args = 0;
597 if (num_arguments > 2) {
598 num_additional_args = num_arguments - 2;
599 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000600
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000601 var use_simple_splice = true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000603 if (IS_ARRAY(this) && num_additional_args !== del_count) {
604 // If we are only deleting/moving a few things near the end of the
605 // array then the simple version is going to be faster, because it
606 // doesn't touch most of the array.
607 var estimated_non_hole_elements = %EstimateNumberOfElements(this);
608 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
609 use_simple_splice = false;
610 }
611 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000612
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000613 if (use_simple_splice) {
614 SimpleSlice(this, start_i, del_count, len, deleted_elements);
615 SimpleMove(this, start_i, del_count, len, num_additional_args);
616 } else {
617 SmartSlice(this, start_i, del_count, len, deleted_elements);
618 SmartMove(this, start_i, del_count, len, num_additional_args);
619 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000620
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000621 // Insert the arguments into the resulting array in
622 // place of the deleted elements.
623 var i = start_i;
624 var arguments_index = 2;
625 var arguments_length = %_ArgumentsLength();
626 while (arguments_index < arguments_length) {
627 this[i++] = %_Arguments(arguments_index++);
628 }
629 this.length = len - del_count + num_additional_args;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000630
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000631 // Return the deleted elements.
632 return deleted_elements;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000633}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634
635
636function ArraySort(comparefn) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000637 // In-place QuickSort algorithm.
638 // For short (length <= 22) arrays, insertion sort is used for efficiency.
639
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000640 if (!IS_FUNCTION(comparefn)) {
641 comparefn = function (x, y) {
642 if (x === y) return 0;
643 if (%_IsSmi(x) && %_IsSmi(y)) {
644 return %SmiLexicographicCompare(x, y);
ager@chromium.org357bf652010-04-12 11:30:10 +0000645 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000646 x = ToString(x);
647 y = ToString(y);
648 if (x == y) return 0;
649 else return x < y ? -1 : 1;
650 };
ager@chromium.org357bf652010-04-12 11:30:10 +0000651 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000652 var global_receiver = %GetGlobalReceiver();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000653
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000654 function InsertionSort(a, from, to) {
655 for (var i = from + 1; i < to; i++) {
656 var element = a[i];
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000657 for (var j = i - 1; j >= from; j--) {
658 var tmp = a[j];
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000659 var order = %_CallFunction(global_receiver, tmp, element, comparefn);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000660 if (order > 0) {
661 a[j + 1] = tmp;
662 } else {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000663 break;
664 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000665 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000666 a[j + 1] = element;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000667 }
668 }
669
670 function QuickSort(a, from, to) {
671 // Insertion sort is faster for short arrays.
ager@chromium.org381abbb2009-02-25 13:23:22 +0000672 if (to - from <= 22) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000673 InsertionSort(a, from, to);
674 return;
675 }
676 var pivot_index = $floor($random() * (to - from)) + from;
677 var pivot = a[pivot_index];
678 // Issue 95: Keep the pivot element out of the comparisons to avoid
679 // infinite recursion if comparefn(pivot, pivot) != 0.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000680 %_SwapElements(a, from, pivot_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000681 var low_end = from; // Upper bound of the elements lower than pivot.
ager@chromium.org381abbb2009-02-25 13:23:22 +0000682 var high_start = to; // Lower bound of the elements greater than pivot.
683 // From low_end to i are elements equal to pivot.
684 // From i to high_start are elements that haven't been compared yet.
685 for (var i = from + 1; i < high_start; ) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000686 var element = a[i];
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000687 var order = %_CallFunction(global_receiver, element, pivot, comparefn);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000688 if (order < 0) {
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000689 %_SwapElements(a, i, low_end);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000690 i++;
ager@chromium.org381abbb2009-02-25 13:23:22 +0000691 low_end++;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000692 } else if (order > 0) {
693 high_start--;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000694 %_SwapElements(a, i, high_start);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000695 } else { // order == 0
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000696 i++;
697 }
698 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000699 QuickSort(a, from, low_end);
700 QuickSort(a, high_start, to);
701 }
702
ager@chromium.org5ec48922009-05-05 07:25:34 +0000703 // Copies elements in the range 0..length from obj's prototype chain
704 // to obj itself, if obj has holes. Returns one more than the maximal index
705 // of a prototype property.
706 function CopyFromPrototype(obj, length) {
707 var max = 0;
708 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
709 var indices = %GetArrayKeys(proto, length);
710 if (indices.length > 0) {
711 if (indices[0] == -1) {
712 // It's an interval.
713 var proto_length = indices[1];
714 for (var i = 0; i < proto_length; i++) {
715 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
716 obj[i] = proto[i];
717 if (i >= max) { max = i + 1; }
718 }
719 }
720 } else {
721 for (var i = 0; i < indices.length; i++) {
722 var index = indices[i];
723 if (!IS_UNDEFINED(index) &&
724 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
725 obj[index] = proto[index];
726 if (index >= max) { max = index + 1; }
727 }
728 }
729 }
730 }
731 }
732 return max;
733 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000734
ager@chromium.org5ec48922009-05-05 07:25:34 +0000735 // Set a value of "undefined" on all indices in the range from..to
736 // where a prototype of obj has an element. I.e., shadow all prototype
737 // elements in that range.
738 function ShadowPrototypeElements(obj, from, to) {
739 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
740 var indices = %GetArrayKeys(proto, to);
741 if (indices.length > 0) {
742 if (indices[0] == -1) {
743 // It's an interval.
744 var proto_length = indices[1];
745 for (var i = from; i < proto_length; i++) {
746 if (proto.hasOwnProperty(i)) {
747 obj[i] = void 0;
748 }
749 }
750 } else {
751 for (var i = 0; i < indices.length; i++) {
752 var index = indices[i];
753 if (!IS_UNDEFINED(index) && from <= index &&
754 proto.hasOwnProperty(index)) {
755 obj[index] = void 0;
756 }
757 }
758 }
759 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000760 }
761 }
762
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000763 function SafeRemoveArrayHoles(obj) {
764 // Copy defined elements from the end to fill in all holes and undefineds
765 // in the beginning of the array. Write undefineds and holes at the end
766 // after loop is finished.
767 var first_undefined = 0;
768 var last_defined = length - 1;
769 var num_holes = 0;
770 while (first_undefined < last_defined) {
771 // Find first undefined element.
772 while (first_undefined < last_defined &&
773 !IS_UNDEFINED(obj[first_undefined])) {
774 first_undefined++;
775 }
776 // Maintain the invariant num_holes = the number of holes in the original
777 // array with indices <= first_undefined or > last_defined.
778 if (!obj.hasOwnProperty(first_undefined)) {
779 num_holes++;
780 }
781
782 // Find last defined element.
783 while (first_undefined < last_defined &&
784 IS_UNDEFINED(obj[last_defined])) {
785 if (!obj.hasOwnProperty(last_defined)) {
786 num_holes++;
787 }
788 last_defined--;
789 }
790 if (first_undefined < last_defined) {
791 // Fill in hole or undefined.
792 obj[first_undefined] = obj[last_defined];
793 obj[last_defined] = void 0;
794 }
795 }
796 // If there were any undefineds in the entire array, first_undefined
797 // points to one past the last defined element. Make this true if
798 // there were no undefineds, as well, so that first_undefined == number
799 // of defined elements.
800 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
801 // Fill in the undefineds and the holes. There may be a hole where
802 // an undefined should be and vice versa.
803 var i;
804 for (i = first_undefined; i < length - num_holes; i++) {
805 obj[i] = void 0;
806 }
807 for (i = length - num_holes; i < length; i++) {
808 // For compatability with Webkit, do not expose elements in the prototype.
809 if (i in obj.__proto__) {
810 obj[i] = void 0;
811 } else {
812 delete obj[i];
813 }
814 }
815
816 // Return the number of defined elements.
817 return first_undefined;
818 }
819
ager@chromium.org357bf652010-04-12 11:30:10 +0000820 var length = TO_UINT32(this.length);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000821 if (length < 2) return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000822
ager@chromium.org5ec48922009-05-05 07:25:34 +0000823 var is_array = IS_ARRAY(this);
824 var max_prototype_element;
825 if (!is_array) {
826 // For compatibility with JSC, we also sort elements inherited from
827 // the prototype chain on non-Array objects.
828 // We do this by copying them to this object and sorting only
829 // local elements. This is not very efficient, but sorting with
830 // inherited elements happens very, very rarely, if at all.
831 // The specification allows "implementation dependent" behavior
832 // if an element on the prototype chain has an element that
833 // might interact with sorting.
834 max_prototype_element = CopyFromPrototype(this, length);
835 }
836
837 var num_non_undefined = %RemoveArrayHoles(this, length);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000838 if (num_non_undefined == -1) {
839 // There were indexed accessors in the array. Move array holes and
840 // undefineds to the end using a Javascript function that is safe
841 // in the presence of accessors.
842 num_non_undefined = SafeRemoveArrayHoles(this);
843 }
ager@chromium.org5ec48922009-05-05 07:25:34 +0000844
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000845 QuickSort(this, 0, num_non_undefined);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000846
847 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
848 // For compatibility with JSC, we shadow any elements in the prototype
849 // chain that has become exposed by sort moving a hole to its position.
850 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000851 }
852
853 return this;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000854}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000855
856
857// The following functions cannot be made efficient on sparse arrays while
858// preserving the semantics, since the calls to the receiver function can add
859// or delete elements from the array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860function ArrayFilter(f, receiver) {
861 if (!IS_FUNCTION(f)) {
862 throw MakeTypeError('called_non_callable', [ f ]);
863 }
864 // Pull out the length so that modifications to the length in the
865 // loop will not affect the looping.
866 var length = this.length;
867 var result = [];
ager@chromium.org381abbb2009-02-25 13:23:22 +0000868 var result_length = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000869 for (var i = 0; i < length; i++) {
870 var current = this[i];
871 if (!IS_UNDEFINED(current) || i in this) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000872 if (f.call(receiver, current, i, this)) result[result_length++] = current;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000873 }
874 }
875 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000876}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000877
878
879function ArrayForEach(f, receiver) {
880 if (!IS_FUNCTION(f)) {
881 throw MakeTypeError('called_non_callable', [ f ]);
882 }
883 // Pull out the length so that modifications to the length in the
884 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000885 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000886 for (var i = 0; i < length; i++) {
887 var current = this[i];
888 if (!IS_UNDEFINED(current) || i in this) {
889 f.call(receiver, current, i, this);
890 }
891 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000892}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000893
894
895// Executes the function once for each element present in the
896// array until it finds one where callback returns true.
897function ArraySome(f, receiver) {
898 if (!IS_FUNCTION(f)) {
899 throw MakeTypeError('called_non_callable', [ f ]);
900 }
901 // Pull out the length so that modifications to the length in the
902 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000903 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000904 for (var i = 0; i < length; i++) {
905 var current = this[i];
906 if (!IS_UNDEFINED(current) || i in this) {
907 if (f.call(receiver, current, i, this)) return true;
908 }
909 }
910 return false;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000911}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000912
913
914function ArrayEvery(f, receiver) {
915 if (!IS_FUNCTION(f)) {
916 throw MakeTypeError('called_non_callable', [ f ]);
917 }
918 // Pull out the length so that modifications to the length in the
919 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000920 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000921 for (var i = 0; i < length; i++) {
922 var current = this[i];
923 if (!IS_UNDEFINED(current) || i in this) {
924 if (!f.call(receiver, current, i, this)) return false;
925 }
926 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000927 return true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000928}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000929
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000930function ArrayMap(f, receiver) {
931 if (!IS_FUNCTION(f)) {
932 throw MakeTypeError('called_non_callable', [ f ]);
933 }
934 // Pull out the length so that modifications to the length in the
935 // loop will not affect the looping.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000936 var length = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000937 var result = new $Array(length);
938 for (var i = 0; i < length; i++) {
939 var current = this[i];
940 if (!IS_UNDEFINED(current) || i in this) {
941 result[i] = f.call(receiver, current, i, this);
942 }
943 }
944 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000945}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000946
947
948function ArrayIndexOf(element, index) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000949 var length = TO_UINT32(this.length);
950 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000951 if (IS_UNDEFINED(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000952 index = 0;
953 } else {
954 index = TO_INTEGER(index);
955 // If index is negative, index from the end of the array.
956 if (index < 0) index = length + index;
957 // If index is still negative, search the entire array.
958 if (index < 0) index = 0;
959 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000960 var min = index;
961 var max = length;
962 if (UseSparseVariant(this, length, true)) {
963 var intervals = %GetArrayKeys(this, length);
964 if (intervals.length == 2 && intervals[0] < 0) {
965 // A single interval.
966 var intervalMin = -(intervals[0] + 1);
967 var intervalMax = intervalMin + intervals[1];
968 min = MAX(min, intervalMin);
969 max = intervalMax; // Capped by length already.
970 // Fall through to loop below.
971 } else {
972 if (intervals.length == 0) return -1;
973 // Get all the keys in sorted order.
974 var sortedKeys = GetSortedArrayKeys(this, intervals);
975 var n = sortedKeys.length;
976 var i = 0;
977 while (i < n && sortedKeys[i] < index) i++;
978 while (i < n) {
979 var key = sortedKeys[i];
980 if (!IS_UNDEFINED(key) && this[key] === element) return key;
981 i++;
982 }
983 return -1;
984 }
985 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000986 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000987 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000988 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000989 if (this[i] === element) return i;
990 }
991 return -1;
992 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000993 // Lookup through the array.
994 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000995 if (IS_UNDEFINED(this[i]) && i in this) {
996 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000997 }
998 }
999 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001000}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001
1002
1003function ArrayLastIndexOf(element, index) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00001004 var length = TO_UINT32(this.length);
1005 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001006 if (%_ArgumentsLength() < 2) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007 index = length - 1;
1008 } else {
1009 index = TO_INTEGER(index);
1010 // If index is negative, index from end of the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001011 if (index < 0) index += length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001012 // If index is still negative, do not search the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001013 if (index < 0) return -1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001014 else if (index >= length) index = length - 1;
1015 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001016 var min = 0;
1017 var max = index;
1018 if (UseSparseVariant(this, length, true)) {
1019 var intervals = %GetArrayKeys(this, index + 1);
1020 if (intervals.length == 2 && intervals[0] < 0) {
1021 // A single interval.
1022 var intervalMin = -(intervals[0] + 1);
1023 var intervalMax = intervalMin + intervals[1];
1024 min = MAX(min, intervalMin);
1025 max = intervalMax; // Capped by index already.
1026 // Fall through to loop below.
1027 } else {
1028 if (intervals.length == 0) return -1;
1029 // Get all the keys in sorted order.
1030 var sortedKeys = GetSortedArrayKeys(this, intervals);
1031 var i = sortedKeys.length - 1;
1032 while (i >= 0) {
1033 var key = sortedKeys[i];
1034 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1035 i--;
1036 }
1037 return -1;
1038 }
1039 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001041 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001042 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001043 if (this[i] === element) return i;
1044 }
1045 return -1;
1046 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001047 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001048 if (IS_UNDEFINED(this[i]) && i in this) {
1049 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001050 }
1051 }
1052 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001053}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001054
1055
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001056function ArrayReduce(callback, current) {
1057 if (!IS_FUNCTION(callback)) {
1058 throw MakeTypeError('called_non_callable', [callback]);
1059 }
1060 // Pull out the length so that modifications to the length in the
1061 // loop will not affect the looping.
1062 var length = this.length;
1063 var i = 0;
1064
1065 find_initial: if (%_ArgumentsLength() < 2) {
1066 for (; i < length; i++) {
1067 current = this[i];
1068 if (!IS_UNDEFINED(current) || i in this) {
1069 i++;
1070 break find_initial;
1071 }
1072 }
1073 throw MakeTypeError('reduce_no_initial', []);
1074 }
1075
1076 for (; i < length; i++) {
1077 var element = this[i];
1078 if (!IS_UNDEFINED(element) || i in this) {
1079 current = callback.call(null, current, element, i, this);
1080 }
1081 }
1082 return current;
1083}
1084
1085function ArrayReduceRight(callback, current) {
1086 if (!IS_FUNCTION(callback)) {
1087 throw MakeTypeError('called_non_callable', [callback]);
1088 }
1089 var i = this.length - 1;
1090
1091 find_initial: if (%_ArgumentsLength() < 2) {
1092 for (; i >= 0; i--) {
1093 current = this[i];
1094 if (!IS_UNDEFINED(current) || i in this) {
1095 i--;
1096 break find_initial;
1097 }
1098 }
1099 throw MakeTypeError('reduce_no_initial', []);
1100 }
1101
1102 for (; i >= 0; i--) {
1103 var element = this[i];
1104 if (!IS_UNDEFINED(element) || i in this) {
1105 current = callback.call(null, current, element, i, this);
1106 }
1107 }
1108 return current;
1109}
1110
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001111// ES5, 15.4.3.2
1112function ArrayIsArray(obj) {
1113 return IS_ARRAY(obj);
1114}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001115
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001116
1117// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001118function SetupArray() {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001119 // Setup non-enumerable constructor property on the Array.prototype
1120 // object.
1121 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1122
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001123 // Setup non-enumerable functions on the Array object.
1124 InstallFunctions($Array, DONT_ENUM, $Array(
1125 "isArray", ArrayIsArray
1126 ));
1127
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001128 var specialFunctions = %SpecialArrayFunctions({});
1129
1130 function getFunction(name, jsBuiltin, len) {
1131 var f = jsBuiltin;
1132 if (specialFunctions.hasOwnProperty(name)) {
1133 f = specialFunctions[name];
1134 }
1135 if (!IS_UNDEFINED(len)) {
1136 %FunctionSetLength(f, len);
1137 }
1138 return f;
1139 }
1140
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001141 // Setup non-enumerable functions of the Array.prototype object and
1142 // set their names.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001143 // Manipulate the length of some of the functions to meet
1144 // expectations set by ECMA-262 or Mozilla.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001145 InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
1146 "toString", getFunction("toString", ArrayToString),
1147 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1148 "join", getFunction("join", ArrayJoin),
1149 "pop", getFunction("pop", ArrayPop),
1150 "push", getFunction("push", ArrayPush, 1),
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001151 "concat", getFunction("concat", ArrayConcat, 1),
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001152 "reverse", getFunction("reverse", ArrayReverse),
1153 "shift", getFunction("shift", ArrayShift),
1154 "unshift", getFunction("unshift", ArrayUnshift, 1),
1155 "slice", getFunction("slice", ArraySlice, 2),
1156 "splice", getFunction("splice", ArraySplice, 2),
1157 "sort", getFunction("sort", ArraySort),
1158 "filter", getFunction("filter", ArrayFilter, 1),
1159 "forEach", getFunction("forEach", ArrayForEach, 1),
1160 "some", getFunction("some", ArraySome, 1),
1161 "every", getFunction("every", ArrayEvery, 1),
1162 "map", getFunction("map", ArrayMap, 1),
1163 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1164 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1165 "reduce", getFunction("reduce", ArrayReduce, 1),
1166 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1167 ));
lrn@chromium.org25156de2010-04-06 13:10:27 +00001168
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001169 %FinishArrayPrototypeSetup($Array.prototype);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001170}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171
1172
1173SetupArray();