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)
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.
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.
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
26 #include "ArrayPrototype.h"
27 #include "CopiedSpace.h"
28 #include "CopiedSpaceInlineMethods.h"
29 #include "CachedCall.h"
31 #include "Executable.h"
32 #include "GetterSetter.h"
33 #include "PropertyNameArray.h"
34 #include <wtf/AVLTree.h>
35 #include <wtf/Assertions.h>
36 #include <wtf/OwnPtr.h>
37 #include <Operations.h>
45 ASSERT_CLASS_FITS_IN_CELL(JSArray
);
46 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray
);
48 // Overview of JSArray
50 // Properties of JSArray objects may be stored in one of three locations:
51 // * The regular JSObject property map.
52 // * A storage vector.
53 // * A sparse map of array entries.
55 // Properties with non-numeric identifiers, with identifiers that are not representable
56 // as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX
57 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit
58 // integer) are not considered array indices and will be stored in the JSObject property map.
60 // All properties with a numeric identifer, representable as an unsigned integer i,
61 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the
62 // storage vector or the sparse map. An array index i will be handled in the following
65 // * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector,
66 // unless the array is in SparseMode in which case all properties go into the map.
67 // * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either
68 // be stored in the storage vector or in the sparse array, depending on the density of
69 // data that would be stored in the vector (a vector being used where at least
70 // (1 / minDensityMultiplier) of the entries would be populated).
71 // * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored
72 // in the sparse array.
74 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
75 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
76 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
77 // (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
78 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
80 // These values have to be macros to be used in max() and min() without introducing
81 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
82 #define MIN_SPARSE_ARRAY_INDEX 10000U
83 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
84 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
85 #define MAX_ARRAY_INDEX 0xFFFFFFFEU
87 // The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate
88 // for an array that was created with a sepcified length (e.g. a = new Array(123))
89 #define BASE_VECTOR_LEN 4U
91 // The upper bound to the size we'll grow a zero length array when the first element
93 #define FIRST_VECTOR_GROW 4U
95 // Our policy for when to use a vector and when to use a sparse map.
96 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
97 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
98 // as long as it is 1/8 full. If more sparse than that, we use a map.
99 static const unsigned minDensityMultiplier
= 8;
101 const ClassInfo
JSArray::s_info
= {"Array", &JSNonFinalObject::s_info
, 0, 0, CREATE_METHOD_TABLE(JSArray
)};
103 // We keep track of the size of the last array after it was grown. We use this
104 // as a simple heuristic for as the value to grow the next array from size 0.
105 // This value is capped by the constant FIRST_VECTOR_GROW defined above.
106 static unsigned lastArraySize
= 0;
108 static inline bool isDenseEnoughForVector(unsigned length
, unsigned numValues
)
110 return length
<= MIN_SPARSE_ARRAY_INDEX
|| length
/ minDensityMultiplier
<= numValues
;
113 static bool reject(ExecState
* exec
, bool throwException
, const char* message
)
116 throwTypeError(exec
, message
);
120 #if !CHECK_ARRAY_CONSISTENCY
122 inline void JSArray::checkConsistency(ConsistencyCheckType
)
128 void JSArray::finishCreation(JSGlobalData
& globalData
, unsigned initialLength
)
130 Base::finishCreation(globalData
);
131 ASSERT(inherits(&s_info
));
133 unsigned initialVectorLength
= BASE_VECTOR_LEN
;
134 unsigned initialStorageSize
= storageSize(initialVectorLength
);
136 void* newStorage
= 0;
137 if (!globalData
.heap
.tryAllocateStorage(initialStorageSize
, &newStorage
))
140 m_storage
= static_cast<ArrayStorage
*>(newStorage
);
141 m_storage
->m_allocBase
= m_storage
;
142 m_storage
->m_length
= initialLength
;
143 m_vectorLength
= initialVectorLength
;
144 m_storage
->m_numValuesInVector
= 0;
145 #if CHECK_ARRAY_CONSISTENCY
146 m_storage
->m_inCompactInitialization
= false;
152 JSArray
* JSArray::tryFinishCreationUninitialized(JSGlobalData
& globalData
, unsigned initialLength
)
154 Base::finishCreation(globalData
);
155 ASSERT(inherits(&s_info
));
157 // Check for lengths larger than we can handle with a vector.
158 if (initialLength
> MAX_STORAGE_VECTOR_LENGTH
)
161 unsigned initialVectorLength
= max(initialLength
, BASE_VECTOR_LEN
);
162 unsigned initialStorageSize
= storageSize(initialVectorLength
);
164 void* newStorage
= 0;
165 if (!globalData
.heap
.tryAllocateStorage(initialStorageSize
, &newStorage
))
168 m_storage
= static_cast<ArrayStorage
*>(newStorage
);
169 m_storage
->m_allocBase
= m_storage
;
170 m_storage
->m_length
= initialLength
;
171 m_vectorLength
= initialVectorLength
;
172 m_storage
->m_numValuesInVector
= initialLength
;
174 #if CHECK_ARRAY_CONSISTENCY
175 m_storage
->m_initializationIndex
= 0;
176 m_storage
->m_inCompactInitialization
= true;
182 // This function can be called multiple times on the same object.
183 void JSArray::finalize(JSCell
* cell
)
185 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
186 thisObject
->checkConsistency(DestructorConsistencyCheck
);
187 thisObject
->deallocateSparseMap();
190 inline SparseArrayValueMap::AddResult
SparseArrayValueMap::add(JSArray
* array
, unsigned i
)
192 SparseArrayEntry entry
;
193 entry
.setWithoutWriteBarrier(jsUndefined());
195 AddResult result
= m_map
.add(i
, entry
);
196 size_t capacity
= m_map
.capacity();
197 if (capacity
!= m_reportedCapacity
) {
198 Heap::heap(array
)->reportExtraMemoryCost((capacity
- m_reportedCapacity
) * (sizeof(unsigned) + sizeof(WriteBarrier
<Unknown
>)));
199 m_reportedCapacity
= capacity
;
204 inline void SparseArrayValueMap::put(ExecState
* exec
, JSArray
* array
, unsigned i
, JSValue value
, bool shouldThrow
)
206 AddResult result
= add(array
, i
);
207 SparseArrayEntry
& entry
= result
.iterator
->second
;
209 // To save a separate find & add, we first always add to the sparse map.
210 // In the uncommon case that this is a new property, and the array is not
211 // extensible, this is not the right thing to have done - so remove again.
212 if (result
.isNewEntry
&& !array
->isExtensible()) {
213 remove(result
.iterator
);
215 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
219 if (!(entry
.attributes
& Accessor
)) {
220 if (entry
.attributes
& ReadOnly
) {
222 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
226 entry
.set(exec
->globalData(), array
, value
);
230 JSValue accessor
= entry
.Base::get();
231 ASSERT(accessor
.isGetterSetter());
232 JSObject
* setter
= asGetterSetter(accessor
)->setter();
236 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
241 CallType callType
= setter
->methodTable()->getCallData(setter
, callData
);
242 MarkedArgumentBuffer args
;
244 call(exec
, setter
, callType
, callData
, array
, args
);
247 inline bool SparseArrayValueMap::putDirect(ExecState
* exec
, JSArray
* array
, unsigned i
, JSValue value
, bool shouldThrow
)
249 AddResult result
= add(array
, i
);
250 SparseArrayEntry
& entry
= result
.iterator
->second
;
252 // To save a separate find & add, we first always add to the sparse map.
253 // In the uncommon case that this is a new property, and the array is not
254 // extensible, this is not the right thing to have done - so remove again.
255 if (result
.isNewEntry
&& !array
->isExtensible()) {
256 remove(result
.iterator
);
257 return reject(exec
, shouldThrow
, "Attempting to define property on object that is not extensible.");
260 entry
.attributes
= 0;
261 entry
.set(exec
->globalData(), array
, value
);
265 inline void SparseArrayEntry::get(PropertySlot
& slot
) const
267 JSValue value
= Base::get();
270 if (LIKELY(!value
.isGetterSetter())) {
271 slot
.setValue(value
);
275 JSObject
* getter
= asGetterSetter(value
)->getter();
281 slot
.setGetterSlot(getter
);
284 inline void SparseArrayEntry::get(PropertyDescriptor
& descriptor
) const
286 descriptor
.setDescriptor(Base::get(), attributes
);
289 inline JSValue
SparseArrayEntry::get(ExecState
* exec
, JSArray
* array
) const
291 JSValue result
= Base::get();
294 if (LIKELY(!result
.isGetterSetter()))
297 JSObject
* getter
= asGetterSetter(result
)->getter();
299 return jsUndefined();
302 CallType callType
= getter
->methodTable()->getCallData(getter
, callData
);
303 return call(exec
, getter
, callType
, callData
, array
, exec
->emptyList());
306 inline JSValue
SparseArrayEntry::getNonSparseMode() const
312 inline void SparseArrayValueMap::visitChildren(SlotVisitor
& visitor
)
314 iterator end
= m_map
.end();
315 for (iterator it
= m_map
.begin(); it
!= end
; ++it
)
316 visitor
.append(&it
->second
);
319 void JSArray::allocateSparseMap(JSGlobalData
& globalData
)
321 m_sparseValueMap
= new SparseArrayValueMap
;
322 globalData
.heap
.addFinalizer(this, finalize
);
325 void JSArray::deallocateSparseMap()
327 delete m_sparseValueMap
;
328 m_sparseValueMap
= 0;
331 void JSArray::enterDictionaryMode(JSGlobalData
& globalData
)
333 ArrayStorage
* storage
= m_storage
;
334 SparseArrayValueMap
* map
= m_sparseValueMap
;
337 allocateSparseMap(globalData
);
338 map
= m_sparseValueMap
;
341 if (map
->sparseMode())
344 map
->setSparseMode();
346 unsigned usedVectorLength
= min(storage
->m_length
, m_vectorLength
);
347 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
348 JSValue value
= storage
->m_vector
[i
].get();
349 // This will always be a new entry in the map, so no need to check we can write,
350 // and attributes are default so no need to set them.
352 map
->add(this, i
).iterator
->second
.set(globalData
, this, value
);
355 void* newRawStorage
= 0;
356 if (!globalData
.heap
.tryAllocateStorage(storageSize(0), &newRawStorage
))
359 ArrayStorage
* newStorage
= static_cast<ArrayStorage
*>(newRawStorage
);
360 memcpy(newStorage
, m_storage
, storageSize(0));
361 newStorage
->m_allocBase
= newStorage
;
362 m_storage
= newStorage
;
367 void JSArray::putDescriptor(ExecState
* exec
, SparseArrayEntry
* entryInMap
, PropertyDescriptor
& descriptor
, PropertyDescriptor
& oldDescriptor
)
369 if (descriptor
.isDataDescriptor()) {
370 if (descriptor
.value())
371 entryInMap
->set(exec
->globalData(), this, descriptor
.value());
372 else if (oldDescriptor
.isAccessorDescriptor())
373 entryInMap
->set(exec
->globalData(), this, jsUndefined());
374 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~Accessor
;
378 if (descriptor
.isAccessorDescriptor()) {
379 JSObject
* getter
= 0;
380 if (descriptor
.getterPresent())
381 getter
= descriptor
.getterObject();
382 else if (oldDescriptor
.isAccessorDescriptor())
383 getter
= oldDescriptor
.getterObject();
384 JSObject
* setter
= 0;
385 if (descriptor
.setterPresent())
386 setter
= descriptor
.setterObject();
387 else if (oldDescriptor
.isAccessorDescriptor())
388 setter
= oldDescriptor
.setterObject();
390 GetterSetter
* accessor
= GetterSetter::create(exec
);
392 accessor
->setGetter(exec
->globalData(), getter
);
394 accessor
->setSetter(exec
->globalData(), setter
);
396 entryInMap
->set(exec
->globalData(), this, accessor
);
397 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
) & ~ReadOnly
;
401 ASSERT(descriptor
.isGenericDescriptor());
402 entryInMap
->attributes
= descriptor
.attributesOverridingCurrent(oldDescriptor
);
405 // Defined in ES5.1 8.12.9
406 bool JSArray::defineOwnNumericProperty(ExecState
* exec
, unsigned index
, PropertyDescriptor
& descriptor
, bool throwException
)
408 ASSERT(index
!= 0xFFFFFFFF);
410 if (!inSparseMode()) {
411 // Fast case: we're putting a regular property to a regular array
412 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
413 // – however if the property currently exists missing attributes will override from their current 'true'
414 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
415 if (!descriptor
.attributes()) {
416 ASSERT(!descriptor
.isAccessorDescriptor());
417 return putDirectIndex(exec
, index
, descriptor
.value(), throwException
);
420 enterDictionaryMode(exec
->globalData());
423 SparseArrayValueMap
* map
= m_sparseValueMap
;
426 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
427 SparseArrayValueMap::AddResult result
= map
->add(this, index
);
428 SparseArrayEntry
* entryInMap
= &result
.iterator
->second
;
430 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
431 // 3. If current is undefined and extensible is false, then Reject.
432 // 4. If current is undefined and extensible is true, then
433 if (result
.isNewEntry
) {
434 if (!isExtensible()) {
435 map
->remove(result
.iterator
);
436 return reject(exec
, throwException
, "Attempting to define property on object that is not extensible.");
439 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
440 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
441 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
442 // created property is set to its default value.
443 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
444 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
445 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
446 // is set to its default value.
449 PropertyDescriptor defaults
;
450 entryInMap
->setWithoutWriteBarrier(jsUndefined());
451 entryInMap
->attributes
= DontDelete
| DontEnum
| ReadOnly
;
452 entryInMap
->get(defaults
);
454 putDescriptor(exec
, entryInMap
, descriptor
, defaults
);
455 if (index
>= m_storage
->m_length
)
456 m_storage
->m_length
= index
+ 1;
460 // 5. Return true, if every field in Desc is absent.
461 // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12).
462 PropertyDescriptor current
;
463 entryInMap
->get(current
);
464 if (descriptor
.isEmpty() || descriptor
.equalTo(exec
, current
))
467 // 7. If the [[Configurable]] field of current is false then
468 if (!current
.configurable()) {
469 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
470 if (descriptor
.configurablePresent() && descriptor
.configurable())
471 return reject(exec
, throwException
, "Attempting to change configurable attribute of unconfigurable property.");
472 // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
473 if (descriptor
.enumerablePresent() && current
.enumerable() != descriptor
.enumerable())
474 return reject(exec
, throwException
, "Attempting to change enumerable attribute of unconfigurable property.");
477 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
478 if (!descriptor
.isGenericDescriptor()) {
479 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
480 if (current
.isDataDescriptor() != descriptor
.isDataDescriptor()) {
481 // 9.a. Reject, if the [[Configurable]] field of current is false.
482 if (!current
.configurable())
483 return reject(exec
, throwException
, "Attempting to change access mechanism for an unconfigurable property.");
484 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
485 // data property to an accessor property. Preserve the existing values of the converted property‘s
486 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property‘s attributes to
487 // their default values.
488 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
489 // Preserve the existing values of the converted property‘s [[Configurable]] and [[Enumerable]]
490 // attributes and set the rest of the property‘s attributes to their default values.
491 } else if (current
.isDataDescriptor() && descriptor
.isDataDescriptor()) {
492 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
493 // 10.a. If the [[Configurable]] field of current is false, then
494 if (!current
.configurable() && !current
.writable()) {
495 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
496 if (descriptor
.writable())
497 return reject(exec
, throwException
, "Attempting to change writable attribute of unconfigurable property.");
498 // 10.a.ii. If the [[Writable]] field of current is false, then
499 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
500 if (descriptor
.value() && !sameValue(exec
, descriptor
.value(), current
.value()))
501 return reject(exec
, throwException
, "Attempting to change value of a readonly property.");
503 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
505 ASSERT(current
.isAccessorDescriptor() && current
.getterPresent() && current
.setterPresent());
506 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
507 if (!current
.configurable()) {
508 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
509 if (descriptor
.setterPresent() && descriptor
.setter() != current
.setter())
510 return reject(exec
, throwException
, "Attempting to change the setter of an unconfigurable property.");
511 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
512 if (descriptor
.getterPresent() && descriptor
.getter() != current
.getter())
513 return reject(exec
, throwException
, "Attempting to change the getter of an unconfigurable property.");
518 // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field.
519 putDescriptor(exec
, entryInMap
, descriptor
, current
);
524 void JSArray::setLengthWritable(ExecState
* exec
, bool writable
)
526 ASSERT(isLengthWritable() || !writable
);
527 if (!isLengthWritable() || writable
)
530 enterDictionaryMode(exec
->globalData());
532 SparseArrayValueMap
* map
= m_sparseValueMap
;
534 map
->setLengthIsReadOnly();
537 // Defined in ES5.1 15.4.5.1
538 bool JSArray::defineOwnProperty(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
540 JSArray
* array
= jsCast
<JSArray
*>(object
);
542 // 3. If P is "length", then
543 if (propertyName
== exec
->propertyNames().length
) {
544 // All paths through length definition call the default [[DefineOwnProperty]], hence:
545 // from ES5.1 8.12.9 7.a.
546 if (descriptor
.configurablePresent() && descriptor
.configurable())
547 return reject(exec
, throwException
, "Attempting to change configurable attribute of unconfigurable property.");
548 // from ES5.1 8.12.9 7.b.
549 if (descriptor
.enumerablePresent() && descriptor
.enumerable())
550 return reject(exec
, throwException
, "Attempting to change enumerable attribute of unconfigurable property.");
552 // a. If the [[Value]] field of Desc is absent, then
553 // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
554 if (descriptor
.isAccessorDescriptor())
555 return reject(exec
, throwException
, "Attempting to change access mechanism for an unconfigurable property.");
556 // from ES5.1 8.12.9 10.a.
557 if (!array
->isLengthWritable() && descriptor
.writablePresent() && descriptor
.writable())
558 return reject(exec
, throwException
, "Attempting to change writable attribute of unconfigurable property.");
559 // This descriptor is either just making length read-only, or changing nothing!
560 if (!descriptor
.value()) {
561 if (descriptor
.writablePresent())
562 array
->setLengthWritable(exec
, descriptor
.writable());
566 // b. Let newLenDesc be a copy of Desc.
567 // c. Let newLen be ToUint32(Desc.[[Value]]).
568 unsigned newLen
= descriptor
.value().toUInt32(exec
);
569 // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
570 if (newLen
!= descriptor
.value().toNumber(exec
)) {
571 throwError(exec
, createRangeError(exec
, "Invalid array length"));
575 // Based on SameValue check in 8.12.9, this is always okay.
576 if (newLen
== array
->length()) {
577 if (descriptor
.writablePresent())
578 array
->setLengthWritable(exec
, descriptor
.writable());
582 // e. Set newLenDesc.[[Value] to newLen.
583 // f. If newLen >= oldLen, then
584 // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
585 // g. Reject if oldLenDesc.[[Writable]] is false.
586 if (!array
->isLengthWritable())
587 return reject(exec
, throwException
, "Attempting to change value of a readonly property.");
589 // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
591 // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
592 // i.ii. Let newWritable be false.
593 // i.iii. Set newLenDesc.[[Writable] to true.
594 // j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
595 // k. If succeeded is false, return false.
596 // l. While newLen < oldLen repeat,
597 // l.i. Set oldLen to oldLen – 1.
598 // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
599 // l.iii. If deleteSucceeded is false, then
600 if (!array
->setLength(exec
, newLen
, throwException
)) {
601 // 1. Set newLenDesc.[[Value] to oldLen+1.
602 // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
603 // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
605 if (descriptor
.writablePresent())
606 array
->setLengthWritable(exec
, descriptor
.writable());
610 // m. If newWritable is false, then
611 // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
612 // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
614 if (descriptor
.writablePresent())
615 array
->setLengthWritable(exec
, descriptor
.writable());
620 // 4. Else if P is an array index (15.4), then
622 // a. Let index be ToUint32(P).
623 unsigned index
= propertyName
.toArrayIndex(isArrayIndex
);
625 // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
626 if (index
>= array
->length() && !array
->isLengthWritable())
627 return reject(exec
, throwException
, "Attempting to define numeric property on array with non-writable length property.");
628 // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
629 // d. Reject if succeeded is false.
630 // e. If index >= oldLen
631 // e.i. Set oldLenDesc.[[Value]] to index + 1.
632 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
634 return array
->defineOwnNumericProperty(exec
, index
, descriptor
, throwException
);
637 return JSObject::defineOwnProperty(object
, exec
, propertyName
, descriptor
, throwException
);
640 bool JSArray::getOwnPropertySlotByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
642 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
643 ArrayStorage
* storage
= thisObject
->m_storage
;
645 if (i
>= storage
->m_length
) {
646 if (i
> MAX_ARRAY_INDEX
)
647 return thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, i
), slot
);
651 if (i
< thisObject
->m_vectorLength
) {
652 JSValue value
= storage
->m_vector
[i
].get();
654 slot
.setValue(value
);
657 } else if (SparseArrayValueMap
* map
= thisObject
->m_sparseValueMap
) {
658 SparseArrayValueMap::iterator it
= map
->find(i
);
659 if (it
!= map
->notFound()) {
660 it
->second
.get(slot
);
665 return JSObject::getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, i
), slot
);
668 bool JSArray::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
670 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
671 if (propertyName
== exec
->propertyNames().length
) {
672 slot
.setValue(jsNumber(thisObject
->length()));
677 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
679 return JSArray::getOwnPropertySlotByIndex(thisObject
, exec
, i
, slot
);
681 return JSObject::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
684 bool JSArray::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
686 JSArray
* thisObject
= jsCast
<JSArray
*>(object
);
687 if (propertyName
== exec
->propertyNames().length
) {
688 descriptor
.setDescriptor(jsNumber(thisObject
->length()), thisObject
->isLengthWritable() ? DontDelete
| DontEnum
: DontDelete
| DontEnum
| ReadOnly
);
692 ArrayStorage
* storage
= thisObject
->m_storage
;
695 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
697 if (i
>= storage
->m_length
)
699 if (i
< thisObject
->m_vectorLength
) {
700 WriteBarrier
<Unknown
>& value
= storage
->m_vector
[i
];
702 descriptor
.setDescriptor(value
.get(), 0);
705 } else if (SparseArrayValueMap
* map
= thisObject
->m_sparseValueMap
) {
706 SparseArrayValueMap::iterator it
= map
->find(i
);
707 if (it
!= map
->notFound()) {
708 it
->second
.get(descriptor
);
713 return JSObject::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
717 void JSArray::put(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
719 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
721 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
723 putByIndex(thisObject
, exec
, i
, value
, slot
.isStrictMode());
727 if (propertyName
== exec
->propertyNames().length
) {
728 unsigned newLength
= value
.toUInt32(exec
);
729 if (value
.toNumber(exec
) != static_cast<double>(newLength
)) {
730 throwError(exec
, createRangeError(exec
, "Invalid array length"));
733 thisObject
->setLength(exec
, newLength
, slot
.isStrictMode());
737 JSObject::put(thisObject
, exec
, propertyName
, value
, slot
);
740 void JSArray::putByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
742 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
743 thisObject
->checkConsistency();
745 ArrayStorage
* storage
= thisObject
->m_storage
;
747 // Fast case - store to the vector.
748 if (i
< thisObject
->m_vectorLength
) {
749 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
750 unsigned length
= storage
->m_length
;
752 // Update m_length & m_numValuesInVector as necessary.
755 storage
->m_length
= length
;
756 ++storage
->m_numValuesInVector
;
757 } else if (!valueSlot
)
758 ++storage
->m_numValuesInVector
;
760 valueSlot
.set(exec
->globalData(), thisObject
, value
);
761 thisObject
->checkConsistency();
765 // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property.
766 if (UNLIKELY(i
> MAX_ARRAY_INDEX
)) {
767 PutPropertySlot
slot(shouldThrow
);
768 thisObject
->methodTable()->put(thisObject
, exec
, Identifier::from(exec
, i
), value
, slot
);
772 // For all other cases, call putByIndexBeyondVectorLength.
773 thisObject
->putByIndexBeyondVectorLength(exec
, i
, value
, shouldThrow
);
774 thisObject
->checkConsistency();
777 void JSArray::putByIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
779 JSGlobalData
& globalData
= exec
->globalData();
781 // i should be a valid array index that is outside of the current vector.
782 ASSERT(i
>= m_vectorLength
);
783 ASSERT(i
<= MAX_ARRAY_INDEX
);
785 ArrayStorage
* storage
= m_storage
;
786 SparseArrayValueMap
* map
= m_sparseValueMap
;
788 // First, handle cases where we don't currently have a sparse map.
790 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
791 ASSERT(isExtensible());
793 // Update m_length if necessary.
794 if (i
>= storage
->m_length
)
795 storage
->m_length
= i
+ 1;
797 // Check that it is sensible to still be using a vector, and then try to grow the vector.
798 if (LIKELY((isDenseEnoughForVector(i
, storage
->m_numValuesInVector
)) && increaseVectorLength(globalData
, i
+ 1))) {
799 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
801 storage
->m_vector
[i
].set(globalData
, this, value
);
802 ++storage
->m_numValuesInVector
;
805 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
806 allocateSparseMap(exec
->globalData());
807 map
= m_sparseValueMap
;
808 map
->put(exec
, this, i
, value
, shouldThrow
);
812 // Update m_length if necessary.
813 unsigned length
= storage
->m_length
;
815 // Prohibit growing the array if length is not writable.
816 if (map
->lengthIsReadOnly() || !isExtensible()) {
818 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
822 storage
->m_length
= length
;
825 // We are currently using a map - check whether we still want to be doing so.
826 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
827 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
828 if (map
->sparseMode() || !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->globalData(), length
)) {
829 map
->put(exec
, this, i
, value
, shouldThrow
);
833 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
835 storage
->m_numValuesInVector
= numValuesInArray
;
837 // Copy all values from the map into the vector, and delete the map.
838 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
839 SparseArrayValueMap::const_iterator end
= map
->end();
840 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
841 vector
[it
->first
].set(globalData
, this, it
->second
.getNonSparseMode());
842 deallocateSparseMap();
844 // Store the new property into the vector.
845 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
847 ++storage
->m_numValuesInVector
;
848 valueSlot
.set(globalData
, this, value
);
851 bool JSArray::putDirectIndexBeyondVectorLength(ExecState
* exec
, unsigned i
, JSValue value
, bool shouldThrow
)
853 JSGlobalData
& globalData
= exec
->globalData();
855 // i should be a valid array index that is outside of the current vector.
856 ASSERT(i
>= m_vectorLength
);
857 ASSERT(i
<= MAX_ARRAY_INDEX
);
859 ArrayStorage
* storage
= m_storage
;
860 SparseArrayValueMap
* map
= m_sparseValueMap
;
862 // First, handle cases where we don't currently have a sparse map.
864 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
865 ASSERT(isExtensible());
867 // Update m_length if necessary.
868 if (i
>= storage
->m_length
)
869 storage
->m_length
= i
+ 1;
871 // Check that it is sensible to still be using a vector, and then try to grow the vector.
872 if (LIKELY((isDenseEnoughForVector(i
, storage
->m_numValuesInVector
)) && increaseVectorLength(globalData
, i
+ 1))) {
873 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
875 storage
->m_vector
[i
].set(globalData
, this, value
);
876 ++storage
->m_numValuesInVector
;
879 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
880 allocateSparseMap(exec
->globalData());
881 map
= m_sparseValueMap
;
882 return map
->putDirect(exec
, this, i
, value
, shouldThrow
);
885 // Update m_length if necessary.
886 unsigned length
= storage
->m_length
;
888 // Prohibit growing the array if length is not writable.
889 if (map
->lengthIsReadOnly())
890 return reject(exec
, shouldThrow
, StrictModeReadonlyPropertyWriteError
);
892 return reject(exec
, shouldThrow
, "Attempting to define property on object that is not extensible.");
894 storage
->m_length
= length
;
897 // We are currently using a map - check whether we still want to be doing so.
898 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
899 unsigned numValuesInArray
= storage
->m_numValuesInVector
+ map
->size();
900 if (map
->sparseMode() || !isDenseEnoughForVector(length
, numValuesInArray
) || !increaseVectorLength(exec
->globalData(), length
))
901 return map
->putDirect(exec
, this, i
, value
, shouldThrow
);
903 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
905 storage
->m_numValuesInVector
= numValuesInArray
;
907 // Copy all values from the map into the vector, and delete the map.
908 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
909 SparseArrayValueMap::const_iterator end
= map
->end();
910 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
)
911 vector
[it
->first
].set(globalData
, this, it
->second
.getNonSparseMode());
912 deallocateSparseMap();
914 // Store the new property into the vector.
915 WriteBarrier
<Unknown
>& valueSlot
= vector
[i
];
917 ++storage
->m_numValuesInVector
;
918 valueSlot
.set(globalData
, this, value
);
922 bool JSArray::deleteProperty(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
)
924 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
926 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
928 return thisObject
->methodTable()->deletePropertyByIndex(thisObject
, exec
, i
);
930 if (propertyName
== exec
->propertyNames().length
)
933 return JSObject::deleteProperty(thisObject
, exec
, propertyName
);
936 bool JSArray::deletePropertyByIndex(JSCell
* cell
, ExecState
* exec
, unsigned i
)
938 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
939 thisObject
->checkConsistency();
941 if (i
> MAX_ARRAY_INDEX
)
942 return thisObject
->methodTable()->deleteProperty(thisObject
, exec
, Identifier::from(exec
, i
));
944 ArrayStorage
* storage
= thisObject
->m_storage
;
946 if (i
< thisObject
->m_vectorLength
) {
947 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
950 --storage
->m_numValuesInVector
;
952 } else if (SparseArrayValueMap
* map
= thisObject
->m_sparseValueMap
) {
953 SparseArrayValueMap::iterator it
= map
->find(i
);
954 if (it
!= map
->notFound()) {
955 if (it
->second
.attributes
& DontDelete
)
961 thisObject
->checkConsistency();
965 static int compareKeysForQSort(const void* a
, const void* b
)
967 unsigned da
= *static_cast<const unsigned*>(a
);
968 unsigned db
= *static_cast<const unsigned*>(b
);
969 return (da
> db
) - (da
< db
);
972 void JSArray::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
974 JSArray
* thisObject
= jsCast
<JSArray
*>(object
);
975 // FIXME: Filling PropertyNameArray with an identifier for every integer
976 // is incredibly inefficient for large arrays. We need a different approach,
977 // which almost certainly means a different structure for PropertyNameArray.
979 ArrayStorage
* storage
= thisObject
->m_storage
;
981 unsigned usedVectorLength
= min(storage
->m_length
, thisObject
->m_vectorLength
);
982 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
983 if (storage
->m_vector
[i
])
984 propertyNames
.add(Identifier::from(exec
, i
));
987 if (SparseArrayValueMap
* map
= thisObject
->m_sparseValueMap
) {
988 Vector
<unsigned> keys
;
989 keys
.reserveCapacity(map
->size());
991 SparseArrayValueMap::const_iterator end
= map
->end();
992 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
) {
993 if (mode
== IncludeDontEnumProperties
|| !(it
->second
.attributes
& DontEnum
))
994 keys
.append(static_cast<unsigned>(it
->first
));
997 qsort(keys
.begin(), keys
.size(), sizeof(unsigned), compareKeysForQSort
);
998 for (unsigned i
= 0; i
< keys
.size(); ++i
)
999 propertyNames
.add(Identifier::from(exec
, keys
[i
]));
1002 if (mode
== IncludeDontEnumProperties
)
1003 propertyNames
.add(exec
->propertyNames().length
);
1005 JSObject::getOwnPropertyNames(thisObject
, exec
, propertyNames
, mode
);
1008 ALWAYS_INLINE
unsigned JSArray::getNewVectorLength(unsigned desiredLength
)
1010 ASSERT(desiredLength
<= MAX_STORAGE_VECTOR_LENGTH
);
1012 unsigned increasedLength
;
1013 unsigned maxInitLength
= min(m_storage
->m_length
, 100000U);
1015 if (desiredLength
< maxInitLength
)
1016 increasedLength
= maxInitLength
;
1017 else if (!m_vectorLength
)
1018 increasedLength
= max(desiredLength
, lastArraySize
);
1020 // Mathematically equivalent to:
1021 // increasedLength = (newLength * 3 + 1) / 2;
1023 // increasedLength = (unsigned)ceil(newLength * 1.5));
1024 // This form is not prone to internal overflow.
1025 increasedLength
= desiredLength
+ (desiredLength
>> 1) + (desiredLength
& 1);
1028 ASSERT(increasedLength
>= desiredLength
);
1030 lastArraySize
= min(increasedLength
, FIRST_VECTOR_GROW
);
1032 return min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
1035 bool JSArray::increaseVectorLength(JSGlobalData
& globalData
, unsigned newLength
)
1037 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
1038 // to the vector. Callers have to account for that, because they can do it more efficiently.
1039 if (newLength
> MAX_STORAGE_VECTOR_LENGTH
)
1042 ArrayStorage
* storage
= m_storage
;
1044 unsigned vectorLength
= m_vectorLength
;
1045 ASSERT(newLength
> vectorLength
);
1046 unsigned newVectorLength
= getNewVectorLength(newLength
);
1048 // Fast case - there is no precapacity. In these cases a realloc makes sense.
1049 if (LIKELY(!m_indexBias
)) {
1050 void* newStorage
= storage
->m_allocBase
;
1051 if (!globalData
.heap
.tryReallocateStorage(&newStorage
, storageSize(vectorLength
), storageSize(newVectorLength
)))
1054 storage
= m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(static_cast<char*>(newStorage
));
1055 m_storage
->m_allocBase
= newStorage
;
1056 ASSERT(m_storage
->m_allocBase
);
1058 m_vectorLength
= newVectorLength
;
1063 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
1064 unsigned newIndexBias
= min(m_indexBias
>> 1, MAX_STORAGE_VECTOR_LENGTH
- newVectorLength
);
1065 // Calculate new stoarge capcity, allowing room for the pre-capacity.
1066 unsigned newStorageCapacity
= newVectorLength
+ newIndexBias
;
1067 void* newAllocBase
= 0;
1068 if (!globalData
.heap
.tryAllocateStorage(storageSize(newStorageCapacity
), &newAllocBase
))
1070 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
1071 ASSERT(m_vectorLength
<= MAX_STORAGE_VECTOR_LENGTH
&& (MAX_STORAGE_VECTOR_LENGTH
- m_vectorLength
) >= m_indexBias
);
1073 m_vectorLength
= newVectorLength
;
1074 m_indexBias
= newIndexBias
;
1075 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(reinterpret_cast<WriteBarrier
<Unknown
>*>(newAllocBase
) + m_indexBias
);
1077 // Copy the ArrayStorage header & current contents of the vector.
1078 memmove(m_storage
, storage
, storageSize(vectorLength
));
1080 // Free the old allocation, update m_allocBase.
1081 m_storage
->m_allocBase
= newAllocBase
;
1086 // This method makes room in the vector, but leaves the new space uncleared.
1087 bool JSArray::unshiftCountSlowCase(JSGlobalData
& globalData
, unsigned count
)
1089 // If not, we should have handled this on the fast path.
1090 ASSERT(count
> m_indexBias
);
1092 ArrayStorage
* storage
= m_storage
;
1095 // Gather 4 key metrics:
1096 // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
1097 // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
1098 // * currentCapacity - what is the current size of the vector, including any pre-capacity.
1099 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
1101 unsigned length
= storage
->m_length
;
1102 unsigned usedVectorLength
= min(m_vectorLength
, length
);
1103 ASSERT(usedVectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
1104 // Check that required vector length is possible, in an overflow-safe fashion.
1105 if (count
> MAX_STORAGE_VECTOR_LENGTH
- usedVectorLength
)
1107 unsigned requiredVectorLength
= usedVectorLength
+ count
;
1108 ASSERT(requiredVectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
1109 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
1110 ASSERT(m_vectorLength
<= MAX_STORAGE_VECTOR_LENGTH
&& (MAX_STORAGE_VECTOR_LENGTH
- m_vectorLength
) >= m_indexBias
);
1111 unsigned currentCapacity
= m_vectorLength
+ m_indexBias
;
1112 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
1113 unsigned desiredCapacity
= min(MAX_STORAGE_VECTOR_LENGTH
, max(BASE_VECTOR_LEN
, requiredVectorLength
) << 1);
1116 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing on.
1118 void* newAllocBase
= 0;
1119 unsigned newStorageCapacity
;
1120 // If the current storage array is sufficiently large (but not too large!) then just keep using it.
1121 if (currentCapacity
> desiredCapacity
&& isDenseEnoughForVector(currentCapacity
, requiredVectorLength
)) {
1122 newAllocBase
= storage
->m_allocBase
;
1123 newStorageCapacity
= currentCapacity
;
1125 if (!globalData
.heap
.tryAllocateStorage(storageSize(desiredCapacity
), &newAllocBase
))
1127 newStorageCapacity
= desiredCapacity
;
1131 // Work out where we're going to move things to.
1133 // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
1134 // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
1135 // If it did, we calculate the amount that will remain based on an atomic decay - leave the
1136 // vector with half the post-capacity it had previously.
1137 unsigned postCapacity
= 0;
1138 if (length
< m_vectorLength
) {
1139 // Atomic decay, + the post-capacity cannot be greater than what is available.
1140 postCapacity
= min((m_vectorLength
- length
) >> 1, newStorageCapacity
- requiredVectorLength
);
1141 // If we're moving contents within the same allocation, the post-capacity is being reduced.
1142 ASSERT(newAllocBase
!= storage
->m_allocBase
|| postCapacity
< m_vectorLength
- length
);
1145 m_vectorLength
= requiredVectorLength
+ postCapacity
;
1146 m_indexBias
= newStorageCapacity
- m_vectorLength
;
1147 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(reinterpret_cast<WriteBarrier
<Unknown
>*>(newAllocBase
) + m_indexBias
);
1150 // Copy array data / header into their new locations, clear post-capacity & free any old allocation.
1152 // If this is being moved within the existing buffer of memory, we are always shifting data
1153 // to the right (since count > m_indexBias). As such this memmove cannot trample the header.
1154 memmove(m_storage
->m_vector
+ count
, storage
->m_vector
, sizeof(WriteBarrier
<Unknown
>) * usedVectorLength
);
1155 memmove(m_storage
, storage
, storageSize(0));
1157 // Are we copying into a new allocation?
1158 if (newAllocBase
!= m_storage
->m_allocBase
) {
1159 // Free the old allocation, update m_allocBase.
1160 m_storage
->m_allocBase
= newAllocBase
;
1166 bool JSArray::setLength(ExecState
* exec
, unsigned newLength
, bool throwException
)
1170 ArrayStorage
* storage
= m_storage
;
1171 unsigned length
= storage
->m_length
;
1173 // If the length is read only then we enter sparse mode, so should enter the following 'if'.
1174 ASSERT(isLengthWritable() || m_sparseValueMap
);
1176 if (SparseArrayValueMap
* map
= m_sparseValueMap
) {
1177 // Fail if the length is not writable.
1178 if (map
->lengthIsReadOnly())
1179 return reject(exec
, throwException
, StrictModeReadonlyPropertyWriteError
);
1181 if (newLength
< length
) {
1182 // Copy any keys we might be interested in into a vector.
1183 Vector
<unsigned> keys
;
1184 keys
.reserveCapacity(min(map
->size(), static_cast<size_t>(length
- newLength
)));
1185 SparseArrayValueMap::const_iterator end
= map
->end();
1186 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
) {
1187 unsigned index
= static_cast<unsigned>(it
->first
);
1188 if (index
< length
&& index
>= newLength
)
1192 // Check if the array is in sparse mode. If so there may be non-configurable
1193 // properties, so we have to perform deletion with caution, if not we can
1194 // delete values in any order.
1195 if (map
->sparseMode()) {
1196 qsort(keys
.begin(), keys
.size(), sizeof(unsigned), compareKeysForQSort
);
1197 unsigned i
= keys
.size();
1199 unsigned index
= keys
[--i
];
1200 SparseArrayValueMap::iterator it
= map
->find(index
);
1201 ASSERT(it
!= map
->notFound());
1202 if (it
->second
.attributes
& DontDelete
) {
1203 storage
->m_length
= index
+ 1;
1204 return reject(exec
, throwException
, "Unable to delete property.");
1209 for (unsigned i
= 0; i
< keys
.size(); ++i
)
1210 map
->remove(keys
[i
]);
1212 deallocateSparseMap();
1217 if (newLength
< length
) {
1218 // Delete properties from the vector.
1219 unsigned usedVectorLength
= min(length
, m_vectorLength
);
1220 for (unsigned i
= newLength
; i
< usedVectorLength
; ++i
) {
1221 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
1222 bool hadValue
= valueSlot
;
1224 storage
->m_numValuesInVector
-= hadValue
;
1228 storage
->m_length
= newLength
;
1234 JSValue
JSArray::pop(ExecState
* exec
)
1237 ArrayStorage
* storage
= m_storage
;
1239 unsigned length
= storage
->m_length
;
1241 if (!isLengthWritable())
1242 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
1243 return jsUndefined();
1246 unsigned index
= length
- 1;
1247 if (index
< m_vectorLength
) {
1248 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[index
];
1250 --storage
->m_numValuesInVector
;
1251 JSValue element
= valueSlot
.get();
1254 ASSERT(isLengthWritable());
1255 storage
->m_length
= index
;
1261 // Let element be the result of calling the [[Get]] internal method of O with argument indx.
1262 JSValue element
= get(exec
, index
);
1263 if (exec
->hadException())
1264 return jsUndefined();
1265 // Call the [[Delete]] internal method of O with arguments indx and true.
1266 deletePropertyByIndex(this, exec
, index
);
1267 // Call the [[Put]] internal method of O with arguments "length", indx, and true.
1268 setLength(exec
, index
, true);
1274 // Push & putIndex are almost identical, with two small differences.
1275 // - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
1276 // - pushing to an array of length 2^32-1 stores the property, but throws a range error.
1277 void JSArray::push(ExecState
* exec
, JSValue value
)
1280 ArrayStorage
* storage
= m_storage
;
1282 // Fast case - push within vector, always update m_length & m_numValuesInVector.
1283 unsigned length
= storage
->m_length
;
1284 if (length
< m_vectorLength
) {
1285 storage
->m_vector
[length
].set(exec
->globalData(), this, value
);
1286 storage
->m_length
= length
+ 1;
1287 ++storage
->m_numValuesInVector
;
1292 // Pushing to an array of length 2^32-1 stores the property, but throws a range error.
1293 if (UNLIKELY(storage
->m_length
== 0xFFFFFFFFu
)) {
1294 methodTable()->putByIndex(this, exec
, storage
->m_length
, value
, true);
1295 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
1296 if (!exec
->hadException())
1297 throwError(exec
, createRangeError(exec
, "Invalid array length"));
1301 // Handled the same as putIndex.
1302 putByIndexBeyondVectorLength(exec
, storage
->m_length
, value
, true);
1306 bool JSArray::shiftCount(ExecState
*, unsigned count
)
1310 ArrayStorage
* storage
= m_storage
;
1312 unsigned oldLength
= storage
->m_length
;
1314 // If the array contains holes or is otherwise in an abnormal state,
1315 // use the generic algorithm in ArrayPrototype.
1316 if (oldLength
!= storage
->m_numValuesInVector
|| inSparseMode())
1322 storage
->m_numValuesInVector
-= count
;
1323 storage
->m_length
-= count
;
1325 if (m_vectorLength
) {
1326 count
= min(m_vectorLength
, (unsigned)count
);
1328 m_vectorLength
-= count
;
1330 if (m_vectorLength
) {
1331 char* newBaseStorage
= reinterpret_cast<char*>(storage
) + count
* sizeof(WriteBarrier
<Unknown
>);
1332 memmove(newBaseStorage
, storage
, storageSize(0));
1333 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(newBaseStorage
);
1335 m_indexBias
+= count
;
1341 // Returns true if the unshift can be handled, false to fallback.
1342 bool JSArray::unshiftCount(ExecState
* exec
, unsigned count
)
1344 ArrayStorage
* storage
= m_storage
;
1345 unsigned length
= storage
->m_length
;
1347 // If the array contains holes or is otherwise in an abnormal state,
1348 // use the generic algorithm in ArrayPrototype.
1349 if (length
!= storage
->m_numValuesInVector
|| inSparseMode())
1352 if (m_indexBias
>= count
) {
1353 m_indexBias
-= count
;
1354 char* newBaseStorage
= reinterpret_cast<char*>(storage
) - count
* sizeof(WriteBarrier
<Unknown
>);
1355 memmove(newBaseStorage
, storage
, storageSize(0));
1356 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(newBaseStorage
);
1357 m_vectorLength
+= count
;
1358 } else if (!unshiftCountSlowCase(exec
->globalData(), count
)) {
1359 throwOutOfMemoryError(exec
);
1363 WriteBarrier
<Unknown
>* vector
= m_storage
->m_vector
;
1364 for (unsigned i
= 0; i
< count
; i
++)
1369 void JSArray::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
1371 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
1372 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
1373 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
1374 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
1376 JSNonFinalObject::visitChildren(thisObject
, visitor
);
1378 if (thisObject
->m_storage
) {
1379 ArrayStorage
* storage
= thisObject
->m_storage
;
1380 void* baseStorage
= storage
->m_allocBase
;
1382 visitor
.copyAndAppend(reinterpret_cast<void**>(&baseStorage
), storageSize(thisObject
->m_vectorLength
+ thisObject
->m_indexBias
), storage
->m_vector
->slot(), thisObject
->m_vectorLength
);
1384 if (baseStorage
!= thisObject
->m_storage
->m_allocBase
) {
1385 thisObject
->m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(static_cast<char*>(baseStorage
) + sizeof(JSValue
) * thisObject
->m_indexBias
);
1386 thisObject
->m_storage
->m_allocBase
= baseStorage
;
1387 ASSERT(thisObject
->m_storage
->m_allocBase
);
1391 if (SparseArrayValueMap
* map
= thisObject
->m_sparseValueMap
)
1392 map
->visitChildren(visitor
);
1395 static int compareNumbersForQSort(const void* a
, const void* b
)
1397 double da
= static_cast<const JSValue
*>(a
)->asNumber();
1398 double db
= static_cast<const JSValue
*>(b
)->asNumber();
1399 return (da
> db
) - (da
< db
);
1402 static int compareByStringPairForQSort(const void* a
, const void* b
)
1404 const ValueStringPair
* va
= static_cast<const ValueStringPair
*>(a
);
1405 const ValueStringPair
* vb
= static_cast<const ValueStringPair
*>(b
);
1406 return codePointCompare(va
->second
, vb
->second
);
1409 void JSArray::sortNumeric(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1411 ASSERT(!inSparseMode());
1413 ArrayStorage
* storage
= m_storage
;
1415 unsigned lengthNotIncludingUndefined
= compactForSorting();
1416 ASSERT(!m_sparseValueMap
);
1418 if (!lengthNotIncludingUndefined
)
1421 bool allValuesAreNumbers
= true;
1422 size_t size
= storage
->m_numValuesInVector
;
1423 for (size_t i
= 0; i
< size
; ++i
) {
1424 if (!storage
->m_vector
[i
].isNumber()) {
1425 allValuesAreNumbers
= false;
1430 if (!allValuesAreNumbers
)
1431 return sort(exec
, compareFunction
, callType
, callData
);
1433 // For numeric comparison, which is fast, qsort is faster than mergesort. We
1434 // also don't require mergesort's stability, since there's no user visible
1435 // side-effect from swapping the order of equal primitive values.
1436 qsort(storage
->m_vector
, size
, sizeof(WriteBarrier
<Unknown
>), compareNumbersForQSort
);
1438 checkConsistency(SortConsistencyCheck
);
1441 void JSArray::sort(ExecState
* exec
)
1443 ASSERT(!inSparseMode());
1445 unsigned lengthNotIncludingUndefined
= compactForSorting();
1446 ASSERT(!m_sparseValueMap
);
1448 if (!lengthNotIncludingUndefined
)
1451 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
1452 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
1453 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
1454 // random or otherwise changing results, effectively making compare function inconsistent.
1456 Vector
<ValueStringPair
> values(lengthNotIncludingUndefined
);
1457 if (!values
.begin()) {
1458 throwOutOfMemoryError(exec
);
1462 Heap::heap(this)->pushTempSortVector(&values
);
1464 bool isSortingPrimitiveValues
= true;
1465 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++) {
1466 JSValue value
= m_storage
->m_vector
[i
].get();
1467 ASSERT(!value
.isUndefined());
1468 values
[i
].first
= value
;
1469 isSortingPrimitiveValues
= isSortingPrimitiveValues
&& value
.isPrimitive();
1472 // FIXME: The following loop continues to call toString on subsequent values even after
1473 // a toString call raises an exception.
1475 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
1476 values
[i
].second
= values
[i
].first
.toUStringInline(exec
);
1478 if (exec
->hadException()) {
1479 Heap::heap(this)->popTempSortVector(&values
);
1483 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
1487 if (isSortingPrimitiveValues
)
1488 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1490 mergesort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1492 // FIXME: The qsort library function is likely to not be a stable sort.
1493 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
1494 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1497 // If the toString function changed the length of the array or vector storage,
1498 // increase the length to handle the orignal number of actual values.
1499 if (m_vectorLength
< lengthNotIncludingUndefined
)
1500 increaseVectorLength(exec
->globalData(), lengthNotIncludingUndefined
);
1501 if (m_storage
->m_length
< lengthNotIncludingUndefined
)
1502 m_storage
->m_length
= lengthNotIncludingUndefined
;
1504 JSGlobalData
& globalData
= exec
->globalData();
1505 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
1506 m_storage
->m_vector
[i
].set(globalData
, this, values
[i
].first
);
1508 Heap::heap(this)->popTempSortVector(&values
);
1510 checkConsistency(SortConsistencyCheck
);
1513 struct AVLTreeNodeForArrayCompare
{
1516 // Child pointers. The high bit of gt is robbed and used as the
1517 // balance factor sign. The high bit of lt is robbed and used as
1518 // the magnitude of the balance factor.
1523 struct AVLTreeAbstractorForArrayCompare
{
1524 typedef int32_t handle
; // Handle is an index into m_nodes vector.
1525 typedef JSValue key
;
1526 typedef int32_t size
;
1528 Vector
<AVLTreeNodeForArrayCompare
> m_nodes
;
1530 JSValue m_compareFunction
;
1531 CallType m_compareCallType
;
1532 const CallData
* m_compareCallData
;
1533 OwnPtr
<CachedCall
> m_cachedCall
;
1535 handle
get_less(handle h
) { return m_nodes
[h
].lt
& 0x7FFFFFFF; }
1536 void set_less(handle h
, handle lh
) { m_nodes
[h
].lt
&= 0x80000000; m_nodes
[h
].lt
|= lh
; }
1537 handle
get_greater(handle h
) { return m_nodes
[h
].gt
& 0x7FFFFFFF; }
1538 void set_greater(handle h
, handle gh
) { m_nodes
[h
].gt
&= 0x80000000; m_nodes
[h
].gt
|= gh
; }
1540 int get_balance_factor(handle h
)
1542 if (m_nodes
[h
].gt
& 0x80000000)
1544 return static_cast<unsigned>(m_nodes
[h
].lt
) >> 31;
1547 void set_balance_factor(handle h
, int bf
)
1550 m_nodes
[h
].lt
&= 0x7FFFFFFF;
1551 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1553 m_nodes
[h
].lt
|= 0x80000000;
1555 m_nodes
[h
].gt
|= 0x80000000;
1557 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1561 int compare_key_key(key va
, key vb
)
1563 ASSERT(!va
.isUndefined());
1564 ASSERT(!vb
.isUndefined());
1566 if (m_exec
->hadException())
1569 double compareResult
;
1571 m_cachedCall
->setThis(jsUndefined());
1572 m_cachedCall
->setArgument(0, va
);
1573 m_cachedCall
->setArgument(1, vb
);
1574 compareResult
= m_cachedCall
->call().toNumber(m_cachedCall
->newCallFrame(m_exec
));
1576 MarkedArgumentBuffer arguments
;
1577 arguments
.append(va
);
1578 arguments
.append(vb
);
1579 compareResult
= call(m_exec
, m_compareFunction
, m_compareCallType
, *m_compareCallData
, jsUndefined(), arguments
).toNumber(m_exec
);
1581 return (compareResult
< 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1584 int compare_key_node(key k
, handle h
) { return compare_key_key(k
, m_nodes
[h
].value
); }
1585 int compare_node_node(handle h1
, handle h2
) { return compare_key_key(m_nodes
[h1
].value
, m_nodes
[h2
].value
); }
1587 static handle
null() { return 0x7FFFFFFF; }
1590 void JSArray::sort(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1592 ASSERT(!inSparseMode());
1596 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
1598 // The maximum tree depth is compiled in - but the caller is clearly up to no good
1599 // if a larger array is passed.
1600 ASSERT(m_storage
->m_length
<= static_cast<unsigned>(std::numeric_limits
<int>::max()));
1601 if (m_storage
->m_length
> static_cast<unsigned>(std::numeric_limits
<int>::max()))
1604 unsigned usedVectorLength
= min(m_storage
->m_length
, m_vectorLength
);
1605 unsigned nodeCount
= usedVectorLength
;
1610 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44> tree
; // Depth 44 is enough for 2^31 items
1611 tree
.abstractor().m_exec
= exec
;
1612 tree
.abstractor().m_compareFunction
= compareFunction
;
1613 tree
.abstractor().m_compareCallType
= callType
;
1614 tree
.abstractor().m_compareCallData
= &callData
;
1615 tree
.abstractor().m_nodes
.grow(nodeCount
);
1617 if (callType
== CallTypeJS
)
1618 tree
.abstractor().m_cachedCall
= adoptPtr(new CachedCall(exec
, jsCast
<JSFunction
*>(compareFunction
), 2));
1620 if (!tree
.abstractor().m_nodes
.begin()) {
1621 throwOutOfMemoryError(exec
);
1625 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
1626 // right out from under us while we're building the tree here.
1628 unsigned numDefined
= 0;
1629 unsigned numUndefined
= 0;
1631 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
1632 for (; numDefined
< usedVectorLength
; ++numDefined
) {
1633 if (numDefined
> m_vectorLength
)
1635 JSValue v
= m_storage
->m_vector
[numDefined
].get();
1636 if (!v
|| v
.isUndefined())
1638 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1639 tree
.insert(numDefined
);
1641 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
1642 if (i
> m_vectorLength
)
1644 JSValue v
= m_storage
->m_vector
[i
].get();
1646 if (v
.isUndefined())
1649 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1650 tree
.insert(numDefined
);
1656 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
1658 ASSERT(!m_sparseValueMap
);
1660 // The array size may have changed. Figure out the new bounds.
1661 unsigned newestUsedVectorLength
= min(m_storage
->m_length
, m_vectorLength
);
1663 unsigned elementsToExtractThreshold
= min(min(newestUsedVectorLength
, numDefined
), static_cast<unsigned>(tree
.abstractor().m_nodes
.size()));
1664 unsigned undefinedElementsThreshold
= min(newestUsedVectorLength
, newUsedVectorLength
);
1665 unsigned clearElementsThreshold
= min(newestUsedVectorLength
, usedVectorLength
);
1667 // Copy the values back into m_storage.
1668 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44>::Iterator iter
;
1669 iter
.start_iter_least(tree
);
1670 JSGlobalData
& globalData
= exec
->globalData();
1671 for (unsigned i
= 0; i
< elementsToExtractThreshold
; ++i
) {
1672 m_storage
->m_vector
[i
].set(globalData
, this, tree
.abstractor().m_nodes
[*iter
].value
);
1676 // Put undefined values back in.
1677 for (unsigned i
= elementsToExtractThreshold
; i
< undefinedElementsThreshold
; ++i
)
1678 m_storage
->m_vector
[i
].setUndefined();
1680 // Ensure that unused values in the vector are zeroed out.
1681 for (unsigned i
= undefinedElementsThreshold
; i
< clearElementsThreshold
; ++i
)
1682 m_storage
->m_vector
[i
].clear();
1684 m_storage
->m_numValuesInVector
= undefinedElementsThreshold
;
1686 checkConsistency(SortConsistencyCheck
);
1689 void JSArray::fillArgList(ExecState
* exec
, MarkedArgumentBuffer
& args
)
1691 ArrayStorage
* storage
= m_storage
;
1693 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
1694 unsigned vectorEnd
= min(storage
->m_length
, m_vectorLength
);
1696 for (; i
< vectorEnd
; ++i
) {
1697 WriteBarrier
<Unknown
>& v
= vector
[i
];
1700 args
.append(v
.get());
1703 for (; i
< storage
->m_length
; ++i
)
1704 args
.append(get(exec
, i
));
1707 void JSArray::copyToArguments(ExecState
* exec
, CallFrame
* callFrame
, uint32_t length
)
1709 ASSERT(length
== this->length());
1710 UNUSED_PARAM(length
);
1712 WriteBarrier
<Unknown
>* vector
= m_storage
->m_vector
;
1713 unsigned vectorEnd
= min(length
, m_vectorLength
);
1714 for (; i
< vectorEnd
; ++i
) {
1715 WriteBarrier
<Unknown
>& v
= vector
[i
];
1718 callFrame
->setArgument(i
, v
.get());
1721 for (; i
< length
; ++i
)
1722 callFrame
->setArgument(i
, get(exec
, i
));
1725 unsigned JSArray::compactForSorting()
1727 ASSERT(!inSparseMode());
1731 ArrayStorage
* storage
= m_storage
;
1733 unsigned usedVectorLength
= min(storage
->m_length
, m_vectorLength
);
1735 unsigned numDefined
= 0;
1736 unsigned numUndefined
= 0;
1738 for (; numDefined
< usedVectorLength
; ++numDefined
) {
1739 JSValue v
= storage
->m_vector
[numDefined
].get();
1740 if (!v
|| v
.isUndefined())
1744 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
1745 JSValue v
= storage
->m_vector
[i
].get();
1747 if (v
.isUndefined())
1750 storage
->m_vector
[numDefined
++].setWithoutWriteBarrier(v
);
1754 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
1756 ASSERT(!m_sparseValueMap
);
1758 for (unsigned i
= numDefined
; i
< newUsedVectorLength
; ++i
)
1759 storage
->m_vector
[i
].setUndefined();
1760 for (unsigned i
= newUsedVectorLength
; i
< usedVectorLength
; ++i
)
1761 storage
->m_vector
[i
].clear();
1763 storage
->m_numValuesInVector
= newUsedVectorLength
;
1765 checkConsistency(SortConsistencyCheck
);
1770 #if CHECK_ARRAY_CONSISTENCY
1772 void JSArray::checkConsistency(ConsistencyCheckType type
)
1774 ArrayStorage
* storage
= m_storage
;
1776 ASSERT(!storage
->m_inCompactInitialization
);
1779 if (type
== SortConsistencyCheck
)
1780 ASSERT(!m_sparseValueMap
);
1782 unsigned numValuesInVector
= 0;
1783 for (unsigned i
= 0; i
< m_vectorLength
; ++i
) {
1784 if (JSValue value
= storage
->m_vector
[i
].get()) {
1785 ASSERT(i
< storage
->m_length
);
1786 if (type
!= DestructorConsistencyCheck
)
1787 value
.isUndefined(); // Likely to crash if the object was deallocated.
1788 ++numValuesInVector
;
1790 if (type
== SortConsistencyCheck
)
1791 ASSERT(i
>= storage
->m_numValuesInVector
);
1794 ASSERT(numValuesInVector
== storage
->m_numValuesInVector
);
1795 ASSERT(numValuesInVector
<= storage
->m_length
);
1797 if (m_sparseValueMap
) {
1798 SparseArrayValueMap::const_iterator end
= m_sparseValueMap
->end();
1799 for (SparseArrayValueMap::const_iterator it
= m_sparseValueMap
->begin(); it
!= end
; ++it
) {
1800 unsigned index
= it
->first
;
1801 ASSERT(index
< storage
->m_length
);
1802 ASSERT(index
>= m_vectorLength
);
1803 ASSERT(index
<= MAX_ARRAY_INDEX
);
1805 if (type
!= DestructorConsistencyCheck
)
1806 it
->second
.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated.