blob: 1cedd8d4761189d4e0b4dd0f77c5fd21859fd1e9 [file] [log] [blame]
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001// Copyright 2012 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:
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +000030// var $Array = global.Array;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000031
32// -------------------------------------------------------------------
33
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000034// Global list of arrays visited during toString, toLocaleString and
35// join invocations.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +000036var visited_arrays = new InternalArray();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037
38
39// Gets a sorted array of array keys. Useful for operations on sparse
40// arrays. Dupes have not been removed.
41function GetSortedArrayKeys(array, intervals) {
42 var length = intervals.length;
43 var keys = [];
44 for (var k = 0; k < length; k++) {
45 var key = intervals[k];
46 if (key < 0) {
47 var j = -1 - key;
48 var limit = j + intervals[++k];
49 for (; j < limit; j++) {
50 var e = array[j];
51 if (!IS_UNDEFINED(e) || j in array) {
52 keys.push(j);
53 }
54 }
55 } else {
56 // The case where key is undefined also ends here.
57 if (!IS_UNDEFINED(key)) {
58 var e = array[key];
59 if (!IS_UNDEFINED(e) || key in array) {
60 keys.push(key);
61 }
62 }
63 }
64 }
65 keys.sort(function(a, b) { return a - b; });
66 return keys;
67}
68
69
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +000070function SparseJoinWithSeparator(array, len, convert, separator) {
71 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
72 var totalLength = 0;
73 var elements = new InternalArray(keys.length * 2);
74 var previousKey = -1;
75 for (var i = 0; i < keys.length; i++) {
76 var key = keys[i];
77 if (key != previousKey) { // keys may contain duplicates.
78 var e = array[key];
79 if (!IS_STRING(e)) e = convert(e);
80 elements[i * 2] = key;
81 elements[i * 2 + 1] = e;
82 previousKey = key;
83 }
84 }
85 return %SparseJoinWithSeparator(elements, len, separator);
86}
87
88
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000089// Optimized for sparse arrays if separator is ''.
90function SparseJoin(array, len, convert) {
91 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000092 var last_key = -1;
93 var keys_length = keys.length;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000094
karlklose@chromium.org8f806e82011-03-07 14:06:08 +000095 var elements = new InternalArray(keys_length);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000096 var elements_length = 0;
97
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098 for (var i = 0; i < keys_length; i++) {
99 var key = keys[i];
100 if (key != last_key) {
101 var e = array[key];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000102 if (!IS_STRING(e)) e = convert(e);
103 elements[elements_length++] = e;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000104 last_key = key;
105 }
106 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000107 return %StringBuilderConcat(elements, elements_length, '');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000108}
109
110
111function UseSparseVariant(object, length, is_array) {
112 return is_array &&
113 length > 1000 &&
114 (!%_IsSmi(length) ||
115 %EstimateNumberOfElements(object) < (length >> 2));
116}
117
118
119function Join(array, length, separator, convert) {
120 if (length == 0) return '';
121
122 var is_array = IS_ARRAY(array);
123
124 if (is_array) {
125 // If the array is cyclic, return the empty string for already
126 // visited arrays.
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000127 if (!%PushIfAbsent(visited_arrays, array)) return '';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000128 }
129
130 // Attempt to convert the elements.
131 try {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000132 if (UseSparseVariant(array, length, is_array)) {
133 if (separator.length == 0) {
134 return SparseJoin(array, length, convert);
135 } else {
136 return SparseJoinWithSeparator(array, length, convert, separator);
137 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000138 }
139
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000140 // Fast case for one-element arrays.
141 if (length == 1) {
142 var e = array[0];
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000143 if (IS_STRING(e)) return e;
144 return convert(e);
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000145 }
146
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000147 // Construct an array for the elements.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000148 var elements = new InternalArray(length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000149
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000150 // We pull the empty separator check outside the loop for speed!
151 if (separator.length == 0) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000152 var elements_length = 0;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000153 for (var i = 0; i < length; i++) {
154 var e = array[i];
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000155 if (!IS_STRING(e)) e = convert(e);
156 elements[elements_length++] = e;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000157 }
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000158 elements.length = elements_length;
159 var result = %_FastAsciiArrayJoin(elements, '');
160 if (!IS_UNDEFINED(result)) return result;
161 return %StringBuilderConcat(elements, elements_length, '');
162 }
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000163 // Non-empty separator case.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000164 // If the first element is a number then use the heuristic that the
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000165 // remaining elements are also likely to be numbers.
166 if (!IS_NUMBER(array[0])) {
167 for (var i = 0; i < length; i++) {
168 var e = array[i];
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000169 if (!IS_STRING(e)) e = convert(e);
170 elements[i] = e;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000171 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000172 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000173 for (var i = 0; i < length; i++) {
174 var e = array[i];
danno@chromium.orgc612e022011-11-10 11:38:15 +0000175 if (IS_NUMBER(e)) {
176 e = %_NumberToString(e);
177 } else if (!IS_STRING(e)) {
178 e = convert(e);
179 }
180 elements[i] = e;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000181 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000182 }
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000183 var result = %_FastAsciiArrayJoin(elements, separator);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000184 if (!IS_UNDEFINED(result)) return result;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +0000185
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000186 return %StringBuilderJoin(elements, length, separator);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000187 } finally {
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000188 // Make sure to remove the last element of the visited array no
189 // matter what happens.
190 if (is_array) visited_arrays.length = visited_arrays.length - 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000191 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000192}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000193
194
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000195function ConvertToString(x) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000196 // Assumes x is a non-string.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000197 if (IS_NUMBER(x)) return %_NumberToString(x);
198 if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
199 return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000200}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000201
202
203function ConvertToLocaleString(e) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000204 if (IS_NULL_OR_UNDEFINED(e)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000205 return '';
206 } else {
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000207 // According to ES5, section 15.4.4.3, the toLocaleString conversion
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000208 // must throw a TypeError if ToObject(e).toLocaleString isn't
209 // callable.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000210 var e_obj = ToObject(e);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000211 return %ToString(e_obj.toLocaleString());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000212 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000213}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000214
215
216// This function implements the optimized splice implementation that can use
217// special array operations to handle sparse arrays in a sensible fashion.
218function SmartSlice(array, start_i, del_count, len, deleted_elements) {
219 // Move deleted elements to a new array (the return value from splice).
220 // Intervals array can contain keys and intervals. See comment in Concat.
221 var intervals = %GetArrayKeys(array, start_i + del_count);
222 var length = intervals.length;
223 for (var k = 0; k < length; k++) {
224 var key = intervals[k];
225 if (key < 0) {
226 var j = -1 - key;
227 var interval_limit = j + intervals[++k];
228 if (j < start_i) {
229 j = start_i;
230 }
231 for (; j < interval_limit; j++) {
232 // ECMA-262 15.4.4.12 line 10. The spec could also be
233 // interpreted such that %HasLocalProperty would be the
234 // appropriate test. We follow KJS in consulting the
235 // prototype.
236 var current = array[j];
237 if (!IS_UNDEFINED(current) || j in array) {
238 deleted_elements[j - start_i] = current;
239 }
240 }
241 } else {
242 if (!IS_UNDEFINED(key)) {
243 if (key >= start_i) {
244 // ECMA-262 15.4.4.12 line 10. The spec could also be
245 // interpreted such that %HasLocalProperty would be the
246 // appropriate test. We follow KJS in consulting the
247 // prototype.
248 var current = array[key];
249 if (!IS_UNDEFINED(current) || key in array) {
250 deleted_elements[key - start_i] = current;
251 }
252 }
253 }
254 }
255 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000256}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000257
258
259// This function implements the optimized splice implementation that can use
260// special array operations to handle sparse arrays in a sensible fashion.
261function SmartMove(array, start_i, del_count, len, num_additional_args) {
262 // Move data to new array.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000263 var new_array = new InternalArray(len - del_count + num_additional_args);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000264 var intervals = %GetArrayKeys(array, len);
265 var length = intervals.length;
266 for (var k = 0; k < length; k++) {
267 var key = intervals[k];
268 if (key < 0) {
269 var j = -1 - key;
270 var interval_limit = j + intervals[++k];
271 while (j < start_i && j < interval_limit) {
272 // The spec could also be interpreted such that
273 // %HasLocalProperty would be the appropriate test. We follow
274 // KJS in consulting the prototype.
275 var current = array[j];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000276 if (!IS_UNDEFINED(current) || j in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 new_array[j] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000278 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 j++;
280 }
281 j = start_i + del_count;
282 while (j < interval_limit) {
283 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also be
284 // interpreted such that %HasLocalProperty would be the
285 // appropriate test. We follow KJS in consulting the
286 // prototype.
287 var current = array[j];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000288 if (!IS_UNDEFINED(current) || j in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000289 new_array[j - del_count + num_additional_args] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000290 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 j++;
292 }
293 } else {
294 if (!IS_UNDEFINED(key)) {
295 if (key < start_i) {
296 // The spec could also be interpreted such that
297 // %HasLocalProperty would be the appropriate test. We follow
298 // KJS in consulting the prototype.
299 var current = array[key];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000300 if (!IS_UNDEFINED(current) || key in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 new_array[key] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000302 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303 } else if (key >= start_i + del_count) {
304 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also
305 // be interpreted such that %HasLocalProperty would be the
306 // appropriate test. We follow KJS in consulting the
307 // prototype.
308 var current = array[key];
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000309 if (!IS_UNDEFINED(current) || key in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000310 new_array[key - del_count + num_additional_args] = current;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000311 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314 }
315 }
316 // Move contents of new_array into this array
317 %MoveArrayContents(new_array, array);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000318}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000319
320
321// This is part of the old simple-minded splice. We are using it either
322// because the receiver is not an array (so we have no choice) or because we
323// know we are not deleting or moving a lot of elements.
324function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
325 for (var i = 0; i < del_count; i++) {
326 var index = start_i + i;
327 // The spec could also be interpreted such that %HasLocalProperty
328 // would be the appropriate test. We follow KJS in consulting the
329 // prototype.
330 var current = array[index];
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000331 if (!IS_UNDEFINED(current) || index in array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000332 deleted_elements[i] = current;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000333 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000334 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000335}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000336
337
338function SimpleMove(array, start_i, del_count, len, num_additional_args) {
339 if (num_additional_args !== del_count) {
340 // Move the existing elements after the elements to be deleted
341 // to the right position in the resulting array.
342 if (num_additional_args > del_count) {
343 for (var i = len - del_count; i > start_i; i--) {
344 var from_index = i + del_count - 1;
345 var to_index = i + num_additional_args - 1;
346 // The spec could also be interpreted such that
347 // %HasLocalProperty would be the appropriate test. We follow
348 // KJS in consulting the prototype.
349 var current = array[from_index];
350 if (!IS_UNDEFINED(current) || from_index in array) {
351 array[to_index] = current;
352 } else {
353 delete array[to_index];
354 }
355 }
356 } else {
357 for (var i = start_i; i < len - del_count; i++) {
358 var from_index = i + del_count;
359 var to_index = i + num_additional_args;
360 // The spec could also be interpreted such that
361 // %HasLocalProperty would be the appropriate test. We follow
362 // KJS in consulting the prototype.
363 var current = array[from_index];
364 if (!IS_UNDEFINED(current) || from_index in array) {
365 array[to_index] = current;
366 } else {
367 delete array[to_index];
368 }
369 }
370 for (var i = len; i > len - del_count + num_additional_args; i--) {
371 delete array[i - 1];
372 }
373 }
374 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000375}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376
377
378// -------------------------------------------------------------------
379
380
381function ArrayToString() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000382 var array;
383 var func;
384 if (IS_ARRAY(this)) {
385 func = this.join;
386 if (func === ArrayJoin) {
387 return Join(this, this.length, ',', ConvertToString);
388 }
389 array = this;
390 } else {
391 array = ToObject(this);
392 func = array.join;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000393 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000394 if (!IS_SPEC_FUNCTION(func)) {
395 return %_CallFunction(array, ObjectToString);
396 }
397 return %_CallFunction(array, func);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000398}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399
400
401function ArrayToLocaleString() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000402 var array = ToObject(this);
403 var arrayLen = array.length;
404 var len = TO_UINT32(arrayLen);
405 if (len === 0) return "";
406 return Join(array, len, ',', ConvertToLocaleString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000407}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408
409
410function ArrayJoin(separator) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000411 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
412 throw MakeTypeError("called_on_null_or_undefined",
413 ["Array.prototype.join"]);
414 }
415
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000416 if (IS_UNDEFINED(separator)) {
417 separator = ',';
418 } else if (!IS_STRING(separator)) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000419 separator = NonStringToString(separator);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000420 }
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000421
422 var result = %_FastAsciiArrayJoin(this, separator);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000423 if (!IS_UNDEFINED(result)) return result;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000424
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000425 return Join(this, TO_UINT32(this.length), separator, ConvertToString);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000426}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000427
428
429// Removes the last element from the array and returns it. See
430// ECMA-262, section 15.4.4.6.
431function ArrayPop() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000432 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
433 throw MakeTypeError("called_on_null_or_undefined",
434 ["Array.prototype.pop"]);
435 }
436
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000437 var n = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000438 if (n == 0) {
439 this.length = n;
440 return;
441 }
442 n--;
443 var value = this[n];
444 this.length = n;
445 delete this[n];
446 return value;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000447}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000448
449
450// Appends the arguments to the end of the array and returns the new
451// length of the array. See ECMA-262, section 15.4.4.7.
452function ArrayPush() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000453 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
454 throw MakeTypeError("called_on_null_or_undefined",
455 ["Array.prototype.push"]);
456 }
457
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000458 var n = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000459 var m = %_ArgumentsLength();
460 for (var i = 0; i < m; i++) {
461 this[i+n] = %_Arguments(i);
462 }
463 this.length = n + m;
464 return this.length;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000465}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000466
467
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000468// Returns an array containing the array elements of the object followed
469// by the array elements of each argument in order. See ECMA-262,
470// section 15.4.4.7.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000471function ArrayConcat(arg1) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000472 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
473 throw MakeTypeError("called_on_null_or_undefined",
474 ["Array.prototype.concat"]);
475 }
476
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000477 var array = ToObject(this);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000478 var arg_count = %_ArgumentsLength();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000479 var arrays = new InternalArray(1 + arg_count);
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000480 arrays[0] = array;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000481 for (var i = 0; i < arg_count; i++) {
482 arrays[i + 1] = %_Arguments(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000483 }
484
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000485 return %ArrayConcat(arrays);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000486}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487
488
489// For implementing reverse() on large, sparse arrays.
490function SparseReverse(array, len) {
491 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
492 var high_counter = keys.length - 1;
493 var low_counter = 0;
494 while (low_counter <= high_counter) {
495 var i = keys[low_counter];
496 var j = keys[high_counter];
497
498 var j_complement = len - j - 1;
499 var low, high;
500
501 if (j_complement <= i) {
502 high = j;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000503 while (keys[--high_counter] == j) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000504 low = j_complement;
505 }
506 if (j_complement >= i) {
507 low = i;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000508 while (keys[++low_counter] == i) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000509 high = len - i - 1;
510 }
511
512 var current_i = array[low];
513 if (!IS_UNDEFINED(current_i) || low in array) {
514 var current_j = array[high];
515 if (!IS_UNDEFINED(current_j) || high in array) {
516 array[low] = current_j;
517 array[high] = current_i;
518 } else {
519 array[high] = current_i;
520 delete array[low];
521 }
522 } else {
523 var current_j = array[high];
524 if (!IS_UNDEFINED(current_j) || high in array) {
525 array[low] = current_j;
526 delete array[high];
527 }
528 }
529 }
530}
531
532
533function ArrayReverse() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000534 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
535 throw MakeTypeError("called_on_null_or_undefined",
536 ["Array.prototype.reverse"]);
537 }
538
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000539 var j = TO_UINT32(this.length) - 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000540
541 if (UseSparseVariant(this, j, IS_ARRAY(this))) {
542 SparseReverse(this, j+1);
543 return this;
544 }
545
546 for (var i = 0; i < j; i++, j--) {
547 var current_i = this[i];
548 if (!IS_UNDEFINED(current_i) || i in this) {
549 var current_j = this[j];
550 if (!IS_UNDEFINED(current_j) || j in this) {
551 this[i] = current_j;
552 this[j] = current_i;
553 } else {
554 this[j] = current_i;
555 delete this[i];
556 }
557 } else {
558 var current_j = this[j];
559 if (!IS_UNDEFINED(current_j) || j in this) {
560 this[i] = current_j;
561 delete this[j];
562 }
563 }
564 }
565 return this;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000566}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000567
568
569function ArrayShift() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000570 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
571 throw MakeTypeError("called_on_null_or_undefined",
572 ["Array.prototype.shift"]);
573 }
574
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000575 var len = TO_UINT32(this.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000576
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000577 if (len === 0) {
578 this.length = 0;
579 return;
580 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000581
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000582 var first = this[0];
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000583
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000584 if (IS_ARRAY(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000585 SmartMove(this, 0, 1, len, 0);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000586 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000587 SimpleMove(this, 0, 1, len, 0);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000588 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000589
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000590 this.length = len - 1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000592 return first;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000593}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000594
595
596function ArrayUnshift(arg1) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000597 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
598 throw MakeTypeError("called_on_null_or_undefined",
599 ["Array.prototype.unshift"]);
600 }
601
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000602 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000603 var num_arguments = %_ArgumentsLength();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000604
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000605 if (IS_ARRAY(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000606 SmartMove(this, 0, 0, len, num_arguments);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000607 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000608 SimpleMove(this, 0, 0, len, num_arguments);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000609 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000610
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000611 for (var i = 0; i < num_arguments; i++) {
612 this[i] = %_Arguments(i);
613 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000614
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000615 this.length = len + num_arguments;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000616
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000617 return len + num_arguments;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000618}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000619
620
621function ArraySlice(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000622 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
623 throw MakeTypeError("called_on_null_or_undefined",
624 ["Array.prototype.slice"]);
625 }
626
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000627 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000628 var start_i = TO_INTEGER(start);
629 var end_i = len;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000630
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000631 if (end !== void 0) end_i = TO_INTEGER(end);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000633 if (start_i < 0) {
634 start_i += len;
635 if (start_i < 0) start_i = 0;
636 } else {
637 if (start_i > len) start_i = len;
638 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000640 if (end_i < 0) {
641 end_i += len;
642 if (end_i < 0) end_i = 0;
643 } else {
644 if (end_i > len) end_i = len;
645 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000646
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000647 var result = [];
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000648
649 if (end_i < start_i) return result;
650
jkummerow@chromium.orge297f592011-06-08 10:05:15 +0000651 if (IS_ARRAY(this) &&
652 (end_i > 1000) &&
653 (%EstimateNumberOfElements(this) < end_i)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000654 SmartSlice(this, start_i, end_i - start_i, len, result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000655 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 SimpleSlice(this, start_i, end_i - start_i, len, result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000657 }
658
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000659 result.length = end_i - start_i;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000660
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000661 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000662}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663
664
665function ArraySplice(start, delete_count) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000666 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
667 throw MakeTypeError("called_on_null_or_undefined",
668 ["Array.prototype.splice"]);
669 }
670
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000671 var num_arguments = %_ArgumentsLength();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000672
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000673 var len = TO_UINT32(this.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000674 var start_i = TO_INTEGER(start);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000675
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676 if (start_i < 0) {
677 start_i += len;
678 if (start_i < 0) start_i = 0;
679 } else {
680 if (start_i > len) start_i = len;
681 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000682
ager@chromium.org5c838252010-02-19 08:53:10 +0000683 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000684 // given as a request to delete all the elements from the start.
685 // And it differs from the case of undefined delete count.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000686 // This does not follow ECMA-262, but we do the same for
687 // compatibility.
688 var del_count = 0;
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000689 if (num_arguments == 1) {
690 del_count = len - start_i;
691 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000692 del_count = TO_INTEGER(delete_count);
693 if (del_count < 0) del_count = 0;
694 if (del_count > len - start_i) del_count = len - start_i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000696
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697 var deleted_elements = [];
698 deleted_elements.length = del_count;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000699
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000700 // Number of elements to add.
701 var num_additional_args = 0;
702 if (num_arguments > 2) {
703 num_additional_args = num_arguments - 2;
704 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000705
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000706 var use_simple_splice = true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000708 if (IS_ARRAY(this) && num_additional_args !== del_count) {
709 // If we are only deleting/moving a few things near the end of the
710 // array then the simple version is going to be faster, because it
711 // doesn't touch most of the array.
712 var estimated_non_hole_elements = %EstimateNumberOfElements(this);
713 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
714 use_simple_splice = false;
715 }
716 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000717
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000718 if (use_simple_splice) {
719 SimpleSlice(this, start_i, del_count, len, deleted_elements);
720 SimpleMove(this, start_i, del_count, len, num_additional_args);
721 } else {
722 SmartSlice(this, start_i, del_count, len, deleted_elements);
723 SmartMove(this, start_i, del_count, len, num_additional_args);
724 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000725
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726 // Insert the arguments into the resulting array in
727 // place of the deleted elements.
728 var i = start_i;
729 var arguments_index = 2;
730 var arguments_length = %_ArgumentsLength();
731 while (arguments_index < arguments_length) {
732 this[i++] = %_Arguments(arguments_index++);
733 }
734 this.length = len - del_count + num_additional_args;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Return the deleted elements.
737 return deleted_elements;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000738}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739
740
741function ArraySort(comparefn) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000742 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
743 throw MakeTypeError("called_on_null_or_undefined",
744 ["Array.prototype.sort"]);
745 }
746
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000747 // In-place QuickSort algorithm.
748 // For short (length <= 22) arrays, insertion sort is used for efficiency.
749
lrn@chromium.org34e60782011-09-15 07:25:40 +0000750 if (!IS_SPEC_FUNCTION(comparefn)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000751 comparefn = function (x, y) {
752 if (x === y) return 0;
753 if (%_IsSmi(x) && %_IsSmi(y)) {
754 return %SmiLexicographicCompare(x, y);
ager@chromium.org357bf652010-04-12 11:30:10 +0000755 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000756 x = ToString(x);
757 y = ToString(y);
758 if (x == y) return 0;
759 else return x < y ? -1 : 1;
760 };
ager@chromium.org357bf652010-04-12 11:30:10 +0000761 }
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000762 var receiver = %GetDefaultReceiver(comparefn);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000763
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000764 var InsertionSort = function InsertionSort(a, from, to) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000765 for (var i = from + 1; i < to; i++) {
766 var element = a[i];
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000767 for (var j = i - 1; j >= from; j--) {
768 var tmp = a[j];
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000769 var order = %_CallFunction(receiver, tmp, element, comparefn);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000770 if (order > 0) {
771 a[j + 1] = tmp;
772 } else {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000773 break;
774 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000775 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000776 a[j + 1] = element;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000777 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000778 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000779
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000780 var GetThirdIndex = function(a, from, to) {
781 var t_array = [];
782 // Use both 'from' and 'to' to determine the pivot candidates.
783 var increment = 200 + ((to - from) & 15);
784 for (var i = from + 1; i < to - 1; i += increment) {
785 t_array.push([i, a[i]]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000786 }
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000787 t_array.sort(function(a, b) {
788 return %_CallFunction(receiver, a[1], b[1], comparefn) } );
789 var third_index = t_array[t_array.length >> 1][0];
790 return third_index;
791 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000792
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000793 var QuickSort = function QuickSort(a, from, to) {
794 var third_index = 0;
795 while (true) {
796 // Insertion sort is faster for short arrays.
797 if (to - from <= 10) {
798 InsertionSort(a, from, to);
799 return;
800 }
801 if (to - from > 1000) {
802 third_index = GetThirdIndex(a, from, to);
803 } else {
804 third_index = from + ((to - from) >> 1);
805 }
806 // Find a pivot as the median of first, last and middle element.
807 var v0 = a[from];
808 var v1 = a[to - 1];
809 var v2 = a[third_index];
810 var c01 = %_CallFunction(receiver, v0, v1, comparefn);
811 if (c01 > 0) {
812 // v1 < v0, so swap them.
813 var tmp = v0;
814 v0 = v1;
815 v1 = tmp;
816 } // v0 <= v1.
817 var c02 = %_CallFunction(receiver, v0, v2, comparefn);
818 if (c02 >= 0) {
819 // v2 <= v0 <= v1.
820 var tmp = v0;
821 v0 = v2;
822 v2 = v1;
823 v1 = tmp;
824 } else {
825 // v0 <= v1 && v0 < v2
826 var c12 = %_CallFunction(receiver, v1, v2, comparefn);
827 if (c12 > 0) {
828 // v0 <= v2 < v1
829 var tmp = v1;
830 v1 = v2;
831 v2 = tmp;
832 }
833 }
834 // v0 <= v1 <= v2
835 a[from] = v0;
836 a[to - 1] = v2;
837 var pivot = v1;
838 var low_end = from + 1; // Upper bound of elements lower than pivot.
839 var high_start = to - 1; // Lower bound of elements greater than pivot.
840 a[third_index] = a[low_end];
841 a[low_end] = pivot;
842
843 // From low_end to i are elements equal to pivot.
844 // From i to high_start are elements that haven't been compared yet.
845 partition: for (var i = low_end + 1; i < high_start; i++) {
846 var element = a[i];
847 var order = %_CallFunction(receiver, element, pivot, comparefn);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000848 if (order < 0) {
ulan@chromium.orgd6899c32012-05-18 14:12:25 +0000849 a[i] = a[low_end];
850 a[low_end] = element;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000851 low_end++;
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000852 } else if (order > 0) {
853 do {
854 high_start--;
855 if (high_start == i) break partition;
856 var top_elem = a[high_start];
857 order = %_CallFunction(receiver, top_elem, pivot, comparefn);
858 } while (order > 0);
859 a[i] = a[high_start];
860 a[high_start] = element;
861 if (order < 0) {
862 element = a[i];
863 a[i] = a[low_end];
864 a[low_end] = element;
865 low_end++;
866 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000867 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000868 }
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000869 if (to - high_start < low_end - from) {
870 QuickSort(a, high_start, to);
871 to = low_end;
872 } else {
873 QuickSort(a, from, low_end);
874 from = high_start;
875 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000876 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000877 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000878
whesse@chromium.org023421e2010-12-21 12:19:12 +0000879 // Copy elements in the range 0..length from obj's prototype chain
880 // to obj itself, if obj has holes. Return one more than the maximal index
ager@chromium.org5ec48922009-05-05 07:25:34 +0000881 // of a prototype property.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000882 var CopyFromPrototype = function CopyFromPrototype(obj, length) {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000883 var max = 0;
884 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
885 var indices = %GetArrayKeys(proto, length);
886 if (indices.length > 0) {
887 if (indices[0] == -1) {
888 // It's an interval.
889 var proto_length = indices[1];
890 for (var i = 0; i < proto_length; i++) {
891 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
892 obj[i] = proto[i];
893 if (i >= max) { max = i + 1; }
894 }
895 }
896 } else {
897 for (var i = 0; i < indices.length; i++) {
898 var index = indices[i];
899 if (!IS_UNDEFINED(index) &&
900 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
901 obj[index] = proto[index];
902 if (index >= max) { max = index + 1; }
903 }
904 }
905 }
906 }
907 }
908 return max;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000909 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000910
ager@chromium.org5ec48922009-05-05 07:25:34 +0000911 // Set a value of "undefined" on all indices in the range from..to
912 // where a prototype of obj has an element. I.e., shadow all prototype
913 // elements in that range.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000914 var ShadowPrototypeElements = function(obj, from, to) {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000915 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
916 var indices = %GetArrayKeys(proto, to);
917 if (indices.length > 0) {
918 if (indices[0] == -1) {
919 // It's an interval.
920 var proto_length = indices[1];
921 for (var i = from; i < proto_length; i++) {
922 if (proto.hasOwnProperty(i)) {
923 obj[i] = void 0;
924 }
925 }
926 } else {
927 for (var i = 0; i < indices.length; i++) {
928 var index = indices[i];
929 if (!IS_UNDEFINED(index) && from <= index &&
930 proto.hasOwnProperty(index)) {
931 obj[index] = void 0;
932 }
933 }
934 }
935 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000936 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000937 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000938
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000939 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000940 // Copy defined elements from the end to fill in all holes and undefineds
941 // in the beginning of the array. Write undefineds and holes at the end
942 // after loop is finished.
943 var first_undefined = 0;
944 var last_defined = length - 1;
945 var num_holes = 0;
946 while (first_undefined < last_defined) {
947 // Find first undefined element.
948 while (first_undefined < last_defined &&
949 !IS_UNDEFINED(obj[first_undefined])) {
950 first_undefined++;
951 }
952 // Maintain the invariant num_holes = the number of holes in the original
953 // array with indices <= first_undefined or > last_defined.
954 if (!obj.hasOwnProperty(first_undefined)) {
955 num_holes++;
956 }
957
958 // Find last defined element.
959 while (first_undefined < last_defined &&
960 IS_UNDEFINED(obj[last_defined])) {
961 if (!obj.hasOwnProperty(last_defined)) {
962 num_holes++;
963 }
964 last_defined--;
965 }
966 if (first_undefined < last_defined) {
967 // Fill in hole or undefined.
968 obj[first_undefined] = obj[last_defined];
969 obj[last_defined] = void 0;
970 }
971 }
972 // If there were any undefineds in the entire array, first_undefined
973 // points to one past the last defined element. Make this true if
974 // there were no undefineds, as well, so that first_undefined == number
975 // of defined elements.
976 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
977 // Fill in the undefineds and the holes. There may be a hole where
978 // an undefined should be and vice versa.
979 var i;
980 for (i = first_undefined; i < length - num_holes; i++) {
981 obj[i] = void 0;
982 }
983 for (i = length - num_holes; i < length; i++) {
984 // For compatability with Webkit, do not expose elements in the prototype.
985 if (i in obj.__proto__) {
986 obj[i] = void 0;
987 } else {
988 delete obj[i];
989 }
990 }
991
992 // Return the number of defined elements.
993 return first_undefined;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000994 };
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000995
ager@chromium.org357bf652010-04-12 11:30:10 +0000996 var length = TO_UINT32(this.length);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000997 if (length < 2) return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998
ager@chromium.org5ec48922009-05-05 07:25:34 +0000999 var is_array = IS_ARRAY(this);
1000 var max_prototype_element;
1001 if (!is_array) {
1002 // For compatibility with JSC, we also sort elements inherited from
1003 // the prototype chain on non-Array objects.
1004 // We do this by copying them to this object and sorting only
1005 // local elements. This is not very efficient, but sorting with
1006 // inherited elements happens very, very rarely, if at all.
1007 // The specification allows "implementation dependent" behavior
1008 // if an element on the prototype chain has an element that
1009 // might interact with sorting.
1010 max_prototype_element = CopyFromPrototype(this, length);
1011 }
1012
1013 var num_non_undefined = %RemoveArrayHoles(this, length);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001014 if (num_non_undefined == -1) {
1015 // There were indexed accessors in the array. Move array holes and
1016 // undefineds to the end using a Javascript function that is safe
1017 // in the presence of accessors.
1018 num_non_undefined = SafeRemoveArrayHoles(this);
1019 }
ager@chromium.org5ec48922009-05-05 07:25:34 +00001020
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00001021 QuickSort(this, 0, num_non_undefined);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001022
1023 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
1024 // For compatibility with JSC, we shadow any elements in the prototype
1025 // chain that has become exposed by sort moving a hole to its position.
1026 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001027 }
1028
1029 return this;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031
1032
1033// The following functions cannot be made efficient on sparse arrays while
1034// preserving the semantics, since the calls to the receiver function can add
1035// or delete elements from the array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001036function ArrayFilter(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001037 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1038 throw MakeTypeError("called_on_null_or_undefined",
1039 ["Array.prototype.filter"]);
1040 }
1041
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001042 // Pull out the length so that modifications to the length in the
1043 // loop will not affect the looping and side effects are visible.
1044 var array = ToObject(this);
1045 var length = ToUint32(array.length);
1046
lrn@chromium.org34e60782011-09-15 07:25:40 +00001047 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001048 throw MakeTypeError('called_non_callable', [ f ]);
1049 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001050 if (IS_NULL_OR_UNDEFINED(receiver)) {
1051 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001052 } else if (!IS_SPEC_OBJECT(receiver)) {
1053 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001054 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001055
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001056 var result = new $Array();
1057 var accumulator = new InternalArray();
1058 var accumulator_length = 0;
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001059 if (%DebugCallbackSupportsStepping(f)) {
1060 for (var i = 0; i < length; i++) {
1061 if (i in array) {
1062 var element = array[i];
1063 // Prepare break slots for debugger step in.
1064 %DebugPrepareStepInIfStepping(f);
1065 if (%_CallFunction(receiver, element, i, array, f)) {
1066 accumulator[accumulator_length++] = element;
1067 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001068 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001069 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001070 } else {
1071 // This is a duplicate of the previous loop sans debug stepping.
1072 for (var i = 0; i < length; i++) {
1073 if (i in array) {
1074 var element = array[i];
1075 if (%_CallFunction(receiver, element, i, array, f)) {
1076 accumulator[accumulator_length++] = element;
1077 }
1078 }
1079 }
1080 // End of duplicate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001081 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001082 %MoveArrayContents(accumulator, result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001083 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001084}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001085
1086
1087function ArrayForEach(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001088 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1089 throw MakeTypeError("called_on_null_or_undefined",
1090 ["Array.prototype.forEach"]);
1091 }
1092
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001093 // Pull out the length so that modifications to the length in the
1094 // loop will not affect the looping and side effects are visible.
1095 var array = ToObject(this);
1096 var length = TO_UINT32(array.length);
1097
lrn@chromium.org34e60782011-09-15 07:25:40 +00001098 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001099 throw MakeTypeError('called_non_callable', [ f ]);
1100 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001101 if (IS_NULL_OR_UNDEFINED(receiver)) {
1102 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001103 } else if (!IS_SPEC_OBJECT(receiver)) {
1104 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001105 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001106 if (%DebugCallbackSupportsStepping(f)) {
1107 for (var i = 0; i < length; i++) {
1108 if (i in array) {
1109 var element = array[i];
1110 // Prepare break slots for debugger step in.
1111 %DebugPrepareStepInIfStepping(f);
1112 %_CallFunction(receiver, element, i, array, f);
1113 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001114 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001115 } else {
1116 // This is a duplicate of the previous loop sans debug stepping.
1117 for (var i = 0; i < length; i++) {
1118 if (i in array) {
1119 var element = array[i];
1120 %_CallFunction(receiver, element, i, array, f);
1121 }
1122 }
1123 // End of duplicate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001124 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001125}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001126
1127
1128// Executes the function once for each element present in the
1129// array until it finds one where callback returns true.
1130function ArraySome(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001131 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1132 throw MakeTypeError("called_on_null_or_undefined",
1133 ["Array.prototype.some"]);
1134 }
1135
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001136 // Pull out the length so that modifications to the length in the
1137 // loop will not affect the looping and side effects are visible.
1138 var array = ToObject(this);
1139 var length = TO_UINT32(array.length);
1140
lrn@chromium.org34e60782011-09-15 07:25:40 +00001141 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001142 throw MakeTypeError('called_non_callable', [ f ]);
1143 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001144 if (IS_NULL_OR_UNDEFINED(receiver)) {
1145 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001146 } else if (!IS_SPEC_OBJECT(receiver)) {
1147 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001148 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001149
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001150 if (%DebugCallbackSupportsStepping(f)) {
1151 for (var i = 0; i < length; i++) {
1152 if (i in array) {
1153 var element = array[i];
1154 // Prepare break slots for debugger step in.
1155 %DebugPrepareStepInIfStepping(f);
1156 if (%_CallFunction(receiver, element, i, array, f)) return true;
1157 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001158 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001159 } else {
1160 // This is a duplicate of the previous loop sans debug stepping.
1161 for (var i = 0; i < length; i++) {
1162 if (i in array) {
1163 var element = array[i];
1164 if (%_CallFunction(receiver, element, i, array, f)) return true;
1165 }
1166 }
1167 // End of duplicate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001168 }
1169 return false;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001170}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171
1172
1173function ArrayEvery(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001174 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1175 throw MakeTypeError("called_on_null_or_undefined",
1176 ["Array.prototype.every"]);
1177 }
1178
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001179 // Pull out the length so that modifications to the length in the
1180 // loop will not affect the looping and side effects are visible.
1181 var array = ToObject(this);
1182 var length = TO_UINT32(array.length);
1183
lrn@chromium.org34e60782011-09-15 07:25:40 +00001184 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001185 throw MakeTypeError('called_non_callable', [ f ]);
1186 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001187 if (IS_NULL_OR_UNDEFINED(receiver)) {
1188 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001189 } else if (!IS_SPEC_OBJECT(receiver)) {
1190 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001191 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001192
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001193 if (%DebugCallbackSupportsStepping(f)) {
1194 for (var i = 0; i < length; i++) {
1195 if (i in array) {
1196 var element = array[i];
1197 // Prepare break slots for debugger step in.
1198 %DebugPrepareStepInIfStepping(f);
1199 if (!%_CallFunction(receiver, element, i, array, f)) return false;
1200 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001201 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001202 } else {
1203 // This is a duplicate of the previous loop sans debug stepping.
1204 for (var i = 0; i < length; i++) {
1205 if (i in array) {
1206 var element = array[i];
1207 if (!%_CallFunction(receiver, element, i, array, f)) return false;
1208 }
1209 }
1210 // End of duplicate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001211 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001212 return true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001213}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001214
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001215function ArrayMap(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001216 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1217 throw MakeTypeError("called_on_null_or_undefined",
1218 ["Array.prototype.map"]);
1219 }
1220
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001221 // Pull out the length so that modifications to the length in the
1222 // loop will not affect the looping and side effects are visible.
1223 var array = ToObject(this);
1224 var length = TO_UINT32(array.length);
1225
lrn@chromium.org34e60782011-09-15 07:25:40 +00001226 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001227 throw MakeTypeError('called_non_callable', [ f ]);
1228 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001229 if (IS_NULL_OR_UNDEFINED(receiver)) {
1230 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001231 } else if (!IS_SPEC_OBJECT(receiver)) {
1232 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001233 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001234
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001235 var result = new $Array();
1236 var accumulator = new InternalArray(length);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001237 if (%DebugCallbackSupportsStepping(f)) {
1238 for (var i = 0; i < length; i++) {
1239 if (i in array) {
1240 var element = array[i];
1241 // Prepare break slots for debugger step in.
1242 %DebugPrepareStepInIfStepping(f);
1243 accumulator[i] = %_CallFunction(receiver, element, i, array, f);
1244 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001245 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001246 } else {
1247 // This is a duplicate of the previous loop sans debug stepping.
1248 for (var i = 0; i < length; i++) {
1249 if (i in array) {
1250 var element = array[i];
1251 accumulator[i] = %_CallFunction(receiver, element, i, array, f);
1252 }
1253 }
1254 // End of duplicate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001255 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001256 %MoveArrayContents(accumulator, result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001257 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001258}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001259
1260
1261function ArrayIndexOf(element, index) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001262 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1263 throw MakeTypeError("called_on_null_or_undefined",
1264 ["Array.prototype.indexOf"]);
1265 }
1266
ricow@chromium.org65fae842010-08-25 15:26:24 +00001267 var length = TO_UINT32(this.length);
1268 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001269 if (IS_UNDEFINED(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001270 index = 0;
1271 } else {
1272 index = TO_INTEGER(index);
1273 // If index is negative, index from the end of the array.
ricow@chromium.org83aa5492011-02-07 12:42:56 +00001274 if (index < 0) {
1275 index = length + index;
1276 // If index is still negative, search the entire array.
1277 if (index < 0) index = 0;
1278 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001279 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001280 var min = index;
1281 var max = length;
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001282 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001283 var intervals = %GetArrayKeys(this, length);
1284 if (intervals.length == 2 && intervals[0] < 0) {
1285 // A single interval.
1286 var intervalMin = -(intervals[0] + 1);
1287 var intervalMax = intervalMin + intervals[1];
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001288 if (min < intervalMin) min = intervalMin;
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001289 max = intervalMax; // Capped by length already.
1290 // Fall through to loop below.
1291 } else {
1292 if (intervals.length == 0) return -1;
1293 // Get all the keys in sorted order.
1294 var sortedKeys = GetSortedArrayKeys(this, intervals);
1295 var n = sortedKeys.length;
1296 var i = 0;
1297 while (i < n && sortedKeys[i] < index) i++;
1298 while (i < n) {
1299 var key = sortedKeys[i];
1300 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1301 i++;
1302 }
1303 return -1;
1304 }
1305 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00001306 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001307 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001308 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001309 if (this[i] === element) return i;
1310 }
1311 return -1;
1312 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001313 // Lookup through the array.
1314 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001315 if (IS_UNDEFINED(this[i]) && i in this) {
1316 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001317 }
1318 }
1319 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001320}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001321
1322
1323function ArrayLastIndexOf(element, index) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001324 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1325 throw MakeTypeError("called_on_null_or_undefined",
1326 ["Array.prototype.lastIndexOf"]);
1327 }
1328
ricow@chromium.org65fae842010-08-25 15:26:24 +00001329 var length = TO_UINT32(this.length);
1330 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001331 if (%_ArgumentsLength() < 2) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001332 index = length - 1;
1333 } else {
1334 index = TO_INTEGER(index);
1335 // If index is negative, index from end of the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001336 if (index < 0) index += length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001337 // If index is still negative, do not search the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001338 if (index < 0) return -1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001339 else if (index >= length) index = length - 1;
1340 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001341 var min = 0;
1342 var max = index;
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001343 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001344 var intervals = %GetArrayKeys(this, index + 1);
1345 if (intervals.length == 2 && intervals[0] < 0) {
1346 // A single interval.
1347 var intervalMin = -(intervals[0] + 1);
1348 var intervalMax = intervalMin + intervals[1];
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001349 if (min < intervalMin) min = intervalMin;
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001350 max = intervalMax; // Capped by index already.
1351 // Fall through to loop below.
1352 } else {
1353 if (intervals.length == 0) return -1;
1354 // Get all the keys in sorted order.
1355 var sortedKeys = GetSortedArrayKeys(this, intervals);
1356 var i = sortedKeys.length - 1;
1357 while (i >= 0) {
1358 var key = sortedKeys[i];
1359 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1360 i--;
1361 }
1362 return -1;
1363 }
1364 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001365 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001366 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001367 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001368 if (this[i] === element) return i;
1369 }
1370 return -1;
1371 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001372 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001373 if (IS_UNDEFINED(this[i]) && i in this) {
1374 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001375 }
1376 }
1377 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001378}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001379
1380
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001381function ArrayReduce(callback, current) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001382 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1383 throw MakeTypeError("called_on_null_or_undefined",
1384 ["Array.prototype.reduce"]);
1385 }
1386
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001387 // Pull out the length so that modifications to the length in the
1388 // loop will not affect the looping and side effects are visible.
1389 var array = ToObject(this);
1390 var length = ToUint32(array.length);
1391
lrn@chromium.org34e60782011-09-15 07:25:40 +00001392 if (!IS_SPEC_FUNCTION(callback)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001393 throw MakeTypeError('called_non_callable', [callback]);
1394 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001395
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001396 var i = 0;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001397 find_initial: if (%_ArgumentsLength() < 2) {
1398 for (; i < length; i++) {
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001399 current = array[i];
1400 if (!IS_UNDEFINED(current) || i in array) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001401 i++;
1402 break find_initial;
1403 }
1404 }
1405 throw MakeTypeError('reduce_no_initial', []);
1406 }
1407
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001408 var receiver = %GetDefaultReceiver(callback);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001409
1410 if (%DebugCallbackSupportsStepping(callback)) {
1411 for (; i < length; i++) {
1412 if (i in array) {
1413 var element = array[i];
1414 // Prepare break slots for debugger step in.
1415 %DebugPrepareStepInIfStepping(callback);
1416 current =
1417 %_CallFunction(receiver, current, element, i, array, callback);
1418 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001419 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001420 } else {
1421 // This is a duplicate of the previous loop sans debug stepping.
1422 for (; i < length; i++) {
1423 if (i in array) {
1424 var element = array[i];
1425 current =
1426 %_CallFunction(receiver, current, element, i, array, callback);
1427 }
1428 }
1429 // End of duplicate.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001430 }
1431 return current;
1432}
1433
1434function ArrayReduceRight(callback, current) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001435 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1436 throw MakeTypeError("called_on_null_or_undefined",
1437 ["Array.prototype.reduceRight"]);
1438 }
1439
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001440 // Pull out the length so that side effects are visible before the
1441 // callback function is checked.
1442 var array = ToObject(this);
1443 var length = ToUint32(array.length);
1444
lrn@chromium.org34e60782011-09-15 07:25:40 +00001445 if (!IS_SPEC_FUNCTION(callback)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001446 throw MakeTypeError('called_non_callable', [callback]);
1447 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001448
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001449 var i = length - 1;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001450 find_initial: if (%_ArgumentsLength() < 2) {
1451 for (; i >= 0; i--) {
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001452 current = array[i];
1453 if (!IS_UNDEFINED(current) || i in array) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001454 i--;
1455 break find_initial;
1456 }
1457 }
1458 throw MakeTypeError('reduce_no_initial', []);
1459 }
1460
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001461 var receiver = %GetDefaultReceiver(callback);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001462
1463 if (%DebugCallbackSupportsStepping(callback)) {
1464 for (; i >= 0; i--) {
1465 if (i in array) {
1466 var element = array[i];
1467 // Prepare break slots for debugger step in.
1468 %DebugPrepareStepInIfStepping(callback);
1469 current =
1470 %_CallFunction(receiver, current, element, i, array, callback);
1471 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001472 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00001473 } else {
1474 // This is a duplicate of the previous loop sans debug stepping.
1475 for (; i >= 0; i--) {
1476 if (i in array) {
1477 var element = array[i];
1478 current =
1479 %_CallFunction(receiver, current, element, i, array, callback);
1480 }
1481 }
1482 // End of duplicate.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001483 }
1484 return current;
1485}
1486
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001487// ES5, 15.4.3.2
1488function ArrayIsArray(obj) {
1489 return IS_ARRAY(obj);
1490}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001491
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001492
1493// -------------------------------------------------------------------
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001494function SetUpArray() {
1495 %CheckIsBootstrapping();
1496 // Set up non-enumerable constructor property on the Array.prototype
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001497 // object.
1498 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1499
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001500 // Set up non-enumerable functions on the Array object.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001501 InstallFunctions($Array, DONT_ENUM, $Array(
1502 "isArray", ArrayIsArray
1503 ));
1504
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001505 var specialFunctions = %SpecialArrayFunctions({});
1506
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001507 var getFunction = function(name, jsBuiltin, len) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001508 var f = jsBuiltin;
1509 if (specialFunctions.hasOwnProperty(name)) {
1510 f = specialFunctions[name];
1511 }
1512 if (!IS_UNDEFINED(len)) {
1513 %FunctionSetLength(f, len);
1514 }
1515 return f;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001516 };
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001517
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001518 // Set up non-enumerable functions of the Array.prototype object and
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001519 // set their names.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001520 // Manipulate the length of some of the functions to meet
1521 // expectations set by ECMA-262 or Mozilla.
ricow@chromium.org27bf2882011-11-17 08:34:43 +00001522 InstallFunctions($Array.prototype, DONT_ENUM, $Array(
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001523 "toString", getFunction("toString", ArrayToString),
1524 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1525 "join", getFunction("join", ArrayJoin),
1526 "pop", getFunction("pop", ArrayPop),
1527 "push", getFunction("push", ArrayPush, 1),
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001528 "concat", getFunction("concat", ArrayConcat, 1),
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001529 "reverse", getFunction("reverse", ArrayReverse),
1530 "shift", getFunction("shift", ArrayShift),
1531 "unshift", getFunction("unshift", ArrayUnshift, 1),
1532 "slice", getFunction("slice", ArraySlice, 2),
1533 "splice", getFunction("splice", ArraySplice, 2),
1534 "sort", getFunction("sort", ArraySort),
1535 "filter", getFunction("filter", ArrayFilter, 1),
1536 "forEach", getFunction("forEach", ArrayForEach, 1),
1537 "some", getFunction("some", ArraySome, 1),
1538 "every", getFunction("every", ArrayEvery, 1),
1539 "map", getFunction("map", ArrayMap, 1),
1540 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1541 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1542 "reduce", getFunction("reduce", ArrayReduce, 1),
1543 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1544 ));
lrn@chromium.org25156de2010-04-06 13:10:27 +00001545
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001546 %FinishArrayPrototypeSetup($Array.prototype);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001547
1548 // The internal Array prototype doesn't need to be fancy, since it's never
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001549 // exposed to user code.
1550 // Adding only the functions that are actually used.
1551 SetUpLockedPrototype(InternalArray, $Array(), $Array(
1552 "join", getFunction("join", ArrayJoin),
1553 "pop", getFunction("pop", ArrayPop),
1554 "push", getFunction("push", ArrayPush)
1555 ));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001556}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001557
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001558SetUpArray();