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 }