1 /*
2  *  This file is part of the KDE libraries
3  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved.
5  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
6  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23 
24 #include "array_instance.h"
25 
26 #include "PropertyNameArray.h"
27 #include "JSVariableObject.h"
28 #include <wtf/Assertions.h>
29 #include <wtf/HashMap.h>
30 
31 #include <algorithm>
32 #include <stdio.h>
33 
34 using std::min;
35 using std::max;
36 
37 namespace KJS
38 {
39 
40 struct ArrayEntity {
ArrayEntityKJS::ArrayEntity41     ArrayEntity()
42         : value(nullptr),
43           attributes(0)
44     {
45     }
46 
ArrayEntityKJS::ArrayEntity47     ArrayEntity(JSValue *jsVal, uint32_t attr)
48         : value(jsVal),
49           attributes(attr)
50     {
51     }
52 
53     JSValue *value;
54     uint32_t attributes;
55 };
56 
57 typedef HashMap<unsigned, ArrayEntity> SparseArrayValueMap;
58 
59 struct ArrayStorage {
60     unsigned m_numValuesInVector;
61     SparseArrayValueMap *m_sparseValueMap;
62     ArrayEntity m_vector[1];
63 };
64 
65 // (2^32)-1
66 static const unsigned maxArrayLength = 0xFFFFFFFFU;
67 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer
68 static const unsigned maxArrayIndex = 0xFFFFFFFEU;
69 
70 // Our policy for when to use a vector and when to use a sparse map.
71 // For all array indices under sparseArrayCutoff, we always use a vector.
72 // When indices greater than sparseArrayCutoff are involved, we use a vector
73 // as long as it is 1/8 full. If more sparse than that, we use a map.
74 static const unsigned sparseArrayCutoff = 10000;
75 static const unsigned minDensityMultiplier = 8;
76 
77 static const unsigned mergeSortCutoff = 10000;
78 
79 const ClassInfo ArrayInstance::info = {"Array", nullptr, nullptr, nullptr};
80 
storageSize(unsigned vectorLength)81 static inline size_t storageSize(unsigned vectorLength)
82 {
83     return sizeof(ArrayStorage) - sizeof(ArrayEntity) + vectorLength * sizeof(ArrayEntity);
84 }
85 
increasedVectorLength(unsigned newLength)86 static inline unsigned increasedVectorLength(unsigned newLength)
87 {
88     return (newLength * 3 + 1) / 2;
89 }
90 
isDenseEnoughForVector(unsigned length,unsigned numValues)91 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
92 {
93     return length / minDensityMultiplier <= numValues;
94 }
95 
ArrayInstance(JSObject * prototype,unsigned initialLength)96 ArrayInstance::ArrayInstance(JSObject *prototype, unsigned initialLength)
97     : JSObject(prototype)
98 {
99     unsigned initialCapacity = min(initialLength, sparseArrayCutoff);
100 
101     m_length = initialLength;
102     m_vectorLength = initialCapacity;
103     m_storage = static_cast<ArrayStorage *>(fastCalloc(storageSize(initialCapacity), 1));
104     m_lengthAttributes = DontDelete | DontEnum;
105 
106     Collector::reportExtraMemoryCost(initialCapacity * sizeof(ArrayEntity));
107 }
108 
ArrayInstance(JSObject * prototype,const List & list)109 ArrayInstance::ArrayInstance(JSObject *prototype, const List &list)
110     : JSObject(prototype)
111 {
112     unsigned length = list.size();
113 
114     m_length = length;
115     m_vectorLength = length;
116     m_lengthAttributes = DontDelete | DontEnum;
117 
118     ArrayStorage *storage = static_cast<ArrayStorage *>(fastMalloc(storageSize(length)));
119 
120     storage->m_numValuesInVector = length;
121     storage->m_sparseValueMap = nullptr;
122 
123     ListIterator it = list.begin();
124     for (unsigned i = 0; i < length; ++i) {
125         storage->m_vector[i].value = it++;
126         storage->m_vector[i].attributes = 0;
127     }
128 
129     m_storage = storage;
130 
131     // When the array is created non-empty, its cells are filled, so it's really no worse than
132     // a property map. Therefore don't report extra memory cost.
133 }
134 
~ArrayInstance()135 ArrayInstance::~ArrayInstance()
136 {
137     delete m_storage->m_sparseValueMap;
138     fastFree(m_storage);
139 }
140 
getItem(unsigned i) const141 JSValue *ArrayInstance::getItem(unsigned i) const
142 {
143     ASSERT(i <= maxArrayIndex);
144 
145     ArrayEntity *ent = getArrayEntity(i);
146     if (ent) {
147         return ent->value;
148     }
149     return jsUndefined();
150 }
151 
lengthGetter(ExecState *,JSObject *,const Identifier &,const PropertySlot & slot)152 JSValue *ArrayInstance::lengthGetter(ExecState *, JSObject *, const Identifier &, const PropertySlot &slot)
153 {
154     return jsNumber(static_cast<ArrayInstance *>(slot.slotBase())->m_length);
155 }
156 
inlineGetOwnPropertySlot(ExecState * exec,unsigned i,PropertySlot & slot)157 ALWAYS_INLINE bool ArrayInstance::inlineGetOwnPropertySlot(ExecState *exec, unsigned i, PropertySlot &slot)
158 {
159     if (i >= m_length) {
160         if (i > maxArrayIndex) {
161             return getOwnPropertySlot(exec, Identifier::from(i), slot);
162         }
163         return false;
164     }
165 
166     ArrayEntity *ent = getArrayEntity(i);
167     if (ent) {
168         if (ent->attributes & GetterSetter) {
169             GetterSetterImp *gs = static_cast<GetterSetterImp *>(ent->value);
170             JSObject *getterFunc = gs->getGetter();
171             if (getterFunc) {
172                 slot.setGetterSlot(this, getterFunc);
173             } else {
174                 slot.setUndefined(this);
175             }
176             return true;
177         }
178         slot.setValueSlot(this, &ent->value);
179         return true;
180     }
181 
182     return false;
183 }
184 
getArrayEntity(unsigned int i) const185 ArrayEntity *ArrayInstance::getArrayEntity(unsigned int i) const
186 {
187     if (i >= m_length) {
188         return nullptr;
189     }
190 
191     ArrayStorage *storage = m_storage;
192     if (i < m_vectorLength) {
193         if (storage->m_vector[i].value) {
194             return &storage->m_vector[i];
195         }
196     }
197 
198     SparseArrayValueMap *map = storage->m_sparseValueMap;
199     if (map && i > 0 && i <= maxArrayIndex) {
200         SparseArrayValueMap::iterator it = map->find(i);
201         if (it != map->end()) {
202             return &it->second;
203         }
204     }
205 
206     return nullptr;
207 }
208 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)209 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
210 {
211     if (propertyName == exec->propertyNames().length) {
212         slot.setCustom(this, lengthGetter);
213         return true;
214     }
215 
216     bool isArrayIndex;
217     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
218     if (isArrayIndex) {
219         return inlineGetOwnPropertySlot(exec, i, slot);
220     }
221 
222     return JSObject::getOwnPropertySlot(exec, propertyName, slot);
223 }
224 
getOwnPropertySlot(ExecState * exec,unsigned i,PropertySlot & slot)225 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, unsigned i, PropertySlot &slot)
226 {
227     return inlineGetOwnPropertySlot(exec, i, slot);
228 }
229 
230 // ECMA 15.4.5.1
put(ExecState * exec,const Identifier & propertyName,JSValue * value,int attributes)231 void ArrayInstance::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attributes)
232 {
233     bool isArrayIndex;
234     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
235     if (isArrayIndex) {
236         put(exec, i, value, attributes);
237         return;
238     }
239 
240     if (propertyName == exec->propertyNames().length) {
241         if (m_lengthAttributes & ReadOnly) {
242             return;
243         }
244         unsigned newLength = JSValue::toUInt32(value, exec);
245         if (JSValue::toNumber(value, exec) != static_cast<double>(newLength)) {
246             throwError(exec, RangeError, "Invalid array length.");
247             return;
248         }
249         m_lengthAttributes = attributes;
250         setLength(newLength);
251         return;
252     }
253 
254     JSObject::put(exec, propertyName, value, attributes);
255 }
256 
put(ExecState * exec,unsigned i,JSValue * value,int attributes)257 void ArrayInstance::put(ExecState *exec, unsigned i, JSValue *value, int attributes)
258 {
259     ArrayEntity *ent = getArrayEntity(i);
260     if (ent) {
261         if (ent->attributes & ReadOnly) {
262             return;
263         }
264         attributes |= ent->attributes;
265 
266         JSValue *gs = ent->value;
267         if (gs && !JSValue::isUndefined(gs)) {
268             if (ent->attributes & GetterSetter) {
269                 JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
270 
271                 if (!setterFunc) {
272                     if (false) { //if strict is true
273                         throwError(exec, TypeError, "setting a property that has only a getter");
274                     }
275                     return;
276                 }
277 
278                 List args;
279                 args.append(value);
280 
281                 setterFunc->call(exec, this, args);
282                 return;
283             }
284         }
285     }
286 
287     KJS::ArrayInstance::putDirect(i, value, attributes);
288 }
289 
deleteProperty(ExecState * exec,const Identifier & propertyName)290 bool ArrayInstance::deleteProperty(ExecState *exec, const Identifier &propertyName)
291 {
292     bool isArrayIndex;
293     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
294     if (isArrayIndex) {
295         return deleteProperty(exec, i);
296     }
297 
298     if (propertyName == exec->propertyNames().length) {
299         return false;
300     }
301 
302     return JSObject::deleteProperty(exec, propertyName);
303 }
304 
deleteProperty(ExecState * exec,unsigned i)305 bool ArrayInstance::deleteProperty(ExecState *exec, unsigned i)
306 {
307     ArrayStorage *storage = m_storage;
308 
309     if (i < m_vectorLength) {
310         ArrayEntity *ent = &storage->m_vector[i];
311         if (ent->value) {
312             if (ent->attributes & DontDelete) {
313                 return false;
314             }
315 
316             JSValue *&valueSlot = ent->value;
317             bool hadValue = valueSlot;
318             valueSlot = nullptr;
319             storage->m_numValuesInVector -= hadValue;
320             ent->value = nullptr;
321             return hadValue;
322         }
323     }
324 
325     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
326         SparseArrayValueMap::iterator it = map->find(i);
327         if (it != map->end()) {
328             if ((*it).second.attributes & DontDelete) {
329                 return false;
330             }
331             map->remove(it->first);
332             return true;
333         }
334     }
335 
336     if (i > maxArrayIndex) {
337         return JSObject::deleteProperty(exec, Identifier::from(i));
338     }
339 
340     return true;
341 }
342 
getOwnPropertyNames(ExecState * exec,PropertyNameArray & propertyNames,PropertyMap::PropertyMode mode)343 void ArrayInstance::getOwnPropertyNames(ExecState *exec, PropertyNameArray &propertyNames, PropertyMap::PropertyMode mode)
344 {
345     // FIXME: Filling PropertyNameArray with an identifier for every integer
346     // is incredibly inefficient for large arrays. We need a different approach.
347 
348     ArrayStorage *storage = m_storage;
349 
350     unsigned usedVectorLength = min(m_length, m_vectorLength);
351     for (unsigned i = 0; i < usedVectorLength; ++i) {
352         if (storage->m_vector[i].value &&
353                 (!(storage->m_vector[i].attributes & DontEnum) ||
354                  mode == PropertyMap::IncludeDontEnumProperties)) {
355             propertyNames.add(Identifier::from(i));
356         }
357     }
358 
359     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
360         SparseArrayValueMap::iterator end = map->end();
361         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
362             if (!((*it).second.attributes & DontEnum) ||
363                     mode == PropertyMap::IncludeDontEnumProperties) {
364                 propertyNames.add(Identifier::from(it->first));
365             }
366     }
367 
368     if (mode == PropertyMap::IncludeDontEnumProperties) {
369         propertyNames.add(exec->propertyNames().length);
370     }
371 
372     JSObject::getOwnPropertyNames(exec, propertyNames, mode);
373 }
374 
getOwnPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & descriptor)375 bool ArrayInstance::getOwnPropertyDescriptor(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &descriptor)
376 {
377     if (propertyName == exec->propertyNames().length) {
378         descriptor.setPropertyDescriptorValues(exec, jsNumber(m_length), m_lengthAttributes);
379         return true;
380     }
381 
382     bool isArrayIndex;
383     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
384 
385     if (isArrayIndex) {
386         ArrayEntity *ent = getArrayEntity(i);
387         if (ent) {
388             descriptor.setPropertyDescriptorValues(exec, ent->value, ent->attributes);
389             return true;
390         }
391     }
392     return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor);
393 }
394 
395 //ECMAScript Edition 5.1r6 - 15.4.5.1
defineOwnProperty(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & desc,bool shouldThrow)396 bool ArrayInstance::defineOwnProperty(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc, bool shouldThrow)
397 {
398     PropertyDescriptor oldLenDesc;
399     getOwnPropertyDescriptor(exec, exec->propertyNames().length, oldLenDesc);
400     unsigned int oldLen = JSValue::toUInt32(oldLenDesc.value(), exec);
401 
402     //4a
403     bool isArrayIndex;
404     unsigned index = propertyName.toArrayIndex(&isArrayIndex);
405 
406     //Step 3
407     if (propertyName == exec->propertyNames().length) {
408         //a
409         if (!desc.value()) {
410             return JSObject::defineOwnProperty(exec, propertyName, desc, shouldThrow);
411         }
412 
413         //b
414         PropertyDescriptor newLenDesc(desc);
415         //c
416         unsigned int newLen = JSValue::toUInt32(desc.value(), exec);
417         //d
418         if (newLen != JSValue::toNumber(desc.value(), exec) || JSValue::toNumber(desc.value(), exec) > maxArrayLength) {
419             throwError(exec, RangeError, "Index out of range");
420             return false;
421         }
422         //e
423         newLenDesc.setValue(jsNumber(newLen));
424         //f
425         if (newLen >= oldLen) {
426             return JSObject::defineOwnProperty(exec, propertyName, newLenDesc, shouldThrow);
427         }
428         //g
429         if (!oldLenDesc.writable()) {
430             if (shouldThrow) {
431                 throwError(exec, TypeError, "length is not writable");
432             }
433             return false;
434         }
435         //h
436         bool newWriteable;
437         if (!newLenDesc.writableSet() || newLenDesc.writable()) {
438             newWriteable = true;
439         } else { //i
440             if (anyItemHasAttribute(DontDelete)) {
441                 newLenDesc.setWritable(false);
442             } else {
443                 newLenDesc.setWritable(true);
444             }
445             newWriteable = false;
446         }
447         //j
448         bool succeeded = JSObject::defineOwnProperty(exec, propertyName, newLenDesc, shouldThrow);
449         //k
450         if (!succeeded) {
451             return false;
452         }
453         //l
454         while (newLen < oldLen) {
455             --oldLen;
456             bool deleteSucceeded = deleteProperty(exec, oldLen); // 3. argument should be false
457             if (!deleteSucceeded) {
458                 newLenDesc.setValue(jsNumber(oldLen + 1));
459                 if (!newWriteable) {
460                     newLenDesc.setWritable(false);
461                 }
462                 JSObject::defineOwnProperty(exec, propertyName, newLenDesc, false);
463                 if (shouldThrow) {
464                     throwError(exec, TypeError);
465                 }
466                 return false;
467             }
468         }
469         //m
470         if (!newWriteable) {
471             PropertyDescriptor writableDesc;
472             writableDesc.setWritable(false);
473             return JSObject::defineOwnProperty(exec, propertyName, writableDesc, false);
474         }
475         return true;
476     } else if (isArrayIndex) {//Step 4
477         //b
478         if (index >= oldLen && !oldLenDesc.writable()) {
479             if (shouldThrow) {
480                 throwError(exec, TypeError);
481             }
482             return false;
483         }
484         //c
485         bool succeeded = JSObject::defineOwnProperty(exec, propertyName, desc, false);
486         //d
487         if (!succeeded) {
488             if (shouldThrow) {
489                 throwError(exec, TypeError);
490             }
491             return false;
492         }
493         //e
494         if (index >= oldLen) {
495             oldLenDesc.setValue(jsNumber(index + 1));
496             JSObject::defineOwnProperty(exec, exec->propertyNames().length, oldLenDesc, false);
497         }
498         return true;
499     }
500 
501     return JSObject::defineOwnProperty(exec, propertyName, desc, shouldThrow);
502 }
503 
getPropertyAttributes(const Identifier & propertyName,unsigned int & attributes) const504 bool ArrayInstance::getPropertyAttributes(const Identifier &propertyName, unsigned int &attributes) const
505 {
506     bool isArrayIndex;
507     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
508 
509     if (isArrayIndex) {
510         ArrayEntity *ent = getArrayEntity(i);
511         if (ent) {
512             attributes = ent->attributes;
513             return true;
514         }
515     }
516 
517     return KJS::JSObject::getPropertyAttributes(propertyName, attributes);
518 }
519 
getDirect(const Identifier & propertyName) const520 JSValue *ArrayInstance::getDirect(const Identifier &propertyName) const
521 {
522     bool isArrayIndex;
523     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
524 
525     if (isArrayIndex) {
526         ArrayEntity *ent = getArrayEntity(i);
527         if (ent) {
528             if (ent->value) {
529                 return ent->value;
530             }
531         }
532     }
533 
534     return KJS::JSObject::getDirect(propertyName);
535 }
536 
putDirect(unsigned i,JSValue * value,int attributes)537 void ArrayInstance::putDirect(unsigned i, JSValue *value, int attributes)
538 {
539     unsigned length = m_length;
540 
541     if (i >= length) {
542         if (i > maxArrayIndex) {
543             KJS::JSObject::putDirect(Identifier::from(i), value, attributes);
544             return;
545         }
546         length = i + 1;
547         m_length = length;
548     }
549 
550     ArrayStorage *storage = m_storage;
551 
552     if (i < m_vectorLength) {
553         ArrayEntity *ent = &storage->m_vector[i];
554         if (!ent->value && !isExtensible()) {
555             return;
556         }
557         JSValue *&valueSlot = ent->value;
558         storage->m_numValuesInVector += !valueSlot;
559         valueSlot = value;
560         ent->attributes = attributes;
561         return;
562     }
563 
564     if (!isExtensible()) {
565         return;
566     }
567 
568     SparseArrayValueMap *map = storage->m_sparseValueMap;
569     if (i >= sparseArrayCutoff) {
570         // If the index is high, go to the map unless we're pretty dense.
571         if (!map) {
572             map = new SparseArrayValueMap;
573             storage->m_sparseValueMap = map;
574 
575             // If we create a sparse map, we need to ensure that there is at least one spot
576             // in the vector map, however, since the sparse map can't put/get key 0.
577             // It's safe to do it here, since put(0) will always put it in the vector part,
578             // but we have to do it before a get(0) or it will crash
579             if (!m_vectorLength) {
580                 increaseVectorLength(1);
581             }
582         }
583 
584         map->set(i, ArrayEntity(value, attributes));
585         return;
586     }
587 
588     // note: an invariant here is that indices < sparseArrayCutoff
589     // are always inside the vector portion.
590 
591     // lowish indices or high density -> we have decided that we'll put the new item into the vector.
592     // Fast case is when there is no sparse map, so we can increase the vector size without moving values from the sparse map.
593     if (!map || map->isEmpty()) {
594         increaseVectorLength(i + 1);
595         storage = m_storage;
596         ++storage->m_numValuesInVector;
597         storage->m_vector[i].value = value;
598         storage->m_vector[i].attributes = attributes;
599         return;
600     }
601 
602     // Decide just how large we want to make the vector to be.
603     unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
604     unsigned newVectorLength = increasedVectorLength(i + 1);
605 
606     // First, count how much stuff we are guaranteed to move over, now
607     // that we've decided to at least include i in the vector.
608     for (unsigned j = max(m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) {
609         newNumValuesInVector += map->contains(j);
610     }
611     if (i >= sparseArrayCutoff) {
612         newNumValuesInVector -= map->contains(i);
613     }
614 
615     // Continue increasing the vector portion as long as the things in the map are dense enough
616     if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
617         unsigned proposedNewNumValuesInVector = newNumValuesInVector;
618         while (true) {
619             unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
620             for (unsigned j = max(newVectorLength, sparseArrayCutoff); j < proposedNewVectorLength; ++j) {
621                 proposedNewNumValuesInVector += map->contains(j);
622             }
623             if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) {
624                 break;
625             }
626             newVectorLength = proposedNewVectorLength;
627             newNumValuesInVector = proposedNewNumValuesInVector;
628         }
629     }
630 
631     storage = static_cast<ArrayStorage *>(fastRealloc(storage, storageSize(newVectorLength)));
632 
633     unsigned vectorLength = m_vectorLength;
634 
635     // Special case: if we just added a single value, we don't have to scan the map
636     // to see what to remove from it
637     if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
638         for (unsigned j = vectorLength; j < newVectorLength; ++j) {
639             storage->m_vector[j].value = nullptr;
640         }
641         if (i > sparseArrayCutoff) {
642             map->remove(i);
643         }
644     } else {
645         // Move over things from the map to the new array portion
646         for (unsigned j = vectorLength; j < max(vectorLength, sparseArrayCutoff); ++j) {
647             storage->m_vector[j].value = nullptr;
648         }
649         for (unsigned j = max(vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) {
650             storage->m_vector[j] = map->take(j);
651         }
652     }
653 
654     storage->m_vector[i].value = value;
655     storage->m_vector[i].attributes = attributes;
656 
657     m_vectorLength = newVectorLength;
658     storage->m_numValuesInVector = newNumValuesInVector;
659 
660     m_storage = storage;
661 }
662 
putDirect(const Identifier & propertyName,JSValue * value,int attr)663 void ArrayInstance::putDirect(const Identifier &propertyName, JSValue *value, int attr)
664 {
665     bool isArrayIndex;
666     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
667 
668     if (isArrayIndex) {
669         KJS::ArrayInstance::putDirect(i, value, attr);
670         return;
671     }
672 
673     KJS::JSObject::putDirect(propertyName, value, attr);
674 }
675 
putDirect(const Identifier & propertyName,int value,int attr)676 void ArrayInstance::putDirect(const Identifier &propertyName, int value, int attr)
677 {
678     bool isArrayIndex;
679     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
680 
681     if (isArrayIndex) {
682         KJS::ArrayInstance::putDirect(i, jsNumber(value), attr);
683         return;
684     }
685 
686     KJS::JSObject::putDirect(propertyName, jsNumber(value), attr);
687 }
688 
removeDirect(const Identifier & propertyName)689 void ArrayInstance::removeDirect(const Identifier &propertyName)
690 {
691     bool isArrayIndex;
692     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
693 
694     if (isArrayIndex) {
695         ArrayStorage *storage = m_storage;
696 
697         if (i < m_vectorLength) {
698             ArrayEntity *ent = &storage->m_vector[i];
699             if (ent->value) {
700                 JSValue *&valueSlot = ent->value;
701                 bool hadValue = valueSlot;
702                 valueSlot = nullptr;
703                 storage->m_numValuesInVector -= hadValue;
704                 ent->value = nullptr;
705                 return;
706             }
707         }
708 
709         if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
710             SparseArrayValueMap::iterator it = map->find(i);
711             if (it != map->end()) {
712                 map->remove(it->first);
713                 return;
714             }
715         }
716     }
717 
718     JSObject::removeDirect(Identifier::from(i));
719 }
720 
increaseVectorLength(unsigned newLength)721 void ArrayInstance::increaseVectorLength(unsigned newLength)
722 {
723     // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
724     // to the vector. Callers have to account for that, because they can do it more efficiently.
725 
726     ArrayStorage *storage = m_storage;
727 
728     unsigned vectorLength = m_vectorLength;
729     ASSERT(newLength > vectorLength);
730     unsigned newVectorLength = increasedVectorLength(newLength);
731 
732     storage = static_cast<ArrayStorage *>(fastRealloc(storage, storageSize(newVectorLength)));
733     m_vectorLength = newVectorLength;
734 
735     for (unsigned i = vectorLength; i < newVectorLength; ++i) {
736         storage->m_vector[i].value = nullptr;
737     }
738 
739     m_storage = storage;
740 }
741 
setLength(unsigned newLength)742 void ArrayInstance::setLength(unsigned newLength)
743 {
744     ArrayStorage *storage = m_storage;
745 
746     unsigned length = m_length;
747     unsigned newLenSet = newLength;
748 
749     if (newLength < length) {
750         unsigned usedVectorLength = min(length, m_vectorLength);
751         if (usedVectorLength > 0) {
752             for (unsigned i = usedVectorLength - 1; i >= newLength && i > 0; --i) {
753                 ArrayEntity *ent = &storage->m_vector[i];
754                 if (ent->value) {
755                     if (ent->attributes & DontDelete) {
756                         newLenSet = i + 1;
757                         break;
758                     }
759                     JSValue *&valueSlot = ent->value;
760                     bool hadValue = valueSlot;
761                     valueSlot = nullptr;
762                     ent->value = nullptr;
763                     storage->m_numValuesInVector -= hadValue;
764                 }
765             }
766         }
767 
768         if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
769             SparseArrayValueMap copy = *map;
770             SparseArrayValueMap::iterator end = copy.end();
771             for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
772                 if (it->first >= newLength) {
773                     if (it->second.attributes & DontDelete) {
774                         newLenSet = it->first + 1;
775                     }
776                 }
777             }
778 
779             for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
780                 if (it->first >= newLenSet) {
781                     map->remove(it->first);
782                 }
783             }
784 
785             if (map->isEmpty()) {
786                 delete map;
787                 storage->m_sparseValueMap = nullptr;
788             }
789         }
790     }
791 
792     m_length = newLenSet;
793 }
794 
mark()795 void ArrayInstance::mark()
796 {
797     JSObject::mark();
798 
799     ArrayStorage *storage = m_storage;
800 
801     unsigned usedVectorLength = min(m_length, m_vectorLength);
802     for (unsigned i = 0; i < usedVectorLength; ++i) {
803         ArrayEntity *ent = &storage->m_vector[i];
804         if (ent->value && !JSValue::marked(ent->value)) {
805             JSValue::mark(ent->value);
806         }
807     }
808 
809     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
810         SparseArrayValueMap::iterator end = map->end();
811         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
812             ArrayEntity *ent = &it->second;
813             if (!JSValue::marked(ent->value)) {
814                 JSValue::mark(ent->value);
815             }
816         }
817     }
818 }
819 
820 static ExecState *execForCompareByStringForQSort;
821 
compareByStringForQSort(const void * a,const void * b)822 static int compareByStringForQSort(const void *a, const void *b)
823 {
824     ExecState *exec = execForCompareByStringForQSort;
825 
826     const ArrayEntity *va = static_cast<const ArrayEntity *>(a);
827     const ArrayEntity *vb = static_cast<const ArrayEntity *>(b);
828 
829     ASSERT(va->value && !JSValue::isUndefined(va->value));
830     ASSERT(vb->value && !JSValue::isUndefined(vb->value));
831 
832     return compare(JSValue::toString(va->value, exec), JSValue::toString(vb->value, exec));
833 }
834 
sort(ExecState * exec)835 void ArrayInstance::sort(ExecState *exec)
836 {
837     unsigned lengthNotIncludingUndefined = compactForSorting();
838 
839     ExecState *oldExec = execForCompareByStringForQSort;
840     execForCompareByStringForQSort = exec;
841 
842 #if HAVE(MERGESORT)
843     // Because mergesort usually does fewer compares, it is faster than qsort here.
844     // However, because it requires extra copies of the storage buffer, don't use it for very
845     // large arrays.
846 
847     // FIXME: Since we sort by string value, a fast algorithm might be to convert all the
848     // values to string once up front, and then use a radix sort. That would be O(N) rather
849     // than O(N log N).
850 
851     if (lengthNotIncludingUndefined < mergeSortCutoff) {
852         // During the sort, we could do a garbage collect, and it's important to still
853         // have references to every object in the array for ArrayInstance::mark.
854         // The mergesort algorithm does not guarantee this, so we sort a copy rather
855         // than the original.
856         size_t size = storageSize(m_vectorLength);
857         ArrayStorage *copy = static_cast<ArrayStorage *>(fastMalloc(size));
858         memcpy(copy, m_storage, size);
859         mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareByStringForQSort);
860         fastFree(m_storage);
861         m_storage = copy;
862         execForCompareByStringForQSort = oldExec;
863         return;
864     }
865 #endif
866 
867     qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareByStringForQSort);
868     execForCompareByStringForQSort = oldExec;
869 }
870 
871 struct CompareWithCompareFunctionArguments {
CompareWithCompareFunctionArgumentsKJS::CompareWithCompareFunctionArguments872     CompareWithCompareFunctionArguments(ExecState *e, JSObject *cf)
873         : exec(e)
874         , compareFunction(cf)
875         , globalObject(e->dynamicInterpreter()->globalObject())
876     {
877     }
878 
879     ExecState *exec;
880     JSObject *compareFunction;
881     List arguments;
882     JSObject *globalObject;
883 };
884 
885 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments;
886 
compareWithCompareFunctionForQSort(const void * a,const void * b)887 static int compareWithCompareFunctionForQSort(const void *a, const void *b)
888 {
889     CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
890 
891     const ArrayEntity *va = static_cast<const ArrayEntity *>(a);
892     const ArrayEntity *vb = static_cast<const ArrayEntity *>(b);
893 
894     ASSERT(va->value && !JSValue::isUndefined(va->value));
895     ASSERT(vb->value && !JSValue::isUndefined(vb->value));
896 
897     args->arguments.clear();
898     args->arguments.append(va->value);
899     args->arguments.append(vb->value);
900     double compareResult = JSValue::toNumber(args->compareFunction->call(args->exec, args->globalObject, args->arguments), args->exec);
901     return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;
902 }
903 
sort(ExecState * exec,JSObject * compareFunction)904 void ArrayInstance::sort(ExecState *exec, JSObject *compareFunction)
905 {
906     size_t lengthNotIncludingUndefined = compactForSorting();
907 
908     CompareWithCompareFunctionArguments *oldArgs = compareWithCompareFunctionArguments;
909     CompareWithCompareFunctionArguments args(exec, compareFunction);
910     compareWithCompareFunctionArguments = &args;
911 
912 #if HAVE(MERGESORT)
913     // Because mergesort usually does fewer compares, it is faster than qsort here.
914     // However, because it requires extra copies of the storage buffer, don't use it for very
915     // large arrays.
916 
917     // FIXME: A tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even
918     // better job of minimizing compares.
919 
920     if (lengthNotIncludingUndefined < mergeSortCutoff) {
921         // During the sort, we could do a garbage collect, and it's important to still
922         // have references to every object in the array for ArrayInstance::mark.
923         // The mergesort algorithm does not guarantee this, so we sort a copy rather
924         // than the original.
925         size_t size = storageSize(m_vectorLength);
926         ArrayStorage *copy = static_cast<ArrayStorage *>(fastMalloc(size));
927         memcpy(copy, m_storage, size);
928         mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareWithCompareFunctionForQSort);
929         fastFree(m_storage);
930         m_storage = copy;
931         compareWithCompareFunctionArguments = oldArgs;
932         return;
933     }
934 #endif
935 
936     qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareWithCompareFunctionForQSort);
937     compareWithCompareFunctionArguments = oldArgs;
938 }
939 
compactForSorting()940 unsigned ArrayInstance::compactForSorting()
941 {
942     ArrayStorage *storage = m_storage;
943 
944     unsigned usedVectorLength = min(m_length, m_vectorLength);
945 
946     unsigned numDefined = 0;
947     unsigned numUndefined = 0;
948 
949     // This compacts normal values (e.g. not undefined) in a contiguous run
950     // at the beginning of the array, and then puts any set undefined values
951     // at the end
952 
953     // count the first contiguous run of defined values in the vector store
954     for (; numDefined < usedVectorLength; ++numDefined) {
955         ArrayEntity *v = &storage->m_vector[numDefined];
956         if (!v->value || JSValue::isUndefined(v->value)) {
957             break;
958         }
959     }
960 
961     // compact the rest, counting along the way
962     for (unsigned i = numDefined; i < usedVectorLength; ++i) {
963         ArrayEntity v = storage->m_vector[i];
964         if (!v.value || JSValue::isUndefined(v.value)) {
965             ++numUndefined;
966         } else {
967             storage->m_vector[numDefined++] = storage->m_vector[i];
968         }
969     }
970 
971     unsigned newUsedVectorLength = numDefined + numUndefined;
972 
973     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
974         newUsedVectorLength += map->size();
975         if (newUsedVectorLength > m_vectorLength) {
976             increaseVectorLength(newUsedVectorLength);
977             storage = m_storage;
978         }
979 
980         SparseArrayValueMap::iterator end = map->end();
981         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
982             storage->m_vector[numDefined++] = it->second;
983         }
984 
985         delete map;
986         storage->m_sparseValueMap = nullptr;
987     }
988 
989     for (unsigned i = numDefined; i < newUsedVectorLength; ++i) {
990         storage->m_vector[i].value = nullptr;
991     }
992     for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) {
993         storage->m_vector[i].value = nullptr;
994     }
995 
996     return numDefined;
997 }
998 
anyItemHasAttribute(unsigned int attributes) const999 bool KJS::ArrayInstance::anyItemHasAttribute(unsigned int attributes) const
1000 {
1001     ArrayStorage *storage = m_storage;
1002 
1003     unsigned usedVectorLength = min(m_length, m_vectorLength);
1004     for (unsigned i = 0; i < usedVectorLength; ++i) {
1005         if (storage->m_vector[i].value && storage->m_vector[i].attributes & attributes) {
1006             return true;
1007         }
1008     }
1009 
1010     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
1011         SparseArrayValueMap::iterator end = map->end();
1012         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
1013             if ((*it).second.attributes & attributes) {
1014                 return true;
1015             }
1016     }
1017 
1018     return false;
1019 }
1020 
1021 }
1022