]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSArray.cpp
adcb8fc33c54ecf6387e211caa8186a52dc34a18
[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();
1416 ASSERT(!m_sparseValueMap);
1417
1418 if (!lengthNotIncludingUndefined)
1419 return;
1420
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;
1426 break;
1427 }
1428 }
1429
1430 if (!allValuesAreNumbers)
1431 return sort(exec, compareFunction, callType, callData);
1432
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);
1437
1438 checkConsistency(SortConsistencyCheck);
1439 }
1440
1441 void JSArray::sort(ExecState* exec)
1442 {
1443 ASSERT(!inSparseMode());
1444
1445 unsigned lengthNotIncludingUndefined = compactForSorting();
1446 ASSERT(!m_sparseValueMap);
1447
1448 if (!lengthNotIncludingUndefined)
1449 return;
1450
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.
1455
1456 Vector<ValueStringPair> values(lengthNotIncludingUndefined);
1457 if (!values.begin()) {
1458 throwOutOfMemoryError(exec);
1459 return;
1460 }
1461
1462 Heap::heap(this)->pushTempSortVector(&values);
1463
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();
1470 }
1471
1472 // FIXME: The following loop continues to call toString on subsequent values even after
1473 // a toString call raises an exception.
1474
1475 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
1476 values[i].second = values[i].first.toUStringInline(exec);
1477
1478 if (exec->hadException()) {
1479 Heap::heap(this)->popTempSortVector(&values);
1480 return;
1481 }
1482
1483 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
1484 // than O(N log N).
1485
1486 #if HAVE(MERGESORT)
1487 if (isSortingPrimitiveValues)
1488 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1489 else
1490 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1491 #else
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);
1495 #endif
1496
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;
1503
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);
1507
1508 Heap::heap(this)->popTempSortVector(&values);
1509
1510 checkConsistency(SortConsistencyCheck);
1511 }
1512
1513 struct AVLTreeNodeForArrayCompare {
1514 JSValue value;
1515
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.
1519 int32_t gt;
1520 int32_t lt;
1521 };
1522
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;
1527
1528 Vector<AVLTreeNodeForArrayCompare> m_nodes;
1529 ExecState* m_exec;
1530 JSValue m_compareFunction;
1531 CallType m_compareCallType;
1532 const CallData* m_compareCallData;
1533 OwnPtr<CachedCall> m_cachedCall;
1534
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; }
1539
1540 int get_balance_factor(handle h)
1541 {
1542 if (m_nodes[h].gt & 0x80000000)
1543 return -1;
1544 return static_cast<unsigned>(m_nodes[h].lt) >> 31;
1545 }
1546
1547 void set_balance_factor(handle h, int bf)
1548 {
1549 if (bf == 0) {
1550 m_nodes[h].lt &= 0x7FFFFFFF;
1551 m_nodes[h].gt &= 0x7FFFFFFF;
1552 } else {
1553 m_nodes[h].lt |= 0x80000000;
1554 if (bf < 0)
1555 m_nodes[h].gt |= 0x80000000;
1556 else
1557 m_nodes[h].gt &= 0x7FFFFFFF;
1558 }
1559 }
1560
1561 int compare_key_key(key va, key vb)
1562 {
1563 ASSERT(!va.isUndefined());
1564 ASSERT(!vb.isUndefined());
1565
1566 if (m_exec->hadException())
1567 return 1;
1568
1569 double compareResult;
1570 if (m_cachedCall) {
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));
1575 } else {
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);
1580 }
1581 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1582 }
1583
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); }
1586
1587 static handle null() { return 0x7FFFFFFF; }
1588 };
1589
1590 void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
1591 {
1592 ASSERT(!inSparseMode());
1593
1594 checkConsistency();
1595
1596 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
1597
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()))
1602 return;
1603
1604 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
1605 unsigned nodeCount = usedVectorLength;
1606
1607 if (!nodeCount)
1608 return;
1609
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);
1616
1617 if (callType == CallTypeJS)
1618 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
1619
1620 if (!tree.abstractor().m_nodes.begin()) {
1621 throwOutOfMemoryError(exec);
1622 return;
1623 }
1624
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.
1627
1628 unsigned numDefined = 0;
1629 unsigned numUndefined = 0;
1630
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)
1634 break;
1635 JSValue v = m_storage->m_vector[numDefined].get();
1636 if (!v || v.isUndefined())
1637 break;
1638 tree.abstractor().m_nodes[numDefined].value = v;
1639 tree.insert(numDefined);
1640 }
1641 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
1642 if (i > m_vectorLength)
1643 break;
1644 JSValue v = m_storage->m_vector[i].get();
1645 if (v) {
1646 if (v.isUndefined())
1647 ++numUndefined;
1648 else {
1649 tree.abstractor().m_nodes[numDefined].value = v;
1650 tree.insert(numDefined);
1651 ++numDefined;
1652 }
1653 }
1654 }
1655
1656 unsigned newUsedVectorLength = numDefined + numUndefined;
1657
1658 ASSERT(!m_sparseValueMap);
1659
1660 // The array size may have changed. Figure out the new bounds.
1661 unsigned newestUsedVectorLength = min(m_storage->m_length, m_vectorLength);
1662
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);
1666
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);
1673 ++iter;
1674 }
1675
1676 // Put undefined values back in.
1677 for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i)
1678 m_storage->m_vector[i].setUndefined();
1679
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();
1683
1684 m_storage->m_numValuesInVector = undefinedElementsThreshold;
1685
1686 checkConsistency(SortConsistencyCheck);
1687 }
1688
1689 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1690 {
1691 ArrayStorage* storage = m_storage;
1692
1693 WriteBarrier<Unknown>* vector = storage->m_vector;
1694 unsigned vectorEnd = min(storage->m_length, m_vectorLength);
1695 unsigned i = 0;
1696 for (; i < vectorEnd; ++i) {
1697 WriteBarrier<Unknown>& v = vector[i];
1698 if (!v)
1699 break;
1700 args.append(v.get());
1701 }
1702
1703 for (; i < storage->m_length; ++i)
1704 args.append(get(exec, i));
1705 }
1706
1707 void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
1708 {
1709 ASSERT(length == this->length());
1710 UNUSED_PARAM(length);
1711 unsigned i = 0;
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];
1716 if (!v)
1717 break;
1718 callFrame->setArgument(i, v.get());
1719 }
1720
1721 for (; i < length; ++i)
1722 callFrame->setArgument(i, get(exec, i));
1723 }
1724
1725 unsigned JSArray::compactForSorting()
1726 {
1727 ASSERT(!inSparseMode());
1728
1729 checkConsistency();
1730
1731 ArrayStorage* storage = m_storage;
1732
1733 unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
1734
1735 unsigned numDefined = 0;
1736 unsigned numUndefined = 0;
1737
1738 for (; numDefined < usedVectorLength; ++numDefined) {
1739 JSValue v = storage->m_vector[numDefined].get();
1740 if (!v || v.isUndefined())
1741 break;
1742 }
1743
1744 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
1745 JSValue v = storage->m_vector[i].get();
1746 if (v) {
1747 if (v.isUndefined())
1748 ++numUndefined;
1749 else
1750 storage->m_vector[numDefined++].setWithoutWriteBarrier(v);
1751 }
1752 }
1753
1754 unsigned newUsedVectorLength = numDefined + numUndefined;
1755
1756 ASSERT(!m_sparseValueMap);
1757
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();
1762
1763 storage->m_numValuesInVector = newUsedVectorLength;
1764
1765 checkConsistency(SortConsistencyCheck);
1766
1767 return numDefined;
1768 }
1769
1770 #if CHECK_ARRAY_CONSISTENCY
1771
1772 void JSArray::checkConsistency(ConsistencyCheckType type)
1773 {
1774 ArrayStorage* storage = m_storage;
1775
1776 ASSERT(!storage->m_inCompactInitialization);
1777
1778 ASSERT(storage);
1779 if (type == SortConsistencyCheck)
1780 ASSERT(!m_sparseValueMap);
1781
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;
1789 } else {
1790 if (type == SortConsistencyCheck)
1791 ASSERT(i >= storage->m_numValuesInVector);
1792 }
1793 }
1794 ASSERT(numValuesInVector == storage->m_numValuesInVector);
1795 ASSERT(numValuesInVector <= storage->m_length);
1796
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);
1804 ASSERT(it->second);
1805 if (type != DestructorConsistencyCheck)
1806 it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated.
1807 }
1808 }
1809 }
1810
1811 #endif
1812
1813 } // namespace JSC