Changeset 26881 in webkit for trunk/JavaScriptCore
- Timestamp:
- Oct 22, 2007, 6:35:17 AM (18 years ago)
- Location:
- trunk/JavaScriptCore
- Files:
-
- 8 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/ChangeLog
r26880 r26881 1 2007-10-22 Darin Adler <[email protected]> 2 3 Reviewed by Maciej. 4 5 - http://bugs.webkit.org/show_bug.cgi?id=15606 6 make cut-off for sparse vs. dense arrays smarter for speed with large arrays 7 8 Makes the morph test in SunSpider 26% faster, and the overall 9 benchmark 3% faster. 10 11 This also fixes some small problems we had with the distinction 12 between nonexistent and undefined values in arrays. 13 14 * kjs/array_instance.h: Tweaked formatting and naming. 15 * kjs/array_instance.cpp: Copied from kjs/array_object.cpp. 16 (KJS::storageSize): Added. Computes the size of the storage given a vector length. 17 (KJS::increasedVectorLength): Added. Implements the rule for resizing the vector. 18 (KJS::isDenseEnoughForVector): Added. 19 (KJS::ArrayInstance::ArrayInstance): Initialize the new fields. 20 (KJS::ArrayInstance::~ArrayInstance): Since m_storage is now never 0, delete it. 21 (KJS::ArrayInstance::getItem): Updated for name changes. 22 (KJS::ArrayInstance::lengthGetter): Ditto. 23 (KJS::ArrayInstance::inlineGetOwnPropertySlot): Added. Allows both versions of 24 getOwnPropertySlot to share more code. 25 (KJS::ArrayInstance::getOwnPropertySlot): Just refactored, no code change. 26 (KJS::ArrayInstance::put): Added logic for extending the vector as long as the 27 array is dense enough. Also keep m_numValuesInVector up to date. 28 (KJS::ArrayInstance::deleteProperty): Added code to keep m_numValuesInVector 29 up to date. 30 (KJS::ArrayInstance::getPropertyNames): Fixed bug where this would omit names 31 for array indices with undefined values. 32 (KJS::ArrayInstance::increaseVectorLength): Renamed from resizeStorage. Also 33 simplified to only handle getting larger. 34 (KJS::ArrayInstance::setLength): Added code to update m_numValuesInVector, to 35 zero out the unused part of the vector and to delete the map if it's no longer 36 needed. 37 (KJS::ArrayInstance::mark): Tweaked formatting. 38 (KJS::compareByStringForQSort): Ditto. 39 (KJS::ArrayInstance::sort): Ditto. 40 (KJS::CompareWithCompareFunctionArguments::CompareWithCompareFunctionArguments): 41 Ditto. 42 (KJS::compareWithCompareFunctionForQSort): Ditto. 43 (KJS::ArrayInstance::compactForSorting): Fixed bug where this would turn 44 undefined values into nonexistent values in some cases. 45 46 * kjs/array_object.h: Removed MAX_ARRAY_INDEX. 47 * kjs/array_object.cpp: Removed ArrayInstance. Moved to a separate file. 48 49 * JavaScriptCore.pri: Added array_instance.cpp. 50 * JavaScriptCore.xcodeproj/project.pbxproj: Ditto. 51 * kjs/AllInOneFile.cpp: Ditto. 52 1 53 2007-10-22 Andrew Wellington <[email protected]> 2 54 -
trunk/JavaScriptCore/JavaScriptCore.pri
r26699 r26881 57 57 kjs/JSWrapperObject.cpp \ 58 58 kjs/PropertyNameArray.cpp \ 59 kjs/array_instance.cpp \ 59 60 kjs/array_object.cpp \ 60 61 kjs/bool_object.cpp \ -
trunk/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj
r26704 r26881 176 176 > 177 177 <File 178 RelativePath="..\..\kjs\array_instance.cpp" 179 > 180 </File> 181 <File 178 182 RelativePath="..\..\kjs\array_instance.h" 179 183 > -
trunk/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
r26787 r26881 575 575 938C4F6B0CA06BCE00D9310A /* DisallowCType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisallowCType.h; sourceTree = "<group>"; }; 576 576 93AA4F770957251F0084B3A7 /* AlwaysInline.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = AlwaysInline.h; sourceTree = "<group>"; tabWidth = 8; }; 577 93ADFCE60CCBD7AC00D30B08 /* array_instance.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = array_instance.cpp; sourceTree = "<group>"; }; 577 578 93B6A0DE0AA64DA40076DE27 /* GetPtr.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GetPtr.h; sourceTree = "<group>"; }; 578 579 93E26BC908B1511900F85226 /* pcre_ord2utf8.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.c; name = pcre_ord2utf8.c; path = pcre/pcre_ord2utf8.c; sourceTree = "<group>"; tabWidth = 8; }; … … 952 953 65400C100A69BAF200509887 /* PropertyNameArray.h */, 953 954 938772E5038BFE19008635CE /* array_instance.h */, 955 93ADFCE60CCBD7AC00D30B08 /* array_instance.cpp */, 954 956 659126BC0BDD1728001921FB /* AllInOneFile.cpp */, 955 957 F692A84D0255597D01FF60F7 /* array_object.cpp */, -
trunk/JavaScriptCore/kjs/AllInOneFile.cpp
r25754 r26881 29 29 #include "function.cpp" 30 30 #include "debugger.cpp" 31 #include "array_instance.cpp" 31 32 #include "array_object.cpp" 32 33 #include "bool_object.cpp" -
trunk/JavaScriptCore/kjs/array_instance.cpp
r26859 r26881 1 // -*- c-basic-offset: 2 -*-2 1 /* 3 * This file is part of the KDE libraries4 2 * Copyright (C) 1999-2000 Harri Porten ([email protected]) 5 3 * Copyright (C) 2003, 2007 Apple Inc. All rights reserved. … … 24 22 25 23 #include "config.h" 26 #include "array_object.h" 27 #include "array_object.lut.h" 24 #include "array_instance.h" 28 25 29 26 #include "PropertyNameArray.h" 30 #include "error_object.h"31 #include "lookup.h"32 #include "operations.h"33 #include <stdio.h>34 27 #include <wtf/Assertions.h> 35 #include <wtf/HashSet.h>36 37 28 38 29 namespace KJS { 39 30 40 typedef HashMap<unsigned, JSValue*> OverflowMap; 41 42 static inline OverflowMap* overflowMap(JSValue** storage) 43 { 44 return storage ? reinterpret_cast<OverflowMap*>(storage[-2]) : 0; 45 } 46 47 // ------------------------------ ArrayInstance ----------------------------- 48 49 const unsigned sparseArrayCutoff = 10000; 31 typedef HashMap<unsigned, JSValue*> SparseArrayValueMap; 32 33 struct ArrayStorage { 34 unsigned m_numValuesInVector; 35 SparseArrayValueMap* m_sparseValueMap; 36 JSValue* m_vector[1]; 37 }; 38 39 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer 40 static const unsigned maxArrayIndex = 0xFFFFFFFEU; 41 42 // Our policy for when to use a vector and when to use a sparse map. 43 // For all array indices under sparseArrayCutoff, we always use a vector. 44 // When indices greater than sparseArrayCutoff are involved, we use a vector 45 // as long as it is 1/8 full. If more sparse than that, we use a map. 46 static const unsigned sparseArrayCutoff = 10000; 47 static const unsigned minDensityMultiplier = 8; 48 49 static const unsigned mergeSortCutoff = 10000; 50 50 51 51 const ClassInfo ArrayInstance::info = {"Array", 0, 0, 0}; 52 52 53 static inline JSValue** allocateStorage(size_t capacity) 54 { 55 if (capacity == 0) 56 return 0; 57 58 // store capacity and overflow map in extra space before the beginning of the storage array to save space 59 JSValue** storage = static_cast<JSValue**>(fastCalloc(capacity + 2, sizeof(JSValue *))) + 2; 60 storage[-1] = reinterpret_cast<JSValue*>(capacity); 61 return storage; 62 } 63 64 static inline void reallocateStorage(JSValue**& storage, size_t newCapacity) 65 { 66 if (!storage) { 67 storage = allocateStorage(newCapacity); 68 return; 69 } 70 71 // store capacity and overflow map in extra space before the beginning of the storage array to save space 72 storage = static_cast<JSValue**>(fastRealloc(storage - 2, (newCapacity + 2) * sizeof (JSValue*))) + 2; 73 storage[-1] = reinterpret_cast<JSValue*>(newCapacity); 74 } 75 76 static inline void freeStorage(JSValue** storage) 77 { 78 fastFree(storage - 2); 79 } 80 81 ArrayInstance::ArrayInstance(JSObject *proto, unsigned initialLength) 82 : JSObject(proto) 83 , length(initialLength) 84 , storageLength(initialLength < sparseArrayCutoff ? initialLength : 0) 85 , storage(allocateStorage(storageLength)) 86 { 87 Collector::reportExtraMemoryCost(storageLength * sizeof(JSValue*)); 88 } 89 90 ArrayInstance::ArrayInstance(JSObject *proto, const List &list) 91 : JSObject(proto) 92 , length(list.size()) 93 , storageLength(length) 94 , storage(allocateStorage(storageLength)) 95 { 96 ListIterator it = list.begin(); 97 unsigned l = length; 98 for (unsigned i = 0; i < l; ++i) 99 storage[i] = it++; 100 // When the array is created non-empty, its cells are filled so it's really no worse than 101 // a property map. Therefore don't report extra memory cost. 53 static inline size_t storageSize(unsigned vectorLength) 54 { 55 return sizeof(ArrayStorage) - sizeof(JSValue*) + vectorLength * sizeof(JSValue*); 56 } 57 58 static inline unsigned increasedVectorLength(unsigned newLength) 59 { 60 return (newLength * 3 + 1) / 2; 61 } 62 63 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) 64 { 65 return length / minDensityMultiplier <= numValues; 66 } 67 68 ArrayInstance::ArrayInstance(JSObject* prototype, unsigned initialLength) 69 : JSObject(prototype) 70 { 71 unsigned initialCapacity = min(initialLength, sparseArrayCutoff); 72 73 m_length = initialLength; 74 m_vectorLength = initialCapacity; 75 m_storage = static_cast<ArrayStorage*>(fastCalloc(storageSize(initialCapacity), 1)); 76 77 Collector::reportExtraMemoryCost(initialCapacity * sizeof(JSValue*)); 78 } 79 80 ArrayInstance::ArrayInstance(JSObject* prototype, const List& list) 81 : JSObject(prototype) 82 { 83 unsigned length = list.size(); 84 85 m_length = length; 86 m_vectorLength = length; 87 88 ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length))); 89 90 storage->m_numValuesInVector = length; 91 storage->m_sparseValueMap = 0; 92 93 ListIterator it = list.begin(); 94 for (unsigned i = 0; i < length; ++i) 95 storage->m_vector[i] = it++; 96 97 m_storage = storage; 98 99 // When the array is created non-empty, its cells are filled, so it's really no worse than 100 // a property map. Therefore don't report extra memory cost. 102 101 } 103 102 104 103 ArrayInstance::~ArrayInstance() 105 104 { 106 if (storage) { 107 delete reinterpret_cast<OverflowMap*>(storage[-2]); 108 freeStorage(storage); 109 } 105 delete m_storage->m_sparseValueMap; 106 fastFree(m_storage); 110 107 } 111 108 112 109 JSValue* ArrayInstance::getItem(unsigned i) const 113 110 { 114 if (i < storageLength) { 115 JSValue* value = storage[i]; 111 ASSERT(i <= maxArrayIndex); 112 113 ArrayStorage* storage = m_storage; 114 115 if (i < m_vectorLength) { 116 JSValue* value = storage->m_vector[i]; 116 117 return value ? value : jsUndefined(); 117 118 } 118 119 119 const OverflowMap* overflow = overflowMap(storage);120 if (! overflow)120 SparseArrayValueMap* map = storage->m_sparseValueMap; 121 if (!map) 121 122 return jsUndefined(); 122 123 123 JSValue* value = overflow->get(i);124 JSValue* value = map->get(i); 124 125 return value ? value : jsUndefined(); 125 126 } 126 127 127 JSValue *ArrayInstance::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) 128 { 129 return jsNumber(static_cast<ArrayInstance *>(slot.slotBase())->length); 128 JSValue* ArrayInstance::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) 129 { 130 return jsNumber(static_cast<ArrayInstance*>(slot.slotBase())->m_length); 131 } 132 133 ALWAYS_INLINE bool ArrayInstance::inlineGetOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) 134 { 135 ArrayStorage* storage = m_storage; 136 137 if (i >= m_length) { 138 if (i > maxArrayIndex) 139 return getOwnPropertySlot(exec, Identifier::from(i), slot); 140 return false; 141 } 142 143 if (i < m_vectorLength) { 144 JSValue*& valueSlot = storage->m_vector[i]; 145 if (valueSlot) { 146 slot.setValueSlot(this, &valueSlot); 147 return true; 148 } 149 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { 150 SparseArrayValueMap::iterator it = map->find(i); 151 if (it != map->end()) { 152 slot.setValueSlot(this, &it->second); 153 return true; 154 } 155 } 156 157 return false; 130 158 } 131 159 132 160 bool ArrayInstance::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 133 161 { 134 if (propertyName == exec->propertyNames().length) { 135 slot.setCustom(this, lengthGetter); 136 return true; 137 } 138 139 bool ok; 140 unsigned index = propertyName.toArrayIndex(&ok); 141 if (!ok) 162 if (propertyName == exec->propertyNames().length) { 163 slot.setCustom(this, lengthGetter); 164 return true; 165 } 166 167 bool isArrayIndex; 168 unsigned i = propertyName.toArrayIndex(&isArrayIndex); 169 if (isArrayIndex) 170 return inlineGetOwnPropertySlot(exec, i, slot); 171 142 172 return JSObject::getOwnPropertySlot(exec, propertyName, slot); 143 144 if (index < storageLength) { 145 JSValue *v = storage[index]; 146 if (!v) 147 return false; 148 slot.setValueSlot(this, &storage[index]); 149 return true; 150 } 151 if (index > MAX_ARRAY_INDEX) 152 return JSObject::getOwnPropertySlot(exec, propertyName, slot); 153 OverflowMap* overflow = overflowMap(storage); 154 if (!overflow) 173 } 174 175 bool ArrayInstance::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) 176 { 177 return inlineGetOwnPropertySlot(exec, i, slot); 178 } 179 180 // ECMA 15.4.5.1 181 void ArrayInstance::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attributes) 182 { 183 bool isArrayIndex; 184 unsigned i = propertyName.toArrayIndex(&isArrayIndex); 185 if (isArrayIndex) { 186 put(exec, i, value, attributes); 187 return; 188 } 189 190 if (propertyName == exec->propertyNames().length) { 191 unsigned newLength = value->toUInt32(exec); 192 if (value->toNumber(exec) != static_cast<double>(newLength)) { 193 throwError(exec, RangeError, "Invalid array length."); 194 return; 195 } 196 setLength(newLength); 197 return; 198 } 199 200 JSObject::put(exec, propertyName, value, attributes); 201 } 202 203 void ArrayInstance::put(ExecState* exec, unsigned i, JSValue* value, int attributes) 204 { 205 if (i > maxArrayIndex) { 206 put(exec, Identifier::from(i), value, attributes); 207 return; 208 } 209 210 ArrayStorage* storage = m_storage; 211 212 unsigned length = m_length; 213 if (i >= length) { 214 length = i + 1; 215 m_length = length; 216 } 217 218 if (i < m_vectorLength) { 219 JSValue*& valueSlot = storage->m_vector[i]; 220 storage->m_numValuesInVector += !valueSlot; 221 valueSlot = value; 222 return; 223 } 224 225 if (i < sparseArrayCutoff) { 226 increaseVectorLength(i + 1); 227 storage = m_storage; 228 ++storage->m_numValuesInVector; 229 storage->m_vector[i] = value; 230 return; 231 } 232 233 SparseArrayValueMap* map = storage->m_sparseValueMap; 234 if (!map || map->isEmpty()) { 235 if (isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) { 236 increaseVectorLength(i + 1); 237 storage = m_storage; 238 ++storage->m_numValuesInVector; 239 storage->m_vector[i] = value; 240 return; 241 } 242 if (!map) { 243 map = new SparseArrayValueMap; 244 storage->m_sparseValueMap = map; 245 } 246 map->add(i, value); 247 return; 248 } 249 250 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; 251 if (!isDenseEnoughForVector(i + 1, newNumValuesInVector)) { 252 map->add(i, value); 253 return; 254 } 255 256 unsigned newVectorLength = increasedVectorLength(i + 1); 257 for (unsigned j = m_vectorLength; j < newVectorLength; ++j) 258 newNumValuesInVector += map->contains(j); 259 newNumValuesInVector -= map->contains(i); 260 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) { 261 unsigned proposedNewNumValuesInVector = newNumValuesInVector; 262 while (true) { 263 unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1); 264 for (unsigned j = newVectorLength; j < proposedNewVectorLength; ++j) 265 proposedNewNumValuesInVector += map->contains(j); 266 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) 267 break; 268 newVectorLength = proposedNewVectorLength; 269 newNumValuesInVector = proposedNewNumValuesInVector; 270 } 271 } 272 273 storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength))); 274 275 unsigned vectorLength = m_vectorLength; 276 if (newNumValuesInVector == storage->m_numValuesInVector + 1) { 277 for (unsigned j = vectorLength; j < newVectorLength; ++j) 278 storage->m_vector[j] = 0; 279 map->remove(i); 280 } else { 281 for (unsigned j = vectorLength; j < newVectorLength; ++j) { 282 SparseArrayValueMap::iterator it = map->find(j); 283 if (it == map->end()) 284 storage->m_vector[j] = 0; 285 else { 286 storage->m_vector[j] = it->second; 287 map->remove(it); 288 } 289 } 290 } 291 292 storage->m_vector[i] = value; 293 294 m_vectorLength = newVectorLength; 295 storage->m_numValuesInVector = newNumValuesInVector; 296 } 297 298 bool ArrayInstance::deleteProperty(ExecState* exec, const Identifier& propertyName) 299 { 300 bool isArrayIndex; 301 unsigned i = propertyName.toArrayIndex(&isArrayIndex); 302 if (isArrayIndex) 303 return deleteProperty(exec, i); 304 305 if (propertyName == exec->propertyNames().length) 306 return false; 307 308 return JSObject::deleteProperty(exec, propertyName); 309 } 310 311 bool ArrayInstance::deleteProperty(ExecState* exec, unsigned i) 312 { 313 ArrayStorage* storage = m_storage; 314 315 if (i < m_vectorLength) { 316 JSValue*& valueSlot = storage->m_vector[i]; 317 bool hadValue = valueSlot; 318 valueSlot = 0; 319 storage->m_numValuesInVector -= hadValue; 320 return hadValue; 321 } 322 323 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { 324 SparseArrayValueMap::iterator it = map->find(i); 325 if (it != map->end()) { 326 map->remove(it); 327 return true; 328 } 329 } 330 331 if (i > maxArrayIndex) 332 return deleteProperty(exec, Identifier::from(i)); 333 155 334 return false; 156 OverflowMap::iterator it = overflow->find(index);157 if (it == overflow->end())158 return false;159 slot.setValueSlot(this, &it->second);160 return true;161 }162 163 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, unsigned index, PropertySlot& slot)164 {165 if (index < storageLength) {166 JSValue *v = storage[index];167 if (!v)168 return false;169 slot.setValueSlot(this, &storage[index]);170 return true;171 }172 if (index > MAX_ARRAY_INDEX)173 return getOwnPropertySlot(exec, Identifier::from(index), slot);174 OverflowMap* overflow = overflowMap(storage);175 if (!overflow)176 return false;177 OverflowMap::iterator it = overflow->find(index);178 if (it == overflow->end())179 return false;180 slot.setValueSlot(this, &it->second);181 return true;182 }183 184 // Special implementation of [[Put]] - see ECMA 15.4.5.1185 void ArrayInstance::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)186 {187 if (propertyName == exec->propertyNames().length) {188 unsigned int newLen = value->toUInt32(exec);189 if (value->toNumber(exec) != double(newLen)) {190 throwError(exec, RangeError, "Invalid array length.");191 return;192 }193 setLength(newLen);194 return;195 }196 197 bool ok;198 unsigned index = propertyName.toArrayIndex(&ok);199 if (ok) {200 put(exec, index, value, attr);201 return;202 }203 204 JSObject::put(exec, propertyName, value, attr);205 }206 207 void ArrayInstance::put(ExecState *exec, unsigned index, JSValue *value, int attr)208 {209 // 0xFFFFFFFF is a bit weird --- it should be treated as a non-array index, even when it's a string210 if (index > MAX_ARRAY_INDEX) {211 put(exec, Identifier::from(index), value, attr);212 return;213 }214 215 if (index < sparseArrayCutoff && index >= storageLength)216 resizeStorage(index + 1);217 218 if (index >= length)219 length = index + 1;220 221 if (index < storageLength) {222 storage[index] = value;223 return;224 }225 226 OverflowMap* overflow = overflowMap(storage);227 if (!overflow) {228 overflow = new OverflowMap;229 if (!storage)230 storage = allocateStorage(1);231 storage[-2] = reinterpret_cast<JSValue*>(overflow);232 }233 overflow->add(index, value);234 }235 236 bool ArrayInstance::deleteProperty(ExecState* exec, const Identifier &propertyName)237 {238 if (propertyName == exec->propertyNames().length)239 return false;240 241 bool ok;242 uint32_t index = propertyName.toArrayIndex(&ok);243 if (ok) {244 if (index >= length)245 return true;246 if (index < storageLength) {247 storage[index] = 0;248 return true;249 }250 if (OverflowMap* overflow = overflowMap(storage)) {251 OverflowMap::iterator it = overflow->find(index);252 if (it == overflow->end())253 return false;254 overflow->remove(it);255 return true;256 }257 return false;258 }259 260 return JSObject::deleteProperty(exec, propertyName);261 }262 263 bool ArrayInstance::deleteProperty(ExecState *exec, unsigned index)264 {265 if (index > MAX_ARRAY_INDEX)266 return deleteProperty(exec, Identifier::from(index));267 268 if (index >= length)269 return true;270 if (index < storageLength) {271 storage[index] = 0;272 return true;273 }274 OverflowMap* overflow = overflowMap(storage);275 if (!overflow)276 return false;277 OverflowMap::iterator it = overflow->find(index);278 if (it == overflow->end())279 return false;280 overflow->remove(it);281 return true;282 335 } 283 336 284 337 void ArrayInstance::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) 285 338 { 286 // avoid fetching this every time through the loop 287 JSValue* undefined = jsUndefined(); 339 // FIXME: Filling PropertyNameArray with an identifier for every integer 340 // is incredibly inefficient for large arrays. We need a different approach. 341 342 ArrayStorage* storage = m_storage; 343 344 unsigned usedVectorLength = min(m_length, m_vectorLength); 345 for (unsigned i = 0; i < usedVectorLength; ++i) { 346 if (storage->m_vector[i]) 347 propertyNames.add(Identifier::from(i)); 348 } 349 350 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { 351 SparseArrayValueMap::iterator end = map->end(); 352 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) 353 propertyNames.add(Identifier::from(it->first)); 354 } 355 356 JSObject::getPropertyNames(exec, propertyNames); 357 } 358 359 void ArrayInstance::increaseVectorLength(unsigned newLength) 360 { 361 ArrayStorage* storage = m_storage; 362 363 unsigned vectorLength = m_vectorLength; 364 ASSERT(newLength > vectorLength); 365 unsigned newVectorLength = increasedVectorLength(newLength); 366 367 storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength))); 368 m_vectorLength = newVectorLength; 369 370 for (unsigned i = vectorLength; i < newVectorLength; ++i) 371 storage->m_vector[i] = 0; 372 373 m_storage = storage; 374 } 375 376 void ArrayInstance::setLength(unsigned newLength) 377 { 378 ArrayStorage* storage = m_storage; 379 380 unsigned length = m_length; 381 382 if (newLength < length) { 383 unsigned usedVectorLength = min(length, m_vectorLength); 384 for (unsigned i = newLength; i < usedVectorLength; ++i) { 385 JSValue*& valueSlot = storage->m_vector[i]; 386 bool hadValue = valueSlot; 387 valueSlot = 0; 388 storage->m_numValuesInVector -= hadValue; 389 } 390 391 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { 392 SparseArrayValueMap copy = *map; 393 SparseArrayValueMap::iterator end = copy.end(); 394 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) { 395 if (it->first >= newLength) 396 map->remove(it->first); 397 } 398 if (map->isEmpty()) { 399 delete map; 400 storage->m_sparseValueMap = 0; 401 } 402 } 403 } 288 404 289 for (unsigned i = 0; i < storageLength; ++i) { 290 JSValue* value = storage[i]; 291 if (value && value != undefined) 292 propertyNames.add(Identifier::from(i)); 293 } 294 295 OverflowMap* overflow = overflowMap(storage); 296 if (overflow) { 297 OverflowMap::iterator end = overflow->end(); 298 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it) { 299 JSValue* value = it->second; 300 if (value && value != undefined) 301 propertyNames.add(Identifier::from(it->first)); 302 } 303 } 304 305 JSObject::getPropertyNames(exec, propertyNames); 306 } 307 308 void ArrayInstance::resizeStorage(unsigned newLength) 309 { 310 if (newLength < storageLength) { 311 memset(storage + newLength, 0, sizeof(JSValue *) * (storageLength - newLength)); 312 } 313 size_t cap = capacity(); 314 if (newLength > cap) { 315 unsigned newCapacity; 316 if (newLength > sparseArrayCutoff) { 317 newCapacity = newLength; 318 } else { 319 newCapacity = (newLength * 3 + 1) / 2; 320 if (newCapacity > sparseArrayCutoff) { 321 newCapacity = sparseArrayCutoff; 322 } 323 } 324 325 reallocateStorage(storage, newCapacity); 326 memset(storage + cap, 0, sizeof(JSValue*) * (newCapacity - cap)); 327 } 328 storageLength = newLength; 329 } 330 331 void ArrayInstance::setLength(unsigned newLength) 332 { 333 if (newLength <= storageLength) 334 resizeStorage(newLength); 335 336 if (newLength < length) { 337 if (OverflowMap* overflow = overflowMap(storage)) { 338 OverflowMap copy = *overflow; 339 OverflowMap::iterator end = copy.end(); 340 for (OverflowMap::iterator it = copy.begin(); it != end; ++it) { 341 if (it->first >= newLength) 342 overflow->remove(it->first); 343 } 344 } 345 } 346 347 length = newLength; 405 m_length = newLength; 348 406 } 349 407 350 408 void ArrayInstance::mark() 351 409 { 352 JSObject::mark(); 353 unsigned l = storageLength; 354 for (unsigned i = 0; i < l; ++i) { 355 JSValue* imp = storage[i]; 356 if (imp && !imp->marked()) 357 imp->mark(); 358 } 359 if (OverflowMap* overflow = overflowMap(storage)) { 360 OverflowMap::iterator end = overflow->end(); 361 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it) { 362 JSValue* value = it->second; 363 if (value && !value->marked()) 364 value->mark(); 365 } 366 } 410 JSObject::mark(); 411 412 ArrayStorage* storage = m_storage; 413 414 unsigned usedVectorLength = min(m_length, m_vectorLength); 415 for (unsigned i = 0; i < usedVectorLength; ++i) { 416 JSValue* value = storage->m_vector[i]; 417 if (value && !value->marked()) 418 value->mark(); 419 } 420 421 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { 422 SparseArrayValueMap copy = *map; 423 SparseArrayValueMap::iterator end = copy.end(); 424 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) { 425 JSValue* value = it->second; 426 if (!value->marked()) 427 value->mark(); 428 } 429 } 367 430 } 368 431 369 432 static ExecState* execForCompareByStringForQSort = 0; 370 433 371 static int compareByStringForQSort(const void *a, const void *b) 372 { 373 ExecState *exec = execForCompareByStringForQSort; 374 JSValue *va = *(JSValue **)a; 375 JSValue *vb = *(JSValue **)b; 434 static int compareByStringForQSort(const void* a, const void* b) 435 { 436 ExecState* exec = execForCompareByStringForQSort; 437 438 JSValue* va = *static_cast<JSValue* const*>(a); 439 JSValue* vb = *static_cast<JSValue* const*>(b); 376 440 ASSERT(!va->isUndefined()); 377 441 ASSERT(!vb->isUndefined()); 442 378 443 return compare(va->toString(exec), vb->toString(exec)); 379 444 } … … 381 446 void ArrayInstance::sort(ExecState* exec) 382 447 { 383 size_tlengthNotIncludingUndefined = compactForSorting();384 448 unsigned lengthNotIncludingUndefined = compactForSorting(); 449 385 450 ExecState* oldExec = execForCompareByStringForQSort; 386 451 execForCompareByStringForQSort = exec; 452 387 453 #if HAVE(MERGESORT) 388 // mergesort usually does fewer compares, so it is actually faster than qsort for JS sorts. 389 // however, becuase it requires extra copies of the storage buffer, don't use it for very 390 // large arrays 391 // FIXME: for sorting by string value, the fastest thing would actually be to convert all the 392 // values to string once up front, and then use a radix sort. That would be O(N) rather than 393 // O(N log N). 394 if (lengthNotIncludingUndefined < sparseArrayCutoff) { 395 JSValue** storageCopy = allocateStorage(capacity()); 396 memcpy(storageCopy, storage, capacity() * sizeof(JSValue*)); 397 mergesort(storageCopy, lengthNotIncludingUndefined, sizeof(JSValue *), compareByStringForQSort); 398 freeStorage(storage); 399 storage = storageCopy; 454 // Because mergesort usually does fewer compares, it is faster than qsort here. 455 // However, because it requires extra copies of the storage buffer, don't use it for very 456 // large arrays. 457 458 // FIXME: Since we sort by string value, a fast algorithm might be to convert all the 459 // values to string once up front, and then use a radix sort. That would be O(N) rather 460 // than O(N log N). 461 462 if (lengthNotIncludingUndefined < mergeSortCutoff) { 463 // During the sort, we could do a garbage collect, and it's important to still 464 // have references to every object in the array for ArrayInstance::mark. 465 // The mergesort algorithm does not guarantee this, so we sort a copy rather 466 // than the original. 467 size_t size = storageSize(m_vectorLength); 468 ArrayStorage* copy = static_cast<ArrayStorage*>(fastMalloc(size)); 469 memcpy(copy, m_storage, size); 470 mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareByStringForQSort); 471 fastFree(m_storage); 472 m_storage = copy; 400 473 execForCompareByStringForQSort = oldExec; 401 474 return; … … 403 476 #endif 404 477 405 qsort( storage, lengthNotIncludingUndefined, sizeof(JSValue*), compareByStringForQSort);478 qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareByStringForQSort); 406 479 execForCompareByStringForQSort = oldExec; 407 480 } … … 413 486 , globalObject(e->dynamicInterpreter()->globalObject()) 414 487 { 415 arguments.append(jsUndefined());416 arguments.append(jsUndefined());417 488 } 418 489 … … 425 496 static CompareWithCompareFunctionArguments* compareWithCompareFunctionArguments = 0; 426 497 427 static int compareWithCompareFunctionForQSort(const void *a, const void *b)498 static int compareWithCompareFunctionForQSort(const void* a, const void* b) 428 499 { 429 500 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments; 430 501 431 JSValue *va = *(JSValue **)a;432 JSValue *vb = *(JSValue **)b;502 JSValue* va = *static_cast<JSValue* const*>(a); 503 JSValue* vb = *static_cast<JSValue* const*>(b); 433 504 ASSERT(!va->isUndefined()); 434 505 ASSERT(!vb->isUndefined()); … … 449 520 CompareWithCompareFunctionArguments args(exec, compareFunction); 450 521 compareWithCompareFunctionArguments = &args; 522 451 523 #if HAVE(MERGESORT) 452 // mergesort usually does fewer compares, so it is actually faster than qsort for JS sorts. 453 // however, becuase it requires extra copies of the storage buffer, don't use it for very 454 // large arrays 455 // FIXME: a tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even 456 // better job of minimizing compares 457 if (lengthNotIncludingUndefined < sparseArrayCutoff) { 458 JSValue** storageCopy = allocateStorage(capacity()); 459 memcpy(storageCopy, storage, capacity() * sizeof(JSValue*)); 460 mergesort(storageCopy, lengthNotIncludingUndefined, sizeof(JSValue *), compareWithCompareFunctionForQSort); 461 freeStorage(storage); 462 storage = storageCopy; 524 // Because mergesort usually does fewer compares, it is faster than qsort here. 525 // However, because it requires extra copies of the storage buffer, don't use it for very 526 // large arrays. 527 528 // FIXME: A tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even 529 // better job of minimizing compares. 530 531 if (lengthNotIncludingUndefined < mergeSortCutoff) { 532 // During the sort, we could do a garbage collect, and it's important to still 533 // have references to every object in the array for ArrayInstance::mark. 534 // The mergesort algorithm does not guarantee this, so we sort a copy rather 535 // than the original. 536 size_t size = storageSize(m_vectorLength); 537 ArrayStorage* copy = static_cast<ArrayStorage*>(fastMalloc(size)); 538 memcpy(copy, m_storage, size); 539 mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort); 540 fastFree(m_storage); 541 m_storage = copy; 463 542 compareWithCompareFunctionArguments = oldArgs; 464 543 return; 465 544 } 466 545 #endif 467 qsort(storage, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort); 546 547 qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort); 468 548 compareWithCompareFunctionArguments = oldArgs; 469 549 } … … 471 551 unsigned ArrayInstance::compactForSorting() 472 552 { 473 JSValue *undefined = jsUndefined(); 474 475 unsigned o = 0; 476 477 for (unsigned i = 0; i != storageLength; ++i) { 478 JSValue *v = storage[i]; 479 if (v && v != undefined) { 480 if (o != i) 481 storage[o] = v; 482 o++; 483 } 484 } 485 486 unsigned newLength = o; 487 488 if (OverflowMap* overflow = overflowMap(storage)) { 489 OverflowMap::iterator end = overflow->end(); 490 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it) 491 newLength += it->second != undefined; 492 493 if (newLength > storageLength) 494 resizeStorage(newLength); 495 496 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it) { 497 JSValue* v = it->second; 498 if (v != undefined) { 499 storage[o] = v; 500 o++; 501 } 502 } 503 delete overflow; 504 storage[-2] = 0; 505 } 506 507 if (newLength != storageLength) 508 memset(storage + o, 0, sizeof(JSValue *) * (storageLength - o)); 509 510 return o; 511 } 512 513 // ------------------------------ ArrayPrototype ---------------------------- 514 515 const ClassInfo ArrayPrototype::info = {"Array", &ArrayInstance::info, &arrayTable, 0}; 516 517 /* Source for array_object.lut.h 518 @begin arrayTable 16 519 toString ArrayProtoFunc::ToString DontEnum|Function 0 520 toLocaleString ArrayProtoFunc::ToLocaleString DontEnum|Function 0 521 concat ArrayProtoFunc::Concat DontEnum|Function 1 522 join ArrayProtoFunc::Join DontEnum|Function 1 523 pop ArrayProtoFunc::Pop DontEnum|Function 0 524 push ArrayProtoFunc::Push DontEnum|Function 1 525 reverse ArrayProtoFunc::Reverse DontEnum|Function 0 526 shift ArrayProtoFunc::Shift DontEnum|Function 0 527 slice ArrayProtoFunc::Slice DontEnum|Function 2 528 sort ArrayProtoFunc::Sort DontEnum|Function 1 529 splice ArrayProtoFunc::Splice DontEnum|Function 2 530 unshift ArrayProtoFunc::UnShift DontEnum|Function 1 531 every ArrayProtoFunc::Every DontEnum|Function 1 532 forEach ArrayProtoFunc::ForEach DontEnum|Function 1 533 some ArrayProtoFunc::Some DontEnum|Function 1 534 indexOf ArrayProtoFunc::IndexOf DontEnum|Function 1 535 lastIndexOf ArrayProtoFunc::LastIndexOf DontEnum|Function 1 536 filter ArrayProtoFunc::Filter DontEnum|Function 1 537 map ArrayProtoFunc::Map DontEnum|Function 1 538 @end 539 */ 540 541 // ECMA 15.4.4 542 ArrayPrototype::ArrayPrototype(ExecState*, ObjectPrototype* objProto) 543 : ArrayInstance(objProto, 0) 544 { 545 } 546 547 bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 548 { 549 return getStaticFunctionSlot<ArrayProtoFunc, ArrayInstance>(exec, &arrayTable, this, propertyName, slot); 550 } 551 552 // ------------------------------ ArrayProtoFunc ---------------------------- 553 554 ArrayProtoFunc::ArrayProtoFunc(ExecState* exec, int i, int len, const Identifier& name) 555 : InternalFunctionImp(static_cast<FunctionPrototype*> 556 (exec->lexicalInterpreter()->builtinFunctionPrototype()), name) 557 , id(i) 558 { 559 put(exec, exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum); 560 } 561 562 static JSValue *getProperty(ExecState *exec, JSObject *obj, unsigned index) 563 { 564 PropertySlot slot; 565 if (!obj->getPropertySlot(exec, index, slot)) 566 return NULL; 567 return slot.getValue(exec, obj, index); 568 } 569 570 // ECMA 15.4.4 571 JSValue* ArrayProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args) 572 { 573 unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); 574 575 JSValue *result = 0; // work around gcc 4.0 bug in uninitialized variable warning 576 577 switch (id) { 578 case ToLocaleString: 579 case ToString: 580 581 if (!thisObj->inherits(&ArrayInstance::info)) 582 return throwError(exec, TypeError); 583 584 // fall through 585 case Join: { 586 static HashSet<JSObject*> visitedElems; 587 static const UString* empty = new UString(""); 588 static const UString* comma = new UString(","); 589 bool alreadyVisited = !visitedElems.add(thisObj).second; 590 if (alreadyVisited) 591 return jsString(*empty); 592 UString separator = *comma; 593 UString str = *empty; 594 595 if (id == Join && !args[0]->isUndefined()) 596 separator = args[0]->toString(exec); 597 for (unsigned int k = 0; k < length; k++) { 598 if (k >= 1) 599 str += separator; 600 if (str.isNull()) { 601 JSObject *error = Error::create(exec, GeneralError, "Out of memory"); 602 exec->setException(error); 553 ArrayStorage* storage = m_storage; 554 555 unsigned usedVectorLength = min(m_length, m_vectorLength); 556 557 unsigned numDefined = 0; 558 unsigned numUndefined = 0; 559 560 for (; numDefined < usedVectorLength; ++numDefined) { 561 JSValue* v = storage->m_vector[numDefined]; 562 if (!v || v->isUndefined()) 603 563 break; 604 } 605 606 JSValue* element = thisObj->get(exec, k); 607 if (element->isUndefinedOrNull()) 608 continue; 609 610 bool fallback = false; 611 if (id == ToLocaleString) { 612 JSObject* o = element->toObject(exec); 613 JSValue* conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); 614 if (conversionFunction->isObject() && static_cast<JSObject*>(conversionFunction)->implementsCall()) 615 str += static_cast<JSObject*>(conversionFunction)->call(exec, o, List())->toString(exec); 564 } 565 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 566 if (JSValue* v = storage->m_vector[i]) { 567 if (v->isUndefined()) 568 ++numUndefined; 616 569 else 617 // try toString() fallback 618 fallback = true; 619 } 620 621 if (id == ToString || id == Join || fallback) 622 str += element->toString(exec); 623 624 if (str.isNull()) { 625 JSObject *error = Error::create(exec, GeneralError, "Out of memory"); 626 exec->setException(error); 627 } 628 629 if (exec->hadException()) 630 break; 631 } 632 visitedElems.remove(thisObj); 633 result = jsString(str); 634 break; 635 } 636 case Concat: { 637 JSObject *arr = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty())); 638 int n = 0; 639 JSValue *curArg = thisObj; 640 JSObject *curObj = static_cast<JSObject *>(thisObj); 641 ListIterator it = args.begin(); 642 for (;;) { 643 if (curArg->isObject() && 644 curObj->inherits(&ArrayInstance::info)) { 645 unsigned int k = 0; 646 // Older versions tried to optimize out getting the length of thisObj 647 // by checking for n != 0, but that doesn't work if thisObj is an empty array. 648 length = curObj->get(exec, exec->propertyNames().length)->toUInt32(exec); 649 while (k < length) { 650 if (JSValue *v = getProperty(exec, curObj, k)) 651 arr->put(exec, n, v); 652 n++; 653 k++; 654 } 655 } else { 656 arr->put(exec, n, curArg); 657 n++; 658 } 659 if (it == args.end()) 660 break; 661 curArg = *it; 662 curObj = static_cast<JSObject *>(it++); // may be 0 663 } 664 arr->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete); 665 666 result = arr; 667 break; 668 } 669 case Pop:{ 670 if (length == 0) { 671 thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete); 672 result = jsUndefined(); 673 } else { 674 result = thisObj->get(exec, length - 1); 675 thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete); 676 } 677 break; 678 } 679 case Push: { 680 for (int n = 0; n < args.size(); n++) 681 thisObj->put(exec, length + n, args[n]); 682 length += args.size(); 683 thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete); 684 result = jsNumber(length); 685 break; 686 } 687 case Reverse: { 688 689 unsigned int middle = length / 2; 690 691 for (unsigned int k = 0; k < middle; k++) { 692 unsigned lk1 = length - k - 1; 693 JSValue *obj2 = getProperty(exec, thisObj, lk1); 694 JSValue *obj = getProperty(exec, thisObj, k); 695 696 if (obj2) 697 thisObj->put(exec, k, obj2); 698 else 699 thisObj->deleteProperty(exec, k); 700 701 if (obj) 702 thisObj->put(exec, lk1, obj); 703 else 704 thisObj->deleteProperty(exec, lk1); 705 } 706 result = thisObj; 707 break; 708 } 709 case Shift: { 710 if (length == 0) { 711 thisObj->put(exec, exec->propertyNames().length, jsNumber(length), DontEnum | DontDelete); 712 result = jsUndefined(); 713 } else { 714 result = thisObj->get(exec, 0); 715 for(unsigned int k = 1; k < length; k++) { 716 if (JSValue *obj = getProperty(exec, thisObj, k)) 717 thisObj->put(exec, k-1, obj); 718 else 719 thisObj->deleteProperty(exec, k-1); 720 } 721 thisObj->deleteProperty(exec, length - 1); 722 thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1), DontEnum | DontDelete); 723 } 724 break; 725 } 726 case Slice: { 727 // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 728 729 // We return a new array 730 JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty())); 731 result = resObj; 732 double begin = 0; 733 if (!args[0]->isUndefined()) { 734 begin = args[0]->toInteger(exec); 735 if (begin >= 0) { // false for NaN 736 if (begin > length) 737 begin = length; 738 } else { 739 begin += length; 740 if (!(begin >= 0)) // true for NaN 741 begin = 0; 742 } 743 } 744 double end = length; 745 if (!args[1]->isUndefined()) { 746 end = args[1]->toInteger(exec); 747 if (end < 0) { // false for NaN 748 end += length; 749 if (end < 0) 750 end = 0; 751 } else { 752 if (!(end <= length)) // true for NaN 753 end = length; 754 } 755 } 756 757 //printf( "Slicing from %d to %d \n", begin, end ); 758 int n = 0; 759 int b = static_cast<int>(begin); 760 int e = static_cast<int>(end); 761 for(int k = b; k < e; k++, n++) { 762 if (JSValue *v = getProperty(exec, thisObj, k)) 763 resObj->put(exec, n, v); 764 } 765 resObj->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete); 766 break; 767 } 768 case Sort:{ 769 #if 0 770 printf("KJS Array::Sort length=%d\n", length); 771 for ( unsigned int i = 0 ; i<length ; ++i ) 772 printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii() ); 773 #endif 774 JSObject *sortFunction = NULL; 775 if (!args[0]->isUndefined()) 776 { 777 sortFunction = args[0]->toObject(exec); 778 if (!sortFunction->implementsCall()) 779 sortFunction = NULL; 780 } 781 782 if (thisObj->classInfo() == &ArrayInstance::info) { 783 if (sortFunction) 784 ((ArrayInstance *)thisObj)->sort(exec, sortFunction); 785 else 786 ((ArrayInstance *)thisObj)->sort(exec); 787 result = thisObj; 788 break; 789 } 790 791 if (length == 0) { 792 thisObj->put(exec, exec->propertyNames().length, jsNumber(0), DontEnum | DontDelete); 793 result = thisObj; 794 break; 795 } 796 797 // "Min" sort. Not the fastest, but definitely less code than heapsort 798 // or quicksort, and much less swapping than bubblesort/insertionsort. 799 for ( unsigned int i = 0 ; i<length-1 ; ++i ) 800 { 801 JSValue *iObj = thisObj->get(exec,i); 802 unsigned int themin = i; 803 JSValue *minObj = iObj; 804 for ( unsigned int j = i+1 ; j<length ; ++j ) 805 { 806 JSValue *jObj = thisObj->get(exec,j); 807 double cmp; 808 if (jObj->isUndefined()) { 809 cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) 810 } else if (minObj->isUndefined()) { 811 cmp = -1; 812 } else if (sortFunction) { 813 List l; 814 l.append(jObj); 815 l.append(minObj); 816 cmp = sortFunction->call(exec, exec->dynamicInterpreter()->globalObject(), l)->toNumber(exec); 817 } else { 818 cmp = (jObj->toString(exec) < minObj->toString(exec)) ? -1 : 1; 819 } 820 if ( cmp < 0 ) 821 { 822 themin = j; 823 minObj = jObj; 824 } 825 } 826 // Swap themin and i 827 if ( themin > i ) 828 { 829 //printf("KJS Array::Sort: swapping %d and %d\n", i, themin ); 830 thisObj->put( exec, i, minObj ); 831 thisObj->put( exec, themin, iObj ); 832 } 833 } 834 #if 0 835 printf("KJS Array::Sort -- Resulting array:\n"); 836 for ( unsigned int i = 0 ; i<length ; ++i ) 837 printf("KJS Array::Sort: %d: %s\n", i, thisObj->get(exec, i)->toString(exec).ascii() ); 838 #endif 839 result = thisObj; 840 break; 841 } 842 case Splice: { 843 // 15.4.4.12 - oh boy this is huge 844 JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty())); 845 result = resObj; 846 int begin = args[0]->toUInt32(exec); 847 if ( begin < 0 ) 848 begin = maxInt( begin + length, 0 ); 849 else 850 begin = minInt( begin, length ); 851 unsigned int deleteCount = minInt( maxInt( args[1]->toUInt32(exec), 0 ), length - begin ); 852 853 //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount ); 854 for(unsigned int k = 0; k < deleteCount; k++) { 855 if (JSValue *v = getProperty(exec, thisObj, k+begin)) 856 resObj->put(exec, k, v); 857 } 858 resObj->put(exec, exec->propertyNames().length, jsNumber(deleteCount), DontEnum | DontDelete); 859 860 unsigned int additionalArgs = maxInt( args.size() - 2, 0 ); 861 if ( additionalArgs != deleteCount ) 862 { 863 if ( additionalArgs < deleteCount ) 864 { 865 for ( unsigned int k = begin; k < length - deleteCount; ++k ) 866 { 867 if (JSValue *v = getProperty(exec, thisObj, k+deleteCount)) 868 thisObj->put(exec, k+additionalArgs, v); 869 else 870 thisObj->deleteProperty(exec, k+additionalArgs); 871 } 872 for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k ) 873 thisObj->deleteProperty(exec, k-1); 874 } 875 else 876 { 877 for ( unsigned int k = length - deleteCount; (int)k > begin; --k ) 878 { 879 if (JSValue *obj = getProperty(exec, thisObj, k + deleteCount - 1)) 880 thisObj->put(exec, k + additionalArgs - 1, obj); 881 else 882 thisObj->deleteProperty(exec, k+additionalArgs-1); 883 } 884 } 885 } 886 for ( unsigned int k = 0; k < additionalArgs; ++k ) 887 { 888 thisObj->put(exec, k+begin, args[k+2]); 889 } 890 thisObj->put(exec, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs), DontEnum | DontDelete); 891 break; 892 } 893 case UnShift: { // 15.4.4.13 894 unsigned int nrArgs = args.size(); 895 for ( unsigned int k = length; k > 0; --k ) 896 { 897 if (JSValue *v = getProperty(exec, thisObj, k - 1)) 898 thisObj->put(exec, k+nrArgs-1, v); 899 else 900 thisObj->deleteProperty(exec, k+nrArgs-1); 901 } 902 for ( unsigned int k = 0; k < nrArgs; ++k ) 903 thisObj->put(exec, k, args[k]); 904 result = jsNumber(length + nrArgs); 905 thisObj->put(exec, exec->propertyNames().length, result, DontEnum | DontDelete); 906 break; 907 } 908 case Filter: 909 case Map: { 910 JSObject *eachFunction = args[0]->toObject(exec); 911 912 if (!eachFunction->implementsCall()) 913 return throwError(exec, TypeError); 914 915 JSObject *applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicInterpreter()->globalObject() : args[1]->toObject(exec); 916 JSObject *resultArray; 917 918 if (id == Filter) 919 resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty())); 920 else { 921 List args; 922 args.append(jsNumber(length)); 923 resultArray = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec, args)); 924 } 925 926 unsigned filterIndex = 0; 927 for (unsigned k = 0; k < length && !exec->hadException(); ++k) { 928 PropertySlot slot; 929 930 if (!thisObj->getPropertySlot(exec, k, slot)) 931 continue; 932 933 JSValue *v = slot.getValue(exec, thisObj, k); 934 935 List eachArguments; 936 937 eachArguments.append(v); 938 eachArguments.append(jsNumber(k)); 939 eachArguments.append(thisObj); 940 941 JSValue *result = eachFunction->call(exec, applyThis, eachArguments); 942 943 if (id == Map) 944 resultArray->put(exec, k, result); 945 else if (result->toBoolean(exec)) 946 resultArray->put(exec, filterIndex++, v); 947 } 948 949 return resultArray; 950 } 951 case Every: 952 case ForEach: 953 case Some: { 954 //Documentation for these three is available at: 955 //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every 956 //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach 957 //http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some 958 959 JSObject *eachFunction = args[0]->toObject(exec); 960 961 if (!eachFunction->implementsCall()) 962 return throwError(exec, TypeError); 963 964 JSObject *applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicInterpreter()->globalObject() : args[1]->toObject(exec); 965 966 if (id == Some || id == Every) 967 result = jsBoolean(id == Every); 968 else 969 result = jsUndefined(); 970 971 for (unsigned k = 0; k < length && !exec->hadException(); ++k) { 972 PropertySlot slot; 973 974 if (!thisObj->getPropertySlot(exec, k, slot)) 975 continue; 976 977 List eachArguments; 978 979 eachArguments.append(slot.getValue(exec, thisObj, k)); 980 eachArguments.append(jsNumber(k)); 981 eachArguments.append(thisObj); 982 983 bool predicateResult = eachFunction->call(exec, applyThis, eachArguments)->toBoolean(exec); 984 985 if (id == Every && !predicateResult) { 986 result = jsBoolean(false); 987 break; 988 } 989 if (id == Some && predicateResult) { 990 result = jsBoolean(true); 991 break; 992 } 993 } 994 break; 995 } 996 997 case IndexOf: { 998 // JavaScript 1.5 Extension by Mozilla 999 // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf 1000 1001 unsigned index = 0; 1002 double d = args[1]->toInteger(exec); 1003 if (d < 0) 1004 d += length; 1005 if (d > 0) { 1006 if (d > length) 1007 index = length; 1008 else 1009 index = static_cast<unsigned>(d); 1010 } 1011 1012 JSValue* searchElement = args[0]; 1013 for (; index < length; ++index) { 1014 JSValue* e = getProperty(exec, thisObj, index); 1015 if (!e) 1016 continue; 1017 if (strictEqual(exec, searchElement, e)) 1018 return jsNumber(index); 1019 } 1020 1021 return jsNumber(-1); 1022 } 1023 case LastIndexOf: { 1024 // JavaScript 1.6 Extension by Mozilla 1025 // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf 1026 1027 int index = length - 1; 1028 double d = args[1]->toInteger(exec); 1029 1030 if (d < 0) { 1031 d += length; 1032 if (d < 0) 1033 return jsNumber(-1); 1034 } 1035 if (d < length) 1036 index = static_cast<int>(d); 1037 1038 JSValue* searchElement = args[0]; 1039 for (; index >= 0; --index) { 1040 JSValue* e = getProperty(exec, thisObj, index); 1041 if (!e) 1042 continue; 1043 if (strictEqual(exec, searchElement, e)) 1044 return jsNumber(index); 1045 } 1046 1047 return jsNumber(-1); 1048 } 1049 default: 1050 ASSERT(0); 1051 result = 0; 1052 break; 1053 } 1054 return result; 1055 } 1056 1057 // ------------------------------ ArrayObjectImp ------------------------------- 1058 1059 ArrayObjectImp::ArrayObjectImp(ExecState *exec, 1060 FunctionPrototype *funcProto, 1061 ArrayPrototype *arrayProto) 1062 : InternalFunctionImp(funcProto) 1063 { 1064 // ECMA 15.4.3.1 Array.prototype 1065 put(exec, exec->propertyNames().prototype, arrayProto, DontEnum|DontDelete|ReadOnly); 1066 1067 // no. of arguments for constructor 1068 put(exec, exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); 1069 } 1070 1071 bool ArrayObjectImp::implementsConstruct() const 1072 { 1073 return true; 1074 } 1075 1076 // ECMA 15.4.2 1077 JSObject *ArrayObjectImp::construct(ExecState *exec, const List &args) 1078 { 1079 // a single numeric argument denotes the array size (!) 1080 if (args.size() == 1 && args[0]->isNumber()) { 1081 uint32_t n = args[0]->toUInt32(exec); 1082 if (n != args[0]->toNumber(exec)) 1083 return throwError(exec, RangeError, "Array size is not a small enough positive integer."); 1084 return new ArrayInstance(exec->lexicalInterpreter()->builtinArrayPrototype(), n); 1085 } 1086 1087 // otherwise the array is constructed with the arguments in it 1088 return new ArrayInstance(exec->lexicalInterpreter()->builtinArrayPrototype(), args); 1089 } 1090 1091 // ECMA 15.6.1 1092 JSValue *ArrayObjectImp::callAsFunction(ExecState *exec, JSObject *, const List &args) 1093 { 1094 // equivalent to 'new Array(....)' 1095 return construct(exec,args); 1096 } 1097 1098 } 570 storage->m_vector[numDefined++] = v; 571 } 572 } 573 574 unsigned newUsedVectorLength = numDefined + numUndefined; 575 576 if (SparseArrayValueMap* map = storage->m_sparseValueMap) { 577 newUsedVectorLength += map->size(); 578 if (newUsedVectorLength > m_vectorLength) { 579 increaseVectorLength(newUsedVectorLength); 580 storage = m_storage; 581 } 582 583 SparseArrayValueMap::iterator end = map->end(); 584 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) 585 storage->m_vector[numDefined++] = it->second; 586 587 delete map; 588 storage->m_sparseValueMap = 0; 589 } 590 591 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 592 storage->m_vector[i] = jsUndefined(); 593 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 594 storage->m_vector[i] = 0; 595 596 return numDefined; 597 } 598 599 } -
trunk/JavaScriptCore/kjs/array_instance.h
r26850 r26881 1 1 // -*- c-basic-offset: 2 -*- 2 2 /* 3 * This file is part of the KDE libraries4 3 * Copyright (C) 1999-2000 Harri Porten ([email protected]) 5 4 * Copyright (C) 2003, 2007 Apple Inc. All rights reserved. … … 28 27 namespace KJS { 29 28 29 struct ArrayStorage; 30 30 31 class ArrayInstance : public JSObject { 31 32 public: 32 ArrayInstance(JSObject *proto, unsigned initialLength);33 ArrayInstance(JSObject *proto, const List &initialValues);33 ArrayInstance(JSObject* prototype, unsigned initialLength); 34 ArrayInstance(JSObject* prototype, const List& initialValues); 34 35 ~ArrayInstance(); 35 36 36 virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);37 virtual bool getOwnPropertySlot(ExecState *, unsigned, PropertySlot&);38 virtual void put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr= None);39 virtual void put(ExecState *exec, unsigned propertyName, JSValue *value, int attr= None);40 virtual bool deleteProperty(ExecState * exec, const Identifier &propertyName);41 virtual bool deleteProperty(ExecState * exec, unsigned propertyName);37 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); 38 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 39 virtual void put(ExecState*, const Identifier& propertyName, JSValue*, int attributes = None); 40 virtual void put(ExecState*, unsigned propertyName, JSValue*, int attributes = None); 41 virtual bool deleteProperty(ExecState *, const Identifier& propertyName); 42 virtual bool deleteProperty(ExecState *, unsigned propertyName); 42 43 virtual void getPropertyNames(ExecState*, PropertyNameArray&); 43 44 44 45 virtual void mark(); 45 46 46 virtual const ClassInfo *classInfo() const { return &info; }47 virtual const ClassInfo* classInfo() const { return &info; } 47 48 static const ClassInfo info; 49 50 unsigned getLength() const { return m_length; } 51 JSValue* getItem(unsigned) const; 52 53 void sort(ExecState*); 54 void sort(ExecState*, JSObject* compareFunction); 55 56 private: 57 static JSValue* lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); 58 bool inlineGetOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 59 60 void setLength(unsigned); 61 void increaseVectorLength(unsigned newLength); 48 62 49 unsigned getLength() const { return length; } 50 JSValue* getItem(unsigned) const; 51 52 void sort(ExecState *exec); 53 void sort(ExecState *exec, JSObject *compareFunction); 54 55 private: 56 static JSValue *lengthGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&); 63 unsigned compactForSorting(); 57 64 58 void setLength(unsigned newLength); 59 60 unsigned compactForSorting(); 61 62 void resizeStorage(unsigned); 63 64 // store capacity in extra space at the beginning of the storage array to save space 65 size_t capacity() { return storage ? reinterpret_cast<size_t>(storage[-1]) : 0; } 66 67 unsigned length; 68 unsigned storageLength; 69 JSValue **storage; 65 unsigned m_length; 66 unsigned m_vectorLength; 67 ArrayStorage* m_storage; 70 68 }; 71 69 -
trunk/JavaScriptCore/kjs/array_object.cpp
r26862 r26881 1 1 // -*- c-basic-offset: 2 -*- 2 2 /* 3 * This file is part of the KDE libraries4 3 * Copyright (C) 1999-2000 Harri Porten ([email protected]) 5 4 * Copyright (C) 2003, 2007 Apple Inc. All rights reserved. … … 27 26 #include "array_object.lut.h" 28 27 29 #include "PropertyNameArray.h"30 28 #include "error_object.h" 31 29 #include "lookup.h" … … 35 33 #include <wtf/HashSet.h> 36 34 37 38 35 namespace KJS { 39 40 typedef HashMap<unsigned, JSValue*> OverflowMap;41 42 static inline OverflowMap* overflowMap(JSValue** storage)43 {44 return storage ? reinterpret_cast<OverflowMap*>(storage[-2]) : 0;45 }46 47 // ------------------------------ ArrayInstance -----------------------------48 49 const unsigned sparseArrayCutoff = 10000;50 51 const ClassInfo ArrayInstance::info = {"Array", 0, 0, 0};52 53 static inline JSValue** allocateStorage(size_t capacity)54 {55 if (capacity == 0)56 return 0;57 58 // store capacity and overflow map in extra space before the beginning of the storage array to save space59 JSValue** storage = static_cast<JSValue**>(fastCalloc(capacity + 2, sizeof(JSValue *))) + 2;60 storage[-1] = reinterpret_cast<JSValue*>(capacity);61 return storage;62 }63 64 static inline void reallocateStorage(JSValue**& storage, size_t newCapacity)65 {66 if (!storage) {67 storage = allocateStorage(newCapacity);68 return;69 }70 71 // store capacity and overflow map in extra space before the beginning of the storage array to save space72 storage = static_cast<JSValue**>(fastRealloc(storage - 2, (newCapacity + 2) * sizeof (JSValue*))) + 2;73 storage[-1] = reinterpret_cast<JSValue*>(newCapacity);74 }75 76 static inline void freeStorage(JSValue** storage)77 {78 if (storage)79 fastFree(storage - 2);80 }81 82 ArrayInstance::ArrayInstance(JSObject *proto, unsigned initialLength)83 : JSObject(proto)84 , length(initialLength)85 , storageLength(initialLength < sparseArrayCutoff ? initialLength : 0)86 , storage(allocateStorage(storageLength))87 {88 Collector::reportExtraMemoryCost(storageLength * sizeof(JSValue*));89 }90 91 ArrayInstance::ArrayInstance(JSObject *proto, const List &list)92 : JSObject(proto)93 , length(list.size())94 , storageLength(length)95 , storage(allocateStorage(storageLength))96 {97 ListIterator it = list.begin();98 unsigned l = length;99 for (unsigned i = 0; i < l; ++i)100 storage[i] = it++;101 // When the array is created non-empty, its cells are filled so it's really no worse than102 // a property map. Therefore don't report extra memory cost.103 }104 105 ArrayInstance::~ArrayInstance()106 {107 if (storage) {108 delete reinterpret_cast<OverflowMap*>(storage[-2]);109 freeStorage(storage);110 }111 }112 113 JSValue* ArrayInstance::getItem(unsigned i) const114 {115 if (i < storageLength) {116 JSValue* value = storage[i];117 return value ? value : jsUndefined();118 }119 120 const OverflowMap* overflow = overflowMap(storage);121 if (!overflow)122 return jsUndefined();123 124 JSValue* value = overflow->get(i);125 return value ? value : jsUndefined();126 }127 128 JSValue *ArrayInstance::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)129 {130 return jsNumber(static_cast<ArrayInstance *>(slot.slotBase())->length);131 }132 133 bool ArrayInstance::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)134 {135 if (propertyName == exec->propertyNames().length) {136 slot.setCustom(this, lengthGetter);137 return true;138 }139 140 bool ok;141 unsigned index = propertyName.toArrayIndex(&ok);142 if (!ok)143 return JSObject::getOwnPropertySlot(exec, propertyName, slot);144 145 if (index < storageLength) {146 JSValue *v = storage[index];147 if (!v)148 return false;149 slot.setValueSlot(this, &storage[index]);150 return true;151 }152 if (index > MAX_ARRAY_INDEX)153 return JSObject::getOwnPropertySlot(exec, propertyName, slot);154 OverflowMap* overflow = overflowMap(storage);155 if (!overflow)156 return false;157 OverflowMap::iterator it = overflow->find(index);158 if (it == overflow->end())159 return false;160 slot.setValueSlot(this, &it->second);161 return true;162 }163 164 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, unsigned index, PropertySlot& slot)165 {166 if (index < storageLength) {167 JSValue *v = storage[index];168 if (!v)169 return false;170 slot.setValueSlot(this, &storage[index]);171 return true;172 }173 if (index > MAX_ARRAY_INDEX)174 return getOwnPropertySlot(exec, Identifier::from(index), slot);175 OverflowMap* overflow = overflowMap(storage);176 if (!overflow)177 return false;178 OverflowMap::iterator it = overflow->find(index);179 if (it == overflow->end())180 return false;181 slot.setValueSlot(this, &it->second);182 return true;183 }184 185 // Special implementation of [[Put]] - see ECMA 15.4.5.1186 void ArrayInstance::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)187 {188 if (propertyName == exec->propertyNames().length) {189 unsigned int newLen = value->toUInt32(exec);190 if (value->toNumber(exec) != double(newLen)) {191 throwError(exec, RangeError, "Invalid array length.");192 return;193 }194 setLength(newLen);195 return;196 }197 198 bool ok;199 unsigned index = propertyName.toArrayIndex(&ok);200 if (ok) {201 put(exec, index, value, attr);202 return;203 }204 205 JSObject::put(exec, propertyName, value, attr);206 }207 208 void ArrayInstance::put(ExecState *exec, unsigned index, JSValue *value, int attr)209 {210 // 0xFFFFFFFF is a bit weird --- it should be treated as a non-array index, even when it's a string211 if (index > MAX_ARRAY_INDEX) {212 put(exec, Identifier::from(index), value, attr);213 return;214 }215 216 if (index < sparseArrayCutoff && index >= storageLength)217 resizeStorage(index + 1);218 219 if (index >= length)220 length = index + 1;221 222 if (index < storageLength) {223 storage[index] = value;224 return;225 }226 227 OverflowMap* overflow = overflowMap(storage);228 if (!overflow) {229 overflow = new OverflowMap;230 if (!storage)231 storage = allocateStorage(1);232 storage[-2] = reinterpret_cast<JSValue*>(overflow);233 }234 overflow->add(index, value);235 }236 237 bool ArrayInstance::deleteProperty(ExecState* exec, const Identifier &propertyName)238 {239 if (propertyName == exec->propertyNames().length)240 return false;241 242 bool ok;243 uint32_t index = propertyName.toArrayIndex(&ok);244 if (ok) {245 if (index >= length)246 return true;247 if (index < storageLength) {248 storage[index] = 0;249 return true;250 }251 if (OverflowMap* overflow = overflowMap(storage)) {252 OverflowMap::iterator it = overflow->find(index);253 if (it == overflow->end())254 return false;255 overflow->remove(it);256 return true;257 }258 return false;259 }260 261 return JSObject::deleteProperty(exec, propertyName);262 }263 264 bool ArrayInstance::deleteProperty(ExecState *exec, unsigned index)265 {266 if (index > MAX_ARRAY_INDEX)267 return deleteProperty(exec, Identifier::from(index));268 269 if (index >= length)270 return true;271 if (index < storageLength) {272 storage[index] = 0;273 return true;274 }275 OverflowMap* overflow = overflowMap(storage);276 if (!overflow)277 return false;278 OverflowMap::iterator it = overflow->find(index);279 if (it == overflow->end())280 return false;281 overflow->remove(it);282 return true;283 }284 285 void ArrayInstance::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)286 {287 // avoid fetching this every time through the loop288 JSValue* undefined = jsUndefined();289 290 for (unsigned i = 0; i < storageLength; ++i) {291 JSValue* value = storage[i];292 if (value && value != undefined)293 propertyNames.add(Identifier::from(i));294 }295 296 OverflowMap* overflow = overflowMap(storage);297 if (overflow) {298 OverflowMap::iterator end = overflow->end();299 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it) {300 JSValue* value = it->second;301 if (value && value != undefined)302 propertyNames.add(Identifier::from(it->first));303 }304 }305 306 JSObject::getPropertyNames(exec, propertyNames);307 }308 309 void ArrayInstance::resizeStorage(unsigned newLength)310 {311 if (newLength < storageLength) {312 memset(storage + newLength, 0, sizeof(JSValue *) * (storageLength - newLength));313 }314 size_t cap = capacity();315 if (newLength > cap) {316 unsigned newCapacity;317 if (newLength > sparseArrayCutoff) {318 newCapacity = newLength;319 } else {320 newCapacity = (newLength * 3 + 1) / 2;321 if (newCapacity > sparseArrayCutoff) {322 newCapacity = sparseArrayCutoff;323 }324 }325 326 reallocateStorage(storage, newCapacity);327 memset(storage + cap, 0, sizeof(JSValue*) * (newCapacity - cap));328 }329 storageLength = newLength;330 }331 332 void ArrayInstance::setLength(unsigned newLength)333 {334 if (newLength <= storageLength)335 resizeStorage(newLength);336 337 if (newLength < length) {338 if (OverflowMap* overflow = overflowMap(storage)) {339 OverflowMap copy = *overflow;340 OverflowMap::iterator end = copy.end();341 for (OverflowMap::iterator it = copy.begin(); it != end; ++it) {342 if (it->first >= newLength)343 overflow->remove(it->first);344 }345 }346 }347 348 length = newLength;349 }350 351 void ArrayInstance::mark()352 {353 JSObject::mark();354 unsigned l = storageLength;355 for (unsigned i = 0; i < l; ++i) {356 JSValue* imp = storage[i];357 if (imp && !imp->marked())358 imp->mark();359 }360 if (OverflowMap* overflow = overflowMap(storage)) {361 OverflowMap::iterator end = overflow->end();362 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it) {363 JSValue* value = it->second;364 if (value && !value->marked())365 value->mark();366 }367 }368 }369 370 static ExecState* execForCompareByStringForQSort = 0;371 372 static int compareByStringForQSort(const void *a, const void *b)373 {374 ExecState *exec = execForCompareByStringForQSort;375 JSValue *va = *(JSValue **)a;376 JSValue *vb = *(JSValue **)b;377 ASSERT(!va->isUndefined());378 ASSERT(!vb->isUndefined());379 return compare(va->toString(exec), vb->toString(exec));380 }381 382 void ArrayInstance::sort(ExecState* exec)383 {384 size_t lengthNotIncludingUndefined = compactForSorting();385 386 ExecState* oldExec = execForCompareByStringForQSort;387 execForCompareByStringForQSort = exec;388 #if HAVE(MERGESORT)389 // mergesort usually does fewer compares, so it is actually faster than qsort for JS sorts.390 // however, becuase it requires extra copies of the storage buffer, don't use it for very391 // large arrays392 // FIXME: for sorting by string value, the fastest thing would actually be to convert all the393 // values to string once up front, and then use a radix sort. That would be O(N) rather than394 // O(N log N).395 if (lengthNotIncludingUndefined < sparseArrayCutoff) {396 JSValue** storageCopy = allocateStorage(capacity());397 memcpy(storageCopy, storage, capacity() * sizeof(JSValue*));398 mergesort(storageCopy, lengthNotIncludingUndefined, sizeof(JSValue *), compareByStringForQSort);399 freeStorage(storage);400 storage = storageCopy;401 execForCompareByStringForQSort = oldExec;402 return;403 }404 #endif405 406 qsort(storage, lengthNotIncludingUndefined, sizeof(JSValue*), compareByStringForQSort);407 execForCompareByStringForQSort = oldExec;408 }409 410 struct CompareWithCompareFunctionArguments {411 CompareWithCompareFunctionArguments(ExecState *e, JSObject *cf)412 : exec(e)413 , compareFunction(cf)414 , globalObject(e->dynamicInterpreter()->globalObject())415 {416 arguments.append(jsUndefined());417 arguments.append(jsUndefined());418 }419 420 ExecState *exec;421 JSObject *compareFunction;422 List arguments;423 JSObject *globalObject;424 };425 426 static CompareWithCompareFunctionArguments* compareWithCompareFunctionArguments = 0;427 428 static int compareWithCompareFunctionForQSort(const void *a, const void *b)429 {430 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;431 432 JSValue *va = *(JSValue **)a;433 JSValue *vb = *(JSValue **)b;434 ASSERT(!va->isUndefined());435 ASSERT(!vb->isUndefined());436 437 args->arguments.clear();438 args->arguments.append(va);439 args->arguments.append(vb);440 double compareResult = args->compareFunction->call441 (args->exec, args->globalObject, args->arguments)->toNumber(args->exec);442 return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;443 }444 445 void ArrayInstance::sort(ExecState* exec, JSObject* compareFunction)446 {447 size_t lengthNotIncludingUndefined = compactForSorting();448 449 CompareWithCompareFunctionArguments* oldArgs = compareWithCompareFunctionArguments;450 CompareWithCompareFunctionArguments args(exec, compareFunction);451 compareWithCompareFunctionArguments = &args;452 #if HAVE(MERGESORT)453 // mergesort usually does fewer compares, so it is actually faster than qsort for JS sorts.454 // however, becuase it requires extra copies of the storage buffer, don't use it for very455 // large arrays456 // FIXME: a tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even457 // better job of minimizing compares458 if (lengthNotIncludingUndefined < sparseArrayCutoff) {459 JSValue** storageCopy = allocateStorage(capacity());460 memcpy(storageCopy, storage, capacity() * sizeof(JSValue*));461 mergesort(storageCopy, lengthNotIncludingUndefined, sizeof(JSValue *), compareWithCompareFunctionForQSort);462 freeStorage(storage);463 storage = storageCopy;464 compareWithCompareFunctionArguments = oldArgs;465 return;466 }467 #endif468 qsort(storage, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort);469 compareWithCompareFunctionArguments = oldArgs;470 }471 472 unsigned ArrayInstance::compactForSorting()473 {474 JSValue *undefined = jsUndefined();475 476 unsigned o = 0;477 478 for (unsigned i = 0; i != storageLength; ++i) {479 JSValue *v = storage[i];480 if (v && v != undefined) {481 if (o != i)482 storage[o] = v;483 o++;484 }485 }486 487 unsigned newLength = o;488 489 if (OverflowMap* overflow = overflowMap(storage)) {490 OverflowMap::iterator end = overflow->end();491 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it)492 newLength += it->second != undefined;493 494 if (newLength > storageLength)495 resizeStorage(newLength);496 497 for (OverflowMap::iterator it = overflow->begin(); it != end; ++it) {498 JSValue* v = it->second;499 if (v != undefined) {500 storage[o] = v;501 o++;502 }503 }504 delete overflow;505 storage[-2] = 0;506 }507 508 if (newLength != storageLength)509 memset(storage + o, 0, sizeof(JSValue *) * (storageLength - o));510 511 return o;512 }513 36 514 37 // ------------------------------ ArrayPrototype ---------------------------- -
trunk/JavaScriptCore/kjs/array_object.h
r15952 r26881 50 50 }; 51 51 52 const unsigned MAX_ARRAY_INDEX = 0xFFFFFFFEu;53 54 52 class ArrayObjectImp : public InternalFunctionImp { 55 53 public:
Note:
See TracChangeset
for help on using the changeset viewer.