File indexing completed on 2024-05-12 15:43:14

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
0004  *  Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved.
0005  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
0006  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
0007  *
0008  *  This library is free software; you can redistribute it and/or
0009  *  modify it under the terms of the GNU Lesser General Public
0010  *  License as published by the Free Software Foundation; either
0011  *  version 2 of the License, or (at your option) any later version.
0012  *
0013  *  This library is distributed in the hope that it will be useful,
0014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  *  Lesser General Public License for more details.
0017  *
0018  *  You should have received a copy of the GNU Lesser General Public
0019  *  License along with this library; if not, write to the Free Software
0020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0021  *
0022  */
0023 
0024 #include "array_instance.h"
0025 
0026 #include "PropertyNameArray.h"
0027 #include "JSVariableObject.h"
0028 #include <wtf/Assertions.h>
0029 #include <wtf/HashMap.h>
0030 
0031 #include <algorithm>
0032 #include <stdio.h>
0033 
0034 using std::min;
0035 using std::max;
0036 
0037 namespace KJS
0038 {
0039 
0040 struct ArrayEntity {
0041     ArrayEntity()
0042         : value(nullptr),
0043           attributes(0)
0044     {
0045     }
0046 
0047     ArrayEntity(JSValue *jsVal, uint32_t attr)
0048         : value(jsVal),
0049           attributes(attr)
0050     {
0051     }
0052 
0053     JSValue *value;
0054     uint32_t attributes;
0055 };
0056 
0057 typedef HashMap<unsigned, ArrayEntity> SparseArrayValueMap;
0058 
0059 struct ArrayStorage {
0060     unsigned m_numValuesInVector;
0061     SparseArrayValueMap *m_sparseValueMap;
0062     ArrayEntity m_vector[1];
0063 };
0064 
0065 // (2^32)-1
0066 static const unsigned maxArrayLength = 0xFFFFFFFFU;
0067 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer
0068 static const unsigned maxArrayIndex = 0xFFFFFFFEU;
0069 
0070 // Our policy for when to use a vector and when to use a sparse map.
0071 // For all array indices under sparseArrayCutoff, we always use a vector.
0072 // When indices greater than sparseArrayCutoff are involved, we use a vector
0073 // as long as it is 1/8 full. If more sparse than that, we use a map.
0074 static const unsigned sparseArrayCutoff = 10000;
0075 static const unsigned minDensityMultiplier = 8;
0076 
0077 static const unsigned mergeSortCutoff = 10000;
0078 
0079 const ClassInfo ArrayInstance::info = {"Array", nullptr, nullptr, nullptr};
0080 
0081 static inline size_t storageSize(unsigned vectorLength)
0082 {
0083     return sizeof(ArrayStorage) - sizeof(ArrayEntity) + vectorLength * sizeof(ArrayEntity);
0084 }
0085 
0086 static inline unsigned increasedVectorLength(unsigned newLength)
0087 {
0088     return (newLength * 3 + 1) / 2;
0089 }
0090 
0091 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
0092 {
0093     return length / minDensityMultiplier <= numValues;
0094 }
0095 
0096 ArrayInstance::ArrayInstance(JSObject *prototype, unsigned initialLength)
0097     : JSObject(prototype)
0098 {
0099     unsigned initialCapacity = min(initialLength, sparseArrayCutoff);
0100 
0101     m_length = initialLength;
0102     m_vectorLength = initialCapacity;
0103     m_storage = static_cast<ArrayStorage *>(fastCalloc(storageSize(initialCapacity), 1));
0104     m_lengthAttributes = DontDelete | DontEnum;
0105 
0106     Collector::reportExtraMemoryCost(initialCapacity * sizeof(ArrayEntity));
0107 }
0108 
0109 ArrayInstance::ArrayInstance(JSObject *prototype, const List &list)
0110     : JSObject(prototype)
0111 {
0112     unsigned length = list.size();
0113 
0114     m_length = length;
0115     m_vectorLength = length;
0116     m_lengthAttributes = DontDelete | DontEnum;
0117 
0118     ArrayStorage *storage = static_cast<ArrayStorage *>(fastMalloc(storageSize(length)));
0119 
0120     storage->m_numValuesInVector = length;
0121     storage->m_sparseValueMap = nullptr;
0122 
0123     ListIterator it = list.begin();
0124     for (unsigned i = 0; i < length; ++i) {
0125         storage->m_vector[i].value = it++;
0126         storage->m_vector[i].attributes = 0;
0127     }
0128 
0129     m_storage = storage;
0130 
0131     // When the array is created non-empty, its cells are filled, so it's really no worse than
0132     // a property map. Therefore don't report extra memory cost.
0133 }
0134 
0135 ArrayInstance::~ArrayInstance()
0136 {
0137     delete m_storage->m_sparseValueMap;
0138     fastFree(m_storage);
0139 }
0140 
0141 JSValue *ArrayInstance::getItem(unsigned i) const
0142 {
0143     ASSERT(i <= maxArrayIndex);
0144 
0145     ArrayEntity *ent = getArrayEntity(i);
0146     if (ent) {
0147         return ent->value;
0148     }
0149     return jsUndefined();
0150 }
0151 
0152 JSValue *ArrayInstance::lengthGetter(ExecState *, JSObject *, const Identifier &, const PropertySlot &slot)
0153 {
0154     return jsNumber(static_cast<ArrayInstance *>(slot.slotBase())->m_length);
0155 }
0156 
0157 ALWAYS_INLINE bool ArrayInstance::inlineGetOwnPropertySlot(ExecState *exec, unsigned i, PropertySlot &slot)
0158 {
0159     if (i >= m_length) {
0160         if (i > maxArrayIndex) {
0161             return getOwnPropertySlot(exec, Identifier::from(i), slot);
0162         }
0163         return false;
0164     }
0165 
0166     ArrayEntity *ent = getArrayEntity(i);
0167     if (ent) {
0168         if (ent->attributes & GetterSetter) {
0169             GetterSetterImp *gs = static_cast<GetterSetterImp *>(ent->value);
0170             JSObject *getterFunc = gs->getGetter();
0171             if (getterFunc) {
0172                 slot.setGetterSlot(this, getterFunc);
0173             } else {
0174                 slot.setUndefined(this);
0175             }
0176             return true;
0177         }
0178         slot.setValueSlot(this, &ent->value);
0179         return true;
0180     }
0181 
0182     return false;
0183 }
0184 
0185 ArrayEntity *ArrayInstance::getArrayEntity(unsigned int i) const
0186 {
0187     if (i >= m_length) {
0188         return nullptr;
0189     }
0190 
0191     ArrayStorage *storage = m_storage;
0192     if (i < m_vectorLength) {
0193         if (storage->m_vector[i].value) {
0194             return &storage->m_vector[i];
0195         }
0196     }
0197 
0198     SparseArrayValueMap *map = storage->m_sparseValueMap;
0199     if (map && i > 0 && i <= maxArrayIndex) {
0200         SparseArrayValueMap::iterator it = map->find(i);
0201         if (it != map->end()) {
0202             return &it->second;
0203         }
0204     }
0205 
0206     return nullptr;
0207 }
0208 
0209 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0210 {
0211     if (propertyName == exec->propertyNames().length) {
0212         slot.setCustom(this, lengthGetter);
0213         return true;
0214     }
0215 
0216     bool isArrayIndex;
0217     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0218     if (isArrayIndex) {
0219         return inlineGetOwnPropertySlot(exec, i, slot);
0220     }
0221 
0222     return JSObject::getOwnPropertySlot(exec, propertyName, slot);
0223 }
0224 
0225 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, unsigned i, PropertySlot &slot)
0226 {
0227     return inlineGetOwnPropertySlot(exec, i, slot);
0228 }
0229 
0230 // ECMA 15.4.5.1
0231 void ArrayInstance::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attributes)
0232 {
0233     bool isArrayIndex;
0234     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0235     if (isArrayIndex) {
0236         put(exec, i, value, attributes);
0237         return;
0238     }
0239 
0240     if (propertyName == exec->propertyNames().length) {
0241         if (m_lengthAttributes & ReadOnly) {
0242             return;
0243         }
0244         unsigned newLength = JSValue::toUInt32(value, exec);
0245         if (JSValue::toNumber(value, exec) != static_cast<double>(newLength)) {
0246             throwError(exec, RangeError, "Invalid array length.");
0247             return;
0248         }
0249         m_lengthAttributes = attributes;
0250         setLength(newLength);
0251         return;
0252     }
0253 
0254     JSObject::put(exec, propertyName, value, attributes);
0255 }
0256 
0257 void ArrayInstance::put(ExecState *exec, unsigned i, JSValue *value, int attributes)
0258 {
0259     ArrayEntity *ent = getArrayEntity(i);
0260     if (ent) {
0261         if (ent->attributes & ReadOnly) {
0262             return;
0263         }
0264         attributes |= ent->attributes;
0265 
0266         JSValue *gs = ent->value;
0267         if (gs && !JSValue::isUndefined(gs)) {
0268             if (ent->attributes & GetterSetter) {
0269                 JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
0270 
0271                 if (!setterFunc) {
0272                     if (false) { //if strict is true
0273                         throwError(exec, TypeError, "setting a property that has only a getter");
0274                     }
0275                     return;
0276                 }
0277 
0278                 List args;
0279                 args.append(value);
0280 
0281                 setterFunc->call(exec, this, args);
0282                 return;
0283             }
0284         }
0285     }
0286 
0287     KJS::ArrayInstance::putDirect(i, value, attributes);
0288 }
0289 
0290 bool ArrayInstance::deleteProperty(ExecState *exec, const Identifier &propertyName)
0291 {
0292     bool isArrayIndex;
0293     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0294     if (isArrayIndex) {
0295         return deleteProperty(exec, i);
0296     }
0297 
0298     if (propertyName == exec->propertyNames().length) {
0299         return false;
0300     }
0301 
0302     return JSObject::deleteProperty(exec, propertyName);
0303 }
0304 
0305 bool ArrayInstance::deleteProperty(ExecState *exec, unsigned i)
0306 {
0307     ArrayStorage *storage = m_storage;
0308 
0309     if (i < m_vectorLength) {
0310         ArrayEntity *ent = &storage->m_vector[i];
0311         if (ent->value) {
0312             if (ent->attributes & DontDelete) {
0313                 return false;
0314             }
0315 
0316             JSValue *&valueSlot = ent->value;
0317             bool hadValue = valueSlot;
0318             valueSlot = nullptr;
0319             storage->m_numValuesInVector -= hadValue;
0320             ent->value = nullptr;
0321             return hadValue;
0322         }
0323     }
0324 
0325     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
0326         SparseArrayValueMap::iterator it = map->find(i);
0327         if (it != map->end()) {
0328             if ((*it).second.attributes & DontDelete) {
0329                 return false;
0330             }
0331             map->remove(it->first);
0332             return true;
0333         }
0334     }
0335 
0336     if (i > maxArrayIndex) {
0337         return JSObject::deleteProperty(exec, Identifier::from(i));
0338     }
0339 
0340     return true;
0341 }
0342 
0343 void ArrayInstance::getOwnPropertyNames(ExecState *exec, PropertyNameArray &propertyNames, PropertyMap::PropertyMode mode)
0344 {
0345     // FIXME: Filling PropertyNameArray with an identifier for every integer
0346     // is incredibly inefficient for large arrays. We need a different approach.
0347 
0348     ArrayStorage *storage = m_storage;
0349 
0350     unsigned usedVectorLength = min(m_length, m_vectorLength);
0351     for (unsigned i = 0; i < usedVectorLength; ++i) {
0352         if (storage->m_vector[i].value &&
0353                 (!(storage->m_vector[i].attributes & DontEnum) ||
0354                  mode == PropertyMap::IncludeDontEnumProperties)) {
0355             propertyNames.add(Identifier::from(i));
0356         }
0357     }
0358 
0359     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
0360         SparseArrayValueMap::iterator end = map->end();
0361         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
0362             if (!((*it).second.attributes & DontEnum) ||
0363                     mode == PropertyMap::IncludeDontEnumProperties) {
0364                 propertyNames.add(Identifier::from(it->first));
0365             }
0366     }
0367 
0368     if (mode == PropertyMap::IncludeDontEnumProperties) {
0369         propertyNames.add(exec->propertyNames().length);
0370     }
0371 
0372     JSObject::getOwnPropertyNames(exec, propertyNames, mode);
0373 }
0374 
0375 bool ArrayInstance::getOwnPropertyDescriptor(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &descriptor)
0376 {
0377     if (propertyName == exec->propertyNames().length) {
0378         descriptor.setPropertyDescriptorValues(exec, jsNumber(m_length), m_lengthAttributes);
0379         return true;
0380     }
0381 
0382     bool isArrayIndex;
0383     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0384 
0385     if (isArrayIndex) {
0386         ArrayEntity *ent = getArrayEntity(i);
0387         if (ent) {
0388             descriptor.setPropertyDescriptorValues(exec, ent->value, ent->attributes);
0389             return true;
0390         }
0391     }
0392     return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor);
0393 }
0394 
0395 //ECMAScript Edition 5.1r6 - 15.4.5.1
0396 bool ArrayInstance::defineOwnProperty(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc, bool shouldThrow)
0397 {
0398     PropertyDescriptor oldLenDesc;
0399     getOwnPropertyDescriptor(exec, exec->propertyNames().length, oldLenDesc);
0400     unsigned int oldLen = JSValue::toUInt32(oldLenDesc.value(), exec);
0401 
0402     //4a
0403     bool isArrayIndex;
0404     unsigned index = propertyName.toArrayIndex(&isArrayIndex);
0405 
0406     //Step 3
0407     if (propertyName == exec->propertyNames().length) {
0408         //a
0409         if (!desc.value()) {
0410             return JSObject::defineOwnProperty(exec, propertyName, desc, shouldThrow);
0411         }
0412 
0413         //b
0414         PropertyDescriptor newLenDesc(desc);
0415         //c
0416         unsigned int newLen = JSValue::toUInt32(desc.value(), exec);
0417         //d
0418         if (newLen != JSValue::toNumber(desc.value(), exec) || JSValue::toNumber(desc.value(), exec) > maxArrayLength) {
0419             throwError(exec, RangeError, "Index out of range");
0420             return false;
0421         }
0422         //e
0423         newLenDesc.setValue(jsNumber(newLen));
0424         //f
0425         if (newLen >= oldLen) {
0426             return JSObject::defineOwnProperty(exec, propertyName, newLenDesc, shouldThrow);
0427         }
0428         //g
0429         if (!oldLenDesc.writable()) {
0430             if (shouldThrow) {
0431                 throwError(exec, TypeError, "length is not writable");
0432             }
0433             return false;
0434         }
0435         //h
0436         bool newWriteable;
0437         if (!newLenDesc.writableSet() || newLenDesc.writable()) {
0438             newWriteable = true;
0439         } else { //i
0440             if (anyItemHasAttribute(DontDelete)) {
0441                 newLenDesc.setWritable(false);
0442             } else {
0443                 newLenDesc.setWritable(true);
0444             }
0445             newWriteable = false;
0446         }
0447         //j
0448         bool succeeded = JSObject::defineOwnProperty(exec, propertyName, newLenDesc, shouldThrow);
0449         //k
0450         if (!succeeded) {
0451             return false;
0452         }
0453         //l
0454         while (newLen < oldLen) {
0455             --oldLen;
0456             bool deleteSucceeded = deleteProperty(exec, oldLen); // 3. argument should be false
0457             if (!deleteSucceeded) {
0458                 newLenDesc.setValue(jsNumber(oldLen + 1));
0459                 if (!newWriteable) {
0460                     newLenDesc.setWritable(false);
0461                 }
0462                 JSObject::defineOwnProperty(exec, propertyName, newLenDesc, false);
0463                 if (shouldThrow) {
0464                     throwError(exec, TypeError);
0465                 }
0466                 return false;
0467             }
0468         }
0469         //m
0470         if (!newWriteable) {
0471             PropertyDescriptor writableDesc;
0472             writableDesc.setWritable(false);
0473             return JSObject::defineOwnProperty(exec, propertyName, writableDesc, false);
0474         }
0475         return true;
0476     } else if (isArrayIndex) {//Step 4
0477         //b
0478         if (index >= oldLen && !oldLenDesc.writable()) {
0479             if (shouldThrow) {
0480                 throwError(exec, TypeError);
0481             }
0482             return false;
0483         }
0484         //c
0485         bool succeeded = JSObject::defineOwnProperty(exec, propertyName, desc, false);
0486         //d
0487         if (!succeeded) {
0488             if (shouldThrow) {
0489                 throwError(exec, TypeError);
0490             }
0491             return false;
0492         }
0493         //e
0494         if (index >= oldLen) {
0495             oldLenDesc.setValue(jsNumber(index + 1));
0496             JSObject::defineOwnProperty(exec, exec->propertyNames().length, oldLenDesc, false);
0497         }
0498         return true;
0499     }
0500 
0501     return JSObject::defineOwnProperty(exec, propertyName, desc, shouldThrow);
0502 }
0503 
0504 bool ArrayInstance::getPropertyAttributes(const Identifier &propertyName, unsigned int &attributes) const
0505 {
0506     bool isArrayIndex;
0507     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0508 
0509     if (isArrayIndex) {
0510         ArrayEntity *ent = getArrayEntity(i);
0511         if (ent) {
0512             attributes = ent->attributes;
0513             return true;
0514         }
0515     }
0516 
0517     return KJS::JSObject::getPropertyAttributes(propertyName, attributes);
0518 }
0519 
0520 JSValue *ArrayInstance::getDirect(const Identifier &propertyName) const
0521 {
0522     bool isArrayIndex;
0523     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0524 
0525     if (isArrayIndex) {
0526         ArrayEntity *ent = getArrayEntity(i);
0527         if (ent) {
0528             if (ent->value) {
0529                 return ent->value;
0530             }
0531         }
0532     }
0533 
0534     return KJS::JSObject::getDirect(propertyName);
0535 }
0536 
0537 void ArrayInstance::putDirect(unsigned i, JSValue *value, int attributes)
0538 {
0539     unsigned length = m_length;
0540 
0541     if (i >= length) {
0542         if (i > maxArrayIndex) {
0543             KJS::JSObject::putDirect(Identifier::from(i), value, attributes);
0544             return;
0545         }
0546         length = i + 1;
0547         m_length = length;
0548     }
0549 
0550     ArrayStorage *storage = m_storage;
0551 
0552     if (i < m_vectorLength) {
0553         ArrayEntity *ent = &storage->m_vector[i];
0554         if (!ent->value && !isExtensible()) {
0555             return;
0556         }
0557         JSValue *&valueSlot = ent->value;
0558         storage->m_numValuesInVector += !valueSlot;
0559         valueSlot = value;
0560         ent->attributes = attributes;
0561         return;
0562     }
0563 
0564     if (!isExtensible()) {
0565         return;
0566     }
0567 
0568     SparseArrayValueMap *map = storage->m_sparseValueMap;
0569     if (i >= sparseArrayCutoff) {
0570         // If the index is high, go to the map unless we're pretty dense.
0571         if (!map) {
0572             map = new SparseArrayValueMap;
0573             storage->m_sparseValueMap = map;
0574 
0575             // If we create a sparse map, we need to ensure that there is at least one spot
0576             // in the vector map, however, since the sparse map can't put/get key 0.
0577             // It's safe to do it here, since put(0) will always put it in the vector part,
0578             // but we have to do it before a get(0) or it will crash
0579             if (!m_vectorLength) {
0580                 increaseVectorLength(1);
0581             }
0582         }
0583 
0584         map->set(i, ArrayEntity(value, attributes));
0585         return;
0586     }
0587 
0588     // note: an invariant here is that indices < sparseArrayCutoff
0589     // are always inside the vector portion.
0590 
0591     // lowish indices or high density -> we have decided that we'll put the new item into the vector.
0592     // Fast case is when there is no sparse map, so we can increase the vector size without moving values from the sparse map.
0593     if (!map || map->isEmpty()) {
0594         increaseVectorLength(i + 1);
0595         storage = m_storage;
0596         ++storage->m_numValuesInVector;
0597         storage->m_vector[i].value = value;
0598         storage->m_vector[i].attributes = attributes;
0599         return;
0600     }
0601 
0602     // Decide just how large we want to make the vector to be.
0603     unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
0604     unsigned newVectorLength = increasedVectorLength(i + 1);
0605 
0606     // First, count how much stuff we are guaranteed to move over, now
0607     // that we've decided to at least include i in the vector.
0608     for (unsigned j = max(m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) {
0609         newNumValuesInVector += map->contains(j);
0610     }
0611     if (i >= sparseArrayCutoff) {
0612         newNumValuesInVector -= map->contains(i);
0613     }
0614 
0615     // Continue increasing the vector portion as long as the things in the map are dense enough
0616     if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
0617         unsigned proposedNewNumValuesInVector = newNumValuesInVector;
0618         while (true) {
0619             unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
0620             for (unsigned j = max(newVectorLength, sparseArrayCutoff); j < proposedNewVectorLength; ++j) {
0621                 proposedNewNumValuesInVector += map->contains(j);
0622             }
0623             if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) {
0624                 break;
0625             }
0626             newVectorLength = proposedNewVectorLength;
0627             newNumValuesInVector = proposedNewNumValuesInVector;
0628         }
0629     }
0630 
0631     storage = static_cast<ArrayStorage *>(fastRealloc(storage, storageSize(newVectorLength)));
0632 
0633     unsigned vectorLength = m_vectorLength;
0634 
0635     // Special case: if we just added a single value, we don't have to scan the map
0636     // to see what to remove from it
0637     if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
0638         for (unsigned j = vectorLength; j < newVectorLength; ++j) {
0639             storage->m_vector[j].value = nullptr;
0640         }
0641         if (i > sparseArrayCutoff) {
0642             map->remove(i);
0643         }
0644     } else {
0645         // Move over things from the map to the new array portion
0646         for (unsigned j = vectorLength; j < max(vectorLength, sparseArrayCutoff); ++j) {
0647             storage->m_vector[j].value = nullptr;
0648         }
0649         for (unsigned j = max(vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) {
0650             storage->m_vector[j] = map->take(j);
0651         }
0652     }
0653 
0654     storage->m_vector[i].value = value;
0655     storage->m_vector[i].attributes = attributes;
0656 
0657     m_vectorLength = newVectorLength;
0658     storage->m_numValuesInVector = newNumValuesInVector;
0659 
0660     m_storage = storage;
0661 }
0662 
0663 void ArrayInstance::putDirect(const Identifier &propertyName, JSValue *value, int attr)
0664 {
0665     bool isArrayIndex;
0666     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0667 
0668     if (isArrayIndex) {
0669         KJS::ArrayInstance::putDirect(i, value, attr);
0670         return;
0671     }
0672 
0673     KJS::JSObject::putDirect(propertyName, value, attr);
0674 }
0675 
0676 void ArrayInstance::putDirect(const Identifier &propertyName, int value, int attr)
0677 {
0678     bool isArrayIndex;
0679     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0680 
0681     if (isArrayIndex) {
0682         KJS::ArrayInstance::putDirect(i, jsNumber(value), attr);
0683         return;
0684     }
0685 
0686     KJS::JSObject::putDirect(propertyName, jsNumber(value), attr);
0687 }
0688 
0689 void ArrayInstance::removeDirect(const Identifier &propertyName)
0690 {
0691     bool isArrayIndex;
0692     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
0693 
0694     if (isArrayIndex) {
0695         ArrayStorage *storage = m_storage;
0696 
0697         if (i < m_vectorLength) {
0698             ArrayEntity *ent = &storage->m_vector[i];
0699             if (ent->value) {
0700                 JSValue *&valueSlot = ent->value;
0701                 bool hadValue = valueSlot;
0702                 valueSlot = nullptr;
0703                 storage->m_numValuesInVector -= hadValue;
0704                 ent->value = nullptr;
0705                 return;
0706             }
0707         }
0708 
0709         if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
0710             SparseArrayValueMap::iterator it = map->find(i);
0711             if (it != map->end()) {
0712                 map->remove(it->first);
0713                 return;
0714             }
0715         }
0716     }
0717 
0718     JSObject::removeDirect(Identifier::from(i));
0719 }
0720 
0721 void ArrayInstance::increaseVectorLength(unsigned newLength)
0722 {
0723     // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
0724     // to the vector. Callers have to account for that, because they can do it more efficiently.
0725 
0726     ArrayStorage *storage = m_storage;
0727 
0728     unsigned vectorLength = m_vectorLength;
0729     ASSERT(newLength > vectorLength);
0730     unsigned newVectorLength = increasedVectorLength(newLength);
0731 
0732     storage = static_cast<ArrayStorage *>(fastRealloc(storage, storageSize(newVectorLength)));
0733     m_vectorLength = newVectorLength;
0734 
0735     for (unsigned i = vectorLength; i < newVectorLength; ++i) {
0736         storage->m_vector[i].value = nullptr;
0737     }
0738 
0739     m_storage = storage;
0740 }
0741 
0742 void ArrayInstance::setLength(unsigned newLength)
0743 {
0744     ArrayStorage *storage = m_storage;
0745 
0746     unsigned length = m_length;
0747     unsigned newLenSet = newLength;
0748 
0749     if (newLength < length) {
0750         unsigned usedVectorLength = min(length, m_vectorLength);
0751         if (usedVectorLength > 0) {
0752             for (unsigned i = usedVectorLength - 1; i >= newLength && i > 0; --i) {
0753                 ArrayEntity *ent = &storage->m_vector[i];
0754                 if (ent->value) {
0755                     if (ent->attributes & DontDelete) {
0756                         newLenSet = i + 1;
0757                         break;
0758                     }
0759                     JSValue *&valueSlot = ent->value;
0760                     bool hadValue = valueSlot;
0761                     valueSlot = nullptr;
0762                     ent->value = nullptr;
0763                     storage->m_numValuesInVector -= hadValue;
0764                 }
0765             }
0766         }
0767 
0768         if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
0769             SparseArrayValueMap copy = *map;
0770             SparseArrayValueMap::iterator end = copy.end();
0771             for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
0772                 if (it->first >= newLength) {
0773                     if (it->second.attributes & DontDelete) {
0774                         newLenSet = it->first + 1;
0775                     }
0776                 }
0777             }
0778 
0779             for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
0780                 if (it->first >= newLenSet) {
0781                     map->remove(it->first);
0782                 }
0783             }
0784 
0785             if (map->isEmpty()) {
0786                 delete map;
0787                 storage->m_sparseValueMap = nullptr;
0788             }
0789         }
0790     }
0791 
0792     m_length = newLenSet;
0793 }
0794 
0795 void ArrayInstance::mark()
0796 {
0797     JSObject::mark();
0798 
0799     ArrayStorage *storage = m_storage;
0800 
0801     unsigned usedVectorLength = min(m_length, m_vectorLength);
0802     for (unsigned i = 0; i < usedVectorLength; ++i) {
0803         ArrayEntity *ent = &storage->m_vector[i];
0804         if (ent->value && !JSValue::marked(ent->value)) {
0805             JSValue::mark(ent->value);
0806         }
0807     }
0808 
0809     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
0810         SparseArrayValueMap::iterator end = map->end();
0811         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
0812             ArrayEntity *ent = &it->second;
0813             if (!JSValue::marked(ent->value)) {
0814                 JSValue::mark(ent->value);
0815             }
0816         }
0817     }
0818 }
0819 
0820 static ExecState *execForCompareByStringForQSort;
0821 
0822 static int compareByStringForQSort(const void *a, const void *b)
0823 {
0824     ExecState *exec = execForCompareByStringForQSort;
0825 
0826     const ArrayEntity *va = static_cast<const ArrayEntity *>(a);
0827     const ArrayEntity *vb = static_cast<const ArrayEntity *>(b);
0828 
0829     ASSERT(va->value && !JSValue::isUndefined(va->value));
0830     ASSERT(vb->value && !JSValue::isUndefined(vb->value));
0831 
0832     return compare(JSValue::toString(va->value, exec), JSValue::toString(vb->value, exec));
0833 }
0834 
0835 void ArrayInstance::sort(ExecState *exec)
0836 {
0837     unsigned lengthNotIncludingUndefined = compactForSorting();
0838 
0839     ExecState *oldExec = execForCompareByStringForQSort;
0840     execForCompareByStringForQSort = exec;
0841 
0842 #if HAVE(MERGESORT)
0843     // Because mergesort usually does fewer compares, it is faster than qsort here.
0844     // However, because it requires extra copies of the storage buffer, don't use it for very
0845     // large arrays.
0846 
0847     // FIXME: Since we sort by string value, a fast algorithm might be to convert all the
0848     // values to string once up front, and then use a radix sort. That would be O(N) rather
0849     // than O(N log N).
0850 
0851     if (lengthNotIncludingUndefined < mergeSortCutoff) {
0852         // During the sort, we could do a garbage collect, and it's important to still
0853         // have references to every object in the array for ArrayInstance::mark.
0854         // The mergesort algorithm does not guarantee this, so we sort a copy rather
0855         // than the original.
0856         size_t size = storageSize(m_vectorLength);
0857         ArrayStorage *copy = static_cast<ArrayStorage *>(fastMalloc(size));
0858         memcpy(copy, m_storage, size);
0859         mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareByStringForQSort);
0860         fastFree(m_storage);
0861         m_storage = copy;
0862         execForCompareByStringForQSort = oldExec;
0863         return;
0864     }
0865 #endif
0866 
0867     qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareByStringForQSort);
0868     execForCompareByStringForQSort = oldExec;
0869 }
0870 
0871 struct CompareWithCompareFunctionArguments {
0872     CompareWithCompareFunctionArguments(ExecState *e, JSObject *cf)
0873         : exec(e)
0874         , compareFunction(cf)
0875         , globalObject(e->dynamicInterpreter()->globalObject())
0876     {
0877     }
0878 
0879     ExecState *exec;
0880     JSObject *compareFunction;
0881     List arguments;
0882     JSObject *globalObject;
0883 };
0884 
0885 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments;
0886 
0887 static int compareWithCompareFunctionForQSort(const void *a, const void *b)
0888 {
0889     CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
0890 
0891     const ArrayEntity *va = static_cast<const ArrayEntity *>(a);
0892     const ArrayEntity *vb = static_cast<const ArrayEntity *>(b);
0893 
0894     ASSERT(va->value && !JSValue::isUndefined(va->value));
0895     ASSERT(vb->value && !JSValue::isUndefined(vb->value));
0896 
0897     args->arguments.clear();
0898     args->arguments.append(va->value);
0899     args->arguments.append(vb->value);
0900     double compareResult = JSValue::toNumber(args->compareFunction->call(args->exec, args->globalObject, args->arguments), args->exec);
0901     return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;
0902 }
0903 
0904 void ArrayInstance::sort(ExecState *exec, JSObject *compareFunction)
0905 {
0906     size_t lengthNotIncludingUndefined = compactForSorting();
0907 
0908     CompareWithCompareFunctionArguments *oldArgs = compareWithCompareFunctionArguments;
0909     CompareWithCompareFunctionArguments args(exec, compareFunction);
0910     compareWithCompareFunctionArguments = &args;
0911 
0912 #if HAVE(MERGESORT)
0913     // Because mergesort usually does fewer compares, it is faster than qsort here.
0914     // However, because it requires extra copies of the storage buffer, don't use it for very
0915     // large arrays.
0916 
0917     // FIXME: A tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even
0918     // better job of minimizing compares.
0919 
0920     if (lengthNotIncludingUndefined < mergeSortCutoff) {
0921         // During the sort, we could do a garbage collect, and it's important to still
0922         // have references to every object in the array for ArrayInstance::mark.
0923         // The mergesort algorithm does not guarantee this, so we sort a copy rather
0924         // than the original.
0925         size_t size = storageSize(m_vectorLength);
0926         ArrayStorage *copy = static_cast<ArrayStorage *>(fastMalloc(size));
0927         memcpy(copy, m_storage, size);
0928         mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareWithCompareFunctionForQSort);
0929         fastFree(m_storage);
0930         m_storage = copy;
0931         compareWithCompareFunctionArguments = oldArgs;
0932         return;
0933     }
0934 #endif
0935 
0936     qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareWithCompareFunctionForQSort);
0937     compareWithCompareFunctionArguments = oldArgs;
0938 }
0939 
0940 unsigned ArrayInstance::compactForSorting()
0941 {
0942     ArrayStorage *storage = m_storage;
0943 
0944     unsigned usedVectorLength = min(m_length, m_vectorLength);
0945 
0946     unsigned numDefined = 0;
0947     unsigned numUndefined = 0;
0948 
0949     // This compacts normal values (e.g. not undefined) in a contiguous run
0950     // at the beginning of the array, and then puts any set undefined values
0951     // at the end
0952 
0953     // count the first contiguous run of defined values in the vector store
0954     for (; numDefined < usedVectorLength; ++numDefined) {
0955         ArrayEntity *v = &storage->m_vector[numDefined];
0956         if (!v->value || JSValue::isUndefined(v->value)) {
0957             break;
0958         }
0959     }
0960 
0961     // compact the rest, counting along the way
0962     for (unsigned i = numDefined; i < usedVectorLength; ++i) {
0963         ArrayEntity v = storage->m_vector[i];
0964         if (!v.value || JSValue::isUndefined(v.value)) {
0965             ++numUndefined;
0966         } else {
0967             storage->m_vector[numDefined++] = storage->m_vector[i];
0968         }
0969     }
0970 
0971     unsigned newUsedVectorLength = numDefined + numUndefined;
0972 
0973     if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
0974         newUsedVectorLength += map->size();
0975         if (newUsedVectorLength > m_vectorLength) {
0976             increaseVectorLength(newUsedVectorLength);
0977             storage = m_storage;
0978         }
0979 
0980         SparseArrayValueMap::iterator end = map->end();
0981         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
0982             storage->m_vector[numDefined++] = it->second;
0983         }
0984 
0985         delete map;
0986         storage->m_sparseValueMap = nullptr;
0987     }
0988 
0989     for (unsigned i = numDefined; i < newUsedVectorLength; ++i) {
0990         storage->m_vector[i].value = nullptr;
0991     }
0992     for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) {
0993         storage->m_vector[i].value = nullptr;
0994     }
0995 
0996     return numDefined;
0997 }
0998 
0999 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 }