]> git.saurik.com Git - apple/javascriptcore.git/blame - kjs/array_instance.cpp
JavaScriptCore-466.1.tar.gz
[apple/javascriptcore.git] / kjs / array_instance.cpp
CommitLineData
b37bf2e1
A
1/*
2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007 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 "array_instance.h"
25
26#include "JSGlobalObject.h"
27#include "PropertyNameArray.h"
28#include <wtf/Assertions.h>
29
30using namespace std;
31
32namespace KJS {
33
34typedef HashMap<unsigned, JSValue*> SparseArrayValueMap;
35
36struct ArrayStorage {
37 unsigned m_numValuesInVector;
38 SparseArrayValueMap* m_sparseValueMap;
39 JSValue* m_vector[1];
40};
41
42// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer
43static const unsigned maxArrayIndex = 0xFFFFFFFEU;
44
45// Our policy for when to use a vector and when to use a sparse map.
46// For all array indices under sparseArrayCutoff, we always use a vector.
47// When indices greater than sparseArrayCutoff are involved, we use a vector
48// as long as it is 1/8 full. If more sparse than that, we use a map.
49// This value has to be a macro to be used in max() and min() without introducing
50// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
51#define sparseArrayCutoff 10000U
52static const unsigned minDensityMultiplier = 8;
53
54static const unsigned copyingSortCutoff = 50000;
55
56const ClassInfo ArrayInstance::info = {"Array", 0, 0};
57
58static inline size_t storageSize(unsigned vectorLength)
59{
60 return sizeof(ArrayStorage) - sizeof(JSValue*) + vectorLength * sizeof(JSValue*);
61}
62
63static inline unsigned increasedVectorLength(unsigned newLength)
64{
65 return (newLength * 3 + 1) / 2;
66}
67
68static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
69{
70 return length / minDensityMultiplier <= numValues;
71}
72
73ArrayInstance::ArrayInstance(JSObject* prototype, unsigned initialLength)
74 : JSObject(prototype)
75{
76 unsigned initialCapacity = min(initialLength, sparseArrayCutoff);
77
78 m_length = initialLength;
79 m_vectorLength = initialCapacity;
80 m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
81
82 Collector::reportExtraMemoryCost(initialCapacity * sizeof(JSValue*));
83}
84
85ArrayInstance::ArrayInstance(JSObject* prototype, const List& list)
86 : JSObject(prototype)
87{
88 unsigned length = list.size();
89
90 m_length = length;
91 m_vectorLength = length;
92
93 ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length)));
94
95 storage->m_numValuesInVector = length;
96 storage->m_sparseValueMap = 0;
97
98 size_t i = 0;
99 List::const_iterator end = list.end();
100 for (List::const_iterator it = list.begin(); it != end; ++it, ++i)
101 storage->m_vector[i] = *it;
102
103 m_storage = storage;
104
105 // When the array is created non-empty, its cells are filled, so it's really no worse than
106 // a property map. Therefore don't report extra memory cost.
107}
108
109ArrayInstance::~ArrayInstance()
110{
111 delete m_storage->m_sparseValueMap;
112 fastFree(m_storage);
113}
114
115JSValue* ArrayInstance::getItem(unsigned i) const
116{
117 ASSERT(i <= maxArrayIndex);
118
119 ArrayStorage* storage = m_storage;
120
121 if (i < m_vectorLength) {
122 JSValue* value = storage->m_vector[i];
123 return value ? value : jsUndefined();
124 }
125
126 SparseArrayValueMap* map = storage->m_sparseValueMap;
127 if (!map)
128 return jsUndefined();
129
130 JSValue* value = map->get(i);
131 return value ? value : jsUndefined();
132}
133
134JSValue* ArrayInstance::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
135{
136 return jsNumber(static_cast<ArrayInstance*>(slot.slotBase())->m_length);
137}
138
139ALWAYS_INLINE bool ArrayInstance::inlineGetOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
140{
141 ArrayStorage* storage = m_storage;
142
143 if (i >= m_length) {
144 if (i > maxArrayIndex)
145 return getOwnPropertySlot(exec, Identifier::from(i), slot);
146 return false;
147 }
148
149 if (i < m_vectorLength) {
150 JSValue*& valueSlot = storage->m_vector[i];
151 if (valueSlot) {
152 slot.setValueSlot(this, &valueSlot);
153 return true;
154 }
155 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
156 if (i >= sparseArrayCutoff) {
157 SparseArrayValueMap::iterator it = map->find(i);
158 if (it != map->end()) {
159 slot.setValueSlot(this, &it->second);
160 return true;
161 }
162 }
163 }
164
165 return false;
166}
167
168bool ArrayInstance::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
169{
170 if (propertyName == exec->propertyNames().length) {
171 slot.setCustom(this, lengthGetter);
172 return true;
173 }
174
175 bool isArrayIndex;
176 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
177 if (isArrayIndex)
178 return inlineGetOwnPropertySlot(exec, i, slot);
179
180 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
181}
182
183bool ArrayInstance::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
184{
185 return inlineGetOwnPropertySlot(exec, i, slot);
186}
187
188// ECMA 15.4.5.1
189void ArrayInstance::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attributes)
190{
191 bool isArrayIndex;
192 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
193 if (isArrayIndex) {
194 put(exec, i, value, attributes);
195 return;
196 }
197
198 if (propertyName == exec->propertyNames().length) {
199 unsigned newLength = value->toUInt32(exec);
200 if (value->toNumber(exec) != static_cast<double>(newLength)) {
201 throwError(exec, RangeError, "Invalid array length.");
202 return;
203 }
204 setLength(newLength);
205 return;
206 }
207
208 JSObject::put(exec, propertyName, value, attributes);
209}
210
211void ArrayInstance::put(ExecState* exec, unsigned i, JSValue* value, int attributes)
212{
213 if (i > maxArrayIndex) {
214 put(exec, Identifier::from(i), value, attributes);
215 return;
216 }
217
218 ArrayStorage* storage = m_storage;
219
220 unsigned length = m_length;
221 if (i >= length) {
222 length = i + 1;
223 m_length = length;
224 }
225
226 if (i < m_vectorLength) {
227 JSValue*& valueSlot = storage->m_vector[i];
228 storage->m_numValuesInVector += !valueSlot;
229 valueSlot = value;
230 return;
231 }
232
233 SparseArrayValueMap* map = storage->m_sparseValueMap;
234
235 if (i >= sparseArrayCutoff) {
236 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
237 // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
238 if (!isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
239 if (!map) {
240 map = new SparseArrayValueMap;
241 storage->m_sparseValueMap = map;
242 }
243 map->set(i, value);
244 return;
245 }
246 }
247
248 // We have decided that we'll put the new item into the vector.
249 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
250 if (!map || map->isEmpty()) {
251 increaseVectorLength(i + 1);
252 storage = m_storage;
253 ++storage->m_numValuesInVector;
254 storage->m_vector[i] = value;
255 return;
256 }
257
258 // Decide how many values it would be best to move from the map.
259 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
260 unsigned newVectorLength = increasedVectorLength(i + 1);
261 for (unsigned j = max(m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
262 newNumValuesInVector += map->contains(j);
263 if (i >= sparseArrayCutoff)
264 newNumValuesInVector -= map->contains(i);
265 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
266 unsigned proposedNewNumValuesInVector = newNumValuesInVector;
267 while (true) {
268 unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
269 for (unsigned j = max(newVectorLength, sparseArrayCutoff); j < proposedNewVectorLength; ++j)
270 proposedNewNumValuesInVector += map->contains(j);
271 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector))
272 break;
273 newVectorLength = proposedNewVectorLength;
274 newNumValuesInVector = proposedNewNumValuesInVector;
275 }
276 }
277
278 storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength)));
279
280 unsigned vectorLength = m_vectorLength;
281 if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
282 for (unsigned j = vectorLength; j < newVectorLength; ++j)
283 storage->m_vector[j] = 0;
284 if (i > sparseArrayCutoff)
285 map->remove(i);
286 } else {
287 for (unsigned j = vectorLength; j < max(vectorLength, sparseArrayCutoff); ++j)
288 storage->m_vector[j] = 0;
289 for (unsigned j = max(vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
290 storage->m_vector[j] = map->take(j);
291 }
292
293 storage->m_vector[i] = value;
294
295 m_vectorLength = newVectorLength;
296 storage->m_numValuesInVector = newNumValuesInVector;
297
298 m_storage = storage;
299}
300
301bool ArrayInstance::deleteProperty(ExecState* exec, const Identifier& propertyName)
302{
303 bool isArrayIndex;
304 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
305 if (isArrayIndex)
306 return deleteProperty(exec, i);
307
308 if (propertyName == exec->propertyNames().length)
309 return false;
310
311 return JSObject::deleteProperty(exec, propertyName);
312}
313
314bool ArrayInstance::deleteProperty(ExecState* exec, unsigned i)
315{
316 ArrayStorage* storage = m_storage;
317
318 if (i < m_vectorLength) {
319 JSValue*& valueSlot = storage->m_vector[i];
320 bool hadValue = valueSlot;
321 valueSlot = 0;
322 storage->m_numValuesInVector -= hadValue;
323 return hadValue;
324 }
325
326 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
327 if (i >= sparseArrayCutoff) {
328 SparseArrayValueMap::iterator it = map->find(i);
329 if (it != map->end()) {
330 map->remove(it);
331 return true;
332 }
333 }
334 }
335
336 if (i > maxArrayIndex)
337 return deleteProperty(exec, Identifier::from(i));
338
339 return false;
340}
341
342void ArrayInstance::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
343{
344 // FIXME: Filling PropertyNameArray with an identifier for every integer
345 // is incredibly inefficient for large arrays. We need a different approach.
346
347 ArrayStorage* storage = m_storage;
348
349 unsigned usedVectorLength = min(m_length, m_vectorLength);
350 for (unsigned i = 0; i < usedVectorLength; ++i) {
351 if (storage->m_vector[i])
352 propertyNames.add(Identifier::from(i));
353 }
354
355 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
356 SparseArrayValueMap::iterator end = map->end();
357 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
358 propertyNames.add(Identifier::from(it->first));
359 }
360
361 JSObject::getPropertyNames(exec, propertyNames);
362}
363
364void ArrayInstance::increaseVectorLength(unsigned newLength)
365{
366 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
367 // to the vector. Callers have to account for that, because they can do it more efficiently.
368
369 ArrayStorage* storage = m_storage;
370
371 unsigned vectorLength = m_vectorLength;
372 ASSERT(newLength > vectorLength);
373 unsigned newVectorLength = increasedVectorLength(newLength);
374
375 storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength)));
376 m_vectorLength = newVectorLength;
377
378 for (unsigned i = vectorLength; i < newVectorLength; ++i)
379 storage->m_vector[i] = 0;
380
381 m_storage = storage;
382}
383
384void ArrayInstance::setLength(unsigned newLength)
385{
386 ArrayStorage* storage = m_storage;
387
388 unsigned length = m_length;
389
390 if (newLength < length) {
391 unsigned usedVectorLength = min(length, m_vectorLength);
392 for (unsigned i = newLength; i < usedVectorLength; ++i) {
393 JSValue*& valueSlot = storage->m_vector[i];
394 bool hadValue = valueSlot;
395 valueSlot = 0;
396 storage->m_numValuesInVector -= hadValue;
397 }
398
399 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
400 SparseArrayValueMap copy = *map;
401 SparseArrayValueMap::iterator end = copy.end();
402 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
403 if (it->first >= newLength)
404 map->remove(it->first);
405 }
406 if (map->isEmpty()) {
407 delete map;
408 storage->m_sparseValueMap = 0;
409 }
410 }
411 }
412
413 m_length = newLength;
414}
415
416void ArrayInstance::mark()
417{
418 JSObject::mark();
419
420 ArrayStorage* storage = m_storage;
421
422 unsigned usedVectorLength = min(m_length, m_vectorLength);
423 for (unsigned i = 0; i < usedVectorLength; ++i) {
424 JSValue* value = storage->m_vector[i];
425 if (value && !value->marked())
426 value->mark();
427 }
428
429 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
430 SparseArrayValueMap::iterator end = map->end();
431 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
432 JSValue* value = it->second;
433 if (!value->marked())
434 value->mark();
435 }
436 }
437}
438
439static int compareByStringPairForQSort(const void* a, const void* b)
440{
441 const std::pair<JSValue*, UString>* va = static_cast<const std::pair<JSValue*, UString>*>(a);
442 const std::pair<JSValue*, UString>* vb = static_cast<const std::pair<JSValue*, UString>*>(b);
443 return compare(va->second, vb->second);
444}
445
446static ExecState* execForCompareByStringForQSort = 0;
447static int compareByStringForQSort(const void* a, const void* b)
448{
449 ExecState* exec = execForCompareByStringForQSort;
450
451 JSValue* va = *static_cast<JSValue* const*>(a);
452 JSValue* vb = *static_cast<JSValue* const*>(b);
453 ASSERT(!va->isUndefined());
454 ASSERT(!vb->isUndefined());
455
456 return compare(va->toString(exec), vb->toString(exec));
457}
458
459void ArrayInstance::sort(ExecState* exec)
460{
461 unsigned lengthNotIncludingUndefined = compactForSorting();
462
463 if (lengthNotIncludingUndefined < copyingSortCutoff) {
464 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
465 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
466 // buffer. For large arrays, we fall back to a qsort on the JavaScriptValues to avoid creating copies.
467
468 Vector<std::pair<JSValue*, UString> > values(lengthNotIncludingUndefined);
469 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
470 JSValue* value = m_storage->m_vector[i];
471 ASSERT(!value->isUndefined());
472 values[i].first = value;
473 values[i].second = value->toString(exec);
474 }
475
476 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
477 // than O(N log N).
478
479#if HAVE(MERGESORT)
480 mergesort(values.begin(), values.size(), sizeof(std::pair<JSValue*, UString>), compareByStringPairForQSort);
481#else
482 qsort(values.begin(), values.size(), sizeof(std::pair<JSValue*, UString>), compareByStringPairForQSort);
483#endif
484 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
485 m_storage->m_vector[i] = values[i].first;
486 return;
487 }
488
489 ExecState* oldExec = execForCompareByStringForQSort;
490 execForCompareByStringForQSort = exec;
491 qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareByStringForQSort);
492 execForCompareByStringForQSort = oldExec;
493}
494
495struct CompareWithCompareFunctionArguments {
496 CompareWithCompareFunctionArguments(ExecState *e, JSObject *cf)
497 : exec(e)
498 , compareFunction(cf)
499 , globalObject(e->dynamicGlobalObject())
500 {
501 }
502
503 ExecState *exec;
504 JSObject *compareFunction;
505 List arguments;
506 JSGlobalObject* globalObject;
507};
508
509static CompareWithCompareFunctionArguments* compareWithCompareFunctionArguments = 0;
510
511static int compareWithCompareFunctionForQSort(const void* a, const void* b)
512{
513 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
514
515 JSValue* va = *static_cast<JSValue* const*>(a);
516 JSValue* vb = *static_cast<JSValue* const*>(b);
517 ASSERT(!va->isUndefined());
518 ASSERT(!vb->isUndefined());
519
520 args->arguments.clear();
521 args->arguments.append(va);
522 args->arguments.append(vb);
523 double compareResult = args->compareFunction->call
524 (args->exec, args->globalObject, args->arguments)->toNumber(args->exec);
525 return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;
526}
527
528void ArrayInstance::sort(ExecState* exec, JSObject* compareFunction)
529{
530 size_t lengthNotIncludingUndefined = compactForSorting();
531
532 CompareWithCompareFunctionArguments* oldArgs = compareWithCompareFunctionArguments;
533 CompareWithCompareFunctionArguments args(exec, compareFunction);
534 compareWithCompareFunctionArguments = &args;
535
536#if HAVE(MERGESORT)
537 // Because mergesort usually does fewer compares, it is faster than qsort here.
538 // However, because it requires extra copies of the storage buffer, don't use it for very
539 // large arrays.
540
541 // FIXME: A tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even
542 // better job of minimizing compares.
543
544 if (lengthNotIncludingUndefined < copyingSortCutoff) {
545 // During the sort, we could do a garbage collect, and it's important to still
546 // have references to every object in the array for ArrayInstance::mark.
547 // The mergesort algorithm does not guarantee this, so we sort a copy rather
548 // than the original.
549 size_t size = storageSize(m_vectorLength);
550 ArrayStorage* copy = static_cast<ArrayStorage*>(fastMalloc(size));
551 memcpy(copy, m_storage, size);
552 mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort);
553 fastFree(m_storage);
554 m_storage = copy;
555 compareWithCompareFunctionArguments = oldArgs;
556 return;
557 }
558#endif
559
560 qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort);
561 compareWithCompareFunctionArguments = oldArgs;
562}
563
564unsigned ArrayInstance::compactForSorting()
565{
566 ArrayStorage* storage = m_storage;
567
568 unsigned usedVectorLength = min(m_length, m_vectorLength);
569
570 unsigned numDefined = 0;
571 unsigned numUndefined = 0;
572
573 for (; numDefined < usedVectorLength; ++numDefined) {
574 JSValue* v = storage->m_vector[numDefined];
575 if (!v || v->isUndefined())
576 break;
577 }
578 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
579 if (JSValue* v = storage->m_vector[i]) {
580 if (v->isUndefined())
581 ++numUndefined;
582 else
583 storage->m_vector[numDefined++] = v;
584 }
585 }
586
587 unsigned newUsedVectorLength = numDefined + numUndefined;
588
589 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
590 newUsedVectorLength += map->size();
591 if (newUsedVectorLength > m_vectorLength) {
592 increaseVectorLength(newUsedVectorLength);
593 storage = m_storage;
594 }
595
596 SparseArrayValueMap::iterator end = map->end();
597 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
598 storage->m_vector[numDefined++] = it->second;
599
600 delete map;
601 storage->m_sparseValueMap = 0;
602 }
603
604 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
605 storage->m_vector[i] = jsUndefined();
606 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
607 storage->m_vector[i] = 0;
608
609 return numDefined;
610}
611
612}