1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
13 (function(global, utils) {
17 %CheckIsBootstrapping();
19 // -------------------------------------------------------------------
22 var GlobalArray = global.Array;
23 var InternalArray = utils.InternalArray;
24 var InternalPackedArray = utils.InternalPackedArray;
28 var ObjectHasOwnProperty;
33 utils.Import(function(from) {
35 MathMin = from.MathMin;
36 ObjectHasOwnProperty = from.ObjectHasOwnProperty;
37 ObjectIsFrozen = from.ObjectIsFrozen;
38 ObjectIsSealed = from.ObjectIsSealed;
39 ObjectToString = from.ObjectToString;
42 // -------------------------------------------------------------------
44 // Global list of arrays visited during toString, toLocaleString and
46 var visited_arrays = new InternalArray();
49 // Gets a sorted array of array keys. Useful for operations on sparse
50 // arrays. Dupes have not been removed.
51 function GetSortedArrayKeys(array, indices) {
52 var keys = new InternalArray();
53 if (IS_NUMBER(indices)) {
56 for (var i = 0; i < limit; ++i) {
58 if (!IS_UNDEFINED(e) || i in array) {
63 var length = indices.length;
64 for (var k = 0; k < length; ++k) {
66 if (!IS_UNDEFINED(key)) {
68 if (!IS_UNDEFINED(e) || key in array) {
73 %_CallFunction(keys, function(a, b) { return a - b; }, ArraySort);
79 function SparseJoinWithSeparatorJS(array, len, convert, separator) {
80 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
82 var elements = new InternalArray(keys.length * 2);
84 for (var i = 0; i < keys.length; i++) {
86 if (key != previousKey) { // keys may contain duplicates.
88 if (!IS_STRING(e)) e = convert(e);
89 elements[i * 2] = key;
90 elements[i * 2 + 1] = e;
94 return %SparseJoinWithSeparator(elements, len, separator);
98 // Optimized for sparse arrays if separator is ''.
99 function SparseJoin(array, len, convert) {
100 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
102 var keys_length = keys.length;
104 var elements = new InternalArray(keys_length);
105 var elements_length = 0;
107 for (var i = 0; i < keys_length; i++) {
109 if (key != last_key) {
111 if (!IS_STRING(e)) e = convert(e);
112 elements[elements_length++] = e;
116 return %StringBuilderConcat(elements, elements_length, '');
120 function UseSparseVariant(array, length, is_array, touched) {
121 // Only use the sparse variant on arrays that are likely to be sparse and the
122 // number of elements touched in the operation is relatively small compared to
123 // the overall size of the array.
124 if (!is_array || length < 1000 || %IsObserved(array) ||
125 %HasComplexElements(array)) {
128 if (!%_IsSmi(length)) {
131 var elements_threshold = length >> 2; // No more than 75% holes
132 var estimated_elements = %EstimateNumberOfElements(array);
133 return (estimated_elements < elements_threshold) &&
134 (touched > estimated_elements * 4);
138 function Join(array, length, separator, convert) {
139 if (length == 0) return '';
141 var is_array = IS_ARRAY(array);
144 // If the array is cyclic, return the empty string for already
146 if (!%PushIfAbsent(visited_arrays, array)) return '';
149 // Attempt to convert the elements.
151 if (UseSparseVariant(array, length, is_array, length)) {
152 %NormalizeElements(array);
153 if (separator.length == 0) {
154 return SparseJoin(array, length, convert);
156 return SparseJoinWithSeparatorJS(array, length, convert, separator);
160 // Fast case for one-element arrays.
163 if (IS_STRING(e)) return e;
167 // Construct an array for the elements.
168 var elements = new InternalArray(length);
170 // We pull the empty separator check outside the loop for speed!
171 if (separator.length == 0) {
172 var elements_length = 0;
173 for (var i = 0; i < length; i++) {
175 if (!IS_STRING(e)) e = convert(e);
176 elements[elements_length++] = e;
178 elements.length = elements_length;
179 var result = %_FastOneByteArrayJoin(elements, '');
180 if (!IS_UNDEFINED(result)) return result;
181 return %StringBuilderConcat(elements, elements_length, '');
183 // Non-empty separator case.
184 // If the first element is a number then use the heuristic that the
185 // remaining elements are also likely to be numbers.
186 if (!IS_NUMBER(array[0])) {
187 for (var i = 0; i < length; i++) {
189 if (!IS_STRING(e)) e = convert(e);
193 for (var i = 0; i < length; i++) {
196 e = %_NumberToString(e);
197 } else if (!IS_STRING(e)) {
203 var result = %_FastOneByteArrayJoin(elements, separator);
204 if (!IS_UNDEFINED(result)) return result;
206 return %StringBuilderJoin(elements, length, separator);
208 // Make sure to remove the last element of the visited array no
209 // matter what happens.
210 if (is_array) visited_arrays.length = visited_arrays.length - 1;
215 function ConvertToString(x) {
216 // Assumes x is a non-string.
217 if (IS_NUMBER(x)) return %_NumberToString(x);
218 if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
219 return (IS_NULL_OR_UNDEFINED(x)) ? '' : $toString($defaultString(x));
223 function ConvertToLocaleString(e) {
224 if (IS_NULL_OR_UNDEFINED(e)) {
227 // According to ES5, section 15.4.4.3, the toLocaleString conversion
228 // must throw a TypeError if ToObject(e).toLocaleString isn't
230 var e_obj = TO_OBJECT(e);
231 return $toString(e_obj.toLocaleString());
236 // This function implements the optimized splice implementation that can use
237 // special array operations to handle sparse arrays in a sensible fashion.
238 function SparseSlice(array, start_i, del_count, len, deleted_elements) {
239 // Move deleted elements to a new array (the return value from splice).
240 var indices = %GetArrayKeys(array, start_i + del_count);
241 if (IS_NUMBER(indices)) {
243 for (var i = start_i; i < limit; ++i) {
244 var current = array[i];
245 if (!IS_UNDEFINED(current) || i in array) {
246 %AddElement(deleted_elements, i - start_i, current);
250 var length = indices.length;
251 for (var k = 0; k < length; ++k) {
252 var key = indices[k];
253 if (!IS_UNDEFINED(key)) {
254 if (key >= start_i) {
255 var current = array[key];
256 if (!IS_UNDEFINED(current) || key in array) {
257 %AddElement(deleted_elements, key - start_i, current);
266 // This function implements the optimized splice implementation that can use
267 // special array operations to handle sparse arrays in a sensible fashion.
268 function SparseMove(array, start_i, del_count, len, num_additional_args) {
269 // Bail out if no moving is necessary.
270 if (num_additional_args === del_count) return;
271 // Move data to new array.
272 var new_array = new InternalArray(
273 // Clamp array length to 2^32-1 to avoid early RangeError.
274 MathMin(len - del_count + num_additional_args, 0xffffffff));
276 var indices = %GetArrayKeys(array, len);
277 if (IS_NUMBER(indices)) {
279 for (var i = 0; i < start_i && i < limit; ++i) {
280 var current = array[i];
281 if (!IS_UNDEFINED(current) || i in array) {
282 new_array[i] = current;
285 for (var i = start_i + del_count; i < limit; ++i) {
286 var current = array[i];
287 if (!IS_UNDEFINED(current) || i in array) {
288 new_array[i - del_count + num_additional_args] = current;
292 var length = indices.length;
293 for (var k = 0; k < length; ++k) {
294 var key = indices[k];
295 if (!IS_UNDEFINED(key)) {
297 var current = array[key];
298 if (!IS_UNDEFINED(current) || key in array) {
299 new_array[key] = current;
301 } else if (key >= start_i + del_count) {
302 var current = array[key];
303 if (!IS_UNDEFINED(current) || key in array) {
304 var new_key = key - del_count + num_additional_args;
305 new_array[new_key] = current;
306 if (new_key > 0xfffffffe) {
307 big_indices = big_indices || new InternalArray();
308 big_indices.push(new_key);
315 // Move contents of new_array into this array
316 %MoveArrayContents(new_array, array);
317 // Add any moved values that aren't elements anymore.
318 if (!IS_UNDEFINED(big_indices)) {
319 var length = big_indices.length;
320 for (var i = 0; i < length; ++i) {
321 var key = big_indices[i];
322 array[key] = new_array[key];
328 // This is part of the old simple-minded splice. We are using it either
329 // because the receiver is not an array (so we have no choice) or because we
330 // know we are not deleting or moving a lot of elements.
331 function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
332 var is_array = IS_ARRAY(array);
333 for (var i = 0; i < del_count; i++) {
334 var index = start_i + i;
335 if (HAS_INDEX(array, index, is_array)) {
336 var current = array[index];
337 // The spec requires [[DefineOwnProperty]] here, %AddElement is close
338 // enough (in that it ignores the prototype).
339 %AddElement(deleted_elements, i, current);
345 function SimpleMove(array, start_i, del_count, len, num_additional_args) {
346 var is_array = IS_ARRAY(array);
347 if (num_additional_args !== del_count) {
348 // Move the existing elements after the elements to be deleted
349 // to the right position in the resulting array.
350 if (num_additional_args > del_count) {
351 for (var i = len - del_count; i > start_i; i--) {
352 var from_index = i + del_count - 1;
353 var to_index = i + num_additional_args - 1;
354 if (HAS_INDEX(array, from_index, is_array)) {
355 array[to_index] = array[from_index];
357 delete array[to_index];
361 for (var i = start_i; i < len - del_count; i++) {
362 var from_index = i + del_count;
363 var to_index = i + num_additional_args;
364 if (HAS_INDEX(array, from_index, is_array)) {
365 array[to_index] = array[from_index];
367 delete array[to_index];
370 for (var i = len; i > len - del_count + num_additional_args; i--) {
378 // -------------------------------------------------------------------
381 function ArrayToString() {
384 if (IS_ARRAY(this)) {
386 if (func === ArrayJoin) {
387 return Join(this, this.length, ',', ConvertToString);
391 array = TO_OBJECT(this);
394 if (!IS_SPEC_FUNCTION(func)) {
395 return %_CallFunction(array, ObjectToString);
397 return %_CallFunction(array, func);
401 function InnerArrayToLocaleString(array, length) {
402 var len = TO_UINT32(length);
403 if (len === 0) return "";
404 return Join(array, len, ',', ConvertToLocaleString);
408 function ArrayToLocaleString() {
409 var array = TO_OBJECT(this);
410 var arrayLen = array.length;
411 return InnerArrayToLocaleString(array, arrayLen);
415 function InnerArrayJoin(separator, array, length) {
416 if (IS_UNDEFINED(separator)) {
418 } else if (!IS_STRING(separator)) {
419 separator = $nonStringToString(separator);
422 var result = %_FastOneByteArrayJoin(array, separator);
423 if (!IS_UNDEFINED(result)) return result;
425 // Fast case for one-element arrays.
428 if (IS_STRING(e)) return e;
429 if (IS_NULL_OR_UNDEFINED(e)) return '';
430 return $nonStringToString(e);
433 return Join(array, length, separator, ConvertToString);
437 function ArrayJoin(separator) {
438 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
440 var array = TO_OBJECT(this);
441 var length = TO_UINT32(array.length);
443 return InnerArrayJoin(separator, array, length);
447 function ObservedArrayPop(n) {
452 $observeBeginPerformSplice(this);
456 $observeEndPerformSplice(this);
457 $observeEnqueueSpliceRecord(this, n, [value], 0);
464 // Removes the last element from the array and returns it. See
465 // ECMA-262, section 15.4.4.6.
466 function ArrayPop() {
467 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
469 var array = TO_OBJECT(this);
470 var n = TO_UINT32(array.length);
476 if (%IsObserved(array))
477 return ObservedArrayPop.call(array, n);
480 var value = array[n];
481 Delete(array, n, true);
487 function ObservedArrayPush() {
488 var n = TO_UINT32(this.length);
489 var m = %_ArgumentsLength();
492 $observeBeginPerformSplice(this);
493 for (var i = 0; i < m; i++) {
494 this[i+n] = %_Arguments(i);
496 var new_length = n + m;
497 this.length = new_length;
499 $observeEndPerformSplice(this);
500 $observeEnqueueSpliceRecord(this, n, [], m);
507 // Appends the arguments to the end of the array and returns the new
508 // length of the array. See ECMA-262, section 15.4.4.7.
509 function ArrayPush() {
510 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
512 if (%IsObserved(this))
513 return ObservedArrayPush.apply(this, arguments);
515 var array = TO_OBJECT(this);
516 var n = TO_UINT32(array.length);
517 var m = %_ArgumentsLength();
519 for (var i = 0; i < m; i++) {
520 array[i+n] = %_Arguments(i);
523 var new_length = n + m;
524 array.length = new_length;
529 // Returns an array containing the array elements of the object followed
530 // by the array elements of each argument in order. See ECMA-262,
532 function ArrayConcatJS(arg1) { // length == 1
533 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.concat");
535 var array = TO_OBJECT(this);
536 var arg_count = %_ArgumentsLength();
537 var arrays = new InternalArray(1 + arg_count);
539 for (var i = 0; i < arg_count; i++) {
540 arrays[i + 1] = %_Arguments(i);
543 return %ArrayConcat(arrays);
547 // For implementing reverse() on large, sparse arrays.
548 function SparseReverse(array, len) {
549 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
550 var high_counter = keys.length - 1;
552 while (low_counter <= high_counter) {
553 var i = keys[low_counter];
554 var j = keys[high_counter];
556 var j_complement = len - j - 1;
559 if (j_complement <= i) {
561 while (keys[--high_counter] == j) { }
564 if (j_complement >= i) {
566 while (keys[++low_counter] == i) { }
570 var current_i = array[low];
571 if (!IS_UNDEFINED(current_i) || low in array) {
572 var current_j = array[high];
573 if (!IS_UNDEFINED(current_j) || high in array) {
574 array[low] = current_j;
575 array[high] = current_i;
577 array[high] = current_i;
581 var current_j = array[high];
582 if (!IS_UNDEFINED(current_j) || high in array) {
583 array[low] = current_j;
590 function PackedArrayReverse(array, len) {
592 for (var i = 0; i < j; i++, j--) {
593 var current_i = array[i];
594 var current_j = array[j];
595 array[i] = current_j;
596 array[j] = current_i;
602 function GenericArrayReverse(array, len) {
604 for (var i = 0; i < j; i++, j--) {
606 var current_i = array[i];
608 var current_j = array[j];
609 array[i] = current_j;
610 array[j] = current_i;
612 array[j] = current_i;
617 var current_j = array[j];
618 array[i] = current_j;
627 function ArrayReverse() {
628 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
630 var array = TO_OBJECT(this);
631 var len = TO_UINT32(array.length);
632 var isArray = IS_ARRAY(array);
634 if (UseSparseVariant(array, len, isArray, len)) {
635 %NormalizeElements(array);
636 SparseReverse(array, len);
638 } else if (isArray && %_HasFastPackedElements(array)) {
639 return PackedArrayReverse(array, len);
641 return GenericArrayReverse(array, len);
646 function ObservedArrayShift(len) {
650 $observeBeginPerformSplice(this);
651 SimpleMove(this, 0, 1, len, 0);
652 this.length = len - 1;
654 $observeEndPerformSplice(this);
655 $observeEnqueueSpliceRecord(this, 0, [first], 0);
662 function ArrayShift() {
663 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
665 var array = TO_OBJECT(this);
666 var len = TO_UINT32(array.length);
673 if (ObjectIsSealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed);
675 if (%IsObserved(array))
676 return ObservedArrayShift.call(array, len);
678 var first = array[0];
680 if (UseSparseVariant(array, len, IS_ARRAY(array), len)) {
681 SparseMove(array, 0, 1, len, 0);
683 SimpleMove(array, 0, 1, len, 0);
686 array.length = len - 1;
692 function ObservedArrayUnshift() {
693 var len = TO_UINT32(this.length);
694 var num_arguments = %_ArgumentsLength();
697 $observeBeginPerformSplice(this);
698 SimpleMove(this, 0, 0, len, num_arguments);
699 for (var i = 0; i < num_arguments; i++) {
700 this[i] = %_Arguments(i);
702 var new_length = len + num_arguments;
703 this.length = new_length;
705 $observeEndPerformSplice(this);
706 $observeEnqueueSpliceRecord(this, 0, [], num_arguments);
713 function ArrayUnshift(arg1) { // length == 1
714 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
716 if (%IsObserved(this))
717 return ObservedArrayUnshift.apply(this, arguments);
719 var array = TO_OBJECT(this);
720 var len = TO_UINT32(array.length);
721 var num_arguments = %_ArgumentsLength();
723 if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) &&
724 !ObjectIsSealed(array)) {
725 SparseMove(array, 0, 0, len, num_arguments);
727 SimpleMove(array, 0, 0, len, num_arguments);
730 for (var i = 0; i < num_arguments; i++) {
731 array[i] = %_Arguments(i);
734 var new_length = len + num_arguments;
735 array.length = new_length;
740 function ArraySlice(start, end) {
741 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
743 var array = TO_OBJECT(this);
744 var len = TO_UINT32(array.length);
745 var start_i = TO_INTEGER(start);
748 if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
752 if (start_i < 0) start_i = 0;
754 if (start_i > len) start_i = len;
759 if (end_i < 0) end_i = 0;
761 if (end_i > len) end_i = len;
766 if (end_i < start_i) return result;
768 if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
769 %NormalizeElements(array);
770 %NormalizeElements(result);
771 SparseSlice(array, start_i, end_i - start_i, len, result);
773 SimpleSlice(array, start_i, end_i - start_i, len, result);
776 result.length = end_i - start_i;
782 function ComputeSpliceStartIndex(start_i, len) {
785 return start_i < 0 ? 0 : start_i;
788 return start_i > len ? len : start_i;
792 function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
793 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
794 // given as a request to delete all the elements from the start.
795 // And it differs from the case of undefined delete count.
796 // This does not follow ECMA-262, but we do the same for
799 if (num_arguments == 1)
800 return len - start_i;
802 del_count = TO_INTEGER(delete_count);
806 if (del_count > len - start_i)
807 return len - start_i;
813 function ObservedArraySplice(start, delete_count) {
814 var num_arguments = %_ArgumentsLength();
815 var len = TO_UINT32(this.length);
816 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
817 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
819 var deleted_elements = [];
820 deleted_elements.length = del_count;
821 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
824 $observeBeginPerformSplice(this);
826 SimpleSlice(this, start_i, del_count, len, deleted_elements);
827 SimpleMove(this, start_i, del_count, len, num_elements_to_add);
829 // Insert the arguments into the resulting array in
830 // place of the deleted elements.
832 var arguments_index = 2;
833 var arguments_length = %_ArgumentsLength();
834 while (arguments_index < arguments_length) {
835 this[i++] = %_Arguments(arguments_index++);
837 this.length = len - del_count + num_elements_to_add;
840 $observeEndPerformSplice(this);
841 if (deleted_elements.length || num_elements_to_add) {
842 $observeEnqueueSpliceRecord(this,
844 deleted_elements.slice(),
845 num_elements_to_add);
849 // Return the deleted elements.
850 return deleted_elements;
854 function ArraySplice(start, delete_count) {
855 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
857 if (%IsObserved(this))
858 return ObservedArraySplice.apply(this, arguments);
860 var num_arguments = %_ArgumentsLength();
861 var array = TO_OBJECT(this);
862 var len = TO_UINT32(array.length);
863 var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
864 var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
866 var deleted_elements = [];
867 deleted_elements.length = del_count;
868 var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
870 if (del_count != num_elements_to_add && ObjectIsSealed(array)) {
871 throw MakeTypeError(kArrayFunctionsOnSealed);
872 } else if (del_count > 0 && ObjectIsFrozen(array)) {
873 throw MakeTypeError(kArrayFunctionsOnFrozen);
876 var changed_elements = del_count;
877 if (num_elements_to_add != del_count) {
878 // If the slice needs to do a actually move elements after the insertion
879 // point, then include those in the estimate of changed elements.
880 changed_elements += len - start_i - del_count;
882 if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
883 %NormalizeElements(array);
884 %NormalizeElements(deleted_elements);
885 SparseSlice(array, start_i, del_count, len, deleted_elements);
886 SparseMove(array, start_i, del_count, len, num_elements_to_add);
888 SimpleSlice(array, start_i, del_count, len, deleted_elements);
889 SimpleMove(array, start_i, del_count, len, num_elements_to_add);
892 // Insert the arguments into the resulting array in
893 // place of the deleted elements.
895 var arguments_index = 2;
896 var arguments_length = %_ArgumentsLength();
897 while (arguments_index < arguments_length) {
898 array[i++] = %_Arguments(arguments_index++);
900 array.length = len - del_count + num_elements_to_add;
902 // Return the deleted elements.
903 return deleted_elements;
907 function InnerArraySort(length, comparefn) {
908 // In-place QuickSort algorithm.
909 // For short (length <= 22) arrays, insertion sort is used for efficiency.
911 if (!IS_SPEC_FUNCTION(comparefn)) {
912 comparefn = function (x, y) {
913 if (x === y) return 0;
914 if (%_IsSmi(x) && %_IsSmi(y)) {
915 return %SmiLexicographicCompare(x, y);
919 if (x == y) return 0;
920 else return x < y ? -1 : 1;
923 var InsertionSort = function InsertionSort(a, from, to) {
924 for (var i = from + 1; i < to; i++) {
926 for (var j = i - 1; j >= from; j--) {
928 var order = %_CallFunction(UNDEFINED, tmp, element, comparefn);
939 var GetThirdIndex = function(a, from, to) {
941 // Use both 'from' and 'to' to determine the pivot candidates.
942 var increment = 200 + ((to - from) & 15);
943 for (var i = from + 1, j = 0; i < to - 1; i += increment, j++) {
944 t_array[j] = [i, a[i]];
946 %_CallFunction(t_array, function(a, b) {
947 return %_CallFunction(UNDEFINED, a[1], b[1], comparefn);
949 var third_index = t_array[t_array.length >> 1][0];
953 var QuickSort = function QuickSort(a, from, to) {
956 // Insertion sort is faster for short arrays.
957 if (to - from <= 10) {
958 InsertionSort(a, from, to);
961 if (to - from > 1000) {
962 third_index = GetThirdIndex(a, from, to);
964 third_index = from + ((to - from) >> 1);
966 // Find a pivot as the median of first, last and middle element.
969 var v2 = a[third_index];
970 var c01 = %_CallFunction(UNDEFINED, v0, v1, comparefn);
972 // v1 < v0, so swap them.
977 var c02 = %_CallFunction(UNDEFINED, v0, v2, comparefn);
985 // v0 <= v1 && v0 < v2
986 var c12 = %_CallFunction(UNDEFINED, v1, v2, comparefn);
998 var low_end = from + 1; // Upper bound of elements lower than pivot.
999 var high_start = to - 1; // Lower bound of elements greater than pivot.
1000 a[third_index] = a[low_end];
1003 // From low_end to i are elements equal to pivot.
1004 // From i to high_start are elements that haven't been compared yet.
1005 partition: for (var i = low_end + 1; i < high_start; i++) {
1007 var order = %_CallFunction(UNDEFINED, element, pivot, comparefn);
1010 a[low_end] = element;
1012 } else if (order > 0) {
1015 if (high_start == i) break partition;
1016 var top_elem = a[high_start];
1017 order = %_CallFunction(UNDEFINED, top_elem, pivot, comparefn);
1018 } while (order > 0);
1019 a[i] = a[high_start];
1020 a[high_start] = element;
1024 a[low_end] = element;
1029 if (to - high_start < low_end - from) {
1030 QuickSort(a, high_start, to);
1033 QuickSort(a, from, low_end);
1039 // Copy elements in the range 0..length from obj's prototype chain
1040 // to obj itself, if obj has holes. Return one more than the maximal index
1041 // of a prototype property.
1042 var CopyFromPrototype = function CopyFromPrototype(obj, length) {
1044 for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
1045 var indices = %GetArrayKeys(proto, length);
1046 if (IS_NUMBER(indices)) {
1047 // It's an interval.
1048 var proto_length = indices;
1049 for (var i = 0; i < proto_length; i++) {
1050 if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
1052 if (i >= max) { max = i + 1; }
1056 for (var i = 0; i < indices.length; i++) {
1057 var index = indices[i];
1058 if (!IS_UNDEFINED(index) && !HAS_OWN_PROPERTY(obj, index)
1059 && HAS_OWN_PROPERTY(proto, index)) {
1060 obj[index] = proto[index];
1061 if (index >= max) { max = index + 1; }
1069 // Set a value of "undefined" on all indices in the range from..to
1070 // where a prototype of obj has an element. I.e., shadow all prototype
1071 // elements in that range.
1072 var ShadowPrototypeElements = function(obj, from, to) {
1073 for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
1074 var indices = %GetArrayKeys(proto, to);
1075 if (IS_NUMBER(indices)) {
1076 // It's an interval.
1077 var proto_length = indices;
1078 for (var i = from; i < proto_length; i++) {
1079 if (HAS_OWN_PROPERTY(proto, i)) {
1084 for (var i = 0; i < indices.length; i++) {
1085 var index = indices[i];
1086 if (!IS_UNDEFINED(index) && from <= index &&
1087 HAS_OWN_PROPERTY(proto, index)) {
1088 obj[index] = UNDEFINED;
1095 var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
1096 // Copy defined elements from the end to fill in all holes and undefineds
1097 // in the beginning of the array. Write undefineds and holes at the end
1098 // after loop is finished.
1099 var first_undefined = 0;
1100 var last_defined = length - 1;
1102 while (first_undefined < last_defined) {
1103 // Find first undefined element.
1104 while (first_undefined < last_defined &&
1105 !IS_UNDEFINED(obj[first_undefined])) {
1108 // Maintain the invariant num_holes = the number of holes in the original
1109 // array with indices <= first_undefined or > last_defined.
1110 if (!HAS_OWN_PROPERTY(obj, first_undefined)) {
1114 // Find last defined element.
1115 while (first_undefined < last_defined &&
1116 IS_UNDEFINED(obj[last_defined])) {
1117 if (!HAS_OWN_PROPERTY(obj, last_defined)) {
1122 if (first_undefined < last_defined) {
1123 // Fill in hole or undefined.
1124 obj[first_undefined] = obj[last_defined];
1125 obj[last_defined] = UNDEFINED;
1128 // If there were any undefineds in the entire array, first_undefined
1129 // points to one past the last defined element. Make this true if
1130 // there were no undefineds, as well, so that first_undefined == number
1131 // of defined elements.
1132 if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
1133 // Fill in the undefineds and the holes. There may be a hole where
1134 // an undefined should be and vice versa.
1136 for (i = first_undefined; i < length - num_holes; i++) {
1139 for (i = length - num_holes; i < length; i++) {
1140 // For compatability with Webkit, do not expose elements in the prototype.
1141 if (i in %_GetPrototype(obj)) {
1148 // Return the number of defined elements.
1149 return first_undefined;
1152 if (length < 2) return this;
1154 var is_array = IS_ARRAY(this);
1155 var max_prototype_element;
1157 // For compatibility with JSC, we also sort elements inherited from
1158 // the prototype chain on non-Array objects.
1159 // We do this by copying them to this object and sorting only
1160 // own elements. This is not very efficient, but sorting with
1161 // inherited elements happens very, very rarely, if at all.
1162 // The specification allows "implementation dependent" behavior
1163 // if an element on the prototype chain has an element that
1164 // might interact with sorting.
1165 max_prototype_element = CopyFromPrototype(this, length);
1168 // %RemoveArrayHoles returns -1 if fast removal is not supported.
1169 var num_non_undefined = %RemoveArrayHoles(this, length);
1171 if (num_non_undefined == -1) {
1172 // The array is observed, or there were indexed accessors in the array.
1173 // Move array holes and undefineds to the end using a Javascript function
1174 // that is safe in the presence of accessors and is observable.
1175 num_non_undefined = SafeRemoveArrayHoles(this);
1178 QuickSort(this, 0, num_non_undefined);
1180 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
1181 // For compatibility with JSC, we shadow any elements in the prototype
1182 // chain that has become exposed by sort moving a hole to its position.
1183 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
1190 function ArraySort(comparefn) {
1191 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
1193 var array = TO_OBJECT(this);
1194 var length = TO_UINT32(array.length);
1195 return %_CallFunction(array, length, comparefn, InnerArraySort);
1199 // The following functions cannot be made efficient on sparse arrays while
1200 // preserving the semantics, since the calls to the receiver function can add
1201 // or delete elements from the array.
1202 function InnerArrayFilter(f, receiver, array, length) {
1203 if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
1204 var needs_wrapper = false;
1205 if (IS_NULL(receiver)) {
1206 if (%IsSloppyModeFunction(f)) receiver = UNDEFINED;
1207 } else if (!IS_UNDEFINED(receiver)) {
1208 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
1211 var accumulator = new InternalArray();
1212 var accumulator_length = 0;
1213 var is_array = IS_ARRAY(array);
1214 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
1215 for (var i = 0; i < length; i++) {
1216 if (HAS_INDEX(array, i, is_array)) {
1217 var element = array[i];
1218 // Prepare break slots for debugger step in.
1219 if (stepping) %DebugPrepareStepInIfStepping(f);
1220 var new_receiver = needs_wrapper ? TO_OBJECT(receiver) : receiver;
1221 if (%_CallFunction(new_receiver, element, i, array, f)) {
1222 accumulator[accumulator_length++] = element;
1229 function ArrayFilter(f, receiver) {
1230 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
1232 // Pull out the length so that modifications to the length in the
1233 // loop will not affect the looping and side effects are visible.
1234 var array = TO_OBJECT(this);
1235 var length = TO_UINT32(array.length);
1236 var accumulator = InnerArrayFilter(f, receiver, array, length);
1237 var result = new GlobalArray();
1238 %MoveArrayContents(accumulator, result);
1242 function InnerArrayForEach(f, receiver, array, length) {
1243 if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
1244 var needs_wrapper = false;
1245 if (IS_NULL(receiver)) {
1246 if (%IsSloppyModeFunction(f)) receiver = UNDEFINED;
1247 } else if (!IS_UNDEFINED(receiver)) {
1248 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
1251 var is_array = IS_ARRAY(array);
1252 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
1253 for (var i = 0; i < length; i++) {
1254 if (HAS_INDEX(array, i, is_array)) {
1255 var element = array[i];
1256 // Prepare break slots for debugger step in.
1257 if (stepping) %DebugPrepareStepInIfStepping(f);
1258 var new_receiver = needs_wrapper ? TO_OBJECT(receiver) : receiver;
1259 %_CallFunction(new_receiver, element, i, array, f);
1264 function ArrayForEach(f, receiver) {
1265 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
1267 // Pull out the length so that modifications to the length in the
1268 // loop will not affect the looping and side effects are visible.
1269 var array = TO_OBJECT(this);
1270 var length = TO_UINT32(array.length);
1271 InnerArrayForEach(f, receiver, array, length);
1275 function InnerArraySome(f, receiver, array, length) {
1276 if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
1277 var needs_wrapper = false;
1278 if (IS_NULL(receiver)) {
1279 if (%IsSloppyModeFunction(f)) receiver = UNDEFINED;
1280 } else if (!IS_UNDEFINED(receiver)) {
1281 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
1284 var is_array = IS_ARRAY(array);
1285 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
1286 for (var i = 0; i < length; i++) {
1287 if (HAS_INDEX(array, i, is_array)) {
1288 var element = array[i];
1289 // Prepare break slots for debugger step in.
1290 if (stepping) %DebugPrepareStepInIfStepping(f);
1291 var new_receiver = needs_wrapper ? TO_OBJECT(receiver) : receiver;
1292 if (%_CallFunction(new_receiver, element, i, array, f)) return true;
1299 // Executes the function once for each element present in the
1300 // array until it finds one where callback returns true.
1301 function ArraySome(f, receiver) {
1302 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
1304 // Pull out the length so that modifications to the length in the
1305 // loop will not affect the looping and side effects are visible.
1306 var array = TO_OBJECT(this);
1307 var length = TO_UINT32(array.length);
1308 return InnerArraySome(f, receiver, array, length);
1312 function InnerArrayEvery(f, receiver, array, length) {
1313 if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
1314 var needs_wrapper = false;
1315 if (IS_NULL(receiver)) {
1316 if (%IsSloppyModeFunction(f)) receiver = UNDEFINED;
1317 } else if (!IS_UNDEFINED(receiver)) {
1318 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
1321 var is_array = IS_ARRAY(array);
1322 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
1323 for (var i = 0; i < length; i++) {
1324 if (HAS_INDEX(array, i, is_array)) {
1325 var element = array[i];
1326 // Prepare break slots for debugger step in.
1327 if (stepping) %DebugPrepareStepInIfStepping(f);
1328 var new_receiver = needs_wrapper ? TO_OBJECT(receiver) : receiver;
1329 if (!%_CallFunction(new_receiver, element, i, array, f)) return false;
1335 function ArrayEvery(f, receiver) {
1336 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
1338 // Pull out the length so that modifications to the length in the
1339 // loop will not affect the looping and side effects are visible.
1340 var array = TO_OBJECT(this);
1341 var length = TO_UINT32(array.length);
1342 return InnerArrayEvery(f, receiver, array, length);
1346 function InnerArrayMap(f, receiver, array, length) {
1347 if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
1348 var needs_wrapper = false;
1349 if (IS_NULL(receiver)) {
1350 if (%IsSloppyModeFunction(f)) receiver = UNDEFINED;
1351 } else if (!IS_UNDEFINED(receiver)) {
1352 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
1355 var accumulator = new InternalArray(length);
1356 var is_array = IS_ARRAY(array);
1357 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
1358 for (var i = 0; i < length; i++) {
1359 if (HAS_INDEX(array, i, is_array)) {
1360 var element = array[i];
1361 // Prepare break slots for debugger step in.
1362 if (stepping) %DebugPrepareStepInIfStepping(f);
1363 var new_receiver = needs_wrapper ? TO_OBJECT(receiver) : receiver;
1364 accumulator[i] = %_CallFunction(new_receiver, element, i, array, f);
1371 function ArrayMap(f, receiver) {
1372 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
1374 // Pull out the length so that modifications to the length in the
1375 // loop will not affect the looping and side effects are visible.
1376 var array = TO_OBJECT(this);
1377 var length = TO_UINT32(array.length);
1378 var accumulator = InnerArrayMap(f, receiver, array, length);
1379 var result = new GlobalArray();
1380 %MoveArrayContents(accumulator, result);
1385 // For .indexOf, we don't need to pass in the number of arguments
1386 // at the callsite since ToInteger(undefined) == 0; however, for
1387 // .lastIndexOf, we need to pass it, since the behavior for passing
1388 // undefined is 0 but for not including the argument is length-1.
1389 function InnerArrayIndexOf(element, index, length) {
1390 if (length == 0) return -1;
1391 if (IS_UNDEFINED(index)) {
1394 index = TO_INTEGER(index);
1395 // If index is negative, index from the end of the array.
1397 index = length + index;
1398 // If index is still negative, search the entire array.
1399 if (index < 0) index = 0;
1404 if (UseSparseVariant(this, length, IS_ARRAY(this), max - min)) {
1405 %NormalizeElements(this);
1406 var indices = %GetArrayKeys(this, length);
1407 if (IS_NUMBER(indices)) {
1408 // It's an interval.
1409 max = indices; // Capped by length already.
1410 // Fall through to loop below.
1412 if (indices.length == 0) return -1;
1413 // Get all the keys in sorted order.
1414 var sortedKeys = GetSortedArrayKeys(this, indices);
1415 var n = sortedKeys.length;
1417 while (i < n && sortedKeys[i] < index) i++;
1419 var key = sortedKeys[i];
1420 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1426 // Lookup through the array.
1427 if (!IS_UNDEFINED(element)) {
1428 for (var i = min; i < max; i++) {
1429 if (this[i] === element) return i;
1433 // Lookup through the array.
1434 for (var i = min; i < max; i++) {
1435 if (IS_UNDEFINED(this[i]) && i in this) {
1443 function ArrayIndexOf(element, index) {
1444 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
1446 var length = TO_UINT32(this.length);
1447 return %_CallFunction(this, element, index, length, InnerArrayIndexOf);
1451 function InnerArrayLastIndexOf(element, index, length, argumentsLength) {
1452 if (length == 0) return -1;
1453 if (argumentsLength < 2) {
1456 index = TO_INTEGER(index);
1457 // If index is negative, index from end of the array.
1458 if (index < 0) index += length;
1459 // If index is still negative, do not search the array.
1460 if (index < 0) return -1;
1461 else if (index >= length) index = length - 1;
1465 if (UseSparseVariant(this, length, IS_ARRAY(this), index)) {
1466 %NormalizeElements(this);
1467 var indices = %GetArrayKeys(this, index + 1);
1468 if (IS_NUMBER(indices)) {
1469 // It's an interval.
1470 max = indices; // Capped by index already.
1471 // Fall through to loop below.
1473 if (indices.length == 0) return -1;
1474 // Get all the keys in sorted order.
1475 var sortedKeys = GetSortedArrayKeys(this, indices);
1476 var i = sortedKeys.length - 1;
1478 var key = sortedKeys[i];
1479 if (!IS_UNDEFINED(key) && this[key] === element) return key;
1485 // Lookup through the array.
1486 if (!IS_UNDEFINED(element)) {
1487 for (var i = max; i >= min; i--) {
1488 if (this[i] === element) return i;
1492 for (var i = max; i >= min; i--) {
1493 if (IS_UNDEFINED(this[i]) && i in this) {
1501 function ArrayLastIndexOf(element, index) {
1502 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
1504 var length = TO_UINT32(this.length);
1505 return %_CallFunction(this, element, index, length,
1506 %_ArgumentsLength(), InnerArrayLastIndexOf);
1510 function InnerArrayReduce(callback, current, array, length, argumentsLength) {
1511 if (!IS_SPEC_FUNCTION(callback)) {
1512 throw MakeTypeError(kCalledNonCallable, callback);
1515 var is_array = IS_ARRAY(array);
1517 find_initial: if (argumentsLength < 2) {
1518 for (; i < length; i++) {
1519 if (HAS_INDEX(array, i, is_array)) {
1520 current = array[i++];
1524 throw MakeTypeError(kReduceNoInitial);
1527 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback);
1528 for (; i < length; i++) {
1529 if (HAS_INDEX(array, i, is_array)) {
1530 var element = array[i];
1531 // Prepare break slots for debugger step in.
1532 if (stepping) %DebugPrepareStepInIfStepping(callback);
1533 current = %_CallFunction(UNDEFINED, current, element, i, array, callback);
1540 function ArrayReduce(callback, current) {
1541 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
1543 // Pull out the length so that modifications to the length in the
1544 // loop will not affect the looping and side effects are visible.
1545 var array = TO_OBJECT(this);
1546 var length = TO_UINT32(array.length);
1547 return InnerArrayReduce(callback, current, array, length,
1548 %_ArgumentsLength());
1552 function InnerArrayReduceRight(callback, current, array, length,
1554 if (!IS_SPEC_FUNCTION(callback)) {
1555 throw MakeTypeError(kCalledNonCallable, callback);
1558 var is_array = IS_ARRAY(array);
1560 find_initial: if (argumentsLength < 2) {
1561 for (; i >= 0; i--) {
1562 if (HAS_INDEX(array, i, is_array)) {
1563 current = array[i--];
1567 throw MakeTypeError(kReduceNoInitial);
1570 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback);
1571 for (; i >= 0; i--) {
1572 if (HAS_INDEX(array, i, is_array)) {
1573 var element = array[i];
1574 // Prepare break slots for debugger step in.
1575 if (stepping) %DebugPrepareStepInIfStepping(callback);
1576 current = %_CallFunction(UNDEFINED, current, element, i, array, callback);
1583 function ArrayReduceRight(callback, current) {
1584 CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
1586 // Pull out the length so that side effects are visible before the
1587 // callback function is checked.
1588 var array = TO_OBJECT(this);
1589 var length = TO_UINT32(array.length);
1590 return InnerArrayReduceRight(callback, current, array, length,
1591 %_ArgumentsLength());
1595 function ArrayIsArray(obj) {
1596 return IS_ARRAY(obj);
1600 // -------------------------------------------------------------------
1602 // Set up non-enumerable constructor property on the Array.prototype
1604 %AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray,
1607 // Set up unscopable properties on the Array.prototype object.
1618 %AddNamedProperty(GlobalArray.prototype, symbolUnscopables, unscopables,
1619 DONT_ENUM | READ_ONLY);
1621 // Set up non-enumerable functions on the Array object.
1622 utils.InstallFunctions(GlobalArray, DONT_ENUM, [
1623 "isArray", ArrayIsArray
1626 var specialFunctions = %SpecialArrayFunctions();
1628 var getFunction = function(name, jsBuiltin, len) {
1630 if (specialFunctions.hasOwnProperty(name)) {
1631 f = specialFunctions[name];
1633 if (!IS_UNDEFINED(len)) {
1634 %FunctionSetLength(f, len);
1639 // Set up non-enumerable functions of the Array.prototype object and
1641 // Manipulate the length of some of the functions to meet
1642 // expectations set by ECMA-262 or Mozilla.
1643 utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
1644 "toString", getFunction("toString", ArrayToString),
1645 "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
1646 "join", getFunction("join", ArrayJoin),
1647 "pop", getFunction("pop", ArrayPop),
1648 "push", getFunction("push", ArrayPush, 1),
1649 "concat", getFunction("concat", ArrayConcatJS, 1),
1650 "reverse", getFunction("reverse", ArrayReverse),
1651 "shift", getFunction("shift", ArrayShift),
1652 "unshift", getFunction("unshift", ArrayUnshift, 1),
1653 "slice", getFunction("slice", ArraySlice, 2),
1654 "splice", getFunction("splice", ArraySplice, 2),
1655 "sort", getFunction("sort", ArraySort),
1656 "filter", getFunction("filter", ArrayFilter, 1),
1657 "forEach", getFunction("forEach", ArrayForEach, 1),
1658 "some", getFunction("some", ArraySome, 1),
1659 "every", getFunction("every", ArrayEvery, 1),
1660 "map", getFunction("map", ArrayMap, 1),
1661 "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
1662 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1663 "reduce", getFunction("reduce", ArrayReduce, 1),
1664 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1667 %FinishArrayPrototypeSetup(GlobalArray.prototype);
1669 // The internal Array prototype doesn't need to be fancy, since it's never
1670 // exposed to user code.
1671 // Adding only the functions that are actually used.
1672 utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
1673 "concat", getFunction("concat", ArrayConcatJS),
1674 "indexOf", getFunction("indexOf", ArrayIndexOf),
1675 "join", getFunction("join", ArrayJoin),
1676 "pop", getFunction("pop", ArrayPop),
1677 "push", getFunction("push", ArrayPush),
1678 "shift", getFunction("shift", ArrayShift),
1679 "splice", getFunction("splice", ArraySplice)
1682 utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [
1683 "join", getFunction("join", ArrayJoin),
1684 "pop", getFunction("pop", ArrayPop),
1685 "push", getFunction("push", ArrayPush),
1686 "shift", getFunction("shift", ArrayShift)
1689 // -------------------------------------------------------------------
1692 utils.Export(function(to) {
1693 to.ArrayIndexOf = ArrayIndexOf;
1694 to.ArrayJoin = ArrayJoin;
1695 to.ArrayToString = ArrayToString;
1696 to.InnerArrayEvery = InnerArrayEvery;
1697 to.InnerArrayFilter = InnerArrayFilter;
1698 to.InnerArrayForEach = InnerArrayForEach;
1699 to.InnerArrayIndexOf = InnerArrayIndexOf;
1700 to.InnerArrayJoin = InnerArrayJoin;
1701 to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
1702 to.InnerArrayMap = InnerArrayMap;
1703 to.InnerArrayReduce = InnerArrayReduce;
1704 to.InnerArrayReduceRight = InnerArrayReduceRight;
1705 to.InnerArraySome = InnerArraySome;
1706 to.InnerArraySort = InnerArraySort;
1707 to.InnerArrayToLocaleString = InnerArrayToLocaleString;
1708 to.PackedArrayReverse = PackedArrayReverse;
1711 $arrayConcat = ArrayConcatJS;
1712 $arrayPush = ArrayPush;
1713 $arrayPop = ArrayPop;
1714 $arrayShift = ArrayShift;
1715 $arraySlice = ArraySlice;
1716 $arraySplice = ArraySplice;
1717 $arrayUnshift = ArrayUnshift;