OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function(global, utils, extrasUtils) { | 5 (function(global, utils, extrasUtils) { |
6 | 6 |
7 "use strict"; | 7 "use strict"; |
8 | 8 |
9 %CheckIsBootstrapping(); | 9 %CheckIsBootstrapping(); |
10 | 10 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
66 if (FLAG_harmony_species) { | 66 if (FLAG_harmony_species) { |
67 var result = ObjectDefineProperty(array, i, { | 67 var result = ObjectDefineProperty(array, i, { |
68 value: value, writable: true, configurable: true, enumerable: true | 68 value: value, writable: true, configurable: true, enumerable: true |
69 }); | 69 }); |
70 if (!result) throw MakeTypeError(kStrictCannotAssign, i); | 70 if (!result) throw MakeTypeError(kStrictCannotAssign, i); |
71 } else { | 71 } else { |
72 AddIndexedProperty(array, i, value); | 72 AddIndexedProperty(array, i, value); |
73 } | 73 } |
74 } | 74 } |
75 | 75 |
76 function KeySortCompare(a, b) { | |
77 return a - b; | |
78 } | |
76 | 79 |
77 // Global list of arrays visited during toString, toLocaleString and | |
78 // join invocations. | |
79 var visited_arrays = new InternalArray(); | |
80 | |
81 | |
82 // Gets a sorted array of array keys. Useful for operations on sparse | |
83 // arrays. Dupes have not been removed. | |
84 function GetSortedArrayKeys(array, indices) { | 80 function GetSortedArrayKeys(array, indices) { |
85 var keys = new InternalArray(); | |
86 if (IS_NUMBER(indices)) { | 81 if (IS_NUMBER(indices)) { |
82 var keys = new InternalArray(); | |
87 // It's an interval | 83 // It's an interval |
88 var limit = indices; | 84 var limit = indices; |
89 for (var i = 0; i < limit; ++i) { | 85 for (var i = 0; i < limit; ++i) { |
90 var e = array[i]; | 86 var e = array[i]; |
91 if (!IS_UNDEFINED(e) || i in array) { | 87 if (!IS_UNDEFINED(e) || i in array) { |
92 keys.push(i); | 88 keys.push(i); |
93 } | 89 } |
94 } | 90 } |
95 } else { | |
96 var length = indices.length; | |
97 for (var k = 0; k < length; ++k) { | |
98 var key = indices[k]; | |
99 if (!IS_UNDEFINED(key)) { | |
100 var e = array[key]; | |
101 if (!IS_UNDEFINED(e) || key in array) { | |
102 keys.push(key); | |
103 } | |
104 } | |
105 } | |
106 keys.sort(function(a, b) { return a - b; }); | |
107 } | 91 } |
108 return keys; | 92 return InnerArraySort(indices, indices.length, KeySortCompare); |
109 } | 93 } |
110 | 94 |
111 | 95 |
112 function SparseJoinWithSeparatorJS(array, len, convert, separator) { | 96 function SparseJoinWithSeparatorJS(array, keys, length, convert, separator) { |
113 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); | 97 var keys_length = keys.length; |
114 var totalLength = 0; | 98 var elements = new InternalArray(keys_length * 2); |
115 var elements = new InternalArray(keys.length * 2); | 99 for (var i = 0; i < keys_length; i++) { |
116 var previousKey = -1; | |
117 for (var i = 0; i < keys.length; i++) { | |
118 var key = keys[i]; | 100 var key = keys[i]; |
119 if (key != previousKey) { // keys may contain duplicates. | 101 var e = array[key]; |
120 var e = array[key]; | 102 elements[i * 2] = key; |
121 if (!IS_STRING(e)) e = convert(e); | 103 elements[i * 2 + 1] = IS_STRING(e) ? e : convert(e); |
122 elements[i * 2] = key; | |
123 elements[i * 2 + 1] = e; | |
124 previousKey = key; | |
125 } | |
126 } | 104 } |
127 return %SparseJoinWithSeparator(elements, len, separator); | 105 return %SparseJoinWithSeparator(elements, length, separator); |
128 } | 106 } |
129 | 107 |
130 | 108 |
131 // Optimized for sparse arrays if separator is ''. | 109 // Optimized for sparse arrays if separator is ''. |
132 function SparseJoin(array, len, convert) { | 110 function SparseJoin(array, keys, convert) { |
133 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); | |
134 var last_key = -1; | |
135 var keys_length = keys.length; | 111 var keys_length = keys.length; |
136 | |
137 var elements = new InternalArray(keys_length); | 112 var elements = new InternalArray(keys_length); |
138 var elements_length = 0; | |
139 | |
140 for (var i = 0; i < keys_length; i++) { | 113 for (var i = 0; i < keys_length; i++) { |
141 var key = keys[i]; | 114 var e = array[keys[i]]; |
142 if (key != last_key) { | 115 elements[i] = IS_STRING(e) ? e : convert(e); |
143 var e = array[key]; | |
144 if (!IS_STRING(e)) e = convert(e); | |
145 elements[elements_length++] = e; | |
146 last_key = key; | |
147 } | |
148 } | 116 } |
149 return %StringBuilderConcat(elements, elements_length, ''); | 117 return %StringBuilderConcat(elements, keys_length, ''); |
150 } | 118 } |
151 | 119 |
152 | 120 |
153 function UseSparseVariant(array, length, is_array, touched) { | 121 function UseSparseVariant(array, length, is_array, touched) { |
154 // Only use the sparse variant on arrays that are likely to be sparse and the | 122 // Only use the sparse variant on arrays that are likely to be sparse and the |
155 // number of elements touched in the operation is relatively small compared to | 123 // number of elements touched in the operation is relatively small compared to |
156 // the overall size of the array. | 124 // the overall size of the array. |
157 if (!is_array || length < 1000 || %IsObserved(array) || | 125 if (!is_array || length < 1000 || %IsObserved(array) || |
158 %HasComplexElements(array)) { | 126 %HasComplexElements(array)) { |
159 return false; | 127 return false; |
160 } | 128 } |
161 if (!%_IsSmi(length)) { | 129 if (!%_IsSmi(length)) { |
162 return true; | 130 return true; |
163 } | 131 } |
164 var elements_threshold = length >> 2; // No more than 75% holes | 132 var elements_threshold = length >> 2; // No more than 75% holes |
165 var estimated_elements = %EstimateNumberOfElements(array); | 133 var estimated_elements = %EstimateNumberOfElements(array); |
166 return (estimated_elements < elements_threshold) && | 134 return (estimated_elements < elements_threshold) && |
167 (touched > estimated_elements * 4); | 135 (touched > estimated_elements * 4); |
168 } | 136 } |
169 | 137 |
138 function Stack() { | |
139 this.length = 0; | |
140 this.values = new InternalArray(); | |
141 } | |
142 | |
143 // Predeclare the instance variables on the prototype. Otherwise setting them in | |
144 // the constructor will leak the instance through settings on Object.prototype. | |
145 Stack.prototype.length = null; | |
146 Stack.prototype.values = null; | |
147 | |
148 function StackPush(stack, value) { | |
149 stack.values[stack.length++] = value; | |
150 } | |
151 | |
152 function StackPop(stack) { | |
153 stack.values[--stack.length] = null | |
154 } | |
155 | |
156 function StackHas(stack, v) { | |
157 var length = stack.length; | |
158 var values = stack.values; | |
159 for (var i = 0; i < length; i++) { | |
160 if (values[i] === v) return true; | |
161 } | |
162 return false; | |
163 } | |
164 | |
165 // Global list of arrays visited during toString, toLocaleString and | |
166 // join invocations. | |
167 var visited_arrays = new Stack(); | |
168 | |
169 function DoJoin(array, length, is_array, separator, convert) { | |
170 if (UseSparseVariant(array, length, is_array, length)) { | |
171 %NormalizeElements(array); | |
172 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, length)); | |
173 if (separator === '') { | |
174 if (keys.length === 0) return ''; | |
175 return SparseJoin(array, keys, convert); | |
176 } else { | |
177 return SparseJoinWithSeparatorJS(array, keys, length, convert, separator); | |
178 } | |
179 } | |
180 | |
181 // Fast case for one-element arrays. | |
182 if (length === 1) { | |
183 var e = array[0]; | |
184 return IS_STRING(e) ? e : convert(e); | |
185 } | |
186 | |
187 // Construct an array for the elements. | |
188 var elements = new InternalArray(length); | |
189 | |
190 // We pull the empty separator check outside the loop for speed! | |
191 if (separator === '') { | |
192 for (var i = 0; i < length; i++) { | |
193 var e = array[i]; | |
194 elements[i] = IS_STRING(e) ? e : convert(e); | |
195 } | |
196 return %StringBuilderConcat(elements, length, ''); | |
Camillo Bruni
2016/03/14 10:09:52
As you said, you could probably still create a sil
| |
197 } | |
198 // Non-empty separator case. | |
199 // If the first element is a number then use the heuristic that the | |
200 // remaining elements are also likely to be numbers. | |
201 var e = array[0]; | |
202 if (IS_NUMBER(e)) { | |
203 elements[0] = %_NumberToString(e); | |
204 for (var i = 1; i < length; i++) { | |
205 e = array[i]; | |
206 if (IS_NUMBER(e)) { | |
207 elements[i] = %_NumberToString(e); | |
208 } else { | |
209 elements[i] = IS_STRING(e) ? e : convert(e); | |
210 } | |
211 } | |
212 } else { | |
213 elements[0] = IS_STRING(e) ? e : convert(e); | |
214 for (var i = 1; i < length; i++) { | |
215 e = array[i]; | |
216 elements[i] = IS_STRING(e) ? e : convert(e); | |
217 } | |
218 } | |
219 return %StringBuilderJoin(elements, length, separator); | |
220 } | |
170 | 221 |
171 function Join(array, length, separator, convert) { | 222 function Join(array, length, separator, convert) { |
172 if (length == 0) return ''; | 223 if (length === 0) return ''; |
173 | 224 |
174 var is_array = IS_ARRAY(array); | 225 var is_array = IS_ARRAY(array); |
175 | 226 |
176 if (is_array) { | 227 if (is_array) { |
177 // If the array is cyclic, return the empty string for already | 228 // If the array is cyclic, return the empty string for already |
178 // visited arrays. | 229 // visited arrays. |
179 if (!%PushIfAbsent(visited_arrays, array)) return ''; | 230 if (StackHas(visited_arrays, array)) return ''; |
231 StackPush(visited_arrays, array); | |
180 } | 232 } |
181 | 233 |
182 // Attempt to convert the elements. | 234 // Attempt to convert the elements. |
183 try { | 235 try { |
184 if (UseSparseVariant(array, length, is_array, length)) { | 236 return DoJoin(array, length, is_array, separator, convert); |
185 %NormalizeElements(array); | |
186 if (separator.length == 0) { | |
187 return SparseJoin(array, length, convert); | |
188 } else { | |
189 return SparseJoinWithSeparatorJS(array, length, convert, separator); | |
190 } | |
191 } | |
192 | |
193 // Fast case for one-element arrays. | |
194 if (length == 1) { | |
195 var e = array[0]; | |
196 if (IS_STRING(e)) return e; | |
197 return convert(e); | |
198 } | |
199 | |
200 // Construct an array for the elements. | |
201 var elements = new InternalArray(length); | |
202 | |
203 // We pull the empty separator check outside the loop for speed! | |
204 if (separator.length == 0) { | |
205 var elements_length = 0; | |
206 for (var i = 0; i < length; i++) { | |
207 var e = array[i]; | |
208 if (!IS_STRING(e)) e = convert(e); | |
209 elements[elements_length++] = e; | |
210 } | |
211 elements.length = elements_length; | |
212 return %StringBuilderConcat(elements, elements_length, ''); | |
213 } | |
214 // Non-empty separator case. | |
215 // If the first element is a number then use the heuristic that the | |
216 // remaining elements are also likely to be numbers. | |
217 var e = array[0]; | |
218 if (!IS_NUMBER(e)) { | |
219 if (!IS_STRING(e)) e = convert(e); | |
220 elements[0] = e; | |
221 for (var i = 1; i < length; i++) { | |
222 e = array[i]; | |
223 if (!IS_STRING(e)) e = convert(e); | |
224 elements[i] = e; | |
225 } | |
226 } else { | |
227 elements[0] = %_NumberToString(e); | |
228 for (var i = 1; i < length; i++) { | |
229 e = array[i]; | |
230 if (IS_NUMBER(e)) { | |
231 e = %_NumberToString(e); | |
232 } else if (!IS_STRING(e)) { | |
233 e = convert(e); | |
234 } | |
235 elements[i] = e; | |
236 } | |
237 } | |
238 return %StringBuilderJoin(elements, length, separator); | |
239 } finally { | 237 } finally { |
240 // Make sure to remove the last element of the visited array no | 238 // Make sure to remove the last element of the visited array no |
241 // matter what happens. | 239 // matter what happens. |
242 if (is_array) visited_arrays.length = visited_arrays.length - 1; | 240 if (is_array) StackPop(visited_arrays); |
243 } | 241 } |
244 } | 242 } |
245 | 243 |
246 | 244 |
247 function ConvertToString(x) { | 245 function ConvertToString(x) { |
248 if (IS_NULL_OR_UNDEFINED(x)) { | 246 if (IS_NULL_OR_UNDEFINED(x)) return ''; |
249 return ''; | 247 return TO_STRING(x); |
250 } else { | |
251 return TO_STRING(x); | |
252 } | |
253 } | 248 } |
254 | 249 |
255 | 250 |
256 function ConvertToLocaleString(e) { | 251 function ConvertToLocaleString(e) { |
257 if (IS_NULL_OR_UNDEFINED(e)) { | 252 if (IS_NULL_OR_UNDEFINED(e)) return ''; |
258 return ''; | 253 return TO_STRING(e.toLocaleString()); |
259 } else { | |
260 return TO_STRING(e.toLocaleString()); | |
261 } | |
262 } | 254 } |
263 | 255 |
264 | 256 |
265 // This function implements the optimized splice implementation that can use | 257 // This function implements the optimized splice implementation that can use |
266 // special array operations to handle sparse arrays in a sensible fashion. | 258 // special array operations to handle sparse arrays in a sensible fashion. |
267 function SparseSlice(array, start_i, del_count, len, deleted_elements) { | 259 function SparseSlice(array, start_i, del_count, len, deleted_elements) { |
268 // Move deleted elements to a new array (the return value from splice). | 260 // Move deleted elements to a new array (the return value from splice). |
269 var indices = %GetArrayKeys(array, start_i + del_count); | 261 var indices = %GetArrayKeys(array, start_i + del_count); |
270 if (IS_NUMBER(indices)) { | 262 if (IS_NUMBER(indices)) { |
271 var limit = indices; | 263 var limit = indices; |
(...skipping 1680 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1952 to.InnerArrayIncludes = InnerArrayIncludes; | 1944 to.InnerArrayIncludes = InnerArrayIncludes; |
1953 to.InnerArrayIndexOf = InnerArrayIndexOf; | 1945 to.InnerArrayIndexOf = InnerArrayIndexOf; |
1954 to.InnerArrayJoin = InnerArrayJoin; | 1946 to.InnerArrayJoin = InnerArrayJoin; |
1955 to.InnerArrayLastIndexOf = InnerArrayLastIndexOf; | 1947 to.InnerArrayLastIndexOf = InnerArrayLastIndexOf; |
1956 to.InnerArrayReduce = InnerArrayReduce; | 1948 to.InnerArrayReduce = InnerArrayReduce; |
1957 to.InnerArrayReduceRight = InnerArrayReduceRight; | 1949 to.InnerArrayReduceRight = InnerArrayReduceRight; |
1958 to.InnerArraySome = InnerArraySome; | 1950 to.InnerArraySome = InnerArraySome; |
1959 to.InnerArraySort = InnerArraySort; | 1951 to.InnerArraySort = InnerArraySort; |
1960 to.InnerArrayToLocaleString = InnerArrayToLocaleString; | 1952 to.InnerArrayToLocaleString = InnerArrayToLocaleString; |
1961 to.PackedArrayReverse = PackedArrayReverse; | 1953 to.PackedArrayReverse = PackedArrayReverse; |
1954 to.Stack = Stack; | |
1955 to.StackHas = StackHas; | |
1956 to.StackPush = StackPush; | |
1957 to.StackPop = StackPop; | |
1962 }); | 1958 }); |
1963 | 1959 |
1964 %InstallToContext([ | 1960 %InstallToContext([ |
1965 "array_pop", ArrayPop, | 1961 "array_pop", ArrayPop, |
1966 "array_push", ArrayPush, | 1962 "array_push", ArrayPush, |
1967 "array_shift", ArrayShift, | 1963 "array_shift", ArrayShift, |
1968 "array_splice", ArraySplice, | 1964 "array_splice", ArraySplice, |
1969 "array_slice", ArraySlice, | 1965 "array_slice", ArraySlice, |
1970 "array_unshift", ArrayUnshift, | 1966 "array_unshift", ArrayUnshift, |
1971 ]); | 1967 ]); |
1972 | 1968 |
1973 }); | 1969 }); |
OLD | NEW |