]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSArray.cpp
aa1b8b7d981cfc652f6921cd47982848b83f99a0
[apple/javascriptcore.git] / runtime / JSArray.cpp
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 "CopiedSpace.h"
28 #include "CopiedSpaceInlineMethods.h"
29 #include "CachedCall.h"
30 #include "Error.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>
38
39 using namespace std;
40 using namespace WTF;
41
42 namespace JSC {
43
44
45 ASSERT_CLASS_FITS_IN_CELL(JSArray);
46 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
47
48 // Overview of JSArray
49 //
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.
54 //
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.
59 //
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
63 // fashion:
64 //
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.
73
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>))
79
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
86
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
90
91 // The upper bound to the size we'll grow a zero length array when the first element
92 // is added.
93 #define FIRST_VECTOR_GROW 4U
94
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;
100
101 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
102
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;
107
108 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
109 {
110 return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues;
111 }
112
113 static bool reject(ExecState* exec, bool throwException, const char* message)
114 {
115 if (throwException)
116 throwTypeError(exec, message);
117 return false;
118 }
119
120 #if !CHECK_ARRAY_CONSISTENCY
121
122 inline void JSArray::checkConsistency(ConsistencyCheckType)
123 {
124 }
125
126 #endif
127
128 void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength)
129 {
130 Base::finishCreation(globalData);
131 ASSERT(inherits(&s_info));
132
133 unsigned initialVectorLength = BASE_VECTOR_LEN;
134 unsigned initialStorageSize = storageSize(initialVectorLength);
135
136 void* newStorage = 0;
137 if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
138 CRASH();
139
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;
147 #endif
148
149 checkConsistency();
150 }
151
152 JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength)
153 {
154 Base::finishCreation(globalData);
155 ASSERT(inherits(&s_info));
156
157 // Check for lengths larger than we can handle with a vector.
158 if (initialLength > MAX_STORAGE_VECTOR_LENGTH)
159 return 0;
160
161 unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN);
162 unsigned initialStorageSize = storageSize(initialVectorLength);
163
164 void* newStorage = 0;
165 if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
166 CRASH();
167
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;
173
174 #if CHECK_ARRAY_CONSISTENCY
175 m_storage->m_initializationIndex = 0;
176 m_storage->m_inCompactInitialization = true;
177 #endif
178
179 return this;
180 }
181
182 // This function can be called multiple times on the same object.
183 void JSArray::finalize(JSCell* cell)
184 {
185 JSArray* thisObject = jsCast<JSArray*>(cell);
186 thisObject->checkConsistency(DestructorConsistencyCheck);
187 thisObject->deallocateSparseMap();
188 }
189
190 inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSArray* array, unsigned i)
191 {
192 SparseArrayEntry entry;
193 entry.setWithoutWriteBarrier(jsUndefined());
194
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;
200 }
201 return result;
202 }
203
204 inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow)
205 {
206 AddResult result = add(array, i);
207 SparseArrayEntry& entry = result.iterator->second;
208
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);
214 if (shouldThrow)
215 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
216 return;
217 }
218
219 if (!(entry.attributes & Accessor)) {
220 if (entry.attributes & ReadOnly) {
221 if (shouldThrow)
222 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
223 return;
224 }
225
226 entry.set(exec->globalData(), array, value);
227 return;
228 }
229
230 JSValue accessor = entry.Base::get();
231 ASSERT(accessor.isGetterSetter());
232 JSObject* setter = asGetterSetter(accessor)->setter();
233
234 if (!setter) {
235 if (shouldThrow)
236 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
237 return;
238 }
239
240 CallData callData;
241 CallType callType = setter->methodTable()->getCallData(setter, callData);
242 MarkedArgumentBuffer args;
243 args.append(value);
244 call(exec, setter, callType, callData, array, args);
245 }
246
247 inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow)
248 {
249 AddResult result = add(array, i);
250 SparseArrayEntry& entry = result.iterator->second;
251
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.");
258 }
259
260 entry.attributes = 0;
261 entry.set(exec->globalData(), array, value);
262 return true;
263 }
264
265 inline void SparseArrayEntry::get(PropertySlot& slot) const
266 {
267 JSValue value = Base::get();
268 ASSERT(value);
269
270 if (LIKELY(!value.isGetterSetter())) {
271 slot.setValue(value);
272 return;
273 }
274
275 JSObject* getter = asGetterSetter(value)->getter();
276 if (!getter) {
277 slot.setUndefined();
278 return;
279 }
280
281 slot.setGetterSlot(getter);
282 }
283
284 inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
285 {
286 descriptor.setDescriptor(Base::get(), attributes);
287 }
288
289 inline JSValue SparseArrayEntry::get(ExecState* exec, JSArray* array) const
290 {
291 JSValue result = Base::get();
292 ASSERT(result);
293
294 if (LIKELY(!result.isGetterSetter()))
295 return result;
296
297 JSObject* getter = asGetterSetter(result)->getter();
298 if (!getter)
299 return jsUndefined();
300
301 CallData callData;
302 CallType callType = getter->methodTable()->getCallData(getter, callData);
303 return call(exec, getter, callType, callData, array, exec->emptyList());
304 }
305
306 inline JSValue SparseArrayEntry::getNonSparseMode() const
307 {
308 ASSERT(!attributes);
309 return Base::get();
310 }
311
312 inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor)
313 {
314 iterator end = m_map.end();
315 for (iterator it = m_map.begin(); it != end; ++it)
316 visitor.append(&it->second);
317 }
318
319 void JSArray::allocateSparseMap(JSGlobalData& globalData)
320 {
321 m_sparseValueMap = new SparseArrayValueMap;
322 globalData.heap.addFinalizer(this, finalize);
323 }
324
325 void JSArray::deallocateSparseMap()
326 {
327 delete m_sparseValueMap;
328 m_sparseValueMap = 0;
329 }
330
331 void JSArray::enterDictionaryMode(JSGlobalData& globalData)
332 {
333 ArrayStorage* storage = m_storage;
334 SparseArrayValueMap* map = m_sparseValueMap;
335
336 if (!map) {
337 allocateSparseMap(globalData);
338 map = m_sparseValueMap;
339 }
340
341 if (map->sparseMode())
342 return;
343
344 map->setSparseMode();
345
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.
351 if (value)
352 map->add(this, i).iterator->second.set(globalData, this, value);
353 }
354
355 void* newRawStorage = 0;
356 if (!globalData.heap.tryAllocateStorage(storageSize(0), &newRawStorage))
357 CRASH();
358
359 ArrayStorage* newStorage = static_cast<ArrayStorage*>(newRawStorage);
360 memcpy(newStorage, m_storage, storageSize(0));
361 newStorage->m_allocBase = newStorage;
362 m_storage = newStorage;
363 m_indexBias = 0;
364 m_vectorLength = 0;
365 }
366
367 void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
368 {
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;
375 return;
376 }
377
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();
389
390 GetterSetter* accessor = GetterSetter::create(exec);
391 if (getter)
392 accessor->setGetter(exec->globalData(), getter);
393 if (setter)
394 accessor->setSetter(exec->globalData(), setter);
395
396 entryInMap->set(exec->globalData(), this, accessor);
397 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
398 return;
399 }
400
401 ASSERT(descriptor.isGenericDescriptor());
402 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
403 }
404
405 // Defined in ES5.1 8.12.9
406 bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException)
407 {
408 ASSERT(index != 0xFFFFFFFF);
409
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);
418 }
419
420 enterDictionaryMode(exec->globalData());
421 }
422
423 SparseArrayValueMap* map = m_sparseValueMap;
424 ASSERT(map);
425
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;
429
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.");
437 }
438
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.
447 // 4.c. Return true.
448
449 PropertyDescriptor defaults;
450 entryInMap->setWithoutWriteBarrier(jsUndefined());
451 entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
452 entryInMap->get(defaults);
453
454 putDescriptor(exec, entryInMap, descriptor, defaults);
455 if (index >= m_storage->m_length)
456 m_storage->m_length = index + 1;
457 return true;
458 }
459
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))
465 return true;
466
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.");
475 }
476
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.");
502 }
503 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
504 } else {
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.");
514 }
515 }
516 }
517
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);
520 // 13. Return true.
521 return true;
522 }
523
524 void JSArray::setLengthWritable(ExecState* exec, bool writable)
525 {
526 ASSERT(isLengthWritable() || !writable);
527 if (!isLengthWritable() || writable)
528 return;
529
530 enterDictionaryMode(exec->globalData());
531
532 SparseArrayValueMap* map = m_sparseValueMap;
533 ASSERT(map);
534 map->setLengthIsReadOnly();
535 }
536
537 // Defined in ES5.1 15.4.5.1
538 bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
539 {
540 JSArray* array = jsCast<JSArray*>(object);
541
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.");
551
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());
563 return true;
564 }
565
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"));
572 return false;
573 }
574
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());
579 return true;
580 }
581
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.");
588
589 // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
590 // i. Else,
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.
604 // 4. Reject.
605 if (descriptor.writablePresent())
606 array->setLengthWritable(exec, descriptor.writable());
607 return false;
608 }
609
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
613 // return true.
614 if (descriptor.writablePresent())
615 array->setLengthWritable(exec, descriptor.writable());
616 // n. Return true.
617 return true;
618 }
619
620 // 4. Else if P is an array index (15.4), then
621 bool isArrayIndex;
622 // a. Let index be ToUint32(P).
623 unsigned index = propertyName.toArrayIndex(isArrayIndex);
624 if (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.
633 // f. Return true.
634 return array->defineOwnNumericProperty(exec, index, descriptor, throwException);
635 }
636
637 return JSObject::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
638 }
639
640 bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot)
641 {
642 JSArray* thisObject = jsCast<JSArray*>(cell);
643 ArrayStorage* storage = thisObject->m_storage;
644
645 if (i >= storage->m_length) {
646 if (i > MAX_ARRAY_INDEX)
647 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
648 return false;
649 }
650
651 if (i < thisObject->m_vectorLength) {
652 JSValue value = storage->m_vector[i].get();
653 if (value) {
654 slot.setValue(value);
655 return true;
656 }
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);
661 return true;
662 }
663 }
664
665 return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
666 }
667
668 bool JSArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
669 {
670 JSArray* thisObject = jsCast<JSArray*>(cell);
671 if (propertyName == exec->propertyNames().length) {
672 slot.setValue(jsNumber(thisObject->length()));
673 return true;
674 }
675
676 bool isArrayIndex;
677 unsigned i = propertyName.toArrayIndex(isArrayIndex);
678 if (isArrayIndex)
679 return JSArray::getOwnPropertySlotByIndex(thisObject, exec, i, slot);
680
681 return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
682 }
683
684 bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
685 {
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);
689 return true;
690 }
691
692 ArrayStorage* storage = thisObject->m_storage;
693
694 bool isArrayIndex;
695 unsigned i = propertyName.toArrayIndex(isArrayIndex);
696 if (isArrayIndex) {
697 if (i >= storage->m_length)
698 return false;
699 if (i < thisObject->m_vectorLength) {
700 WriteBarrier<Unknown>& value = storage->m_vector[i];
701 if (value) {
702 descriptor.setDescriptor(value.get(), 0);
703 return true;
704 }
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);
709 return true;
710 }
711 }
712 }
713 return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
714 }
715
716 // ECMA 15.4.5.1
717 void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
718 {
719 JSArray* thisObject = jsCast<JSArray*>(cell);
720 bool isArrayIndex;
721 unsigned i = propertyName.toArrayIndex(isArrayIndex);
722 if (isArrayIndex) {
723 putByIndex(thisObject, exec, i, value, slot.isStrictMode());
724 return;
725 }
726
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"));
731 return;
732 }
733 thisObject->setLength(exec, newLength, slot.isStrictMode());
734 return;
735 }
736
737 JSObject::put(thisObject, exec, propertyName, value, slot);
738 }
739
740 void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
741 {
742 JSArray* thisObject = jsCast<JSArray*>(cell);
743 thisObject->checkConsistency();
744
745 ArrayStorage* storage = thisObject->m_storage;
746
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;
751
752 // Update m_length & m_numValuesInVector as necessary.
753 if (i >= length) {
754 length = i + 1;
755 storage->m_length = length;
756 ++storage->m_numValuesInVector;
757 } else if (!valueSlot)
758 ++storage->m_numValuesInVector;
759
760 valueSlot.set(exec->globalData(), thisObject, value);
761 thisObject->checkConsistency();
762 return;
763 }
764
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);
769 return;
770 }
771
772 // For all other cases, call putByIndexBeyondVectorLength.
773 thisObject->putByIndexBeyondVectorLength(exec, i, value, shouldThrow);
774 thisObject->checkConsistency();
775 }
776
777 void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
778 {
779 JSGlobalData& globalData = exec->globalData();
780
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);
784
785 ArrayStorage* storage = m_storage;
786 SparseArrayValueMap* map = m_sparseValueMap;
787
788 // First, handle cases where we don't currently have a sparse map.
789 if (LIKELY(!map)) {
790 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
791 ASSERT(isExtensible());
792
793 // Update m_length if necessary.
794 if (i >= storage->m_length)
795 storage->m_length = i + 1;
796
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.
800 storage = m_storage;
801 storage->m_vector[i].set(globalData, this, value);
802 ++storage->m_numValuesInVector;
803 return;
804 }
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);
809 return;
810 }
811
812 // Update m_length if necessary.
813 unsigned length = storage->m_length;
814 if (i >= length) {
815 // Prohibit growing the array if length is not writable.
816 if (map->lengthIsReadOnly() || !isExtensible()) {
817 if (shouldThrow)
818 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
819 return;
820 }
821 length = i + 1;
822 storage->m_length = length;
823 }
824
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);
830 return;
831 }
832
833 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
834 storage = m_storage;
835 storage->m_numValuesInVector = numValuesInArray;
836
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();
843
844 // Store the new property into the vector.
845 WriteBarrier<Unknown>& valueSlot = vector[i];
846 if (!valueSlot)
847 ++storage->m_numValuesInVector;
848 valueSlot.set(globalData, this, value);
849 }
850
851 bool JSArray::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
852 {
853 JSGlobalData& globalData = exec->globalData();
854
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);
858
859 ArrayStorage* storage = m_storage;
860 SparseArrayValueMap* map = m_sparseValueMap;
861
862 // First, handle cases where we don't currently have a sparse map.
863 if (LIKELY(!map)) {
864 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
865 ASSERT(isExtensible());
866
867 // Update m_length if necessary.
868 if (i >= storage->m_length)
869 storage->m_length = i + 1;
870
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.
874 storage = m_storage;
875 storage->m_vector[i].set(globalData, this, value);
876 ++storage->m_numValuesInVector;
877 return true;
878 }
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);
883 }
884
885 // Update m_length if necessary.
886 unsigned length = storage->m_length;
887 if (i >= length) {
888 // Prohibit growing the array if length is not writable.
889 if (map->lengthIsReadOnly())
890 return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError);
891 if (!isExtensible())
892 return reject(exec, shouldThrow, "Attempting to define property on object that is not extensible.");
893 length = i + 1;
894 storage->m_length = length;
895 }
896
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);
902
903 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
904 storage = m_storage;
905 storage->m_numValuesInVector = numValuesInArray;
906
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();
913
914 // Store the new property into the vector.
915 WriteBarrier<Unknown>& valueSlot = vector[i];
916 if (!valueSlot)
917 ++storage->m_numValuesInVector;
918 valueSlot.set(globalData, this, value);
919 return true;
920 }
921
922 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName)
923 {
924 JSArray* thisObject = jsCast<JSArray*>(cell);
925 bool isArrayIndex;
926 unsigned i = propertyName.toArrayIndex(isArrayIndex);
927 if (isArrayIndex)
928 return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i);
929
930 if (propertyName == exec->propertyNames().length)
931 return false;
932
933 return JSObject::deleteProperty(thisObject, exec, propertyName);
934 }
935
936 bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
937 {
938 JSArray* thisObject = jsCast<JSArray*>(cell);
939 thisObject->checkConsistency();
940
941 if (i > MAX_ARRAY_INDEX)
942 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));
943
944 ArrayStorage* storage = thisObject->m_storage;
945
946 if (i < thisObject->m_vectorLength) {
947 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
948 if (valueSlot) {
949 valueSlot.clear();
950 --storage->m_numValuesInVector;
951 }
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)
956 return false;
957 map->remove(it);
958 }
959 }
960
961 thisObject->checkConsistency();
962 return true;
963 }
964
965 static int compareKeysForQSort(const void* a, const void* b)
966 {
967 unsigned da = *static_cast<const unsigned*>(a);
968 unsigned db = *static_cast<const unsigned*>(b);
969 return (da > db) - (da < db);
970 }
971
972 void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
973 {
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.
978
979 ArrayStorage* storage = thisObject->m_storage;
980
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));
985 }
986
987 if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
988 Vector<unsigned> keys;
989 keys.reserveCapacity(map->size());
990
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));
995 }
996
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]));
1000 }
1001
1002 if (mode == IncludeDontEnumProperties)
1003 propertyNames.add(exec->propertyNames().length);
1004
1005 JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
1006 }
1007
1008 ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength)
1009 {
1010 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
1011
1012 unsigned increasedLength;
1013 unsigned maxInitLength = min(m_storage->m_length, 100000U);
1014
1015 if (desiredLength < maxInitLength)
1016 increasedLength = maxInitLength;
1017 else if (!m_vectorLength)
1018 increasedLength = max(desiredLength, lastArraySize);
1019 else {
1020 // Mathematically equivalent to:
1021 // increasedLength = (newLength * 3 + 1) / 2;
1022 // or:
1023 // increasedLength = (unsigned)ceil(newLength * 1.5));
1024 // This form is not prone to internal overflow.
1025 increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1);
1026 }
1027
1028 ASSERT(increasedLength >= desiredLength);
1029
1030 lastArraySize = min(increasedLength, FIRST_VECTOR_GROW);
1031
1032 return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
1033 }
1034
1035 bool JSArray::increaseVectorLength(JSGlobalData& globalData, unsigned newLength)
1036 {
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)
1040 return false;
1041
1042 ArrayStorage* storage = m_storage;
1043
1044 unsigned vectorLength = m_vectorLength;
1045 ASSERT(newLength > vectorLength);
1046 unsigned newVectorLength = getNewVectorLength(newLength);
1047
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)))
1052 return false;
1053
1054 storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newStorage));
1055 m_storage->m_allocBase = newStorage;
1056 ASSERT(m_storage->m_allocBase);
1057
1058 m_vectorLength = newVectorLength;
1059
1060 return true;
1061 }
1062
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))
1069 return false;
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);
1072
1073 m_vectorLength = newVectorLength;
1074 m_indexBias = newIndexBias;
1075 m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias);
1076
1077 // Copy the ArrayStorage header & current contents of the vector.
1078 memmove(m_storage, storage, storageSize(vectorLength));
1079
1080 // Free the old allocation, update m_allocBase.
1081 m_storage->m_allocBase = newAllocBase;
1082
1083 return true;
1084 }
1085
1086 // This method makes room in the vector, but leaves the new space uncleared.
1087 bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count)
1088 {
1089 // If not, we should have handled this on the fast path.
1090 ASSERT(count > m_indexBias);
1091
1092 ArrayStorage* storage = m_storage;
1093
1094 // Step 1:
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.
1100
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)
1106 return false;
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);
1114
1115 // Step 2:
1116 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing on.
1117
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;
1124 } else {
1125 if (!globalData.heap.tryAllocateStorage(storageSize(desiredCapacity), &newAllocBase))
1126 return false;
1127 newStorageCapacity = desiredCapacity;
1128 }
1129
1130 // Step 3:
1131 // Work out where we're going to move things to.
1132
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);
1143 }
1144
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);
1148
1149 // Step 4:
1150 // Copy array data / header into their new locations, clear post-capacity & free any old allocation.
1151
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));
1156
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;
1161 }
1162
1163 return true;
1164 }
1165
1166 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
1167 {
1168 checkConsistency();
1169
1170 ArrayStorage* storage = m_storage;
1171 unsigned length = storage->m_length;
1172
1173 // If the length is read only then we enter sparse mode, so should enter the following 'if'.
1174 ASSERT(isLengthWritable() || m_sparseValueMap);
1175
1176 if (SparseArrayValueMap* map = m_sparseValueMap) {
1177 // Fail if the length is not writable.
1178 if (map->lengthIsReadOnly())
1179 return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
1180
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)
1189 keys.append(index);
1190 }
1191
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();
1198 while (i) {
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.");
1205 }
1206 map->remove(it);
1207 }
1208 } else {
1209 for (unsigned i = 0; i < keys.size(); ++i)
1210 map->remove(keys[i]);
1211 if (map->isEmpty())
1212 deallocateSparseMap();
1213 }
1214 }
1215 }
1216
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;
1223 valueSlot.clear();
1224 storage->m_numValuesInVector -= hadValue;
1225 }
1226 }
1227
1228 storage->m_length = newLength;
1229
1230 checkConsistency();
1231 return true;
1232 }
1233
1234 JSValue JSArray::pop(ExecState* exec)
1235 {
1236 checkConsistency();
1237 ArrayStorage* storage = m_storage;
1238
1239 unsigned length = storage->m_length;
1240 if (!length) {
1241 if (!isLengthWritable())
1242 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
1243 return jsUndefined();
1244 }
1245
1246 unsigned index = length - 1;
1247 if (index < m_vectorLength) {
1248 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
1249 if (valueSlot) {
1250 --storage->m_numValuesInVector;
1251 JSValue element = valueSlot.get();
1252 valueSlot.clear();
1253
1254 ASSERT(isLengthWritable());
1255 storage->m_length = index;
1256 checkConsistency();
1257 return element;
1258 }
1259 }
1260
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);
1269 // Return element.
1270 checkConsistency();
1271 return element;
1272 }
1273
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)
1278 {
1279 checkConsistency();
1280 ArrayStorage* storage = m_storage;
1281
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;
1288 checkConsistency();
1289 return;
1290 }
1291
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"));
1298 return;
1299 }
1300
1301 // Handled the same as putIndex.
1302 putByIndexBeyondVectorLength(exec, storage->m_length, value, true);
1303 checkConsistency();
1304 }
1305
1306 bool JSArray::shiftCount(ExecState*, unsigned count)
1307 {
1308 ASSERT(count > 0);
1309
1310 ArrayStorage* storage = m_storage;
1311
1312 unsigned oldLength = storage->m_length;
1313
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())
1317 return false;
1318
1319 if (!oldLength)
1320 return true;
1321
1322 storage->m_numValuesInVector -= count;
1323 storage->m_length -= count;
1324
1325 if (m_vectorLength) {
1326 count = min(m_vectorLength, (unsigned)count);
1327
1328 m_vectorLength -= count;
1329
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);
1334
1335 m_indexBias += count;
1336 }
1337 }
1338 return true;
1339 }
1340
1341 // Returns true if the unshift can be handled, false to fallback.
1342 bool JSArray::unshiftCount(ExecState* exec, unsigned count)
1343 {
1344 ArrayStorage* storage = m_storage;
1345 unsigned length = storage->m_length;
1346
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())
1350 return false;
1351
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);
1360 return true;
1361 }
1362
1363 WriteBarrier<Unknown>* vector = m_storage->m_vector;
1364 for (unsigned i = 0; i < count; i++)
1365 vector[i].clear();
1366 return true;
1367 }
1368
1369 void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor)
1370 {
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());
1375
1376 JSNonFinalObject::visitChildren(thisObject, visitor);
1377
1378 if (thisObject->m_storage) {
1379 ArrayStorage* storage = thisObject->m_storage;
1380 void* baseStorage = storage->m_allocBase;
1381
1382 visitor.copyAndAppend(reinterpret_cast<void**>(&baseStorage), storageSize(thisObject->m_vectorLength + thisObject->m_indexBias), storage->m_vector->slot(), thisObject->m_vectorLength);
1383
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);
1388 }
1389 }
1390
1391 if (SparseArrayValueMap* map = thisObject->m_sparseValueMap)
1392 map->visitChildren(visitor);
1393 }
1394
1395 static int compareNumbersForQSort(const void* a, const void* b)
1396 {
1397 double da = static_cast<const JSValue*>(a)->asNumber();
1398 double db = static_cast<const JSValue*>(b)->asNumber();
1399 return (da > db) - (da < db);
1400 }
1401
1402 static int compareByStringPairForQSort(const void* a, const void* b)
1403 {
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);
1407 }
1408
1409 void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
1410 {
1411 ASSERT(!inSparseMode());
1412
1413 ArrayStorage* storage = m_storage;
1414
1415 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
1416 if (m_sparseValueMap) {
1417 throwOutOfMemoryError(exec);
1418 return;
1419 }
1420
1421 if (!lengthNotIncludingUndefined)
1422 return;
1423
1424 bool allValuesAreNumbers = true;
1425 size_t size = storage->m_numValuesInVector;
1426 for (size_t i = 0; i < size; ++i) {
1427 if (!storage->m_vector[i].isNumber()) {
1428 allValuesAreNumbers = false;
1429 break;
1430 }
1431 }
1432
1433 if (!allValuesAreNumbers)
1434 return sort(exec, compareFunction, callType, callData);
1435
1436 // For numeric comparison, which is fast, qsort is faster than mergesort. We
1437 // also don't require mergesort's stability, since there's no user visible
1438 // side-effect from swapping the order of equal primitive values.
1439 qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort);
1440
1441 checkConsistency(SortConsistencyCheck);
1442 }
1443
1444 void JSArray::sort(ExecState* exec)
1445 {
1446 ASSERT(!inSparseMode());
1447
1448 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
1449 if (m_sparseValueMap) {
1450 throwOutOfMemoryError(exec);
1451 return;
1452 }
1453
1454 if (!lengthNotIncludingUndefined)
1455 return;
1456
1457 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
1458 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
1459 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
1460 // random or otherwise changing results, effectively making compare function inconsistent.
1461
1462 Vector<ValueStringPair> values(lengthNotIncludingUndefined);
1463 if (!values.begin()) {
1464 throwOutOfMemoryError(exec);
1465 return;
1466 }
1467
1468 Heap::heap(this)->pushTempSortVector(&values);
1469
1470 bool isSortingPrimitiveValues = true;
1471 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
1472 JSValue value = m_storage->m_vector[i].get();
1473 ASSERT(!value.isUndefined());
1474 values[i].first = value;
1475 isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
1476 }
1477
1478 // FIXME: The following loop continues to call toString on subsequent values even after
1479 // a toString call raises an exception.
1480
1481 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
1482 values[i].second = values[i].first.toUStringInline(exec);
1483
1484 if (exec->hadException()) {
1485 Heap::heap(this)->popTempSortVector(&values);
1486 return;
1487 }
1488
1489 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
1490 // than O(N log N).
1491
1492 #if HAVE(MERGESORT)
1493 if (isSortingPrimitiveValues)
1494 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1495 else
1496 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1497 #else
1498 // FIXME: The qsort library function is likely to not be a stable sort.
1499 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
1500 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1501 #endif
1502
1503 // If the toString function changed the length of the array or vector storage,
1504 // increase the length to handle the orignal number of actual values.
1505 if (m_vectorLength < lengthNotIncludingUndefined)
1506 increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined);
1507 if (m_storage->m_length < lengthNotIncludingUndefined)
1508 m_storage->m_length = lengthNotIncludingUndefined;
1509
1510 JSGlobalData& globalData = exec->globalData();
1511 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
1512 m_storage->m_vector[i].set(globalData, this, values[i].first);
1513
1514 Heap::heap(this)->popTempSortVector(&values);
1515
1516 checkConsistency(SortConsistencyCheck);
1517 }
1518
1519 struct AVLTreeNodeForArrayCompare {
1520 JSValue value;
1521
1522 // Child pointers. The high bit of gt is robbed and used as the
1523 // balance factor sign. The high bit of lt is robbed and used as
1524 // the magnitude of the balance factor.
1525 int32_t gt;
1526 int32_t lt;
1527 };
1528
1529 struct AVLTreeAbstractorForArrayCompare {
1530 typedef int32_t handle; // Handle is an index into m_nodes vector.
1531 typedef JSValue key;
1532 typedef int32_t size;
1533
1534 Vector<AVLTreeNodeForArrayCompare> m_nodes;
1535 ExecState* m_exec;
1536 JSValue m_compareFunction;
1537 CallType m_compareCallType;
1538 const CallData* m_compareCallData;
1539 OwnPtr<CachedCall> m_cachedCall;
1540
1541 handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
1542 void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
1543 handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
1544 void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
1545
1546 int get_balance_factor(handle h)
1547 {
1548 if (m_nodes[h].gt & 0x80000000)
1549 return -1;
1550 return static_cast<unsigned>(m_nodes[h].lt) >> 31;
1551 }
1552
1553 void set_balance_factor(handle h, int bf)
1554 {
1555 if (bf == 0) {
1556 m_nodes[h].lt &= 0x7FFFFFFF;
1557 m_nodes[h].gt &= 0x7FFFFFFF;
1558 } else {
1559 m_nodes[h].lt |= 0x80000000;
1560 if (bf < 0)
1561 m_nodes[h].gt |= 0x80000000;
1562 else
1563 m_nodes[h].gt &= 0x7FFFFFFF;
1564 }
1565 }
1566
1567 int compare_key_key(key va, key vb)
1568 {
1569 ASSERT(!va.isUndefined());
1570 ASSERT(!vb.isUndefined());
1571
1572 if (m_exec->hadException())
1573 return 1;
1574
1575 double compareResult;
1576 if (m_cachedCall) {
1577 m_cachedCall->setThis(jsUndefined());
1578 m_cachedCall->setArgument(0, va);
1579 m_cachedCall->setArgument(1, vb);
1580 compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec));
1581 } else {
1582 MarkedArgumentBuffer arguments;
1583 arguments.append(va);
1584 arguments.append(vb);
1585 compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, jsUndefined(), arguments).toNumber(m_exec);
1586 }
1587 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1588 }
1589
1590 int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
1591 int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
1592
1593 static handle null() { return 0x7FFFFFFF; }
1594 };
1595
1596 void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
1597 {
1598 ASSERT(!inSparseMode());
1599
1600 checkConsistency();
1601
1602 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
1603
1604 // The maximum tree depth is compiled in - but the caller is clearly up to no good
1605 // if a larger array is passed.
1606 ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
1607 if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
1608 return;
1609
1610 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
1611 unsigned nodeCount = usedVectorLength + (m_sparseValueMap ? m_sparseValueMap->size() : 0);
1612
1613 if (!nodeCount)
1614 return;
1615
1616 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
1617 tree.abstractor().m_exec = exec;
1618 tree.abstractor().m_compareFunction = compareFunction;
1619 tree.abstractor().m_compareCallType = callType;
1620 tree.abstractor().m_compareCallData = &callData;
1621 tree.abstractor().m_nodes.grow(nodeCount);
1622
1623 if (callType == CallTypeJS)
1624 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
1625
1626 if (!tree.abstractor().m_nodes.begin()) {
1627 throwOutOfMemoryError(exec);
1628 return;
1629 }
1630
1631 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
1632 // right out from under us while we're building the tree here.
1633
1634 unsigned numDefined = 0;
1635 unsigned numUndefined = 0;
1636
1637 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
1638 for (; numDefined < usedVectorLength; ++numDefined) {
1639 JSValue v = m_storage->m_vector[numDefined].get();
1640 if (!v || v.isUndefined())
1641 break;
1642 tree.abstractor().m_nodes[numDefined].value = v;
1643 tree.insert(numDefined);
1644 }
1645 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
1646 JSValue v = m_storage->m_vector[i].get();
1647 if (v) {
1648 if (v.isUndefined())
1649 ++numUndefined;
1650 else {
1651 tree.abstractor().m_nodes[numDefined].value = v;
1652 tree.insert(numDefined);
1653 ++numDefined;
1654 }
1655 }
1656 }
1657
1658 unsigned newUsedVectorLength = numDefined + numUndefined;
1659
1660 if (SparseArrayValueMap* map = m_sparseValueMap) {
1661 newUsedVectorLength += map->size();
1662 if (newUsedVectorLength > m_vectorLength) {
1663 // Check that it is possible to allocate an array large enough to hold all the entries.
1664 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) {
1665 throwOutOfMemoryError(exec);
1666 return;
1667 }
1668 }
1669
1670 SparseArrayValueMap::const_iterator end = map->end();
1671 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
1672 tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode();
1673 tree.insert(numDefined);
1674 ++numDefined;
1675 }
1676
1677 deallocateSparseMap();
1678 }
1679
1680 ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
1681
1682 // FIXME: If the compare function changed the length of the array, the following might be
1683 // modifying the vector incorrectly.
1684
1685 // Copy the values back into m_storage.
1686 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
1687 iter.start_iter_least(tree);
1688 JSGlobalData& globalData = exec->globalData();
1689 for (unsigned i = 0; i < numDefined; ++i) {
1690 m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value);
1691 ++iter;
1692 }
1693
1694 // Put undefined values back in.
1695 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
1696 m_storage->m_vector[i].setUndefined();
1697
1698 // Ensure that unused values in the vector are zeroed out.
1699 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
1700 m_storage->m_vector[i].clear();
1701
1702 m_storage->m_numValuesInVector = newUsedVectorLength;
1703
1704 checkConsistency(SortConsistencyCheck);
1705 }
1706
1707 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1708 {
1709 ArrayStorage* storage = m_storage;
1710
1711 WriteBarrier<Unknown>* vector = storage->m_vector;
1712 unsigned vectorEnd = min(storage->m_length, m_vectorLength);
1713 unsigned i = 0;
1714 for (; i < vectorEnd; ++i) {
1715 WriteBarrier<Unknown>& v = vector[i];
1716 if (!v)
1717 break;
1718 args.append(v.get());
1719 }
1720
1721 for (; i < storage->m_length; ++i)
1722 args.append(get(exec, i));
1723 }
1724
1725 void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
1726 {
1727 ASSERT(length == this->length());
1728 UNUSED_PARAM(length);
1729 unsigned i = 0;
1730 WriteBarrier<Unknown>* vector = m_storage->m_vector;
1731 unsigned vectorEnd = min(length, m_vectorLength);
1732 for (; i < vectorEnd; ++i) {
1733 WriteBarrier<Unknown>& v = vector[i];
1734 if (!v)
1735 break;
1736 callFrame->setArgument(i, v.get());
1737 }
1738
1739 for (; i < length; ++i)
1740 callFrame->setArgument(i, get(exec, i));
1741 }
1742
1743 unsigned JSArray::compactForSorting(JSGlobalData& globalData)
1744 {
1745 ASSERT(!inSparseMode());
1746
1747 checkConsistency();
1748
1749 ArrayStorage* storage = m_storage;
1750
1751 unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
1752
1753 unsigned numDefined = 0;
1754 unsigned numUndefined = 0;
1755
1756 for (; numDefined < usedVectorLength; ++numDefined) {
1757 JSValue v = storage->m_vector[numDefined].get();
1758 if (!v || v.isUndefined())
1759 break;
1760 }
1761
1762 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
1763 JSValue v = storage->m_vector[i].get();
1764 if (v) {
1765 if (v.isUndefined())
1766 ++numUndefined;
1767 else
1768 storage->m_vector[numDefined++].setWithoutWriteBarrier(v);
1769 }
1770 }
1771
1772 unsigned newUsedVectorLength = numDefined + numUndefined;
1773
1774 if (SparseArrayValueMap* map = m_sparseValueMap) {
1775 newUsedVectorLength += map->size();
1776 if (newUsedVectorLength > m_vectorLength) {
1777 // Check that it is possible to allocate an array large enough to hold all the entries - if not,
1778 // exception is thrown by caller.
1779 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength))
1780 return 0;
1781
1782 storage = m_storage;
1783 }
1784
1785 SparseArrayValueMap::const_iterator end = map->end();
1786 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
1787 storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode());
1788
1789 deallocateSparseMap();
1790 }
1791
1792 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
1793 storage->m_vector[i].setUndefined();
1794 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
1795 storage->m_vector[i].clear();
1796
1797 storage->m_numValuesInVector = newUsedVectorLength;
1798
1799 checkConsistency(SortConsistencyCheck);
1800
1801 return numDefined;
1802 }
1803
1804 #if CHECK_ARRAY_CONSISTENCY
1805
1806 void JSArray::checkConsistency(ConsistencyCheckType type)
1807 {
1808 ArrayStorage* storage = m_storage;
1809
1810 ASSERT(!storage->m_inCompactInitialization);
1811
1812 ASSERT(storage);
1813 if (type == SortConsistencyCheck)
1814 ASSERT(!m_sparseValueMap);
1815
1816 unsigned numValuesInVector = 0;
1817 for (unsigned i = 0; i < m_vectorLength; ++i) {
1818 if (JSValue value = storage->m_vector[i].get()) {
1819 ASSERT(i < storage->m_length);
1820 if (type != DestructorConsistencyCheck)
1821 value.isUndefined(); // Likely to crash if the object was deallocated.
1822 ++numValuesInVector;
1823 } else {
1824 if (type == SortConsistencyCheck)
1825 ASSERT(i >= storage->m_numValuesInVector);
1826 }
1827 }
1828 ASSERT(numValuesInVector == storage->m_numValuesInVector);
1829 ASSERT(numValuesInVector <= storage->m_length);
1830
1831 if (m_sparseValueMap) {
1832 SparseArrayValueMap::const_iterator end = m_sparseValueMap->end();
1833 for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) {
1834 unsigned index = it->first;
1835 ASSERT(index < storage->m_length);
1836 ASSERT(index >= m_vectorLength);
1837 ASSERT(index <= MAX_ARRAY_INDEX);
1838 ASSERT(it->second);
1839 if (type != DestructorConsistencyCheck)
1840 it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated.
1841 }
1842 }
1843 }
1844
1845 #endif
1846
1847 } // namespace JSC