blob: d61183779c7a283e73218dd3032bf08421aac04c [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
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000780 var QuickSort = function QuickSort(a, from, to) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000781 // Insertion sort is faster for short arrays.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000782 if (to - from <= 10) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000783 InsertionSort(a, from, to);
784 return;
785 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000786 // Find a pivot as the median of first, last and middle element.
787 var v0 = a[from];
788 var v1 = a[to - 1];
789 var middle_index = from + ((to - from) >> 1);
790 var v2 = a[middle_index];
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000791 var c01 = %_CallFunction(receiver, v0, v1, comparefn);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000792 if (c01 > 0) {
793 // v1 < v0, so swap them.
794 var tmp = v0;
795 v0 = v1;
796 v1 = tmp;
797 } // v0 <= v1.
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000798 var c02 = %_CallFunction(receiver, v0, v2, comparefn);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000799 if (c02 >= 0) {
800 // v2 <= v0 <= v1.
801 var tmp = v0;
802 v0 = v2;
803 v2 = v1;
804 v1 = tmp;
805 } else {
806 // v0 <= v1 && v0 < v2
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000807 var c12 = %_CallFunction(receiver, v1, v2, comparefn);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000808 if (c12 > 0) {
809 // v0 <= v2 < v1
810 var tmp = v1;
811 v1 = v2;
812 v2 = tmp;
813 }
814 }
815 // v0 <= v1 <= v2
816 a[from] = v0;
817 a[to - 1] = v2;
818 var pivot = v1;
819 var low_end = from + 1; // Upper bound of elements lower than pivot.
820 var high_start = to - 1; // Lower bound of elements greater than pivot.
821 a[middle_index] = a[low_end];
822 a[low_end] = pivot;
823
ager@chromium.org381abbb2009-02-25 13:23:22 +0000824 // From low_end to i are elements equal to pivot.
825 // From i to high_start are elements that haven't been compared yet.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000826 partition: for (var i = low_end + 1; i < high_start; i++) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000827 var element = a[i];
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000828 var order = %_CallFunction(receiver, element, pivot, comparefn);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000829 if (order < 0) {
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000830 %_SwapElements(a, i, low_end);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000831 low_end++;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000832 } else if (order > 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000833 do {
834 high_start--;
835 if (high_start == i) break partition;
836 var top_elem = a[high_start];
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000837 order = %_CallFunction(receiver, top_elem, pivot, comparefn);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000838 } while (order > 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000839 %_SwapElements(a, i, high_start);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000840 if (order < 0) {
841 %_SwapElements(a, i, low_end);
842 low_end++;
843 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000844 }
845 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000846 QuickSort(a, from, low_end);
847 QuickSort(a, high_start, to);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000848 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000849
whesse@chromium.org023421e2010-12-21 12:19:12 +0000850 // Copy elements in the range 0..length from obj's prototype chain
851 // to obj itself, if obj has holes. Return one more than the maximal index
ager@chromium.org5ec48922009-05-05 07:25:34 +0000852 // of a prototype property.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000853 var CopyFromPrototype = function CopyFromPrototype(obj, length) {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000854 var max = 0;
855 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
856 var indices = %GetArrayKeys(proto, length);
857 if (indices.length > 0) {
858 if (indices[0] == -1) {
859 // It's an interval.
860 var proto_length = indices[1];
861 for (var i = 0; i < proto_length; i++) {
862 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
863 obj[i] = proto[i];
864 if (i >= max) { max = i + 1; }
865 }
866 }
867 } else {
868 for (var i = 0; i < indices.length; i++) {
869 var index = indices[i];
870 if (!IS_UNDEFINED(index) &&
871 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
872 obj[index] = proto[index];
873 if (index >= max) { max = index + 1; }
874 }
875 }
876 }
877 }
878 }
879 return max;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000880 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000881
ager@chromium.org5ec48922009-05-05 07:25:34 +0000882 // Set a value of "undefined" on all indices in the range from..to
883 // where a prototype of obj has an element. I.e., shadow all prototype
884 // elements in that range.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000885 var ShadowPrototypeElements = function(obj, from, to) {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000886 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
887 var indices = %GetArrayKeys(proto, to);
888 if (indices.length > 0) {
889 if (indices[0] == -1) {
890 // It's an interval.
891 var proto_length = indices[1];
892 for (var i = from; i < proto_length; i++) {
893 if (proto.hasOwnProperty(i)) {
894 obj[i] = void 0;
895 }
896 }
897 } else {
898 for (var i = 0; i < indices.length; i++) {
899 var index = indices[i];
900 if (!IS_UNDEFINED(index) && from <= index &&
901 proto.hasOwnProperty(index)) {
902 obj[index] = void 0;
903 }
904 }
905 }
906 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000907 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000908 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000910 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000911 // Copy defined elements from the end to fill in all holes and undefineds
912 // in the beginning of the array. Write undefineds and holes at the end
913 // after loop is finished.
914 var first_undefined = 0;
915 var last_defined = length - 1;
916 var num_holes = 0;
917 while (first_undefined < last_defined) {
918 // Find first undefined element.
919 while (first_undefined < last_defined &&
920 !IS_UNDEFINED(obj[first_undefined])) {
921 first_undefined++;
922 }
923 // Maintain the invariant num_holes = the number of holes in the original
924 // array with indices <= first_undefined or > last_defined.
925 if (!obj.hasOwnProperty(first_undefined)) {
926 num_holes++;
927 }
928
929 // Find last defined element.
930 while (first_undefined < last_defined &&
931 IS_UNDEFINED(obj[last_defined])) {
932 if (!obj.hasOwnProperty(last_defined)) {
933 num_holes++;
934 }
935 last_defined--;
936 }
937 if (first_undefined < last_defined) {
938 // Fill in hole or undefined.
939 obj[first_undefined] = obj[last_defined];
940 obj[last_defined] = void 0;
941 }
942 }
943 // If there were any undefineds in the entire array, first_undefined
944 // points to one past the last defined element. Make this true if
945 // there were no undefineds, as well, so that first_undefined == number
946 // of defined elements.
947 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
948 // Fill in the undefineds and the holes. There may be a hole where
949 // an undefined should be and vice versa.
950 var i;
951 for (i = first_undefined; i < length - num_holes; i++) {
952 obj[i] = void 0;
953 }
954 for (i = length - num_holes; i < length; i++) {
955 // For compatability with Webkit, do not expose elements in the prototype.
956 if (i in obj.__proto__) {
957 obj[i] = void 0;
958 } else {
959 delete obj[i];
960 }
961 }
962
963 // Return the number of defined elements.
964 return first_undefined;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000965 };
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000966
ager@chromium.org357bf652010-04-12 11:30:10 +0000967 var length = TO_UINT32(this.length);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000968 if (length < 2) return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000969
ager@chromium.org5ec48922009-05-05 07:25:34 +0000970 var is_array = IS_ARRAY(this);
971 var max_prototype_element;
972 if (!is_array) {
973 // For compatibility with JSC, we also sort elements inherited from
974 // the prototype chain on non-Array objects.
975 // We do this by copying them to this object and sorting only
976 // local elements. This is not very efficient, but sorting with
977 // inherited elements happens very, very rarely, if at all.
978 // The specification allows "implementation dependent" behavior
979 // if an element on the prototype chain has an element that
980 // might interact with sorting.
981 max_prototype_element = CopyFromPrototype(this, length);
982 }
983
984 var num_non_undefined = %RemoveArrayHoles(this, length);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000985 if (num_non_undefined == -1) {
986 // There were indexed accessors in the array. Move array holes and
987 // undefineds to the end using a Javascript function that is safe
988 // in the presence of accessors.
989 num_non_undefined = SafeRemoveArrayHoles(this);
990 }
ager@chromium.org5ec48922009-05-05 07:25:34 +0000991
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000992 QuickSort(this, 0, num_non_undefined);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000993
994 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
995 // For compatibility with JSC, we shadow any elements in the prototype
996 // chain that has become exposed by sort moving a hole to its position.
997 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998 }
999
1000 return this;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001001}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001002
1003
1004// The following functions cannot be made efficient on sparse arrays while
1005// preserving the semantics, since the calls to the receiver function can add
1006// or delete elements from the array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007function ArrayFilter(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001008 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1009 throw MakeTypeError("called_on_null_or_undefined",
1010 ["Array.prototype.filter"]);
1011 }
1012
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001013 // Pull out the length so that modifications to the length in the
1014 // loop will not affect the looping and side effects are visible.
1015 var array = ToObject(this);
1016 var length = ToUint32(array.length);
1017
lrn@chromium.org34e60782011-09-15 07:25:40 +00001018 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001019 throw MakeTypeError('called_non_callable', [ f ]);
1020 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001021 if (IS_NULL_OR_UNDEFINED(receiver)) {
1022 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001023 } else if (!IS_SPEC_OBJECT(receiver)) {
1024 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001025 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001026
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001027 var result = new $Array();
1028 var accumulator = new InternalArray();
1029 var accumulator_length = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001030 for (var i = 0; i < length; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001031 if (i in array) {
1032 var element = array[i];
1033 if (%_CallFunction(receiver, element, i, array, f)) {
1034 accumulator[accumulator_length++] = element;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001035 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001036 }
1037 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001038 %MoveArrayContents(accumulator, result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001039 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001040}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001041
1042
1043function ArrayForEach(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001044 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1045 throw MakeTypeError("called_on_null_or_undefined",
1046 ["Array.prototype.forEach"]);
1047 }
1048
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001049 // Pull out the length so that modifications to the length in the
1050 // loop will not affect the looping and side effects are visible.
1051 var array = ToObject(this);
1052 var length = TO_UINT32(array.length);
1053
lrn@chromium.org34e60782011-09-15 07:25:40 +00001054 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001055 throw MakeTypeError('called_non_callable', [ f ]);
1056 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001057 if (IS_NULL_OR_UNDEFINED(receiver)) {
1058 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001059 } else if (!IS_SPEC_OBJECT(receiver)) {
1060 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001061 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001062
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001063 for (var i = 0; i < length; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001064 if (i in array) {
1065 var element = array[i];
1066 %_CallFunction(receiver, element, i, array, f);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001067 }
1068 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001069}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070
1071
1072// Executes the function once for each element present in the
1073// array until it finds one where callback returns true.
1074function ArraySome(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001075 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1076 throw MakeTypeError("called_on_null_or_undefined",
1077 ["Array.prototype.some"]);
1078 }
1079
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001080 // Pull out the length so that modifications to the length in the
1081 // loop will not affect the looping and side effects are visible.
1082 var array = ToObject(this);
1083 var length = TO_UINT32(array.length);
1084
lrn@chromium.org34e60782011-09-15 07:25:40 +00001085 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001086 throw MakeTypeError('called_non_callable', [ f ]);
1087 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001088 if (IS_NULL_OR_UNDEFINED(receiver)) {
1089 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001090 } else if (!IS_SPEC_OBJECT(receiver)) {
1091 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001092 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001093
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 for (var i = 0; i < length; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001095 if (i in array) {
1096 var element = array[i];
1097 if (%_CallFunction(receiver, element, i, array, f)) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001098 }
1099 }
1100 return false;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001101}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001102
1103
1104function ArrayEvery(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001105 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1106 throw MakeTypeError("called_on_null_or_undefined",
1107 ["Array.prototype.every"]);
1108 }
1109
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001110 // Pull out the length so that modifications to the length in the
1111 // loop will not affect the looping and side effects are visible.
1112 var array = ToObject(this);
1113 var length = TO_UINT32(array.length);
1114
lrn@chromium.org34e60782011-09-15 07:25:40 +00001115 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001116 throw MakeTypeError('called_non_callable', [ f ]);
1117 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001118 if (IS_NULL_OR_UNDEFINED(receiver)) {
1119 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001120 } else if (!IS_SPEC_OBJECT(receiver)) {
1121 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001122 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001123
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001124 for (var i = 0; i < length; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001125 if (i in array) {
1126 var element = array[i];
1127 if (!%_CallFunction(receiver, element, i, array, f)) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001128 }
1129 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001130 return true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001131}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001132
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001133function ArrayMap(f, receiver) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001134 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1135 throw MakeTypeError("called_on_null_or_undefined",
1136 ["Array.prototype.map"]);
1137 }
1138
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001139 // Pull out the length so that modifications to the length in the
1140 // loop will not affect the looping and side effects are visible.
1141 var array = ToObject(this);
1142 var length = TO_UINT32(array.length);
1143
lrn@chromium.org34e60782011-09-15 07:25:40 +00001144 if (!IS_SPEC_FUNCTION(f)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001145 throw MakeTypeError('called_non_callable', [ f ]);
1146 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001147 if (IS_NULL_OR_UNDEFINED(receiver)) {
1148 receiver = %GetDefaultReceiver(f) || receiver;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001149 } else if (!IS_SPEC_OBJECT(receiver)) {
1150 receiver = ToObject(receiver);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001151 }
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001152
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001153 var result = new $Array();
1154 var accumulator = new InternalArray(length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001155 for (var i = 0; i < length; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001156 if (i in array) {
1157 var element = array[i];
1158 accumulator[i] = %_CallFunction(receiver, element, i, array, f);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001159 }
1160 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001161 %MoveArrayContents(accumulator, result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001162 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001163}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001164
1165
1166function ArrayIndexOf(element, index) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001167 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1168 throw MakeTypeError("called_on_null_or_undefined",
1169 ["Array.prototype.indexOf"]);
1170 }
1171
ricow@chromium.org65fae842010-08-25 15:26:24 +00001172 var length = TO_UINT32(this.length);
1173 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001174 if (IS_UNDEFINED(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001175 index = 0;
1176 } else {
1177 index = TO_INTEGER(index);
1178 // If index is negative, index from the end of the array.
ricow@chromium.org83aa5492011-02-07 12:42:56 +00001179 if (index < 0) {
1180 index = length + index;
1181 // If index is still negative, search the entire array.
1182 if (index < 0) index = 0;
1183 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001184 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001185 var min = index;
1186 var max = length;
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001187 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001188 var intervals = %GetArrayKeys(this, length);
1189 if (intervals.length == 2 && intervals[0] < 0) {
1190 // A single interval.
1191 var intervalMin = -(intervals[0] + 1);
1192 var intervalMax = intervalMin + intervals[1];
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001193 if (min < intervalMin) min = intervalMin;
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001194 max = intervalMax; // Capped by length already.
1195 // Fall through to loop below.
1196 } else {
1197 if (intervals.length == 0) return -1;
1198 // Get all the keys in sorted order.
1199 var sortedKeys = GetSortedArrayKeys(this, intervals);
1200 var n = sortedKeys.length;
1201 var i = 0;
1202 while (i < n && sortedKeys[i] < index) i++;
1203 while (i < n) {
1204 var key = sortedKeys[i];
1205 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1206 i++;
1207 }
1208 return -1;
1209 }
1210 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00001211 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001212 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001213 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001214 if (this[i] === element) return i;
1215 }
1216 return -1;
1217 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001218 // Lookup through the array.
1219 for (var i = min; i < max; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001220 if (IS_UNDEFINED(this[i]) && i in this) {
1221 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001222 }
1223 }
1224 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001225}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001226
1227
1228function ArrayLastIndexOf(element, index) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001229 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1230 throw MakeTypeError("called_on_null_or_undefined",
1231 ["Array.prototype.lastIndexOf"]);
1232 }
1233
ricow@chromium.org65fae842010-08-25 15:26:24 +00001234 var length = TO_UINT32(this.length);
1235 if (length == 0) return -1;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001236 if (%_ArgumentsLength() < 2) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001237 index = length - 1;
1238 } else {
1239 index = TO_INTEGER(index);
1240 // If index is negative, index from end of the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001241 if (index < 0) index += length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001242 // If index is still negative, do not search the array.
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001243 if (index < 0) return -1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244 else if (index >= length) index = length - 1;
1245 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001246 var min = 0;
1247 var max = index;
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001248 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001249 var intervals = %GetArrayKeys(this, index + 1);
1250 if (intervals.length == 2 && intervals[0] < 0) {
1251 // A single interval.
1252 var intervalMin = -(intervals[0] + 1);
1253 var intervalMax = intervalMin + intervals[1];
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001254 if (min < intervalMin) min = intervalMin;
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001255 max = intervalMax; // Capped by index already.
1256 // Fall through to loop below.
1257 } else {
1258 if (intervals.length == 0) return -1;
1259 // Get all the keys in sorted order.
1260 var sortedKeys = GetSortedArrayKeys(this, intervals);
1261 var i = sortedKeys.length - 1;
1262 while (i >= 0) {
1263 var key = sortedKeys[i];
1264 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1265 i--;
1266 }
1267 return -1;
1268 }
1269 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001270 // Lookup through the array.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001271 if (!IS_UNDEFINED(element)) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001272 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001273 if (this[i] === element) return i;
1274 }
1275 return -1;
1276 }
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +00001277 for (var i = max; i >= min; i--) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001278 if (IS_UNDEFINED(this[i]) && i in this) {
1279 return i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001280 }
1281 }
1282 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001283}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001284
1285
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001286function ArrayReduce(callback, current) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001287 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1288 throw MakeTypeError("called_on_null_or_undefined",
1289 ["Array.prototype.reduce"]);
1290 }
1291
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001292 // Pull out the length so that modifications to the length in the
1293 // loop will not affect the looping and side effects are visible.
1294 var array = ToObject(this);
1295 var length = ToUint32(array.length);
1296
lrn@chromium.org34e60782011-09-15 07:25:40 +00001297 if (!IS_SPEC_FUNCTION(callback)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001298 throw MakeTypeError('called_non_callable', [callback]);
1299 }
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001300
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001301 var i = 0;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001302 find_initial: if (%_ArgumentsLength() < 2) {
1303 for (; i < length; i++) {
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001304 current = array[i];
1305 if (!IS_UNDEFINED(current) || i in array) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001306 i++;
1307 break find_initial;
1308 }
1309 }
1310 throw MakeTypeError('reduce_no_initial', []);
1311 }
1312
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001313 var receiver = %GetDefaultReceiver(callback);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001314 for (; i < length; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001315 if (i in array) {
1316 var element = array[i];
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001317 current = %_CallFunction(receiver, current, element, i, array, callback);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001318 }
1319 }
1320 return current;
1321}
1322
1323function ArrayReduceRight(callback, current) {
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.reduceRight"]);
1327 }
1328
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001329 // Pull out the length so that side effects are visible before the
1330 // callback function is checked.
1331 var array = ToObject(this);
1332 var length = ToUint32(array.length);
1333
lrn@chromium.org34e60782011-09-15 07:25:40 +00001334 if (!IS_SPEC_FUNCTION(callback)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001335 throw MakeTypeError('called_non_callable', [callback]);
1336 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001337
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001338 var i = length - 1;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001339 find_initial: if (%_ArgumentsLength() < 2) {
1340 for (; i >= 0; i--) {
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001341 current = array[i];
1342 if (!IS_UNDEFINED(current) || i in array) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001343 i--;
1344 break find_initial;
1345 }
1346 }
1347 throw MakeTypeError('reduce_no_initial', []);
1348 }
1349
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001350 var receiver = %GetDefaultReceiver(callback);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001351 for (; i >= 0; i--) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001352 if (i in array) {
1353 var element = array[i];
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00001354 current = %_CallFunction(receiver, current, element, i, array, callback);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001355 }
1356 }
1357 return current;
1358}
1359
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001360// ES5, 15.4.3.2
1361function ArrayIsArray(obj) {
1362 return IS_ARRAY(obj);
1363}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001364
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001365
1366// -------------------------------------------------------------------
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001367function SetUpArray() {
1368 %CheckIsBootstrapping();
1369 // Set up non-enumerable constructor property on the Array.prototype
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001370 // object.
1371 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1372
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001373 // Set up non-enumerable functions on the Array object.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001374 InstallFunctions($Array, DONT_ENUM, $Array(
1375 "isArray", ArrayIsArray
1376 ));
1377
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001378 var specialFunctions = %SpecialArrayFunctions({});
1379
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001380 var getFunction = function(name, jsBuiltin, len) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001381 var f = jsBuiltin;
1382 if (specialFunctions.hasOwnProperty(name)) {
1383 f = specialFunctions[name];
1384 }
1385 if (!IS_UNDEFINED(len)) {
1386 %FunctionSetLength(f, len);
1387 }
1388 return f;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001389 };
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001390
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001391 // Set up non-enumerable functions of the Array.prototype object and
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001392 // set their names.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001393 // Manipulate the length of some of the functions to meet
1394 // expectations set by ECMA-262 or Mozilla.
ricow@chromium.org27bf2882011-11-17 08:34:43 +00001395 InstallFunctions($Array.prototype, DONT_ENUM, $Array(
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001396 "toString", getFunction("toString", ArrayToString),
1397 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1398 "join", getFunction("join", ArrayJoin),
1399 "pop", getFunction("pop", ArrayPop),
1400 "push", getFunction("push", ArrayPush, 1),
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001401 "concat", getFunction("concat", ArrayConcat, 1),
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001402 "reverse", getFunction("reverse", ArrayReverse),
1403 "shift", getFunction("shift", ArrayShift),
1404 "unshift", getFunction("unshift", ArrayUnshift, 1),
1405 "slice", getFunction("slice", ArraySlice, 2),
1406 "splice", getFunction("splice", ArraySplice, 2),
1407 "sort", getFunction("sort", ArraySort),
1408 "filter", getFunction("filter", ArrayFilter, 1),
1409 "forEach", getFunction("forEach", ArrayForEach, 1),
1410 "some", getFunction("some", ArraySome, 1),
1411 "every", getFunction("every", ArrayEvery, 1),
1412 "map", getFunction("map", ArrayMap, 1),
1413 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1414 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1415 "reduce", getFunction("reduce", ArrayReduce, 1),
1416 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1417 ));
lrn@chromium.org25156de2010-04-06 13:10:27 +00001418
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001419 %FinishArrayPrototypeSetup($Array.prototype);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001420
1421 // The internal Array prototype doesn't need to be fancy, since it's never
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001422 // exposed to user code.
1423 // Adding only the functions that are actually used.
1424 SetUpLockedPrototype(InternalArray, $Array(), $Array(
1425 "join", getFunction("join", ArrayJoin),
1426 "pop", getFunction("pop", ArrayPop),
1427 "push", getFunction("push", ArrayPush)
1428 ));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001429}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001430
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001431SetUpArray();