Index: src/array.js |
diff --git a/src/array.js b/src/array.js |
deleted file mode 100644 |
index 462e5281374689929ff2fadb8e241ff6a38ba114..0000000000000000000000000000000000000000 |
--- a/src/array.js |
+++ /dev/null |
@@ -1,1659 +0,0 @@ |
-// Copyright 2012 the V8 project authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-(function(global, utils) { |
- |
-"use strict"; |
- |
-%CheckIsBootstrapping(); |
- |
-// ------------------------------------------------------------------- |
-// Imports |
- |
-var Delete; |
-var GlobalArray = global.Array; |
-var InternalArray = utils.InternalArray; |
-var InternalPackedArray = utils.InternalPackedArray; |
-var MathMin; |
-var ObjectHasOwnProperty; |
-var ObjectIsFrozen; |
-var ObjectIsSealed; |
-var ObjectToString; |
-var unscopablesSymbol = utils.ImportNow("unscopables_symbol"); |
- |
-utils.Import(function(from) { |
- Delete = from.Delete; |
- MathMin = from.MathMin; |
- ObjectHasOwnProperty = from.ObjectHasOwnProperty; |
- ObjectIsFrozen = from.ObjectIsFrozen; |
- ObjectIsSealed = from.ObjectIsSealed; |
- ObjectToString = from.ObjectToString; |
-}); |
- |
-// ------------------------------------------------------------------- |
- |
-// Global list of arrays visited during toString, toLocaleString and |
-// join invocations. |
-var visited_arrays = new InternalArray(); |
- |
- |
-// Gets a sorted array of array keys. Useful for operations on sparse |
-// arrays. Dupes have not been removed. |
-function GetSortedArrayKeys(array, indices) { |
- var keys = new InternalArray(); |
- if (IS_NUMBER(indices)) { |
- // It's an interval |
- var limit = indices; |
- for (var i = 0; i < limit; ++i) { |
- var e = array[i]; |
- if (!IS_UNDEFINED(e) || i in array) { |
- keys.push(i); |
- } |
- } |
- } else { |
- var length = indices.length; |
- for (var k = 0; k < length; ++k) { |
- var key = indices[k]; |
- if (!IS_UNDEFINED(key)) { |
- var e = array[key]; |
- if (!IS_UNDEFINED(e) || key in array) { |
- keys.push(key); |
- } |
- } |
- } |
- keys.sort(function(a, b) { return a - b; }); |
- } |
- return keys; |
-} |
- |
- |
-function SparseJoinWithSeparatorJS(array, len, convert, separator) { |
- var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); |
- var totalLength = 0; |
- var elements = new InternalArray(keys.length * 2); |
- var previousKey = -1; |
- for (var i = 0; i < keys.length; i++) { |
- var key = keys[i]; |
- if (key != previousKey) { // keys may contain duplicates. |
- var e = array[key]; |
- if (!IS_STRING(e)) e = convert(e); |
- elements[i * 2] = key; |
- elements[i * 2 + 1] = e; |
- previousKey = key; |
- } |
- } |
- return %SparseJoinWithSeparator(elements, len, separator); |
-} |
- |
- |
-// Optimized for sparse arrays if separator is ''. |
-function SparseJoin(array, len, convert) { |
- var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); |
- var last_key = -1; |
- var keys_length = keys.length; |
- |
- var elements = new InternalArray(keys_length); |
- var elements_length = 0; |
- |
- for (var i = 0; i < keys_length; i++) { |
- var key = keys[i]; |
- if (key != last_key) { |
- var e = array[key]; |
- if (!IS_STRING(e)) e = convert(e); |
- elements[elements_length++] = e; |
- last_key = key; |
- } |
- } |
- return %StringBuilderConcat(elements, elements_length, ''); |
-} |
- |
- |
-function UseSparseVariant(array, length, is_array, touched) { |
- // Only use the sparse variant on arrays that are likely to be sparse and the |
- // number of elements touched in the operation is relatively small compared to |
- // the overall size of the array. |
- if (!is_array || length < 1000 || %IsObserved(array) || |
- %HasComplexElements(array)) { |
- return false; |
- } |
- if (!%_IsSmi(length)) { |
- return true; |
- } |
- var elements_threshold = length >> 2; // No more than 75% holes |
- var estimated_elements = %EstimateNumberOfElements(array); |
- return (estimated_elements < elements_threshold) && |
- (touched > estimated_elements * 4); |
-} |
- |
- |
-function Join(array, length, separator, convert) { |
- if (length == 0) return ''; |
- |
- var is_array = IS_ARRAY(array); |
- |
- if (is_array) { |
- // If the array is cyclic, return the empty string for already |
- // visited arrays. |
- if (!%PushIfAbsent(visited_arrays, array)) return ''; |
- } |
- |
- // Attempt to convert the elements. |
- try { |
- if (UseSparseVariant(array, length, is_array, length)) { |
- %NormalizeElements(array); |
- if (separator.length == 0) { |
- return SparseJoin(array, length, convert); |
- } else { |
- return SparseJoinWithSeparatorJS(array, length, convert, separator); |
- } |
- } |
- |
- // Fast case for one-element arrays. |
- if (length == 1) { |
- var e = array[0]; |
- if (IS_STRING(e)) return e; |
- return convert(e); |
- } |
- |
- // Construct an array for the elements. |
- var elements = new InternalArray(length); |
- |
- // We pull the empty separator check outside the loop for speed! |
- if (separator.length == 0) { |
- var elements_length = 0; |
- for (var i = 0; i < length; i++) { |
- var e = array[i]; |
- if (!IS_STRING(e)) e = convert(e); |
- elements[elements_length++] = e; |
- } |
- elements.length = elements_length; |
- var result = %_FastOneByteArrayJoin(elements, ''); |
- if (!IS_UNDEFINED(result)) return result; |
- return %StringBuilderConcat(elements, elements_length, ''); |
- } |
- // Non-empty separator case. |
- // If the first element is a number then use the heuristic that the |
- // remaining elements are also likely to be numbers. |
- if (!IS_NUMBER(array[0])) { |
- for (var i = 0; i < length; i++) { |
- var e = array[i]; |
- if (!IS_STRING(e)) e = convert(e); |
- elements[i] = e; |
- } |
- } else { |
- for (var i = 0; i < length; i++) { |
- var e = array[i]; |
- if (IS_NUMBER(e)) { |
- e = %_NumberToString(e); |
- } else if (!IS_STRING(e)) { |
- e = convert(e); |
- } |
- elements[i] = e; |
- } |
- } |
- var result = %_FastOneByteArrayJoin(elements, separator); |
- if (!IS_UNDEFINED(result)) return result; |
- |
- return %StringBuilderJoin(elements, length, separator); |
- } finally { |
- // Make sure to remove the last element of the visited array no |
- // matter what happens. |
- if (is_array) visited_arrays.length = visited_arrays.length - 1; |
- } |
-} |
- |
- |
-function ConvertToString(x) { |
- if (IS_NULL_OR_UNDEFINED(x)) { |
- return ''; |
- } else { |
- return TO_STRING(x); |
- } |
-} |
- |
- |
-function ConvertToLocaleString(e) { |
- if (IS_NULL_OR_UNDEFINED(e)) { |
- return ''; |
- } else { |
- return TO_STRING(e.toLocaleString()); |
- } |
-} |
- |
- |
-// This function implements the optimized splice implementation that can use |
-// special array operations to handle sparse arrays in a sensible fashion. |
-function SparseSlice(array, start_i, del_count, len, deleted_elements) { |
- // Move deleted elements to a new array (the return value from splice). |
- var indices = %GetArrayKeys(array, start_i + del_count); |
- if (IS_NUMBER(indices)) { |
- var limit = indices; |
- for (var i = start_i; i < limit; ++i) { |
- var current = array[i]; |
- if (!IS_UNDEFINED(current) || i in array) { |
- %AddElement(deleted_elements, i - start_i, current); |
- } |
- } |
- } else { |
- var length = indices.length; |
- for (var k = 0; k < length; ++k) { |
- var key = indices[k]; |
- if (!IS_UNDEFINED(key)) { |
- if (key >= start_i) { |
- var current = array[key]; |
- if (!IS_UNDEFINED(current) || key in array) { |
- %AddElement(deleted_elements, key - start_i, current); |
- } |
- } |
- } |
- } |
- } |
-} |
- |
- |
-// This function implements the optimized splice implementation that can use |
-// special array operations to handle sparse arrays in a sensible fashion. |
-function SparseMove(array, start_i, del_count, len, num_additional_args) { |
- // Bail out if no moving is necessary. |
- if (num_additional_args === del_count) return; |
- // Move data to new array. |
- var new_array = new InternalArray( |
- // Clamp array length to 2^32-1 to avoid early RangeError. |
- MathMin(len - del_count + num_additional_args, 0xffffffff)); |
- var big_indices; |
- var indices = %GetArrayKeys(array, len); |
- if (IS_NUMBER(indices)) { |
- var limit = indices; |
- for (var i = 0; i < start_i && i < limit; ++i) { |
- var current = array[i]; |
- if (!IS_UNDEFINED(current) || i in array) { |
- new_array[i] = current; |
- } |
- } |
- for (var i = start_i + del_count; i < limit; ++i) { |
- var current = array[i]; |
- if (!IS_UNDEFINED(current) || i in array) { |
- new_array[i - del_count + num_additional_args] = current; |
- } |
- } |
- } else { |
- var length = indices.length; |
- for (var k = 0; k < length; ++k) { |
- var key = indices[k]; |
- if (!IS_UNDEFINED(key)) { |
- if (key < start_i) { |
- var current = array[key]; |
- if (!IS_UNDEFINED(current) || key in array) { |
- new_array[key] = current; |
- } |
- } else if (key >= start_i + del_count) { |
- var current = array[key]; |
- if (!IS_UNDEFINED(current) || key in array) { |
- var new_key = key - del_count + num_additional_args; |
- new_array[new_key] = current; |
- if (new_key > 0xfffffffe) { |
- big_indices = big_indices || new InternalArray(); |
- big_indices.push(new_key); |
- } |
- } |
- } |
- } |
- } |
- } |
- // Move contents of new_array into this array |
- %MoveArrayContents(new_array, array); |
- // Add any moved values that aren't elements anymore. |
- if (!IS_UNDEFINED(big_indices)) { |
- var length = big_indices.length; |
- for (var i = 0; i < length; ++i) { |
- var key = big_indices[i]; |
- array[key] = new_array[key]; |
- } |
- } |
-} |
- |
- |
-// This is part of the old simple-minded splice. We are using it either |
-// because the receiver is not an array (so we have no choice) or because we |
-// know we are not deleting or moving a lot of elements. |
-function SimpleSlice(array, start_i, del_count, len, deleted_elements) { |
- var is_array = IS_ARRAY(array); |
- for (var i = 0; i < del_count; i++) { |
- var index = start_i + i; |
- if (HAS_INDEX(array, index, is_array)) { |
- var current = array[index]; |
- // The spec requires [[DefineOwnProperty]] here, %AddElement is close |
- // enough (in that it ignores the prototype). |
- %AddElement(deleted_elements, i, current); |
- } |
- } |
-} |
- |
- |
-function SimpleMove(array, start_i, del_count, len, num_additional_args) { |
- var is_array = IS_ARRAY(array); |
- if (num_additional_args !== del_count) { |
- // Move the existing elements after the elements to be deleted |
- // to the right position in the resulting array. |
- if (num_additional_args > del_count) { |
- for (var i = len - del_count; i > start_i; i--) { |
- var from_index = i + del_count - 1; |
- var to_index = i + num_additional_args - 1; |
- if (HAS_INDEX(array, from_index, is_array)) { |
- array[to_index] = array[from_index]; |
- } else { |
- delete array[to_index]; |
- } |
- } |
- } else { |
- for (var i = start_i; i < len - del_count; i++) { |
- var from_index = i + del_count; |
- var to_index = i + num_additional_args; |
- if (HAS_INDEX(array, from_index, is_array)) { |
- array[to_index] = array[from_index]; |
- } else { |
- delete array[to_index]; |
- } |
- } |
- for (var i = len; i > len - del_count + num_additional_args; i--) { |
- delete array[i - 1]; |
- } |
- } |
- } |
-} |
- |
- |
-// ------------------------------------------------------------------- |
- |
- |
-function ArrayToString() { |
- var array; |
- var func; |
- if (IS_ARRAY(this)) { |
- func = this.join; |
- if (func === ArrayJoin) { |
- return Join(this, this.length, ',', ConvertToString); |
- } |
- array = this; |
- } else { |
- array = TO_OBJECT(this); |
- func = array.join; |
- } |
- if (!IS_CALLABLE(func)) { |
- return %_CallFunction(array, ObjectToString); |
- } |
- return %_Call(func, array); |
-} |
- |
- |
-function InnerArrayToLocaleString(array, length) { |
- var len = TO_LENGTH_OR_UINT32(length); |
- if (len === 0) return ""; |
- return Join(array, len, ',', ConvertToLocaleString); |
-} |
- |
- |
-function ArrayToLocaleString() { |
- var array = TO_OBJECT(this); |
- var arrayLen = array.length; |
- return InnerArrayToLocaleString(array, arrayLen); |
-} |
- |
- |
-function InnerArrayJoin(separator, array, length) { |
- if (IS_UNDEFINED(separator)) { |
- separator = ','; |
- } else { |
- separator = TO_STRING(separator); |
- } |
- |
- var result = %_FastOneByteArrayJoin(array, separator); |
- if (!IS_UNDEFINED(result)) return result; |
- |
- // Fast case for one-element arrays. |
- if (length === 1) { |
- var e = array[0]; |
- if (IS_NULL_OR_UNDEFINED(e)) return ''; |
- return TO_STRING(e); |
- } |
- |
- return Join(array, length, separator, ConvertToString); |
-} |
- |
- |
-function ArrayJoin(separator) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join"); |
- |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- |
- return InnerArrayJoin(separator, array, length); |
-} |
- |
- |
-function ObservedArrayPop(n) { |
- n--; |
- var value = this[n]; |
- |
- try { |
- $observeBeginPerformSplice(this); |
- delete this[n]; |
- this.length = n; |
- } finally { |
- $observeEndPerformSplice(this); |
- $observeEnqueueSpliceRecord(this, n, [value], 0); |
- } |
- |
- return value; |
-} |
- |
- |
-// Removes the last element from the array and returns it. See |
-// ECMA-262, section 15.4.4.6. |
-function ArrayPop() { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop"); |
- |
- var array = TO_OBJECT(this); |
- var n = TO_LENGTH_OR_UINT32(array.length); |
- if (n == 0) { |
- array.length = n; |
- return; |
- } |
- |
- if (%IsObserved(array)) |
- return ObservedArrayPop.call(array, n); |
- |
- n--; |
- var value = array[n]; |
- Delete(array, n, true); |
- array.length = n; |
- return value; |
-} |
- |
- |
-function ObservedArrayPush() { |
- var n = TO_LENGTH_OR_UINT32(this.length); |
- var m = %_ArgumentsLength(); |
- |
- try { |
- $observeBeginPerformSplice(this); |
- for (var i = 0; i < m; i++) { |
- this[i+n] = %_Arguments(i); |
- } |
- var new_length = n + m; |
- this.length = new_length; |
- } finally { |
- $observeEndPerformSplice(this); |
- $observeEnqueueSpliceRecord(this, n, [], m); |
- } |
- |
- return new_length; |
-} |
- |
- |
-// Appends the arguments to the end of the array and returns the new |
-// length of the array. See ECMA-262, section 15.4.4.7. |
-function ArrayPush() { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push"); |
- |
- if (%IsObserved(this)) |
- return ObservedArrayPush.apply(this, arguments); |
- |
- var array = TO_OBJECT(this); |
- var n = TO_LENGTH_OR_UINT32(array.length); |
- var m = %_ArgumentsLength(); |
- |
- for (var i = 0; i < m; i++) { |
- array[i+n] = %_Arguments(i); |
- } |
- |
- var new_length = n + m; |
- array.length = new_length; |
- return new_length; |
-} |
- |
- |
-// For implementing reverse() on large, sparse arrays. |
-function SparseReverse(array, len) { |
- var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); |
- var high_counter = keys.length - 1; |
- var low_counter = 0; |
- while (low_counter <= high_counter) { |
- var i = keys[low_counter]; |
- var j = keys[high_counter]; |
- |
- var j_complement = len - j - 1; |
- var low, high; |
- |
- if (j_complement <= i) { |
- high = j; |
- while (keys[--high_counter] == j) { } |
- low = j_complement; |
- } |
- if (j_complement >= i) { |
- low = i; |
- while (keys[++low_counter] == i) { } |
- high = len - i - 1; |
- } |
- |
- var current_i = array[low]; |
- if (!IS_UNDEFINED(current_i) || low in array) { |
- var current_j = array[high]; |
- if (!IS_UNDEFINED(current_j) || high in array) { |
- array[low] = current_j; |
- array[high] = current_i; |
- } else { |
- array[high] = current_i; |
- delete array[low]; |
- } |
- } else { |
- var current_j = array[high]; |
- if (!IS_UNDEFINED(current_j) || high in array) { |
- array[low] = current_j; |
- delete array[high]; |
- } |
- } |
- } |
-} |
- |
-function PackedArrayReverse(array, len) { |
- var j = len - 1; |
- for (var i = 0; i < j; i++, j--) { |
- var current_i = array[i]; |
- var current_j = array[j]; |
- array[i] = current_j; |
- array[j] = current_i; |
- } |
- return array; |
-} |
- |
- |
-function GenericArrayReverse(array, len) { |
- var j = len - 1; |
- for (var i = 0; i < j; i++, j--) { |
- if (i in array) { |
- var current_i = array[i]; |
- if (j in array) { |
- var current_j = array[j]; |
- array[i] = current_j; |
- array[j] = current_i; |
- } else { |
- array[j] = current_i; |
- delete array[i]; |
- } |
- } else { |
- if (j in array) { |
- var current_j = array[j]; |
- array[i] = current_j; |
- delete array[j]; |
- } |
- } |
- } |
- return array; |
-} |
- |
- |
-function ArrayReverse() { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse"); |
- |
- var array = TO_OBJECT(this); |
- var len = TO_LENGTH_OR_UINT32(array.length); |
- var isArray = IS_ARRAY(array); |
- |
- if (UseSparseVariant(array, len, isArray, len)) { |
- %NormalizeElements(array); |
- SparseReverse(array, len); |
- return array; |
- } else if (isArray && %_HasFastPackedElements(array)) { |
- return PackedArrayReverse(array, len); |
- } else { |
- return GenericArrayReverse(array, len); |
- } |
-} |
- |
- |
-function ObservedArrayShift(len) { |
- var first = this[0]; |
- |
- try { |
- $observeBeginPerformSplice(this); |
- SimpleMove(this, 0, 1, len, 0); |
- this.length = len - 1; |
- } finally { |
- $observeEndPerformSplice(this); |
- $observeEnqueueSpliceRecord(this, 0, [first], 0); |
- } |
- |
- return first; |
-} |
- |
- |
-function ArrayShift() { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift"); |
- |
- var array = TO_OBJECT(this); |
- var len = TO_LENGTH_OR_UINT32(array.length); |
- |
- if (len === 0) { |
- array.length = 0; |
- return; |
- } |
- |
- if (ObjectIsSealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed); |
- |
- if (%IsObserved(array)) |
- return ObservedArrayShift.call(array, len); |
- |
- var first = array[0]; |
- |
- if (UseSparseVariant(array, len, IS_ARRAY(array), len)) { |
- SparseMove(array, 0, 1, len, 0); |
- } else { |
- SimpleMove(array, 0, 1, len, 0); |
- } |
- |
- array.length = len - 1; |
- |
- return first; |
-} |
- |
- |
-function ObservedArrayUnshift() { |
- var len = TO_LENGTH_OR_UINT32(this.length); |
- var num_arguments = %_ArgumentsLength(); |
- |
- try { |
- $observeBeginPerformSplice(this); |
- SimpleMove(this, 0, 0, len, num_arguments); |
- for (var i = 0; i < num_arguments; i++) { |
- this[i] = %_Arguments(i); |
- } |
- var new_length = len + num_arguments; |
- this.length = new_length; |
- } finally { |
- $observeEndPerformSplice(this); |
- $observeEnqueueSpliceRecord(this, 0, [], num_arguments); |
- } |
- |
- return new_length; |
-} |
- |
- |
-function ArrayUnshift(arg1) { // length == 1 |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift"); |
- |
- if (%IsObserved(this)) |
- return ObservedArrayUnshift.apply(this, arguments); |
- |
- var array = TO_OBJECT(this); |
- var len = TO_LENGTH_OR_UINT32(array.length); |
- var num_arguments = %_ArgumentsLength(); |
- |
- if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) && |
- !ObjectIsSealed(array)) { |
- SparseMove(array, 0, 0, len, num_arguments); |
- } else { |
- SimpleMove(array, 0, 0, len, num_arguments); |
- } |
- |
- for (var i = 0; i < num_arguments; i++) { |
- array[i] = %_Arguments(i); |
- } |
- |
- var new_length = len + num_arguments; |
- array.length = new_length; |
- return new_length; |
-} |
- |
- |
-function ArraySlice(start, end) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice"); |
- |
- var array = TO_OBJECT(this); |
- var len = TO_LENGTH_OR_UINT32(array.length); |
- var start_i = TO_INTEGER(start); |
- var end_i = len; |
- |
- if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end); |
- |
- if (start_i < 0) { |
- start_i += len; |
- if (start_i < 0) start_i = 0; |
- } else { |
- if (start_i > len) start_i = len; |
- } |
- |
- if (end_i < 0) { |
- end_i += len; |
- if (end_i < 0) end_i = 0; |
- } else { |
- if (end_i > len) end_i = len; |
- } |
- |
- var result = []; |
- |
- if (end_i < start_i) return result; |
- |
- if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) { |
- %NormalizeElements(array); |
- %NormalizeElements(result); |
- SparseSlice(array, start_i, end_i - start_i, len, result); |
- } else { |
- SimpleSlice(array, start_i, end_i - start_i, len, result); |
- } |
- |
- result.length = end_i - start_i; |
- |
- return result; |
-} |
- |
- |
-function ComputeSpliceStartIndex(start_i, len) { |
- if (start_i < 0) { |
- start_i += len; |
- return start_i < 0 ? 0 : start_i; |
- } |
- |
- return start_i > len ? len : start_i; |
-} |
- |
- |
-function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) { |
- // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is |
- // given as a request to delete all the elements from the start. |
- // And it differs from the case of undefined delete count. |
- // This does not follow ECMA-262, but we do the same for |
- // compatibility. |
- var del_count = 0; |
- if (num_arguments == 1) |
- return len - start_i; |
- |
- del_count = TO_INTEGER(delete_count); |
- if (del_count < 0) |
- return 0; |
- |
- if (del_count > len - start_i) |
- return len - start_i; |
- |
- return del_count; |
-} |
- |
- |
-function ObservedArraySplice(start, delete_count) { |
- var num_arguments = %_ArgumentsLength(); |
- var len = TO_LENGTH_OR_UINT32(this.length); |
- var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len); |
- var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len, |
- start_i); |
- var deleted_elements = []; |
- deleted_elements.length = del_count; |
- var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0; |
- |
- try { |
- $observeBeginPerformSplice(this); |
- |
- SimpleSlice(this, start_i, del_count, len, deleted_elements); |
- SimpleMove(this, start_i, del_count, len, num_elements_to_add); |
- |
- // Insert the arguments into the resulting array in |
- // place of the deleted elements. |
- var i = start_i; |
- var arguments_index = 2; |
- var arguments_length = %_ArgumentsLength(); |
- while (arguments_index < arguments_length) { |
- this[i++] = %_Arguments(arguments_index++); |
- } |
- this.length = len - del_count + num_elements_to_add; |
- |
- } finally { |
- $observeEndPerformSplice(this); |
- if (deleted_elements.length || num_elements_to_add) { |
- $observeEnqueueSpliceRecord(this, |
- start_i, |
- deleted_elements.slice(), |
- num_elements_to_add); |
- } |
- } |
- |
- // Return the deleted elements. |
- return deleted_elements; |
-} |
- |
- |
-function ArraySplice(start, delete_count) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice"); |
- |
- if (%IsObserved(this)) |
- return ObservedArraySplice.apply(this, arguments); |
- |
- var num_arguments = %_ArgumentsLength(); |
- var array = TO_OBJECT(this); |
- var len = TO_LENGTH_OR_UINT32(array.length); |
- var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len); |
- var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len, |
- start_i); |
- var deleted_elements = []; |
- deleted_elements.length = del_count; |
- var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0; |
- |
- if (del_count != num_elements_to_add && ObjectIsSealed(array)) { |
- throw MakeTypeError(kArrayFunctionsOnSealed); |
- } else if (del_count > 0 && ObjectIsFrozen(array)) { |
- throw MakeTypeError(kArrayFunctionsOnFrozen); |
- } |
- |
- var changed_elements = del_count; |
- if (num_elements_to_add != del_count) { |
- // If the slice needs to do a actually move elements after the insertion |
- // point, then include those in the estimate of changed elements. |
- changed_elements += len - start_i - del_count; |
- } |
- if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) { |
- %NormalizeElements(array); |
- %NormalizeElements(deleted_elements); |
- SparseSlice(array, start_i, del_count, len, deleted_elements); |
- SparseMove(array, start_i, del_count, len, num_elements_to_add); |
- } else { |
- SimpleSlice(array, start_i, del_count, len, deleted_elements); |
- SimpleMove(array, start_i, del_count, len, num_elements_to_add); |
- } |
- |
- // Insert the arguments into the resulting array in |
- // place of the deleted elements. |
- var i = start_i; |
- var arguments_index = 2; |
- var arguments_length = %_ArgumentsLength(); |
- while (arguments_index < arguments_length) { |
- array[i++] = %_Arguments(arguments_index++); |
- } |
- array.length = len - del_count + num_elements_to_add; |
- |
- // Return the deleted elements. |
- return deleted_elements; |
-} |
- |
- |
-function InnerArraySort(array, length, comparefn) { |
- // In-place QuickSort algorithm. |
- // For short (length <= 22) arrays, insertion sort is used for efficiency. |
- |
- if (!IS_CALLABLE(comparefn)) { |
- comparefn = function (x, y) { |
- if (x === y) return 0; |
- if (%_IsSmi(x) && %_IsSmi(y)) { |
- return %SmiLexicographicCompare(x, y); |
- } |
- x = TO_STRING(x); |
- y = TO_STRING(y); |
- if (x == y) return 0; |
- else return x < y ? -1 : 1; |
- }; |
- } |
- var InsertionSort = function InsertionSort(a, from, to) { |
- for (var i = from + 1; i < to; i++) { |
- var element = a[i]; |
- for (var j = i - 1; j >= from; j--) { |
- var tmp = a[j]; |
- var order = comparefn(tmp, element); |
- if (order > 0) { |
- a[j + 1] = tmp; |
- } else { |
- break; |
- } |
- } |
- a[j + 1] = element; |
- } |
- }; |
- |
- var GetThirdIndex = function(a, from, to) { |
- var t_array = new InternalArray(); |
- // Use both 'from' and 'to' to determine the pivot candidates. |
- var increment = 200 + ((to - from) & 15); |
- var j = 0; |
- from += 1; |
- to -= 1; |
- for (var i = from; i < to; i += increment) { |
- t_array[j] = [i, a[i]]; |
- j++; |
- } |
- t_array.sort(function(a, b) { |
- return comparefn(a[1], b[1]); |
- }); |
- var third_index = t_array[t_array.length >> 1][0]; |
- return third_index; |
- } |
- |
- var QuickSort = function QuickSort(a, from, to) { |
- var third_index = 0; |
- while (true) { |
- // Insertion sort is faster for short arrays. |
- if (to - from <= 10) { |
- InsertionSort(a, from, to); |
- return; |
- } |
- if (to - from > 1000) { |
- third_index = GetThirdIndex(a, from, to); |
- } else { |
- third_index = from + ((to - from) >> 1); |
- } |
- // Find a pivot as the median of first, last and middle element. |
- var v0 = a[from]; |
- var v1 = a[to - 1]; |
- var v2 = a[third_index]; |
- var c01 = comparefn(v0, v1); |
- if (c01 > 0) { |
- // v1 < v0, so swap them. |
- var tmp = v0; |
- v0 = v1; |
- v1 = tmp; |
- } // v0 <= v1. |
- var c02 = comparefn(v0, v2); |
- if (c02 >= 0) { |
- // v2 <= v0 <= v1. |
- var tmp = v0; |
- v0 = v2; |
- v2 = v1; |
- v1 = tmp; |
- } else { |
- // v0 <= v1 && v0 < v2 |
- var c12 = comparefn(v1, v2); |
- if (c12 > 0) { |
- // v0 <= v2 < v1 |
- var tmp = v1; |
- v1 = v2; |
- v2 = tmp; |
- } |
- } |
- // v0 <= v1 <= v2 |
- a[from] = v0; |
- a[to - 1] = v2; |
- var pivot = v1; |
- var low_end = from + 1; // Upper bound of elements lower than pivot. |
- var high_start = to - 1; // Lower bound of elements greater than pivot. |
- a[third_index] = a[low_end]; |
- a[low_end] = pivot; |
- |
- // From low_end to i are elements equal to pivot. |
- // From i to high_start are elements that haven't been compared yet. |
- partition: for (var i = low_end + 1; i < high_start; i++) { |
- var element = a[i]; |
- var order = comparefn(element, pivot); |
- if (order < 0) { |
- a[i] = a[low_end]; |
- a[low_end] = element; |
- low_end++; |
- } else if (order > 0) { |
- do { |
- high_start--; |
- if (high_start == i) break partition; |
- var top_elem = a[high_start]; |
- order = comparefn(top_elem, pivot); |
- } while (order > 0); |
- a[i] = a[high_start]; |
- a[high_start] = element; |
- if (order < 0) { |
- element = a[i]; |
- a[i] = a[low_end]; |
- a[low_end] = element; |
- low_end++; |
- } |
- } |
- } |
- if (to - high_start < low_end - from) { |
- QuickSort(a, high_start, to); |
- to = low_end; |
- } else { |
- QuickSort(a, from, low_end); |
- from = high_start; |
- } |
- } |
- }; |
- |
- // Copy elements in the range 0..length from obj's prototype chain |
- // to obj itself, if obj has holes. Return one more than the maximal index |
- // of a prototype property. |
- var CopyFromPrototype = function CopyFromPrototype(obj, length) { |
- var max = 0; |
- for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) { |
- var indices = %GetArrayKeys(proto, length); |
- if (IS_NUMBER(indices)) { |
- // It's an interval. |
- var proto_length = indices; |
- for (var i = 0; i < proto_length; i++) { |
- if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) { |
- obj[i] = proto[i]; |
- if (i >= max) { max = i + 1; } |
- } |
- } |
- } else { |
- for (var i = 0; i < indices.length; i++) { |
- var index = indices[i]; |
- if (!IS_UNDEFINED(index) && !HAS_OWN_PROPERTY(obj, index) |
- && HAS_OWN_PROPERTY(proto, index)) { |
- obj[index] = proto[index]; |
- if (index >= max) { max = index + 1; } |
- } |
- } |
- } |
- } |
- return max; |
- }; |
- |
- // Set a value of "undefined" on all indices in the range from..to |
- // where a prototype of obj has an element. I.e., shadow all prototype |
- // elements in that range. |
- var ShadowPrototypeElements = function(obj, from, to) { |
- for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) { |
- var indices = %GetArrayKeys(proto, to); |
- if (IS_NUMBER(indices)) { |
- // It's an interval. |
- var proto_length = indices; |
- for (var i = from; i < proto_length; i++) { |
- if (HAS_OWN_PROPERTY(proto, i)) { |
- obj[i] = UNDEFINED; |
- } |
- } |
- } else { |
- for (var i = 0; i < indices.length; i++) { |
- var index = indices[i]; |
- if (!IS_UNDEFINED(index) && from <= index && |
- HAS_OWN_PROPERTY(proto, index)) { |
- obj[index] = UNDEFINED; |
- } |
- } |
- } |
- } |
- }; |
- |
- var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) { |
- // Copy defined elements from the end to fill in all holes and undefineds |
- // in the beginning of the array. Write undefineds and holes at the end |
- // after loop is finished. |
- var first_undefined = 0; |
- var last_defined = length - 1; |
- var num_holes = 0; |
- while (first_undefined < last_defined) { |
- // Find first undefined element. |
- while (first_undefined < last_defined && |
- !IS_UNDEFINED(obj[first_undefined])) { |
- first_undefined++; |
- } |
- // Maintain the invariant num_holes = the number of holes in the original |
- // array with indices <= first_undefined or > last_defined. |
- if (!HAS_OWN_PROPERTY(obj, first_undefined)) { |
- num_holes++; |
- } |
- |
- // Find last defined element. |
- while (first_undefined < last_defined && |
- IS_UNDEFINED(obj[last_defined])) { |
- if (!HAS_OWN_PROPERTY(obj, last_defined)) { |
- num_holes++; |
- } |
- last_defined--; |
- } |
- if (first_undefined < last_defined) { |
- // Fill in hole or undefined. |
- obj[first_undefined] = obj[last_defined]; |
- obj[last_defined] = UNDEFINED; |
- } |
- } |
- // If there were any undefineds in the entire array, first_undefined |
- // points to one past the last defined element. Make this true if |
- // there were no undefineds, as well, so that first_undefined == number |
- // of defined elements. |
- if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++; |
- // Fill in the undefineds and the holes. There may be a hole where |
- // an undefined should be and vice versa. |
- var i; |
- for (i = first_undefined; i < length - num_holes; i++) { |
- obj[i] = UNDEFINED; |
- } |
- for (i = length - num_holes; i < length; i++) { |
- // For compatability with Webkit, do not expose elements in the prototype. |
- if (i in %_GetPrototype(obj)) { |
- obj[i] = UNDEFINED; |
- } else { |
- delete obj[i]; |
- } |
- } |
- |
- // Return the number of defined elements. |
- return first_undefined; |
- }; |
- |
- if (length < 2) return array; |
- |
- var is_array = IS_ARRAY(array); |
- var max_prototype_element; |
- if (!is_array) { |
- // For compatibility with JSC, we also sort elements inherited from |
- // the prototype chain on non-Array objects. |
- // We do this by copying them to this object and sorting only |
- // own elements. This is not very efficient, but sorting with |
- // inherited elements happens very, very rarely, if at all. |
- // The specification allows "implementation dependent" behavior |
- // if an element on the prototype chain has an element that |
- // might interact with sorting. |
- max_prototype_element = CopyFromPrototype(array, length); |
- } |
- |
- // %RemoveArrayHoles returns -1 if fast removal is not supported. |
- var num_non_undefined = %RemoveArrayHoles(array, length); |
- |
- if (num_non_undefined == -1) { |
- // The array is observed, or there were indexed accessors in the array. |
- // Move array holes and undefineds to the end using a Javascript function |
- // that is safe in the presence of accessors and is observable. |
- num_non_undefined = SafeRemoveArrayHoles(array); |
- } |
- |
- QuickSort(array, 0, num_non_undefined); |
- |
- if (!is_array && (num_non_undefined + 1 < max_prototype_element)) { |
- // For compatibility with JSC, we shadow any elements in the prototype |
- // chain that has become exposed by sort moving a hole to its position. |
- ShadowPrototypeElements(array, num_non_undefined, max_prototype_element); |
- } |
- |
- return array; |
-} |
- |
- |
-function ArraySort(comparefn) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort"); |
- |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- return InnerArraySort(array, length, comparefn); |
-} |
- |
- |
-// The following functions cannot be made efficient on sparse arrays while |
-// preserving the semantics, since the calls to the receiver function can add |
-// or delete elements from the array. |
-function InnerArrayFilter(f, receiver, array, length) { |
- if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); |
- |
- var accumulator = new InternalArray(); |
- var accumulator_length = 0; |
- var is_array = IS_ARRAY(array); |
- var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
- for (var i = 0; i < length; i++) { |
- if (HAS_INDEX(array, i, is_array)) { |
- var element = array[i]; |
- // Prepare break slots for debugger step in. |
- if (stepping) %DebugPrepareStepInIfStepping(f); |
- if (%_Call(f, receiver, element, i, array)) { |
- accumulator[accumulator_length++] = element; |
- } |
- } |
- } |
- return accumulator; |
-} |
- |
-function ArrayFilter(f, receiver) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter"); |
- |
- // Pull out the length so that modifications to the length in the |
- // loop will not affect the looping and side effects are visible. |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- var accumulator = InnerArrayFilter(f, receiver, array, length); |
- var result = new GlobalArray(); |
- %MoveArrayContents(accumulator, result); |
- return result; |
-} |
- |
-function InnerArrayForEach(f, receiver, array, length) { |
- if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); |
- |
- var is_array = IS_ARRAY(array); |
- var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
- for (var i = 0; i < length; i++) { |
- if (HAS_INDEX(array, i, is_array)) { |
- var element = array[i]; |
- // Prepare break slots for debugger step in. |
- if (stepping) %DebugPrepareStepInIfStepping(f); |
- %_Call(f, receiver, element, i, array); |
- } |
- } |
-} |
- |
-function ArrayForEach(f, receiver) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach"); |
- |
- // Pull out the length so that modifications to the length in the |
- // loop will not affect the looping and side effects are visible. |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- InnerArrayForEach(f, receiver, array, length); |
-} |
- |
- |
-function InnerArraySome(f, receiver, array, length) { |
- if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); |
- |
- var is_array = IS_ARRAY(array); |
- var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
- for (var i = 0; i < length; i++) { |
- if (HAS_INDEX(array, i, is_array)) { |
- var element = array[i]; |
- // Prepare break slots for debugger step in. |
- if (stepping) %DebugPrepareStepInIfStepping(f); |
- if (%_Call(f, receiver, element, i, array)) return true; |
- } |
- } |
- return false; |
-} |
- |
- |
-// Executes the function once for each element present in the |
-// array until it finds one where callback returns true. |
-function ArraySome(f, receiver) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some"); |
- |
- // Pull out the length so that modifications to the length in the |
- // loop will not affect the looping and side effects are visible. |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- return InnerArraySome(f, receiver, array, length); |
-} |
- |
- |
-function InnerArrayEvery(f, receiver, array, length) { |
- if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); |
- |
- var is_array = IS_ARRAY(array); |
- var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
- for (var i = 0; i < length; i++) { |
- if (HAS_INDEX(array, i, is_array)) { |
- var element = array[i]; |
- // Prepare break slots for debugger step in. |
- if (stepping) %DebugPrepareStepInIfStepping(f); |
- if (!%_Call(f, receiver, element, i, array)) return false; |
- } |
- } |
- return true; |
-} |
- |
-function ArrayEvery(f, receiver) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every"); |
- |
- // Pull out the length so that modifications to the length in the |
- // loop will not affect the looping and side effects are visible. |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- return InnerArrayEvery(f, receiver, array, length); |
-} |
- |
- |
-function InnerArrayMap(f, receiver, array, length) { |
- if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); |
- |
- var accumulator = new InternalArray(length); |
- var is_array = IS_ARRAY(array); |
- var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
- for (var i = 0; i < length; i++) { |
- if (HAS_INDEX(array, i, is_array)) { |
- var element = array[i]; |
- // Prepare break slots for debugger step in. |
- if (stepping) %DebugPrepareStepInIfStepping(f); |
- accumulator[i] = %_Call(f, receiver, element, i, array); |
- } |
- } |
- return accumulator; |
-} |
- |
- |
-function ArrayMap(f, receiver) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map"); |
- |
- // Pull out the length so that modifications to the length in the |
- // loop will not affect the looping and side effects are visible. |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- var accumulator = InnerArrayMap(f, receiver, array, length); |
- var result = new GlobalArray(); |
- %MoveArrayContents(accumulator, result); |
- return result; |
-} |
- |
- |
-// For .indexOf, we don't need to pass in the number of arguments |
-// at the callsite since ToInteger(undefined) == 0; however, for |
-// .lastIndexOf, we need to pass it, since the behavior for passing |
-// undefined is 0 but for not including the argument is length-1. |
-function InnerArrayIndexOf(array, element, index, length) { |
- if (length == 0) return -1; |
- if (IS_UNDEFINED(index)) { |
- index = 0; |
- } else { |
- index = TO_INTEGER(index); |
- // If index is negative, index from the end of the array. |
- if (index < 0) { |
- index = length + index; |
- // If index is still negative, search the entire array. |
- if (index < 0) index = 0; |
- } |
- } |
- var min = index; |
- var max = length; |
- if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) { |
- %NormalizeElements(array); |
- var indices = %GetArrayKeys(array, length); |
- if (IS_NUMBER(indices)) { |
- // It's an interval. |
- max = indices; // Capped by length already. |
- // Fall through to loop below. |
- } else { |
- if (indices.length == 0) return -1; |
- // Get all the keys in sorted order. |
- var sortedKeys = GetSortedArrayKeys(array, indices); |
- var n = sortedKeys.length; |
- var i = 0; |
- while (i < n && sortedKeys[i] < index) i++; |
- while (i < n) { |
- var key = sortedKeys[i]; |
- if (!IS_UNDEFINED(key) && array[key] === element) return key; |
- i++; |
- } |
- return -1; |
- } |
- } |
- // Lookup through the array. |
- if (!IS_UNDEFINED(element)) { |
- for (var i = min; i < max; i++) { |
- if (array[i] === element) return i; |
- } |
- return -1; |
- } |
- // Lookup through the array. |
- for (var i = min; i < max; i++) { |
- if (IS_UNDEFINED(array[i]) && i in array) { |
- return i; |
- } |
- } |
- return -1; |
-} |
- |
- |
-function ArrayIndexOf(element, index) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf"); |
- |
- var length = TO_LENGTH_OR_UINT32(this.length); |
- return InnerArrayIndexOf(this, element, index, length); |
-} |
- |
- |
-function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) { |
- if (length == 0) return -1; |
- if (argumentsLength < 2) { |
- index = length - 1; |
- } else { |
- index = TO_INTEGER(index); |
- // If index is negative, index from end of the array. |
- if (index < 0) index += length; |
- // If index is still negative, do not search the array. |
- if (index < 0) return -1; |
- else if (index >= length) index = length - 1; |
- } |
- var min = 0; |
- var max = index; |
- if (UseSparseVariant(array, length, IS_ARRAY(array), index)) { |
- %NormalizeElements(array); |
- var indices = %GetArrayKeys(array, index + 1); |
- if (IS_NUMBER(indices)) { |
- // It's an interval. |
- max = indices; // Capped by index already. |
- // Fall through to loop below. |
- } else { |
- if (indices.length == 0) return -1; |
- // Get all the keys in sorted order. |
- var sortedKeys = GetSortedArrayKeys(array, indices); |
- var i = sortedKeys.length - 1; |
- while (i >= 0) { |
- var key = sortedKeys[i]; |
- if (!IS_UNDEFINED(key) && array[key] === element) return key; |
- i--; |
- } |
- return -1; |
- } |
- } |
- // Lookup through the array. |
- if (!IS_UNDEFINED(element)) { |
- for (var i = max; i >= min; i--) { |
- if (array[i] === element) return i; |
- } |
- return -1; |
- } |
- for (var i = max; i >= min; i--) { |
- if (IS_UNDEFINED(array[i]) && i in array) { |
- return i; |
- } |
- } |
- return -1; |
-} |
- |
- |
-function ArrayLastIndexOf(element, index) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf"); |
- |
- var length = TO_LENGTH_OR_UINT32(this.length); |
- return InnerArrayLastIndexOf(this, element, index, length, |
- %_ArgumentsLength()); |
-} |
- |
- |
-function InnerArrayReduce(callback, current, array, length, argumentsLength) { |
- if (!IS_CALLABLE(callback)) { |
- throw MakeTypeError(kCalledNonCallable, callback); |
- } |
- |
- var is_array = IS_ARRAY(array); |
- var i = 0; |
- find_initial: if (argumentsLength < 2) { |
- for (; i < length; i++) { |
- if (HAS_INDEX(array, i, is_array)) { |
- current = array[i++]; |
- break find_initial; |
- } |
- } |
- throw MakeTypeError(kReduceNoInitial); |
- } |
- |
- var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback); |
- for (; i < length; i++) { |
- if (HAS_INDEX(array, i, is_array)) { |
- var element = array[i]; |
- // Prepare break slots for debugger step in. |
- if (stepping) %DebugPrepareStepInIfStepping(callback); |
- current = callback(current, element, i, array); |
- } |
- } |
- return current; |
-} |
- |
- |
-function ArrayReduce(callback, current) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce"); |
- |
- // Pull out the length so that modifications to the length in the |
- // loop will not affect the looping and side effects are visible. |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- return InnerArrayReduce(callback, current, array, length, |
- %_ArgumentsLength()); |
-} |
- |
- |
-function InnerArrayReduceRight(callback, current, array, length, |
- argumentsLength) { |
- if (!IS_CALLABLE(callback)) { |
- throw MakeTypeError(kCalledNonCallable, callback); |
- } |
- |
- var is_array = IS_ARRAY(array); |
- var i = length - 1; |
- find_initial: if (argumentsLength < 2) { |
- for (; i >= 0; i--) { |
- if (HAS_INDEX(array, i, is_array)) { |
- current = array[i--]; |
- break find_initial; |
- } |
- } |
- throw MakeTypeError(kReduceNoInitial); |
- } |
- |
- var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback); |
- for (; i >= 0; i--) { |
- if (HAS_INDEX(array, i, is_array)) { |
- var element = array[i]; |
- // Prepare break slots for debugger step in. |
- if (stepping) %DebugPrepareStepInIfStepping(callback); |
- current = callback(current, element, i, array); |
- } |
- } |
- return current; |
-} |
- |
- |
-function ArrayReduceRight(callback, current) { |
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight"); |
- |
- // Pull out the length so that side effects are visible before the |
- // callback function is checked. |
- var array = TO_OBJECT(this); |
- var length = TO_LENGTH_OR_UINT32(array.length); |
- return InnerArrayReduceRight(callback, current, array, length, |
- %_ArgumentsLength()); |
-} |
- |
-// ES5, 15.4.3.2 |
-function ArrayIsArray(obj) { |
- return IS_ARRAY(obj); |
-} |
- |
- |
-// ------------------------------------------------------------------- |
- |
-// Set up non-enumerable constructor property on the Array.prototype |
-// object. |
-%AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray, |
- DONT_ENUM); |
- |
-// Set up unscopable properties on the Array.prototype object. |
-var unscopables = { |
- __proto__: null, |
- copyWithin: true, |
- entries: true, |
- fill: true, |
- find: true, |
- findIndex: true, |
- keys: true, |
-}; |
- |
-%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables, |
- DONT_ENUM | READ_ONLY); |
- |
-// Set up non-enumerable functions on the Array object. |
-utils.InstallFunctions(GlobalArray, DONT_ENUM, [ |
- "isArray", ArrayIsArray |
-]); |
- |
-var specialFunctions = %SpecialArrayFunctions(); |
- |
-var getFunction = function(name, jsBuiltin, len) { |
- var f = jsBuiltin; |
- if (specialFunctions.hasOwnProperty(name)) { |
- f = specialFunctions[name]; |
- } |
- if (!IS_UNDEFINED(len)) { |
- %FunctionSetLength(f, len); |
- } |
- return f; |
-}; |
- |
-// Set up non-enumerable functions of the Array.prototype object and |
-// set their names. |
-// Manipulate the length of some of the functions to meet |
-// expectations set by ECMA-262 or Mozilla. |
-utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [ |
- "toString", getFunction("toString", ArrayToString), |
- "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString), |
- "join", getFunction("join", ArrayJoin), |
- "pop", getFunction("pop", ArrayPop), |
- "push", getFunction("push", ArrayPush, 1), |
- "reverse", getFunction("reverse", ArrayReverse), |
- "shift", getFunction("shift", ArrayShift), |
- "unshift", getFunction("unshift", ArrayUnshift, 1), |
- "slice", getFunction("slice", ArraySlice, 2), |
- "splice", getFunction("splice", ArraySplice, 2), |
- "sort", getFunction("sort", ArraySort), |
- "filter", getFunction("filter", ArrayFilter, 1), |
- "forEach", getFunction("forEach", ArrayForEach, 1), |
- "some", getFunction("some", ArraySome, 1), |
- "every", getFunction("every", ArrayEvery, 1), |
- "map", getFunction("map", ArrayMap, 1), |
- "indexOf", getFunction("indexOf", ArrayIndexOf, 1), |
- "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1), |
- "reduce", getFunction("reduce", ArrayReduce, 1), |
- "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1) |
-]); |
- |
-%FinishArrayPrototypeSetup(GlobalArray.prototype); |
- |
-// The internal Array prototype doesn't need to be fancy, since it's never |
-// exposed to user code. |
-// Adding only the functions that are actually used. |
-utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [ |
- "indexOf", getFunction("indexOf", ArrayIndexOf), |
- "join", getFunction("join", ArrayJoin), |
- "pop", getFunction("pop", ArrayPop), |
- "push", getFunction("push", ArrayPush), |
- "shift", getFunction("shift", ArrayShift), |
- "sort", getFunction("sort", ArraySort), |
- "splice", getFunction("splice", ArraySplice) |
-]); |
- |
-utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [ |
- "join", getFunction("join", ArrayJoin), |
- "pop", getFunction("pop", ArrayPop), |
- "push", getFunction("push", ArrayPush), |
- "shift", getFunction("shift", ArrayShift) |
-]); |
- |
-// ------------------------------------------------------------------- |
-// Exports |
- |
-utils.Export(function(to) { |
- to.ArrayIndexOf = ArrayIndexOf; |
- to.ArrayJoin = ArrayJoin; |
- to.ArrayPush = ArrayPush; |
- to.ArrayToString = ArrayToString; |
- to.InnerArrayEvery = InnerArrayEvery; |
- to.InnerArrayFilter = InnerArrayFilter; |
- to.InnerArrayForEach = InnerArrayForEach; |
- to.InnerArrayIndexOf = InnerArrayIndexOf; |
- to.InnerArrayJoin = InnerArrayJoin; |
- to.InnerArrayLastIndexOf = InnerArrayLastIndexOf; |
- to.InnerArrayMap = InnerArrayMap; |
- to.InnerArrayReduce = InnerArrayReduce; |
- to.InnerArrayReduceRight = InnerArrayReduceRight; |
- to.InnerArraySome = InnerArraySome; |
- to.InnerArraySort = InnerArraySort; |
- to.InnerArrayToLocaleString = InnerArrayToLocaleString; |
- to.PackedArrayReverse = PackedArrayReverse; |
-}); |
- |
-%InstallToContext([ |
- "array_pop", ArrayPop, |
- "array_push", ArrayPush, |
- "array_shift", ArrayShift, |
- "array_splice", ArraySplice, |
- "array_slice", ArraySlice, |
- "array_unshift", ArrayUnshift, |
-]); |
- |
-}); |