blob: 6ed1476080d49c5d1e552d63011c34964e798ea8 [file] [log] [blame]
Ben Murdochb0fe1622011-05-05 13:52:32 +01001// Copyright 2010 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// This file relies on the fact that the following declarations have been made
29// in runtime.js:
30// const $Array = global.Array;
31
32// -------------------------------------------------------------------
33
34// Global list of arrays visited during toString, toLocaleString and
35// join invocations.
Ben Murdoche0cee9b2011-05-25 10:26:03 +010036var visited_arrays = new InternalArray();
Steve Blocka7e24c12009-10-30 11:49:00 +000037
38
39// Gets a sorted array of array keys. Useful for operations on sparse
40// arrays. Dupes have not been removed.
41function GetSortedArrayKeys(array, intervals) {
42 var length = intervals.length;
43 var keys = [];
44 for (var k = 0; k < length; k++) {
45 var key = intervals[k];
46 if (key < 0) {
47 var j = -1 - key;
48 var limit = j + intervals[++k];
49 for (; j < limit; j++) {
50 var e = array[j];
51 if (!IS_UNDEFINED(e) || j in array) {
52 keys.push(j);
53 }
54 }
55 } else {
56 // The case where key is undefined also ends here.
57 if (!IS_UNDEFINED(key)) {
58 var e = array[key];
59 if (!IS_UNDEFINED(e) || key in array) {
60 keys.push(key);
61 }
62 }
63 }
64 }
65 keys.sort(function(a, b) { return a - b; });
66 return keys;
67}
68
69
70// Optimized for sparse arrays if separator is ''.
71function SparseJoin(array, len, convert) {
72 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
Steve Blocka7e24c12009-10-30 11:49:00 +000073 var last_key = -1;
74 var keys_length = keys.length;
Leon Clarkee46be812010-01-19 14:06:41 +000075
Ben Murdoche0cee9b2011-05-25 10:26:03 +010076 var elements = new InternalArray(keys_length);
Leon Clarkee46be812010-01-19 14:06:41 +000077 var elements_length = 0;
78
Steve Blocka7e24c12009-10-30 11:49:00 +000079 for (var i = 0; i < keys_length; i++) {
80 var key = keys[i];
81 if (key != last_key) {
82 var e = array[key];
Leon Clarkee46be812010-01-19 14:06:41 +000083 if (!IS_STRING(e)) e = convert(e);
84 elements[elements_length++] = e;
Steve Blocka7e24c12009-10-30 11:49:00 +000085 last_key = key;
86 }
87 }
Leon Clarkee46be812010-01-19 14:06:41 +000088 return %StringBuilderConcat(elements, elements_length, '');
Steve Blocka7e24c12009-10-30 11:49:00 +000089}
90
91
92function UseSparseVariant(object, length, is_array) {
93 return is_array &&
94 length > 1000 &&
95 (!%_IsSmi(length) ||
96 %EstimateNumberOfElements(object) < (length >> 2));
97}
98
99
100function Join(array, length, separator, convert) {
101 if (length == 0) return '';
102
103 var is_array = IS_ARRAY(array);
104
105 if (is_array) {
106 // If the array is cyclic, return the empty string for already
107 // visited arrays.
108 if (!%PushIfAbsent(visited_arrays, array)) return '';
109 }
110
111 // Attempt to convert the elements.
112 try {
Leon Clarkee46be812010-01-19 14:06:41 +0000113 if (UseSparseVariant(array, length, is_array) && (separator.length == 0)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000114 return SparseJoin(array, length, convert);
115 }
116
117 // Fast case for one-element arrays.
118 if (length == 1) {
119 var e = array[0];
Ben Murdochb8e0da22011-05-16 14:20:40 +0100120 if (IS_STRING(e)) return e;
121 return convert(e);
Steve Blocka7e24c12009-10-30 11:49:00 +0000122 }
123
Leon Clarkee46be812010-01-19 14:06:41 +0000124 // Construct an array for the elements.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100125 var elements = new InternalArray(length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000126
Steve Blockd0582a62009-12-15 09:54:21 +0000127 // We pull the empty separator check outside the loop for speed!
128 if (separator.length == 0) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100129 var elements_length = 0;
Steve Blockd0582a62009-12-15 09:54:21 +0000130 for (var i = 0; i < length; i++) {
131 var e = array[i];
Ben Murdoch086aeea2011-05-13 15:57:08 +0100132 if (!IS_UNDEFINED(e)) {
Leon Clarkee46be812010-01-19 14:06:41 +0000133 if (!IS_STRING(e)) e = convert(e);
134 elements[elements_length++] = e;
Steve Blockd0582a62009-12-15 09:54:21 +0000135 }
136 }
Ben Murdoch086aeea2011-05-13 15:57:08 +0100137 elements.length = elements_length;
138 var result = %_FastAsciiArrayJoin(elements, '');
139 if (!IS_UNDEFINED(result)) return result;
140 return %StringBuilderConcat(elements, elements_length, '');
141 }
Ben Murdochb8e0da22011-05-16 14:20:40 +0100142 // Non-empty separator case.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100143 // If the first element is a number then use the heuristic that the
Ben Murdochb8e0da22011-05-16 14:20:40 +0100144 // remaining elements are also likely to be numbers.
145 if (!IS_NUMBER(array[0])) {
146 for (var i = 0; i < length; i++) {
147 var e = array[i];
Ben Murdoch086aeea2011-05-13 15:57:08 +0100148 if (!IS_STRING(e)) e = convert(e);
149 elements[i] = e;
Steve Blocka7e24c12009-10-30 11:49:00 +0000150 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100151 } else {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100152 for (var i = 0; i < length; i++) {
153 var e = array[i];
154 if (IS_NUMBER(e)) elements[i] = %_NumberToString(e);
155 else {
156 if (!IS_STRING(e)) e = convert(e);
157 elements[i] = e;
158 }
159 }
Ben Murdoch086aeea2011-05-13 15:57:08 +0100160 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100161 var result = %_FastAsciiArrayJoin(elements, separator);
162 if (!IS_UNDEFINED(result)) return result;
163
164 return %StringBuilderJoin(elements, length, separator);
Steve Blocka7e24c12009-10-30 11:49:00 +0000165 } finally {
Steve Block1e0659c2011-05-24 12:43:12 +0100166 // Make sure to remove the last element of the visited array no
167 // matter what happens.
168 if (is_array) visited_arrays.length = visited_arrays.length - 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000169 }
170}
171
172
Ben Murdochb0fe1622011-05-05 13:52:32 +0100173function ConvertToString(x) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100174 // Assumes x is a non-string.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100175 if (IS_NUMBER(x)) return %_NumberToString(x);
176 if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
177 return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
Steve Blocka7e24c12009-10-30 11:49:00 +0000178}
179
180
181function ConvertToLocaleString(e) {
Leon Clarkee46be812010-01-19 14:06:41 +0000182 if (e == null) {
183 return '';
184 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000185 // e_obj's toLocaleString might be overwritten, check if it is a function.
186 // Call ToString if toLocaleString is not a function.
187 // See issue 877615.
188 var e_obj = ToObject(e);
189 if (IS_FUNCTION(e_obj.toLocaleString))
Steve Blockd0582a62009-12-15 09:54:21 +0000190 return ToString(e_obj.toLocaleString());
Steve Blocka7e24c12009-10-30 11:49:00 +0000191 else
192 return ToString(e);
193 }
194}
195
196
197// This function implements the optimized splice implementation that can use
198// special array operations to handle sparse arrays in a sensible fashion.
199function SmartSlice(array, start_i, del_count, len, deleted_elements) {
200 // Move deleted elements to a new array (the return value from splice).
201 // Intervals array can contain keys and intervals. See comment in Concat.
202 var intervals = %GetArrayKeys(array, start_i + del_count);
203 var length = intervals.length;
204 for (var k = 0; k < length; k++) {
205 var key = intervals[k];
206 if (key < 0) {
207 var j = -1 - key;
208 var interval_limit = j + intervals[++k];
209 if (j < start_i) {
210 j = start_i;
211 }
212 for (; j < interval_limit; j++) {
213 // ECMA-262 15.4.4.12 line 10. The spec could also be
214 // interpreted such that %HasLocalProperty would be the
215 // appropriate test. We follow KJS in consulting the
216 // prototype.
217 var current = array[j];
218 if (!IS_UNDEFINED(current) || j in array) {
219 deleted_elements[j - start_i] = current;
220 }
221 }
222 } else {
223 if (!IS_UNDEFINED(key)) {
224 if (key >= start_i) {
225 // ECMA-262 15.4.4.12 line 10. The spec could also be
226 // interpreted such that %HasLocalProperty would be the
227 // appropriate test. We follow KJS in consulting the
228 // prototype.
229 var current = array[key];
230 if (!IS_UNDEFINED(current) || key in array) {
231 deleted_elements[key - start_i] = current;
232 }
233 }
234 }
235 }
236 }
237}
238
239
240// This function implements the optimized splice implementation that can use
241// special array operations to handle sparse arrays in a sensible fashion.
242function SmartMove(array, start_i, del_count, len, num_additional_args) {
243 // Move data to new array.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100244 var new_array = new InternalArray(len - del_count + num_additional_args);
Steve Blocka7e24c12009-10-30 11:49:00 +0000245 var intervals = %GetArrayKeys(array, len);
246 var length = intervals.length;
247 for (var k = 0; k < length; k++) {
248 var key = intervals[k];
249 if (key < 0) {
250 var j = -1 - key;
251 var interval_limit = j + intervals[++k];
252 while (j < start_i && j < interval_limit) {
253 // The spec could also be interpreted such that
254 // %HasLocalProperty would be the appropriate test. We follow
255 // KJS in consulting the prototype.
256 var current = array[j];
257 if (!IS_UNDEFINED(current) || j in array) {
258 new_array[j] = current;
259 }
260 j++;
261 }
262 j = start_i + del_count;
263 while (j < interval_limit) {
264 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also be
265 // interpreted such that %HasLocalProperty would be the
266 // appropriate test. We follow KJS in consulting the
267 // prototype.
268 var current = array[j];
269 if (!IS_UNDEFINED(current) || j in array) {
270 new_array[j - del_count + num_additional_args] = current;
271 }
272 j++;
273 }
274 } else {
275 if (!IS_UNDEFINED(key)) {
276 if (key < start_i) {
277 // The spec could also be interpreted such that
278 // %HasLocalProperty would be the appropriate test. We follow
279 // KJS in consulting the prototype.
280 var current = array[key];
281 if (!IS_UNDEFINED(current) || key in array) {
282 new_array[key] = current;
283 }
284 } else if (key >= start_i + del_count) {
285 // ECMA-262 15.4.4.12 lines 24 and 41. The spec could also
286 // be interpreted such that %HasLocalProperty would be the
287 // appropriate test. We follow KJS in consulting the
288 // prototype.
289 var current = array[key];
290 if (!IS_UNDEFINED(current) || key in array) {
291 new_array[key - del_count + num_additional_args] = current;
292 }
293 }
294 }
295 }
296 }
297 // Move contents of new_array into this array
298 %MoveArrayContents(new_array, array);
299}
300
301
302// This is part of the old simple-minded splice. We are using it either
303// because the receiver is not an array (so we have no choice) or because we
304// know we are not deleting or moving a lot of elements.
305function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
306 for (var i = 0; i < del_count; i++) {
307 var index = start_i + i;
308 // The spec could also be interpreted such that %HasLocalProperty
309 // would be the appropriate test. We follow KJS in consulting the
310 // prototype.
311 var current = array[index];
312 if (!IS_UNDEFINED(current) || index in array)
313 deleted_elements[i] = current;
314 }
315}
316
317
318function SimpleMove(array, start_i, del_count, len, num_additional_args) {
319 if (num_additional_args !== del_count) {
320 // Move the existing elements after the elements to be deleted
321 // to the right position in the resulting array.
322 if (num_additional_args > del_count) {
323 for (var i = len - del_count; i > start_i; i--) {
324 var from_index = i + del_count - 1;
325 var to_index = i + num_additional_args - 1;
326 // The spec could also be interpreted such that
327 // %HasLocalProperty would be the appropriate test. We follow
328 // KJS in consulting the prototype.
329 var current = array[from_index];
330 if (!IS_UNDEFINED(current) || from_index in array) {
331 array[to_index] = current;
332 } else {
333 delete array[to_index];
334 }
335 }
336 } else {
337 for (var i = start_i; i < len - del_count; i++) {
338 var from_index = i + del_count;
339 var to_index = i + num_additional_args;
340 // The spec could also be interpreted such that
341 // %HasLocalProperty would be the appropriate test. We follow
342 // KJS in consulting the prototype.
343 var current = array[from_index];
344 if (!IS_UNDEFINED(current) || from_index in array) {
345 array[to_index] = current;
346 } else {
347 delete array[to_index];
348 }
349 }
350 for (var i = len; i > len - del_count + num_additional_args; i--) {
351 delete array[i - 1];
352 }
353 }
354 }
355}
356
357
358// -------------------------------------------------------------------
359
360
361function ArrayToString() {
362 if (!IS_ARRAY(this)) {
363 throw new $TypeError('Array.prototype.toString is not generic');
364 }
365 return Join(this, this.length, ',', ConvertToString);
366}
367
368
369function ArrayToLocaleString() {
370 if (!IS_ARRAY(this)) {
371 throw new $TypeError('Array.prototype.toString is not generic');
372 }
373 return Join(this, this.length, ',', ConvertToLocaleString);
374}
375
376
377function ArrayJoin(separator) {
Leon Clarkee46be812010-01-19 14:06:41 +0000378 if (IS_UNDEFINED(separator)) {
379 separator = ',';
380 } else if (!IS_STRING(separator)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100381 separator = NonStringToString(separator);
Leon Clarkee46be812010-01-19 14:06:41 +0000382 }
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800383
384 var result = %_FastAsciiArrayJoin(this, separator);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100385 if (!IS_UNDEFINED(result)) return result;
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800386
Ben Murdochb0fe1622011-05-05 13:52:32 +0100387 return Join(this, TO_UINT32(this.length), separator, ConvertToString);
Steve Blocka7e24c12009-10-30 11:49:00 +0000388}
389
390
391// Removes the last element from the array and returns it. See
392// ECMA-262, section 15.4.4.6.
393function ArrayPop() {
Leon Clarkee46be812010-01-19 14:06:41 +0000394 var n = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000395 if (n == 0) {
396 this.length = n;
397 return;
398 }
399 n--;
400 var value = this[n];
401 this.length = n;
402 delete this[n];
403 return value;
404}
405
406
407// Appends the arguments to the end of the array and returns the new
408// length of the array. See ECMA-262, section 15.4.4.7.
409function ArrayPush() {
Leon Clarkee46be812010-01-19 14:06:41 +0000410 var n = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000411 var m = %_ArgumentsLength();
412 for (var i = 0; i < m; i++) {
413 this[i+n] = %_Arguments(i);
414 }
415 this.length = n + m;
416 return this.length;
417}
418
419
420function ArrayConcat(arg1) { // length == 1
Steve Blocka7e24c12009-10-30 11:49:00 +0000421 var arg_count = %_ArgumentsLength();
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100422 var arrays = new InternalArray(1 + arg_count);
Steve Blocka7e24c12009-10-30 11:49:00 +0000423 arrays[0] = this;
424 for (var i = 0; i < arg_count; i++) {
425 arrays[i + 1] = %_Arguments(i);
426 }
427
428 return %ArrayConcat(arrays);
429}
430
431
432// For implementing reverse() on large, sparse arrays.
433function SparseReverse(array, len) {
434 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
435 var high_counter = keys.length - 1;
436 var low_counter = 0;
437 while (low_counter <= high_counter) {
438 var i = keys[low_counter];
439 var j = keys[high_counter];
440
441 var j_complement = len - j - 1;
442 var low, high;
443
444 if (j_complement <= i) {
445 high = j;
446 while (keys[--high_counter] == j);
447 low = j_complement;
448 }
449 if (j_complement >= i) {
450 low = i;
451 while (keys[++low_counter] == i);
452 high = len - i - 1;
453 }
454
455 var current_i = array[low];
456 if (!IS_UNDEFINED(current_i) || low in array) {
457 var current_j = array[high];
458 if (!IS_UNDEFINED(current_j) || high in array) {
459 array[low] = current_j;
460 array[high] = current_i;
461 } else {
462 array[high] = current_i;
463 delete array[low];
464 }
465 } else {
466 var current_j = array[high];
467 if (!IS_UNDEFINED(current_j) || high in array) {
468 array[low] = current_j;
469 delete array[high];
470 }
471 }
472 }
473}
474
475
476function ArrayReverse() {
Leon Clarkee46be812010-01-19 14:06:41 +0000477 var j = TO_UINT32(this.length) - 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000478
479 if (UseSparseVariant(this, j, IS_ARRAY(this))) {
480 SparseReverse(this, j+1);
481 return this;
482 }
483
484 for (var i = 0; i < j; i++, j--) {
485 var current_i = this[i];
486 if (!IS_UNDEFINED(current_i) || i in this) {
487 var current_j = this[j];
488 if (!IS_UNDEFINED(current_j) || j in this) {
489 this[i] = current_j;
490 this[j] = current_i;
491 } else {
492 this[j] = current_i;
493 delete this[i];
494 }
495 } else {
496 var current_j = this[j];
497 if (!IS_UNDEFINED(current_j) || j in this) {
498 this[i] = current_j;
499 delete this[j];
500 }
501 }
502 }
503 return this;
504}
505
506
507function ArrayShift() {
Leon Clarkee46be812010-01-19 14:06:41 +0000508 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000509
510 if (len === 0) {
511 this.length = 0;
512 return;
513 }
514
515 var first = this[0];
516
517 if (IS_ARRAY(this))
518 SmartMove(this, 0, 1, len, 0);
519 else
520 SimpleMove(this, 0, 1, len, 0);
521
522 this.length = len - 1;
523
524 return first;
525}
526
527
528function ArrayUnshift(arg1) { // length == 1
Leon Clarkee46be812010-01-19 14:06:41 +0000529 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000530 var num_arguments = %_ArgumentsLength();
531
532 if (IS_ARRAY(this))
533 SmartMove(this, 0, 0, len, num_arguments);
534 else
535 SimpleMove(this, 0, 0, len, num_arguments);
536
537 for (var i = 0; i < num_arguments; i++) {
538 this[i] = %_Arguments(i);
539 }
540
541 this.length = len + num_arguments;
542
543 return len + num_arguments;
544}
545
546
547function ArraySlice(start, end) {
Leon Clarkee46be812010-01-19 14:06:41 +0000548 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000549 var start_i = TO_INTEGER(start);
550 var end_i = len;
551
552 if (end !== void 0) end_i = TO_INTEGER(end);
553
554 if (start_i < 0) {
555 start_i += len;
556 if (start_i < 0) start_i = 0;
557 } else {
558 if (start_i > len) start_i = len;
559 }
560
561 if (end_i < 0) {
562 end_i += len;
563 if (end_i < 0) end_i = 0;
564 } else {
565 if (end_i > len) end_i = len;
566 }
567
568 var result = [];
569
570 if (end_i < start_i) return result;
571
572 if (IS_ARRAY(this)) {
573 SmartSlice(this, start_i, end_i - start_i, len, result);
574 } else {
575 SimpleSlice(this, start_i, end_i - start_i, len, result);
576 }
577
578 result.length = end_i - start_i;
579
580 return result;
581}
582
583
584function ArraySplice(start, delete_count) {
585 var num_arguments = %_ArgumentsLength();
586
Leon Clarkee46be812010-01-19 14:06:41 +0000587 var len = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000588 var start_i = TO_INTEGER(start);
589
590 if (start_i < 0) {
591 start_i += len;
592 if (start_i < 0) start_i = 0;
593 } else {
594 if (start_i > len) start_i = len;
595 }
596
Andrei Popescu402d9372010-02-26 13:31:12 +0000597 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
Steve Block1e0659c2011-05-24 12:43:12 +0100598 // given as a request to delete all the elements from the start.
599 // And it differs from the case of undefined delete count.
Steve Blocka7e24c12009-10-30 11:49:00 +0000600 // This does not follow ECMA-262, but we do the same for
601 // compatibility.
602 var del_count = 0;
Steve Block1e0659c2011-05-24 12:43:12 +0100603 if (num_arguments == 1) {
604 del_count = len - start_i;
605 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000606 del_count = TO_INTEGER(delete_count);
607 if (del_count < 0) del_count = 0;
608 if (del_count > len - start_i) del_count = len - start_i;
Steve Blocka7e24c12009-10-30 11:49:00 +0000609 }
610
611 var deleted_elements = [];
612 deleted_elements.length = del_count;
613
614 // Number of elements to add.
615 var num_additional_args = 0;
616 if (num_arguments > 2) {
617 num_additional_args = num_arguments - 2;
618 }
619
620 var use_simple_splice = true;
621
622 if (IS_ARRAY(this) && num_additional_args !== del_count) {
623 // If we are only deleting/moving a few things near the end of the
624 // array then the simple version is going to be faster, because it
625 // doesn't touch most of the array.
626 var estimated_non_hole_elements = %EstimateNumberOfElements(this);
627 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
628 use_simple_splice = false;
629 }
630 }
631
632 if (use_simple_splice) {
633 SimpleSlice(this, start_i, del_count, len, deleted_elements);
634 SimpleMove(this, start_i, del_count, len, num_additional_args);
635 } else {
636 SmartSlice(this, start_i, del_count, len, deleted_elements);
637 SmartMove(this, start_i, del_count, len, num_additional_args);
638 }
639
640 // Insert the arguments into the resulting array in
641 // place of the deleted elements.
642 var i = start_i;
643 var arguments_index = 2;
644 var arguments_length = %_ArgumentsLength();
645 while (arguments_index < arguments_length) {
646 this[i++] = %_Arguments(arguments_index++);
647 }
648 this.length = len - del_count + num_additional_args;
649
650 // Return the deleted elements.
651 return deleted_elements;
652}
653
654
655function ArraySort(comparefn) {
656 // In-place QuickSort algorithm.
657 // For short (length <= 22) arrays, insertion sort is used for efficiency.
658
Steve Block6ded16b2010-05-10 14:33:55 +0100659 if (!IS_FUNCTION(comparefn)) {
660 comparefn = function (x, y) {
661 if (x === y) return 0;
662 if (%_IsSmi(x) && %_IsSmi(y)) {
663 return %SmiLexicographicCompare(x, y);
664 }
665 x = ToString(x);
666 y = ToString(y);
667 if (x == y) return 0;
668 else return x < y ? -1 : 1;
669 };
670 }
671 var global_receiver = %GetGlobalReceiver();
Steve Blocka7e24c12009-10-30 11:49:00 +0000672
673 function InsertionSort(a, from, to) {
674 for (var i = from + 1; i < to; i++) {
675 var element = a[i];
Steve Block6ded16b2010-05-10 14:33:55 +0100676 for (var j = i - 1; j >= from; j--) {
677 var tmp = a[j];
678 var order = %_CallFunction(global_receiver, tmp, element, comparefn);
679 if (order > 0) {
680 a[j + 1] = tmp;
681 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000682 break;
683 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000684 }
Steve Block6ded16b2010-05-10 14:33:55 +0100685 a[j + 1] = element;
Steve Blocka7e24c12009-10-30 11:49:00 +0000686 }
687 }
688
689 function QuickSort(a, from, to) {
690 // Insertion sort is faster for short arrays.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100691 if (to - from <= 10) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000692 InsertionSort(a, from, to);
693 return;
694 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100695 // Find a pivot as the median of first, last and middle element.
696 var v0 = a[from];
697 var v1 = a[to - 1];
698 var middle_index = from + ((to - from) >> 1);
699 var v2 = a[middle_index];
700 var c01 = %_CallFunction(global_receiver, v0, v1, comparefn);
701 if (c01 > 0) {
702 // v1 < v0, so swap them.
703 var tmp = v0;
704 v0 = v1;
705 v1 = tmp;
706 } // v0 <= v1.
707 var c02 = %_CallFunction(global_receiver, v0, v2, comparefn);
708 if (c02 >= 0) {
709 // v2 <= v0 <= v1.
710 var tmp = v0;
711 v0 = v2;
712 v2 = v1;
713 v1 = tmp;
714 } else {
715 // v0 <= v1 && v0 < v2
716 var c12 = %_CallFunction(global_receiver, v1, v2, comparefn);
717 if (c12 > 0) {
718 // v0 <= v2 < v1
719 var tmp = v1;
720 v1 = v2;
721 v2 = tmp;
722 }
723 }
724 // v0 <= v1 <= v2
725 a[from] = v0;
726 a[to - 1] = v2;
727 var pivot = v1;
728 var low_end = from + 1; // Upper bound of elements lower than pivot.
729 var high_start = to - 1; // Lower bound of elements greater than pivot.
730 a[middle_index] = a[low_end];
731 a[low_end] = pivot;
732
Steve Blocka7e24c12009-10-30 11:49:00 +0000733 // From low_end to i are elements equal to pivot.
734 // From i to high_start are elements that haven't been compared yet.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100735 partition: for (var i = low_end + 1; i < high_start; i++) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000736 var element = a[i];
Steve Block6ded16b2010-05-10 14:33:55 +0100737 var order = %_CallFunction(global_receiver, element, pivot, comparefn);
Steve Blocka7e24c12009-10-30 11:49:00 +0000738 if (order < 0) {
Steve Block6ded16b2010-05-10 14:33:55 +0100739 %_SwapElements(a, i, low_end);
Steve Blocka7e24c12009-10-30 11:49:00 +0000740 low_end++;
741 } else if (order > 0) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100742 do {
743 high_start--;
744 if (high_start == i) break partition;
745 var top_elem = a[high_start];
746 order = %_CallFunction(global_receiver, top_elem, pivot, comparefn);
747 } while (order > 0);
Steve Block6ded16b2010-05-10 14:33:55 +0100748 %_SwapElements(a, i, high_start);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100749 if (order < 0) {
750 %_SwapElements(a, i, low_end);
751 low_end++;
752 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000753 }
754 }
755 QuickSort(a, from, low_end);
756 QuickSort(a, high_start, to);
757 }
758
Ben Murdochb0fe1622011-05-05 13:52:32 +0100759 // Copy elements in the range 0..length from obj's prototype chain
760 // to obj itself, if obj has holes. Return one more than the maximal index
Steve Blocka7e24c12009-10-30 11:49:00 +0000761 // of a prototype property.
762 function CopyFromPrototype(obj, length) {
763 var max = 0;
764 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
765 var indices = %GetArrayKeys(proto, length);
766 if (indices.length > 0) {
767 if (indices[0] == -1) {
768 // It's an interval.
769 var proto_length = indices[1];
770 for (var i = 0; i < proto_length; i++) {
771 if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
772 obj[i] = proto[i];
773 if (i >= max) { max = i + 1; }
774 }
775 }
776 } else {
777 for (var i = 0; i < indices.length; i++) {
778 var index = indices[i];
779 if (!IS_UNDEFINED(index) &&
780 !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
781 obj[index] = proto[index];
782 if (index >= max) { max = index + 1; }
783 }
784 }
785 }
786 }
787 }
788 return max;
789 }
790
791 // Set a value of "undefined" on all indices in the range from..to
792 // where a prototype of obj has an element. I.e., shadow all prototype
793 // elements in that range.
794 function ShadowPrototypeElements(obj, from, to) {
795 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
796 var indices = %GetArrayKeys(proto, to);
797 if (indices.length > 0) {
798 if (indices[0] == -1) {
799 // It's an interval.
800 var proto_length = indices[1];
801 for (var i = from; i < proto_length; i++) {
802 if (proto.hasOwnProperty(i)) {
803 obj[i] = void 0;
804 }
805 }
806 } else {
807 for (var i = 0; i < indices.length; i++) {
808 var index = indices[i];
809 if (!IS_UNDEFINED(index) && from <= index &&
810 proto.hasOwnProperty(index)) {
811 obj[index] = void 0;
812 }
813 }
814 }
815 }
816 }
817 }
818
819 function SafeRemoveArrayHoles(obj) {
820 // Copy defined elements from the end to fill in all holes and undefineds
821 // in the beginning of the array. Write undefineds and holes at the end
822 // after loop is finished.
823 var first_undefined = 0;
824 var last_defined = length - 1;
825 var num_holes = 0;
826 while (first_undefined < last_defined) {
827 // Find first undefined element.
828 while (first_undefined < last_defined &&
829 !IS_UNDEFINED(obj[first_undefined])) {
830 first_undefined++;
831 }
832 // Maintain the invariant num_holes = the number of holes in the original
833 // array with indices <= first_undefined or > last_defined.
834 if (!obj.hasOwnProperty(first_undefined)) {
835 num_holes++;
836 }
837
838 // Find last defined element.
839 while (first_undefined < last_defined &&
840 IS_UNDEFINED(obj[last_defined])) {
841 if (!obj.hasOwnProperty(last_defined)) {
842 num_holes++;
843 }
844 last_defined--;
845 }
846 if (first_undefined < last_defined) {
847 // Fill in hole or undefined.
848 obj[first_undefined] = obj[last_defined];
849 obj[last_defined] = void 0;
850 }
851 }
852 // If there were any undefineds in the entire array, first_undefined
853 // points to one past the last defined element. Make this true if
854 // there were no undefineds, as well, so that first_undefined == number
855 // of defined elements.
856 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
857 // Fill in the undefineds and the holes. There may be a hole where
858 // an undefined should be and vice versa.
859 var i;
860 for (i = first_undefined; i < length - num_holes; i++) {
861 obj[i] = void 0;
862 }
863 for (i = length - num_holes; i < length; i++) {
864 // For compatability with Webkit, do not expose elements in the prototype.
865 if (i in obj.__proto__) {
866 obj[i] = void 0;
867 } else {
868 delete obj[i];
869 }
870 }
871
872 // Return the number of defined elements.
873 return first_undefined;
874 }
875
Steve Block6ded16b2010-05-10 14:33:55 +0100876 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000877 if (length < 2) return this;
878
879 var is_array = IS_ARRAY(this);
880 var max_prototype_element;
881 if (!is_array) {
882 // For compatibility with JSC, we also sort elements inherited from
883 // the prototype chain on non-Array objects.
884 // We do this by copying them to this object and sorting only
885 // local elements. This is not very efficient, but sorting with
886 // inherited elements happens very, very rarely, if at all.
887 // The specification allows "implementation dependent" behavior
888 // if an element on the prototype chain has an element that
889 // might interact with sorting.
890 max_prototype_element = CopyFromPrototype(this, length);
891 }
892
893 var num_non_undefined = %RemoveArrayHoles(this, length);
894 if (num_non_undefined == -1) {
895 // There were indexed accessors in the array. Move array holes and
896 // undefineds to the end using a Javascript function that is safe
897 // in the presence of accessors.
898 num_non_undefined = SafeRemoveArrayHoles(this);
899 }
900
901 QuickSort(this, 0, num_non_undefined);
902
903 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
904 // For compatibility with JSC, we shadow any elements in the prototype
905 // chain that has become exposed by sort moving a hole to its position.
906 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
907 }
908
909 return this;
910}
911
912
913// The following functions cannot be made efficient on sparse arrays while
914// preserving the semantics, since the calls to the receiver function can add
915// or delete elements from the array.
916function ArrayFilter(f, receiver) {
917 if (!IS_FUNCTION(f)) {
918 throw MakeTypeError('called_non_callable', [ f ]);
919 }
920 // Pull out the length so that modifications to the length in the
921 // loop will not affect the looping.
922 var length = this.length;
923 var result = [];
924 var result_length = 0;
925 for (var i = 0; i < length; i++) {
926 var current = this[i];
927 if (!IS_UNDEFINED(current) || i in this) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100928 if (f.call(receiver, current, i, this)) {
929 result[result_length++] = current;
930 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000931 }
932 }
933 return result;
934}
935
936
937function ArrayForEach(f, receiver) {
938 if (!IS_FUNCTION(f)) {
939 throw MakeTypeError('called_non_callable', [ f ]);
940 }
941 // Pull out the length so that modifications to the length in the
942 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +0000943 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000944 for (var i = 0; i < length; i++) {
945 var current = this[i];
946 if (!IS_UNDEFINED(current) || i in this) {
947 f.call(receiver, current, i, this);
948 }
949 }
950}
951
952
953// Executes the function once for each element present in the
954// array until it finds one where callback returns true.
955function ArraySome(f, receiver) {
956 if (!IS_FUNCTION(f)) {
957 throw MakeTypeError('called_non_callable', [ f ]);
958 }
959 // Pull out the length so that modifications to the length in the
960 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +0000961 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000962 for (var i = 0; i < length; i++) {
963 var current = this[i];
964 if (!IS_UNDEFINED(current) || i in this) {
965 if (f.call(receiver, current, i, this)) return true;
966 }
967 }
968 return false;
969}
970
971
972function ArrayEvery(f, receiver) {
973 if (!IS_FUNCTION(f)) {
974 throw MakeTypeError('called_non_callable', [ f ]);
975 }
976 // Pull out the length so that modifications to the length in the
977 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +0000978 var length = TO_UINT32(this.length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000979 for (var i = 0; i < length; i++) {
980 var current = this[i];
981 if (!IS_UNDEFINED(current) || i in this) {
982 if (!f.call(receiver, current, i, this)) return false;
983 }
984 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000985 return true;
986}
987
Steve Blocka7e24c12009-10-30 11:49:00 +0000988function ArrayMap(f, receiver) {
989 if (!IS_FUNCTION(f)) {
990 throw MakeTypeError('called_non_callable', [ f ]);
991 }
992 // Pull out the length so that modifications to the length in the
993 // loop will not affect the looping.
Leon Clarkee46be812010-01-19 14:06:41 +0000994 var length = TO_UINT32(this.length);
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100995 var result = new $Array();
996 var accumulator = new InternalArray(length);
Steve Blocka7e24c12009-10-30 11:49:00 +0000997 for (var i = 0; i < length; i++) {
998 var current = this[i];
999 if (!IS_UNDEFINED(current) || i in this) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001000 accumulator[i] = f.call(receiver, current, i, this);
Steve Blocka7e24c12009-10-30 11:49:00 +00001001 }
1002 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001003 %MoveArrayContents(accumulator, result);
Steve Blocka7e24c12009-10-30 11:49:00 +00001004 return result;
1005}
1006
1007
1008function ArrayIndexOf(element, index) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001009 var length = TO_UINT32(this.length);
1010 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001011 if (IS_UNDEFINED(index)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001012 index = 0;
1013 } else {
1014 index = TO_INTEGER(index);
1015 // If index is negative, index from the end of the array.
Steve Block1e0659c2011-05-24 12:43:12 +01001016 if (index < 0) {
1017 index = length + index;
1018 // If index is still negative, search the entire array.
1019 if (index < 0) index = 0;
1020 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001021 }
Steve Block59151502010-09-22 15:07:15 +01001022 var min = index;
1023 var max = length;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001024 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001025 var intervals = %GetArrayKeys(this, length);
1026 if (intervals.length == 2 && intervals[0] < 0) {
1027 // A single interval.
1028 var intervalMin = -(intervals[0] + 1);
1029 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001030 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001031 max = intervalMax; // Capped by length already.
1032 // Fall through to loop below.
1033 } else {
1034 if (intervals.length == 0) return -1;
1035 // Get all the keys in sorted order.
1036 var sortedKeys = GetSortedArrayKeys(this, intervals);
1037 var n = sortedKeys.length;
1038 var i = 0;
1039 while (i < n && sortedKeys[i] < index) i++;
1040 while (i < n) {
1041 var key = sortedKeys[i];
1042 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1043 i++;
1044 }
1045 return -1;
1046 }
1047 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001048 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001049 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001050 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001051 if (this[i] === element) return i;
1052 }
1053 return -1;
1054 }
Steve Block59151502010-09-22 15:07:15 +01001055 // Lookup through the array.
1056 for (var i = min; i < max; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +01001057 if (IS_UNDEFINED(this[i]) && i in this) {
1058 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001059 }
1060 }
1061 return -1;
1062}
1063
1064
1065function ArrayLastIndexOf(element, index) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001066 var length = TO_UINT32(this.length);
1067 if (length == 0) return -1;
Steve Block8defd9f2010-07-08 12:39:36 +01001068 if (%_ArgumentsLength() < 2) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001069 index = length - 1;
1070 } else {
1071 index = TO_INTEGER(index);
1072 // If index is negative, index from end of the array.
Steve Block59151502010-09-22 15:07:15 +01001073 if (index < 0) index += length;
Steve Blocka7e24c12009-10-30 11:49:00 +00001074 // If index is still negative, do not search the array.
Steve Block59151502010-09-22 15:07:15 +01001075 if (index < 0) return -1;
Steve Blocka7e24c12009-10-30 11:49:00 +00001076 else if (index >= length) index = length - 1;
1077 }
Steve Block59151502010-09-22 15:07:15 +01001078 var min = 0;
1079 var max = index;
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001080 if (UseSparseVariant(this, length, IS_ARRAY(this))) {
Steve Block59151502010-09-22 15:07:15 +01001081 var intervals = %GetArrayKeys(this, index + 1);
1082 if (intervals.length == 2 && intervals[0] < 0) {
1083 // A single interval.
1084 var intervalMin = -(intervals[0] + 1);
1085 var intervalMax = intervalMin + intervals[1];
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001086 if (min < intervalMin) min = intervalMin;
Steve Block59151502010-09-22 15:07:15 +01001087 max = intervalMax; // Capped by index already.
1088 // Fall through to loop below.
1089 } else {
1090 if (intervals.length == 0) return -1;
1091 // Get all the keys in sorted order.
1092 var sortedKeys = GetSortedArrayKeys(this, intervals);
1093 var i = sortedKeys.length - 1;
1094 while (i >= 0) {
1095 var key = sortedKeys[i];
1096 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1097 i--;
1098 }
1099 return -1;
1100 }
1101 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001102 // Lookup through the array.
Steve Block6ded16b2010-05-10 14:33:55 +01001103 if (!IS_UNDEFINED(element)) {
Steve Block59151502010-09-22 15:07:15 +01001104 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001105 if (this[i] === element) return i;
1106 }
1107 return -1;
1108 }
Steve Block59151502010-09-22 15:07:15 +01001109 for (var i = max; i >= min; i--) {
Steve Block6ded16b2010-05-10 14:33:55 +01001110 if (IS_UNDEFINED(this[i]) && i in this) {
1111 return i;
Steve Blocka7e24c12009-10-30 11:49:00 +00001112 }
1113 }
1114 return -1;
1115}
1116
1117
1118function ArrayReduce(callback, current) {
1119 if (!IS_FUNCTION(callback)) {
1120 throw MakeTypeError('called_non_callable', [callback]);
1121 }
1122 // Pull out the length so that modifications to the length in the
1123 // loop will not affect the looping.
1124 var length = this.length;
1125 var i = 0;
1126
1127 find_initial: if (%_ArgumentsLength() < 2) {
1128 for (; i < length; i++) {
1129 current = this[i];
1130 if (!IS_UNDEFINED(current) || i in this) {
1131 i++;
1132 break find_initial;
1133 }
1134 }
1135 throw MakeTypeError('reduce_no_initial', []);
1136 }
1137
1138 for (; i < length; i++) {
1139 var element = this[i];
1140 if (!IS_UNDEFINED(element) || i in this) {
1141 current = callback.call(null, current, element, i, this);
1142 }
1143 }
1144 return current;
1145}
1146
1147function ArrayReduceRight(callback, current) {
1148 if (!IS_FUNCTION(callback)) {
1149 throw MakeTypeError('called_non_callable', [callback]);
1150 }
1151 var i = this.length - 1;
1152
1153 find_initial: if (%_ArgumentsLength() < 2) {
1154 for (; i >= 0; i--) {
1155 current = this[i];
1156 if (!IS_UNDEFINED(current) || i in this) {
1157 i--;
1158 break find_initial;
1159 }
1160 }
1161 throw MakeTypeError('reduce_no_initial', []);
1162 }
1163
1164 for (; i >= 0; i--) {
1165 var element = this[i];
1166 if (!IS_UNDEFINED(element) || i in this) {
1167 current = callback.call(null, current, element, i, this);
1168 }
1169 }
1170 return current;
1171}
1172
Steve Block3ce2e202009-11-05 08:53:23 +00001173// ES5, 15.4.3.2
1174function ArrayIsArray(obj) {
1175 return IS_ARRAY(obj);
1176}
Steve Blocka7e24c12009-10-30 11:49:00 +00001177
Steve Blocka7e24c12009-10-30 11:49:00 +00001178
1179// -------------------------------------------------------------------
1180function SetupArray() {
1181 // Setup non-enumerable constructor property on the Array.prototype
1182 // object.
1183 %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
1184
Steve Block3ce2e202009-11-05 08:53:23 +00001185 // Setup non-enumerable functions on the Array object.
1186 InstallFunctions($Array, DONT_ENUM, $Array(
1187 "isArray", ArrayIsArray
1188 ));
1189
Steve Block6ded16b2010-05-10 14:33:55 +01001190 var specialFunctions = %SpecialArrayFunctions({});
1191
1192 function getFunction(name, jsBuiltin, len) {
1193 var f = jsBuiltin;
1194 if (specialFunctions.hasOwnProperty(name)) {
1195 f = specialFunctions[name];
1196 }
1197 if (!IS_UNDEFINED(len)) {
1198 %FunctionSetLength(f, len);
1199 }
1200 return f;
1201 }
1202
Steve Blocka7e24c12009-10-30 11:49:00 +00001203 // Setup non-enumerable functions of the Array.prototype object and
1204 // set their names.
Steve Blocka7e24c12009-10-30 11:49:00 +00001205 // Manipulate the length of some of the functions to meet
1206 // expectations set by ECMA-262 or Mozilla.
Steve Block6ded16b2010-05-10 14:33:55 +01001207 InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
1208 "toString", getFunction("toString", ArrayToString),
1209 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1210 "join", getFunction("join", ArrayJoin),
1211 "pop", getFunction("pop", ArrayPop),
1212 "push", getFunction("push", ArrayPush, 1),
1213 "concat", getFunction("concat", ArrayConcat, 1),
1214 "reverse", getFunction("reverse", ArrayReverse),
1215 "shift", getFunction("shift", ArrayShift),
1216 "unshift", getFunction("unshift", ArrayUnshift, 1),
1217 "slice", getFunction("slice", ArraySlice, 2),
1218 "splice", getFunction("splice", ArraySplice, 2),
1219 "sort", getFunction("sort", ArraySort),
1220 "filter", getFunction("filter", ArrayFilter, 1),
1221 "forEach", getFunction("forEach", ArrayForEach, 1),
1222 "some", getFunction("some", ArraySome, 1),
1223 "every", getFunction("every", ArrayEvery, 1),
1224 "map", getFunction("map", ArrayMap, 1),
1225 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1226 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1227 "reduce", getFunction("reduce", ArrayReduce, 1),
1228 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1229 ));
1230
1231 %FinishArrayPrototypeSetup($Array.prototype);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001232
1233 // The internal Array prototype doesn't need to be fancy, since it's never
1234 // exposed to user code, so no hidden prototypes or DONT_ENUM attributes
1235 // are necessary.
1236 // The null __proto__ ensures that we never inherit any user created
1237 // getters or setters from, e.g., Object.prototype.
1238 InternalArray.prototype.__proto__ = null;
1239 // Adding only the functions that are actually used, and a toString.
1240 InternalArray.prototype.join = getFunction("join", ArrayJoin);
1241 InternalArray.prototype.pop = getFunction("pop", ArrayPop);
1242 InternalArray.prototype.push = getFunction("push", ArrayPush);
1243 InternalArray.prototype.toString = function() {
1244 return "Internal Array, length " + this.length;
1245 };
Steve Blocka7e24c12009-10-30 11:49:00 +00001246}
1247
1248
1249SetupArray();