|
1 /* |
|
2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) |
|
3 * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved. |
|
4 * Copyright (C) 2003 Peter Kelly (pmk@post.com) |
|
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
|
6 * |
|
7 * This library is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU Lesser General Public |
|
9 * License as published by the Free Software Foundation; either |
|
10 * version 2 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This library is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 * Lesser General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU Lesser General Public |
|
18 * License along with this library; if not, write to the Free Software |
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
20 * |
|
21 */ |
|
22 |
|
23 #include "config.h" |
|
24 #include "JSArray.h" |
|
25 |
|
26 #include "ArrayPrototype.h" |
|
27 #include "CachedCall.h" |
|
28 #include "Error.h" |
|
29 #include "Executable.h" |
|
30 #include "PropertyNameArray.h" |
|
31 #include <wtf/AVLTree.h> |
|
32 #include <wtf/Assertions.h> |
|
33 #include <wtf/OwnPtr.h> |
|
34 #include <Operations.h> |
|
35 |
|
36 using namespace std; |
|
37 using namespace WTF; |
|
38 |
|
39 namespace JSC { |
|
40 |
|
41 ASSERT_CLASS_FITS_IN_CELL(JSArray); |
|
42 |
|
43 // Overview of JSArray |
|
44 // |
|
45 // Properties of JSArray objects may be stored in one of three locations: |
|
46 // * The regular JSObject property map. |
|
47 // * A storage vector. |
|
48 // * A sparse map of array entries. |
|
49 // |
|
50 // Properties with non-numeric identifiers, with identifiers that are not representable |
|
51 // as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX |
|
52 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit |
|
53 // integer) are not considered array indices and will be stored in the JSObject property map. |
|
54 // |
|
55 // All properties with a numeric identifer, representable as an unsigned integer i, |
|
56 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the |
|
57 // storage vector or the sparse map. An array index i will be handled in the following |
|
58 // fashion: |
|
59 // |
|
60 // * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector. |
|
61 // * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either |
|
62 // be stored in the storage vector or in the sparse array, depending on the density of |
|
63 // data that would be stored in the vector (a vector being used where at least |
|
64 // (1 / minDensityMultiplier) of the entries would be populated). |
|
65 // * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored |
|
66 // in the sparse array. |
|
67 |
|
68 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize |
|
69 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage |
|
70 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(JSValue)) + |
|
71 // (vectorLength * sizeof(JSValue)) must be <= 0xFFFFFFFFU (which is maximum value of size_t). |
|
72 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue)) |
|
73 |
|
74 // These values have to be macros to be used in max() and min() without introducing |
|
75 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. |
|
76 #define MIN_SPARSE_ARRAY_INDEX 10000U |
|
77 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1) |
|
78 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer. |
|
79 #define MAX_ARRAY_INDEX 0xFFFFFFFEU |
|
80 |
|
81 // Our policy for when to use a vector and when to use a sparse map. |
|
82 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector. |
|
83 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector |
|
84 // as long as it is 1/8 full. If more sparse than that, we use a map. |
|
85 static const unsigned minDensityMultiplier = 8; |
|
86 |
|
87 const ClassInfo JSArray::info = {"Array", 0, 0, 0}; |
|
88 |
|
89 static inline size_t storageSize(unsigned vectorLength) |
|
90 { |
|
91 ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH); |
|
92 |
|
93 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH) |
|
94 // - as asserted above - the following calculation cannot overflow. |
|
95 size_t size = (sizeof(ArrayStorage) - sizeof(JSValue)) + (vectorLength * sizeof(JSValue)); |
|
96 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that |
|
97 // MAX_STORAGE_VECTOR_LENGTH is correctly defined). |
|
98 ASSERT(((size - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(JSValue)))); |
|
99 |
|
100 return size; |
|
101 } |
|
102 |
|
103 static inline unsigned increasedVectorLength(unsigned newLength) |
|
104 { |
|
105 ASSERT(newLength <= MAX_STORAGE_VECTOR_LENGTH); |
|
106 |
|
107 // Mathematically equivalent to: |
|
108 // increasedLength = (newLength * 3 + 1) / 2; |
|
109 // or: |
|
110 // increasedLength = (unsigned)ceil(newLength * 1.5)); |
|
111 // This form is not prone to internal overflow. |
|
112 unsigned increasedLength = newLength + (newLength >> 1) + (newLength & 1); |
|
113 ASSERT(increasedLength >= newLength); |
|
114 |
|
115 return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); |
|
116 } |
|
117 |
|
118 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) |
|
119 { |
|
120 return length / minDensityMultiplier <= numValues; |
|
121 } |
|
122 |
|
123 #if !CHECK_ARRAY_CONSISTENCY |
|
124 |
|
125 inline void JSArray::checkConsistency(ConsistencyCheckType) |
|
126 { |
|
127 } |
|
128 |
|
129 #endif |
|
130 |
|
131 JSArray::JSArray(NonNullPassRefPtr<Structure> structure) |
|
132 : JSObject(structure) |
|
133 { |
|
134 unsigned initialCapacity = 0; |
|
135 |
|
136 m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); |
|
137 m_vectorLength = initialCapacity; |
|
138 |
|
139 checkConsistency(); |
|
140 } |
|
141 |
|
142 JSArray::JSArray(NonNullPassRefPtr<Structure> structure, unsigned initialLength, ArrayCreationMode creationMode) |
|
143 : JSObject(structure) |
|
144 { |
|
145 unsigned initialCapacity; |
|
146 if (creationMode == CreateCompact) |
|
147 initialCapacity = initialLength; |
|
148 else |
|
149 initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX); |
|
150 |
|
151 m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); |
|
152 m_vectorLength = initialCapacity; |
|
153 m_storage->m_sparseValueMap = 0; |
|
154 m_storage->subclassData = 0; |
|
155 m_storage->reportedMapCapacity = 0; |
|
156 |
|
157 if (creationMode == CreateCompact) { |
|
158 #if CHECK_ARRAY_CONSISTENCY |
|
159 m_storage->m_inCompactInitialization = !!initialCapacity; |
|
160 #endif |
|
161 m_storage->m_length = 0; |
|
162 m_storage->m_numValuesInVector = initialCapacity; |
|
163 } else { |
|
164 #if CHECK_ARRAY_CONSISTENCY |
|
165 m_storage->m_inCompactInitialization = false; |
|
166 #endif |
|
167 m_storage->m_length = initialLength; |
|
168 m_storage->m_numValuesInVector = 0; |
|
169 JSValue* vector = m_storage->m_vector; |
|
170 for (size_t i = 0; i < initialCapacity; ++i) |
|
171 vector[i] = JSValue(); |
|
172 } |
|
173 |
|
174 checkConsistency(); |
|
175 |
|
176 Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue)); |
|
177 } |
|
178 |
|
179 JSArray::JSArray(NonNullPassRefPtr<Structure> structure, const ArgList& list) |
|
180 : JSObject(structure) |
|
181 { |
|
182 unsigned initialCapacity = list.size(); |
|
183 |
|
184 m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); |
|
185 m_storage->m_length = initialCapacity; |
|
186 m_vectorLength = initialCapacity; |
|
187 m_storage->m_numValuesInVector = initialCapacity; |
|
188 m_storage->m_sparseValueMap = 0; |
|
189 m_storage->subclassData = 0; |
|
190 m_storage->reportedMapCapacity = 0; |
|
191 #if CHECK_ARRAY_CONSISTENCY |
|
192 m_storage->m_inCompactInitialization = false; |
|
193 #endif |
|
194 |
|
195 size_t i = 0; |
|
196 ArgList::const_iterator end = list.end(); |
|
197 for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i) |
|
198 m_storage->m_vector[i] = *it; |
|
199 |
|
200 checkConsistency(); |
|
201 |
|
202 Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity)); |
|
203 } |
|
204 |
|
205 JSArray::~JSArray() |
|
206 { |
|
207 ASSERT(vptr() == JSGlobalData::jsArrayVPtr); |
|
208 checkConsistency(DestructorConsistencyCheck); |
|
209 |
|
210 delete m_storage->m_sparseValueMap; |
|
211 fastFree(m_storage); |
|
212 } |
|
213 |
|
214 bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) |
|
215 { |
|
216 ArrayStorage* storage = m_storage; |
|
217 |
|
218 if (i >= storage->m_length) { |
|
219 if (i > MAX_ARRAY_INDEX) |
|
220 return getOwnPropertySlot(exec, Identifier::from(exec, i), slot); |
|
221 return false; |
|
222 } |
|
223 |
|
224 if (i < m_vectorLength) { |
|
225 JSValue& valueSlot = storage->m_vector[i]; |
|
226 if (valueSlot) { |
|
227 slot.setValueSlot(&valueSlot); |
|
228 return true; |
|
229 } |
|
230 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { |
|
231 if (i >= MIN_SPARSE_ARRAY_INDEX) { |
|
232 SparseArrayValueMap::iterator it = map->find(i); |
|
233 if (it != map->end()) { |
|
234 slot.setValueSlot(&it->second); |
|
235 return true; |
|
236 } |
|
237 } |
|
238 } |
|
239 |
|
240 return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, i), slot); |
|
241 } |
|
242 |
|
243 bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
|
244 { |
|
245 if (propertyName == exec->propertyNames().length) { |
|
246 slot.setValue(jsNumber(exec, length())); |
|
247 return true; |
|
248 } |
|
249 |
|
250 bool isArrayIndex; |
|
251 unsigned i = propertyName.toArrayIndex(&isArrayIndex); |
|
252 if (isArrayIndex) |
|
253 return JSArray::getOwnPropertySlot(exec, i, slot); |
|
254 |
|
255 return JSObject::getOwnPropertySlot(exec, propertyName, slot); |
|
256 } |
|
257 |
|
258 bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) |
|
259 { |
|
260 if (propertyName == exec->propertyNames().length) { |
|
261 descriptor.setDescriptor(jsNumber(exec, length()), DontDelete | DontEnum); |
|
262 return true; |
|
263 } |
|
264 |
|
265 bool isArrayIndex; |
|
266 unsigned i = propertyName.toArrayIndex(&isArrayIndex); |
|
267 if (isArrayIndex) { |
|
268 if (i >= m_storage->m_length) |
|
269 return false; |
|
270 if (i < m_vectorLength) { |
|
271 JSValue& value = m_storage->m_vector[i]; |
|
272 if (value) { |
|
273 descriptor.setDescriptor(value, 0); |
|
274 return true; |
|
275 } |
|
276 } else if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { |
|
277 if (i >= MIN_SPARSE_ARRAY_INDEX) { |
|
278 SparseArrayValueMap::iterator it = map->find(i); |
|
279 if (it != map->end()) { |
|
280 descriptor.setDescriptor(it->second, 0); |
|
281 return true; |
|
282 } |
|
283 } |
|
284 } |
|
285 } |
|
286 return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); |
|
287 } |
|
288 |
|
289 // ECMA 15.4.5.1 |
|
290 void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) |
|
291 { |
|
292 bool isArrayIndex; |
|
293 unsigned i = propertyName.toArrayIndex(&isArrayIndex); |
|
294 if (isArrayIndex) { |
|
295 put(exec, i, value); |
|
296 return; |
|
297 } |
|
298 |
|
299 if (propertyName == exec->propertyNames().length) { |
|
300 unsigned newLength = value.toUInt32(exec); |
|
301 if (value.toNumber(exec) != static_cast<double>(newLength)) { |
|
302 throwError(exec, createRangeError(exec, "Invalid array length.")); |
|
303 return; |
|
304 } |
|
305 setLength(newLength); |
|
306 return; |
|
307 } |
|
308 |
|
309 JSObject::put(exec, propertyName, value, slot); |
|
310 } |
|
311 |
|
312 void JSArray::put(ExecState* exec, unsigned i, JSValue value) |
|
313 { |
|
314 checkConsistency(); |
|
315 |
|
316 unsigned length = m_storage->m_length; |
|
317 if (i >= length && i <= MAX_ARRAY_INDEX) { |
|
318 length = i + 1; |
|
319 m_storage->m_length = length; |
|
320 } |
|
321 |
|
322 if (i < m_vectorLength) { |
|
323 JSValue& valueSlot = m_storage->m_vector[i]; |
|
324 if (valueSlot) { |
|
325 valueSlot = value; |
|
326 checkConsistency(); |
|
327 return; |
|
328 } |
|
329 valueSlot = value; |
|
330 ++m_storage->m_numValuesInVector; |
|
331 checkConsistency(); |
|
332 return; |
|
333 } |
|
334 |
|
335 putSlowCase(exec, i, value); |
|
336 } |
|
337 |
|
338 NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value) |
|
339 { |
|
340 ArrayStorage* storage = m_storage; |
|
341 SparseArrayValueMap* map = storage->m_sparseValueMap; |
|
342 |
|
343 if (i >= MIN_SPARSE_ARRAY_INDEX) { |
|
344 if (i > MAX_ARRAY_INDEX) { |
|
345 PutPropertySlot slot; |
|
346 put(exec, Identifier::from(exec, i), value, slot); |
|
347 return; |
|
348 } |
|
349 |
|
350 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end |
|
351 // (which will only be compacted as we reach indices that are less than MIN_SPARSE_ARRAY_INDEX) - but this makes the check much faster. |
|
352 if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) { |
|
353 if (!map) { |
|
354 map = new SparseArrayValueMap; |
|
355 storage->m_sparseValueMap = map; |
|
356 } |
|
357 |
|
358 pair<SparseArrayValueMap::iterator, bool> result = map->add(i, value); |
|
359 if (!result.second) { // pre-existing entry |
|
360 result.first->second = value; |
|
361 return; |
|
362 } |
|
363 |
|
364 size_t capacity = map->capacity(); |
|
365 if (capacity != storage->reportedMapCapacity) { |
|
366 Heap::heap(this)->reportExtraMemoryCost((capacity - storage->reportedMapCapacity) * (sizeof(unsigned) + sizeof(JSValue))); |
|
367 storage->reportedMapCapacity = capacity; |
|
368 } |
|
369 return; |
|
370 } |
|
371 } |
|
372 |
|
373 // We have decided that we'll put the new item into the vector. |
|
374 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it. |
|
375 if (!map || map->isEmpty()) { |
|
376 if (increaseVectorLength(i + 1)) { |
|
377 storage = m_storage; |
|
378 storage->m_vector[i] = value; |
|
379 ++storage->m_numValuesInVector; |
|
380 checkConsistency(); |
|
381 } else |
|
382 throwOutOfMemoryError(exec); |
|
383 return; |
|
384 } |
|
385 |
|
386 // Decide how many values it would be best to move from the map. |
|
387 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; |
|
388 unsigned newVectorLength = increasedVectorLength(i + 1); |
|
389 for (unsigned j = max(m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) |
|
390 newNumValuesInVector += map->contains(j); |
|
391 if (i >= MIN_SPARSE_ARRAY_INDEX) |
|
392 newNumValuesInVector -= map->contains(i); |
|
393 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) { |
|
394 unsigned proposedNewNumValuesInVector = newNumValuesInVector; |
|
395 // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further. |
|
396 while (newVectorLength < MAX_STORAGE_VECTOR_LENGTH) { |
|
397 unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1); |
|
398 for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j) |
|
399 proposedNewNumValuesInVector += map->contains(j); |
|
400 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) |
|
401 break; |
|
402 newVectorLength = proposedNewVectorLength; |
|
403 newNumValuesInVector = proposedNewNumValuesInVector; |
|
404 } |
|
405 } |
|
406 |
|
407 if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) { |
|
408 throwOutOfMemoryError(exec); |
|
409 return; |
|
410 } |
|
411 |
|
412 unsigned vectorLength = m_vectorLength; |
|
413 |
|
414 if (newNumValuesInVector == storage->m_numValuesInVector + 1) { |
|
415 for (unsigned j = vectorLength; j < newVectorLength; ++j) |
|
416 storage->m_vector[j] = JSValue(); |
|
417 if (i > MIN_SPARSE_ARRAY_INDEX) |
|
418 map->remove(i); |
|
419 } else { |
|
420 for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j) |
|
421 storage->m_vector[j] = JSValue(); |
|
422 for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) |
|
423 storage->m_vector[j] = map->take(j); |
|
424 } |
|
425 |
|
426 storage->m_vector[i] = value; |
|
427 |
|
428 m_vectorLength = newVectorLength; |
|
429 storage->m_numValuesInVector = newNumValuesInVector; |
|
430 |
|
431 m_storage = storage; |
|
432 |
|
433 checkConsistency(); |
|
434 |
|
435 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); |
|
436 } |
|
437 |
|
438 bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName) |
|
439 { |
|
440 bool isArrayIndex; |
|
441 unsigned i = propertyName.toArrayIndex(&isArrayIndex); |
|
442 if (isArrayIndex) |
|
443 return deleteProperty(exec, i); |
|
444 |
|
445 if (propertyName == exec->propertyNames().length) |
|
446 return false; |
|
447 |
|
448 return JSObject::deleteProperty(exec, propertyName); |
|
449 } |
|
450 |
|
451 bool JSArray::deleteProperty(ExecState* exec, unsigned i) |
|
452 { |
|
453 checkConsistency(); |
|
454 |
|
455 ArrayStorage* storage = m_storage; |
|
456 |
|
457 if (i < m_vectorLength) { |
|
458 JSValue& valueSlot = storage->m_vector[i]; |
|
459 if (!valueSlot) { |
|
460 checkConsistency(); |
|
461 return false; |
|
462 } |
|
463 valueSlot = JSValue(); |
|
464 --storage->m_numValuesInVector; |
|
465 checkConsistency(); |
|
466 return true; |
|
467 } |
|
468 |
|
469 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { |
|
470 if (i >= MIN_SPARSE_ARRAY_INDEX) { |
|
471 SparseArrayValueMap::iterator it = map->find(i); |
|
472 if (it != map->end()) { |
|
473 map->remove(it); |
|
474 checkConsistency(); |
|
475 return true; |
|
476 } |
|
477 } |
|
478 } |
|
479 |
|
480 checkConsistency(); |
|
481 |
|
482 if (i > MAX_ARRAY_INDEX) |
|
483 return deleteProperty(exec, Identifier::from(exec, i)); |
|
484 |
|
485 return false; |
|
486 } |
|
487 |
|
488 void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
|
489 { |
|
490 // FIXME: Filling PropertyNameArray with an identifier for every integer |
|
491 // is incredibly inefficient for large arrays. We need a different approach, |
|
492 // which almost certainly means a different structure for PropertyNameArray. |
|
493 |
|
494 ArrayStorage* storage = m_storage; |
|
495 |
|
496 unsigned usedVectorLength = min(storage->m_length, m_vectorLength); |
|
497 for (unsigned i = 0; i < usedVectorLength; ++i) { |
|
498 if (storage->m_vector[i]) |
|
499 propertyNames.add(Identifier::from(exec, i)); |
|
500 } |
|
501 |
|
502 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { |
|
503 SparseArrayValueMap::iterator end = map->end(); |
|
504 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) |
|
505 propertyNames.add(Identifier::from(exec, it->first)); |
|
506 } |
|
507 |
|
508 if (mode == IncludeDontEnumProperties) |
|
509 propertyNames.add(exec->propertyNames().length); |
|
510 |
|
511 JSObject::getOwnPropertyNames(exec, propertyNames, mode); |
|
512 } |
|
513 |
|
514 bool JSArray::increaseVectorLength(unsigned newLength) |
|
515 { |
|
516 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map |
|
517 // to the vector. Callers have to account for that, because they can do it more efficiently. |
|
518 |
|
519 ArrayStorage* storage = m_storage; |
|
520 |
|
521 unsigned vectorLength = m_vectorLength; |
|
522 ASSERT(newLength > vectorLength); |
|
523 ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX); |
|
524 unsigned newVectorLength = increasedVectorLength(newLength); |
|
525 |
|
526 if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) |
|
527 return false; |
|
528 |
|
529 m_vectorLength = newVectorLength; |
|
530 |
|
531 for (unsigned i = vectorLength; i < newVectorLength; ++i) |
|
532 storage->m_vector[i] = JSValue(); |
|
533 |
|
534 m_storage = storage; |
|
535 |
|
536 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); |
|
537 |
|
538 return true; |
|
539 } |
|
540 |
|
541 void JSArray::setLength(unsigned newLength) |
|
542 { |
|
543 #if CHECK_ARRAY_CONSISTENCY |
|
544 if (!m_storage->m_inCompactInitialization) |
|
545 checkConsistency(); |
|
546 else |
|
547 m_storage->m_inCompactInitialization = false; |
|
548 #endif |
|
549 |
|
550 ArrayStorage* storage = m_storage; |
|
551 |
|
552 unsigned length = m_storage->m_length; |
|
553 |
|
554 if (newLength < length) { |
|
555 unsigned usedVectorLength = min(length, m_vectorLength); |
|
556 for (unsigned i = newLength; i < usedVectorLength; ++i) { |
|
557 JSValue& valueSlot = storage->m_vector[i]; |
|
558 bool hadValue = valueSlot; |
|
559 valueSlot = JSValue(); |
|
560 storage->m_numValuesInVector -= hadValue; |
|
561 } |
|
562 |
|
563 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { |
|
564 SparseArrayValueMap copy = *map; |
|
565 SparseArrayValueMap::iterator end = copy.end(); |
|
566 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) { |
|
567 if (it->first >= newLength) |
|
568 map->remove(it->first); |
|
569 } |
|
570 if (map->isEmpty()) { |
|
571 delete map; |
|
572 storage->m_sparseValueMap = 0; |
|
573 } |
|
574 } |
|
575 } |
|
576 |
|
577 m_storage->m_length = newLength; |
|
578 |
|
579 checkConsistency(); |
|
580 } |
|
581 |
|
582 JSValue JSArray::pop() |
|
583 { |
|
584 checkConsistency(); |
|
585 |
|
586 unsigned length = m_storage->m_length; |
|
587 if (!length) |
|
588 return jsUndefined(); |
|
589 |
|
590 --length; |
|
591 |
|
592 JSValue result; |
|
593 |
|
594 if (length < m_vectorLength) { |
|
595 JSValue& valueSlot = m_storage->m_vector[length]; |
|
596 if (valueSlot) { |
|
597 --m_storage->m_numValuesInVector; |
|
598 result = valueSlot; |
|
599 valueSlot = JSValue(); |
|
600 } else |
|
601 result = jsUndefined(); |
|
602 } else { |
|
603 result = jsUndefined(); |
|
604 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { |
|
605 SparseArrayValueMap::iterator it = map->find(length); |
|
606 if (it != map->end()) { |
|
607 result = it->second; |
|
608 map->remove(it); |
|
609 if (map->isEmpty()) { |
|
610 delete map; |
|
611 m_storage->m_sparseValueMap = 0; |
|
612 } |
|
613 } |
|
614 } |
|
615 } |
|
616 |
|
617 m_storage->m_length = length; |
|
618 |
|
619 checkConsistency(); |
|
620 |
|
621 return result; |
|
622 } |
|
623 |
|
624 void JSArray::push(ExecState* exec, JSValue value) |
|
625 { |
|
626 checkConsistency(); |
|
627 |
|
628 if (m_storage->m_length < m_vectorLength) { |
|
629 m_storage->m_vector[m_storage->m_length] = value; |
|
630 ++m_storage->m_numValuesInVector; |
|
631 ++m_storage->m_length; |
|
632 checkConsistency(); |
|
633 return; |
|
634 } |
|
635 |
|
636 if (m_storage->m_length < MIN_SPARSE_ARRAY_INDEX) { |
|
637 SparseArrayValueMap* map = m_storage->m_sparseValueMap; |
|
638 if (!map || map->isEmpty()) { |
|
639 if (increaseVectorLength(m_storage->m_length + 1)) { |
|
640 m_storage->m_vector[m_storage->m_length] = value; |
|
641 ++m_storage->m_numValuesInVector; |
|
642 ++m_storage->m_length; |
|
643 checkConsistency(); |
|
644 return; |
|
645 } |
|
646 checkConsistency(); |
|
647 throwOutOfMemoryError(exec); |
|
648 return; |
|
649 } |
|
650 } |
|
651 |
|
652 putSlowCase(exec, m_storage->m_length++, value); |
|
653 } |
|
654 |
|
655 void JSArray::markChildren(MarkStack& markStack) |
|
656 { |
|
657 markChildrenDirect(markStack); |
|
658 } |
|
659 |
|
660 static int compareNumbersForQSort(const void* a, const void* b) |
|
661 { |
|
662 double da = static_cast<const JSValue*>(a)->uncheckedGetNumber(); |
|
663 double db = static_cast<const JSValue*>(b)->uncheckedGetNumber(); |
|
664 return (da > db) - (da < db); |
|
665 } |
|
666 |
|
667 typedef std::pair<JSValue, UString> ValueStringPair; |
|
668 |
|
669 static int compareByStringPairForQSort(const void* a, const void* b) |
|
670 { |
|
671 const ValueStringPair* va = static_cast<const ValueStringPair*>(a); |
|
672 const ValueStringPair* vb = static_cast<const ValueStringPair*>(b); |
|
673 return codePointCompare(va->second, vb->second); |
|
674 } |
|
675 |
|
676 void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) |
|
677 { |
|
678 unsigned lengthNotIncludingUndefined = compactForSorting(); |
|
679 if (m_storage->m_sparseValueMap) { |
|
680 throwOutOfMemoryError(exec); |
|
681 return; |
|
682 } |
|
683 |
|
684 if (!lengthNotIncludingUndefined) |
|
685 return; |
|
686 |
|
687 bool allValuesAreNumbers = true; |
|
688 size_t size = m_storage->m_numValuesInVector; |
|
689 for (size_t i = 0; i < size; ++i) { |
|
690 if (!m_storage->m_vector[i].isNumber()) { |
|
691 allValuesAreNumbers = false; |
|
692 break; |
|
693 } |
|
694 } |
|
695 |
|
696 if (!allValuesAreNumbers) |
|
697 return sort(exec, compareFunction, callType, callData); |
|
698 |
|
699 // For numeric comparison, which is fast, qsort is faster than mergesort. We |
|
700 // also don't require mergesort's stability, since there's no user visible |
|
701 // side-effect from swapping the order of equal primitive values. |
|
702 qsort(m_storage->m_vector, size, sizeof(JSValue), compareNumbersForQSort); |
|
703 |
|
704 checkConsistency(SortConsistencyCheck); |
|
705 } |
|
706 |
|
707 void JSArray::sort(ExecState* exec) |
|
708 { |
|
709 unsigned lengthNotIncludingUndefined = compactForSorting(); |
|
710 if (m_storage->m_sparseValueMap) { |
|
711 throwOutOfMemoryError(exec); |
|
712 return; |
|
713 } |
|
714 |
|
715 if (!lengthNotIncludingUndefined) |
|
716 return; |
|
717 |
|
718 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. |
|
719 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary |
|
720 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return |
|
721 // random or otherwise changing results, effectively making compare function inconsistent. |
|
722 |
|
723 Vector<ValueStringPair> values(lengthNotIncludingUndefined); |
|
724 if (!values.begin()) { |
|
725 throwOutOfMemoryError(exec); |
|
726 return; |
|
727 } |
|
728 |
|
729 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { |
|
730 JSValue value = m_storage->m_vector[i]; |
|
731 ASSERT(!value.isUndefined()); |
|
732 values[i].first = value; |
|
733 } |
|
734 |
|
735 // FIXME: While calling these toString functions, the array could be mutated. |
|
736 // In that case, objects pointed to by values in this vector might get garbage-collected! |
|
737 |
|
738 // FIXME: The following loop continues to call toString on subsequent values even after |
|
739 // a toString call raises an exception. |
|
740 |
|
741 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) |
|
742 values[i].second = values[i].first.toString(exec); |
|
743 |
|
744 if (exec->hadException()) |
|
745 return; |
|
746 |
|
747 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather |
|
748 // than O(N log N). |
|
749 |
|
750 #if HAVE(MERGESORT) |
|
751 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); |
|
752 #else |
|
753 // FIXME: The qsort library function is likely to not be a stable sort. |
|
754 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. |
|
755 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); |
|
756 #endif |
|
757 |
|
758 // FIXME: If the toString function changed the length of the array, this might be |
|
759 // modifying the vector incorrectly. |
|
760 |
|
761 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) |
|
762 m_storage->m_vector[i] = values[i].first; |
|
763 |
|
764 checkConsistency(SortConsistencyCheck); |
|
765 } |
|
766 |
|
767 struct AVLTreeNodeForArrayCompare { |
|
768 JSValue value; |
|
769 |
|
770 // Child pointers. The high bit of gt is robbed and used as the |
|
771 // balance factor sign. The high bit of lt is robbed and used as |
|
772 // the magnitude of the balance factor. |
|
773 int32_t gt; |
|
774 int32_t lt; |
|
775 }; |
|
776 |
|
777 struct AVLTreeAbstractorForArrayCompare { |
|
778 typedef int32_t handle; // Handle is an index into m_nodes vector. |
|
779 typedef JSValue key; |
|
780 typedef int32_t size; |
|
781 |
|
782 Vector<AVLTreeNodeForArrayCompare> m_nodes; |
|
783 ExecState* m_exec; |
|
784 JSValue m_compareFunction; |
|
785 CallType m_compareCallType; |
|
786 const CallData* m_compareCallData; |
|
787 JSValue m_globalThisValue; |
|
788 OwnPtr<CachedCall> m_cachedCall; |
|
789 |
|
790 handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; } |
|
791 void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; } |
|
792 handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; } |
|
793 void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; } |
|
794 |
|
795 int get_balance_factor(handle h) |
|
796 { |
|
797 if (m_nodes[h].gt & 0x80000000) |
|
798 return -1; |
|
799 return static_cast<unsigned>(m_nodes[h].lt) >> 31; |
|
800 } |
|
801 |
|
802 void set_balance_factor(handle h, int bf) |
|
803 { |
|
804 if (bf == 0) { |
|
805 m_nodes[h].lt &= 0x7FFFFFFF; |
|
806 m_nodes[h].gt &= 0x7FFFFFFF; |
|
807 } else { |
|
808 m_nodes[h].lt |= 0x80000000; |
|
809 if (bf < 0) |
|
810 m_nodes[h].gt |= 0x80000000; |
|
811 else |
|
812 m_nodes[h].gt &= 0x7FFFFFFF; |
|
813 } |
|
814 } |
|
815 |
|
816 int compare_key_key(key va, key vb) |
|
817 { |
|
818 ASSERT(!va.isUndefined()); |
|
819 ASSERT(!vb.isUndefined()); |
|
820 |
|
821 if (m_exec->hadException()) |
|
822 return 1; |
|
823 |
|
824 double compareResult; |
|
825 if (m_cachedCall) { |
|
826 m_cachedCall->setThis(m_globalThisValue); |
|
827 m_cachedCall->setArgument(0, va); |
|
828 m_cachedCall->setArgument(1, vb); |
|
829 compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec)); |
|
830 } else { |
|
831 MarkedArgumentBuffer arguments; |
|
832 arguments.append(va); |
|
833 arguments.append(vb); |
|
834 compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments).toNumber(m_exec); |
|
835 } |
|
836 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent. |
|
837 } |
|
838 |
|
839 int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); } |
|
840 int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); } |
|
841 |
|
842 static handle null() { return 0x7FFFFFFF; } |
|
843 }; |
|
844 |
|
845 void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) |
|
846 { |
|
847 checkConsistency(); |
|
848 |
|
849 // FIXME: This ignores exceptions raised in the compare function or in toNumber. |
|
850 |
|
851 // The maximum tree depth is compiled in - but the caller is clearly up to no good |
|
852 // if a larger array is passed. |
|
853 ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); |
|
854 if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) |
|
855 return; |
|
856 |
|
857 if (!m_storage->m_length) |
|
858 return; |
|
859 |
|
860 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); |
|
861 |
|
862 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items |
|
863 tree.abstractor().m_exec = exec; |
|
864 tree.abstractor().m_compareFunction = compareFunction; |
|
865 tree.abstractor().m_compareCallType = callType; |
|
866 tree.abstractor().m_compareCallData = &callData; |
|
867 tree.abstractor().m_globalThisValue = exec->globalThisValue(); |
|
868 tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0)); |
|
869 |
|
870 if (callType == CallTypeJS) |
|
871 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, asFunction(compareFunction), 2, exec->exceptionSlot())); |
|
872 |
|
873 if (!tree.abstractor().m_nodes.begin()) { |
|
874 throwOutOfMemoryError(exec); |
|
875 return; |
|
876 } |
|
877 |
|
878 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified |
|
879 // right out from under us while we're building the tree here. |
|
880 |
|
881 unsigned numDefined = 0; |
|
882 unsigned numUndefined = 0; |
|
883 |
|
884 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. |
|
885 for (; numDefined < usedVectorLength; ++numDefined) { |
|
886 JSValue v = m_storage->m_vector[numDefined]; |
|
887 if (!v || v.isUndefined()) |
|
888 break; |
|
889 tree.abstractor().m_nodes[numDefined].value = v; |
|
890 tree.insert(numDefined); |
|
891 } |
|
892 for (unsigned i = numDefined; i < usedVectorLength; ++i) { |
|
893 JSValue v = m_storage->m_vector[i]; |
|
894 if (v) { |
|
895 if (v.isUndefined()) |
|
896 ++numUndefined; |
|
897 else { |
|
898 tree.abstractor().m_nodes[numDefined].value = v; |
|
899 tree.insert(numDefined); |
|
900 ++numDefined; |
|
901 } |
|
902 } |
|
903 } |
|
904 |
|
905 unsigned newUsedVectorLength = numDefined + numUndefined; |
|
906 |
|
907 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { |
|
908 newUsedVectorLength += map->size(); |
|
909 if (newUsedVectorLength > m_vectorLength) { |
|
910 // Check that it is possible to allocate an array large enough to hold all the entries. |
|
911 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) { |
|
912 throwOutOfMemoryError(exec); |
|
913 return; |
|
914 } |
|
915 } |
|
916 |
|
917 SparseArrayValueMap::iterator end = map->end(); |
|
918 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) { |
|
919 tree.abstractor().m_nodes[numDefined].value = it->second; |
|
920 tree.insert(numDefined); |
|
921 ++numDefined; |
|
922 } |
|
923 |
|
924 delete map; |
|
925 m_storage->m_sparseValueMap = 0; |
|
926 } |
|
927 |
|
928 ASSERT(tree.abstractor().m_nodes.size() >= numDefined); |
|
929 |
|
930 // FIXME: If the compare function changed the length of the array, the following might be |
|
931 // modifying the vector incorrectly. |
|
932 |
|
933 // Copy the values back into m_storage. |
|
934 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; |
|
935 iter.start_iter_least(tree); |
|
936 for (unsigned i = 0; i < numDefined; ++i) { |
|
937 m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value; |
|
938 ++iter; |
|
939 } |
|
940 |
|
941 // Put undefined values back in. |
|
942 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) |
|
943 m_storage->m_vector[i] = jsUndefined(); |
|
944 |
|
945 // Ensure that unused values in the vector are zeroed out. |
|
946 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) |
|
947 m_storage->m_vector[i] = JSValue(); |
|
948 |
|
949 m_storage->m_numValuesInVector = newUsedVectorLength; |
|
950 |
|
951 checkConsistency(SortConsistencyCheck); |
|
952 } |
|
953 |
|
954 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) |
|
955 { |
|
956 JSValue* vector = m_storage->m_vector; |
|
957 unsigned vectorEnd = min(m_storage->m_length, m_vectorLength); |
|
958 unsigned i = 0; |
|
959 for (; i < vectorEnd; ++i) { |
|
960 JSValue& v = vector[i]; |
|
961 if (!v) |
|
962 break; |
|
963 args.append(v); |
|
964 } |
|
965 |
|
966 for (; i < m_storage->m_length; ++i) |
|
967 args.append(get(exec, i)); |
|
968 } |
|
969 |
|
970 void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize) |
|
971 { |
|
972 ASSERT(m_storage->m_length >= maxSize); |
|
973 UNUSED_PARAM(maxSize); |
|
974 JSValue* vector = m_storage->m_vector; |
|
975 unsigned vectorEnd = min(maxSize, m_vectorLength); |
|
976 unsigned i = 0; |
|
977 for (; i < vectorEnd; ++i) { |
|
978 JSValue& v = vector[i]; |
|
979 if (!v) |
|
980 break; |
|
981 buffer[i] = v; |
|
982 } |
|
983 |
|
984 for (; i < maxSize; ++i) |
|
985 buffer[i] = get(exec, i); |
|
986 } |
|
987 |
|
988 unsigned JSArray::compactForSorting() |
|
989 { |
|
990 checkConsistency(); |
|
991 |
|
992 ArrayStorage* storage = m_storage; |
|
993 |
|
994 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); |
|
995 |
|
996 unsigned numDefined = 0; |
|
997 unsigned numUndefined = 0; |
|
998 |
|
999 for (; numDefined < usedVectorLength; ++numDefined) { |
|
1000 JSValue v = storage->m_vector[numDefined]; |
|
1001 if (!v || v.isUndefined()) |
|
1002 break; |
|
1003 } |
|
1004 for (unsigned i = numDefined; i < usedVectorLength; ++i) { |
|
1005 JSValue v = storage->m_vector[i]; |
|
1006 if (v) { |
|
1007 if (v.isUndefined()) |
|
1008 ++numUndefined; |
|
1009 else |
|
1010 storage->m_vector[numDefined++] = v; |
|
1011 } |
|
1012 } |
|
1013 |
|
1014 unsigned newUsedVectorLength = numDefined + numUndefined; |
|
1015 |
|
1016 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { |
|
1017 newUsedVectorLength += map->size(); |
|
1018 if (newUsedVectorLength > m_vectorLength) { |
|
1019 // Check that it is possible to allocate an array large enough to hold all the entries - if not, |
|
1020 // exception is thrown by caller. |
|
1021 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) |
|
1022 return 0; |
|
1023 storage = m_storage; |
|
1024 } |
|
1025 |
|
1026 SparseArrayValueMap::iterator end = map->end(); |
|
1027 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) |
|
1028 storage->m_vector[numDefined++] = it->second; |
|
1029 |
|
1030 delete map; |
|
1031 storage->m_sparseValueMap = 0; |
|
1032 } |
|
1033 |
|
1034 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) |
|
1035 storage->m_vector[i] = jsUndefined(); |
|
1036 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) |
|
1037 storage->m_vector[i] = JSValue(); |
|
1038 |
|
1039 storage->m_numValuesInVector = newUsedVectorLength; |
|
1040 |
|
1041 checkConsistency(SortConsistencyCheck); |
|
1042 |
|
1043 return numDefined; |
|
1044 } |
|
1045 |
|
1046 void* JSArray::subclassData() const |
|
1047 { |
|
1048 return m_storage->subclassData; |
|
1049 } |
|
1050 |
|
1051 void JSArray::setSubclassData(void* d) |
|
1052 { |
|
1053 m_storage->subclassData = d; |
|
1054 } |
|
1055 |
|
1056 #if CHECK_ARRAY_CONSISTENCY |
|
1057 |
|
1058 void JSArray::checkConsistency(ConsistencyCheckType type) |
|
1059 { |
|
1060 ASSERT(m_storage); |
|
1061 if (type == SortConsistencyCheck) |
|
1062 ASSERT(!m_storage->m_sparseValueMap); |
|
1063 |
|
1064 unsigned numValuesInVector = 0; |
|
1065 for (unsigned i = 0; i < m_vectorLength; ++i) { |
|
1066 if (JSValue value = m_storage->m_vector[i]) { |
|
1067 ASSERT(i < m_storage->m_length); |
|
1068 if (type != DestructorConsistencyCheck) |
|
1069 value.isUndefined(); // Likely to crash if the object was deallocated. |
|
1070 ++numValuesInVector; |
|
1071 } else { |
|
1072 if (type == SortConsistencyCheck) |
|
1073 ASSERT(i >= m_storage->m_numValuesInVector); |
|
1074 } |
|
1075 } |
|
1076 ASSERT(numValuesInVector == m_storage->m_numValuesInVector); |
|
1077 ASSERT(numValuesInVector <= m_storage->m_length); |
|
1078 |
|
1079 if (m_storage->m_sparseValueMap) { |
|
1080 SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end(); |
|
1081 for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) { |
|
1082 unsigned index = it->first; |
|
1083 ASSERT(index < m_storage->m_length); |
|
1084 ASSERT(index >= m_vectorLength); |
|
1085 ASSERT(index <= MAX_ARRAY_INDEX); |
|
1086 ASSERT(it->second); |
|
1087 if (type != DestructorConsistencyCheck) |
|
1088 it->second.isUndefined(); // Likely to crash if the object was deallocated. |
|
1089 } |
|
1090 } |
|
1091 } |
|
1092 |
|
1093 #endif |
|
1094 |
|
1095 } // namespace JSC |