Changeset 128400 in webkit for trunk/Source/JavaScriptCore/runtime
- Timestamp:
- Sep 12, 2012, 9:18:52 PM (13 years ago)
- Location:
- trunk/Source/JavaScriptCore/runtime
- Files:
-
- 13 added
- 1 deleted
- 39 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/runtime/Arguments.cpp
r127191 r128400 256 256 } 257 257 258 return JSObject::deleteProperty (thisObject, exec, Identifier(exec, String::number(i)));258 return JSObject::deletePropertyByIndex(thisObject, exec, i); 259 259 } 260 260 … … 309 309 PropertySlot slot; 310 310 if ((!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) 311 object->putDirect (exec->globalData(), propertyName, thisObject->argument(i).get(), 0);311 object->putDirectMayBeIndex(exec, propertyName, thisObject->argument(i).get()); 312 312 if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow)) 313 313 return false; -
trunk/Source/JavaScriptCore/runtime/ArrayConstructor.cpp
r127505 r128400 26 26 27 27 #include "ArrayPrototype.h" 28 #include "ButterflyInlineMethods.h" 29 #include "CopiedSpaceInlineMethods.h" 28 30 #include "Error.h" 29 31 #include "ExceptionHelpers.h" -
trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
r127505 r128400 25 25 #include "ArrayPrototype.h" 26 26 27 #include "ButterflyInlineMethods.h" 27 28 #include "CachedCall.h" 28 29 #include "CodeBlock.h" 30 #include "CopiedSpaceInlineMethods.h" 29 31 #include "Interpreter.h" 30 32 #include "JIT.h" … … 115 117 */ 116 118 119 ArrayPrototype* ArrayPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) 120 { 121 Butterfly* butterfly = createArrayButterfly(exec->globalData(), 0); 122 ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure, butterfly); 123 prototype->finishCreation(globalObject); 124 return prototype; 125 } 126 117 127 // ECMA 15.4.4 118 ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure )119 : JSArray(globalObject->globalData(), structure )128 ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure, Butterfly* butterfly) 129 : JSArray(globalObject->globalData(), structure, butterfly) 120 130 { 121 131 } … … 293 303 for (unsigned k = 0; k < length; k++) { 294 304 JSValue element; 295 if (thisObj->canGetIndex (k))296 element = thisObj->getIndex (k);305 if (thisObj->canGetIndexQuickly(k)) 306 element = thisObj->getIndexQuickly(k); 297 307 else 298 308 element = thisObj->get(exec, k); … … 414 424 415 425 for (; k < length; k++) { 416 if (!array->canGetIndex (k))426 if (!array->canGetIndexQuickly(k)) 417 427 break; 418 428 419 JSValue element = array->getIndex (k);429 JSValue element = array->getIndexQuickly(k); 420 430 if (!element.isUndefinedOrNull()) 421 431 stringJoiner.append(element.toWTFStringInline(exec)); … … 629 639 CallType callType = getCallData(function, callData); 630 640 631 if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparse Mode()) {641 if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseIndexingMode()) { 632 642 if (isNumericCompareFunction(exec, callType, callData)) 633 643 asArray(thisObj)->sortNumeric(exec, function, callType, callData); … … 789 799 CachedCall cachedCall(exec, f, 3); 790 800 for (; k < length && !exec->hadException(); ++k) { 791 if (!array->canGetIndex (k))801 if (!array->canGetIndexQuickly(k)) 792 802 break; 793 JSValue v = array->getIndex (k);803 JSValue v = array->getIndexQuickly(k); 794 804 cachedCall.setThis(applyThis); 795 805 cachedCall.setArgument(0, v); … … 847 857 CachedCall cachedCall(exec, f, 3); 848 858 for (; k < length && !exec->hadException(); ++k) { 849 if (UNLIKELY(!array->canGetIndex (k)))859 if (UNLIKELY(!array->canGetIndexQuickly(k))) 850 860 break; 851 861 852 862 cachedCall.setThis(applyThis); 853 cachedCall.setArgument(0, array->getIndex (k));863 cachedCall.setArgument(0, array->getIndexQuickly(k)); 854 864 cachedCall.setArgument(1, jsNumber(k)); 855 865 cachedCall.setArgument(2, thisObj); … … 910 920 CachedCall cachedCall(exec, f, 3); 911 921 for (; k < length && !exec->hadException(); ++k) { 912 if (UNLIKELY(!array->canGetIndex (k)))922 if (UNLIKELY(!array->canGetIndexQuickly(k))) 913 923 break; 914 924 915 925 cachedCall.setThis(applyThis); 916 cachedCall.setArgument(0, array->getIndex (k));926 cachedCall.setArgument(0, array->getIndexQuickly(k)); 917 927 cachedCall.setArgument(1, jsNumber(k)); 918 928 cachedCall.setArgument(2, thisObj); … … 966 976 CachedCall cachedCall(exec, f, 3); 967 977 for (; k < length && !exec->hadException(); ++k) { 968 if (UNLIKELY(!array->canGetIndex (k)))978 if (UNLIKELY(!array->canGetIndexQuickly(k))) 969 979 break; 970 980 971 981 cachedCall.setThis(applyThis); 972 cachedCall.setArgument(0, array->getIndex (k));982 cachedCall.setArgument(0, array->getIndexQuickly(k)); 973 983 cachedCall.setArgument(1, jsNumber(k)); 974 984 cachedCall.setArgument(2, thisObj); … … 1018 1028 CachedCall cachedCall(exec, f, 3); 1019 1029 for (; k < length && !exec->hadException(); ++k) { 1020 if (UNLIKELY(!array->canGetIndex (k)))1030 if (UNLIKELY(!array->canGetIndexQuickly(k))) 1021 1031 break; 1022 1032 1023 1033 cachedCall.setThis(applyThis); 1024 cachedCall.setArgument(0, array->getIndex (k));1034 cachedCall.setArgument(0, array->getIndexQuickly(k)); 1025 1035 cachedCall.setArgument(1, jsNumber(k)); 1026 1036 cachedCall.setArgument(2, thisObj); … … 1076 1086 if (exec->argumentCount() >= 2) 1077 1087 rv = exec->argument(1); 1078 else if (array && array->canGetIndex (0)){1079 rv = array->getIndex (0);1088 else if (array && array->canGetIndexQuickly(0)) { 1089 rv = array->getIndexQuickly(0); 1080 1090 i = 1; 1081 1091 } else { … … 1098 1108 cachedCall.setArgument(0, rv); 1099 1109 JSValue v; 1100 if (LIKELY(array->canGetIndex (i)))1101 v = array->getIndex (i);1110 if (LIKELY(array->canGetIndexQuickly(i))) 1111 v = array->getIndexQuickly(i); 1102 1112 else 1103 1113 break; // length has been made unsafe while we enumerate fallback to slow path … … 1153 1163 if (exec->argumentCount() >= 2) 1154 1164 rv = exec->argument(1); 1155 else if (array && array->canGetIndex (length - 1)){1156 rv = array->getIndex (length - 1);1165 else if (array && array->canGetIndexQuickly(length - 1)) { 1166 rv = array->getIndexQuickly(length - 1); 1157 1167 i = 1; 1158 1168 } else { … … 1175 1185 cachedCall.setThis(jsUndefined()); 1176 1186 cachedCall.setArgument(0, rv); 1177 if (UNLIKELY(!array->canGetIndex (idx)))1187 if (UNLIKELY(!array->canGetIndexQuickly(idx))) 1178 1188 break; // length has been made unsafe while we enumerate fallback to slow path 1179 cachedCall.setArgument(1, array->getIndex (idx));1189 cachedCall.setArgument(1, array->getIndexQuickly(idx)); 1180 1190 cachedCall.setArgument(2, jsNumber(idx)); 1181 1191 cachedCall.setArgument(3, array); -
trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h
r116828 r128400 29 29 class ArrayPrototype : public JSArray { 30 30 private: 31 ArrayPrototype(JSGlobalObject*, Structure* );31 ArrayPrototype(JSGlobalObject*, Structure*, Butterfly*); 32 32 33 33 public: 34 34 typedef JSArray Base; 35 35 36 static ArrayPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) 37 { 38 ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure); 39 prototype->finishCreation(globalObject); 40 return prototype; 41 } 36 static ArrayPrototype* create(ExecState*, JSGlobalObject*, Structure*); 42 37 43 38 static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); … … 48 43 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) 49 44 { 50 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info );45 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage); 51 46 } 52 47 -
trunk/Source/JavaScriptCore/runtime/ClassInfo.h
r127191 r128400 73 73 GetOwnPropertyNamesFunctionPtr getOwnPropertyNames; 74 74 75 typedef void (*GetOwnNonIndexPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 76 GetOwnNonIndexPropertyNamesFunctionPtr getOwnNonIndexPropertyNames; 77 75 78 typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 76 79 GetPropertyNamesFunctionPtr getPropertyNames; … … 125 128 &ClassName::defaultValue, \ 126 129 &ClassName::getOwnPropertyNames, \ 130 &ClassName::getOwnNonIndexPropertyNames, \ 127 131 &ClassName::getPropertyNames, \ 128 132 &ClassName::className, \ -
trunk/Source/JavaScriptCore/runtime/JSActivation.cpp
r128260 r128400 108 108 } 109 109 110 void JSActivation::getOwn PropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)110 void JSActivation::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 111 111 { 112 112 JSActivation* thisObject = jsCast<JSActivation*>(object); … … 123 123 propertyNames.add(Identifier(exec, it->first.get())); 124 124 } 125 // Skip the JSVariableObject implementation of getOwn PropertyNames126 JSObject::getOwn PropertyNames(thisObject, exec, propertyNames, mode);125 // Skip the JSVariableObject implementation of getOwnNonIndexPropertyNames 126 JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); 127 127 } 128 128 -
trunk/Source/JavaScriptCore/runtime/JSActivation.h
r128260 r128400 66 66 67 67 static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); 68 static void getOwn PropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);68 static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 69 69 JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); 70 70 -
trunk/Source/JavaScriptCore/runtime/JSArray.cpp
r127505 r128400 1 1 /* 2 2 * Copyright (C) 1999-2000 Harri Porten ([email protected]) 3 * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.3 * Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. 4 4 * Copyright (C) 2003 Peter Kelly ([email protected]) 5 5 * Copyright (C) 2006 Alexey Proskuryakov ([email protected]) … … 25 25 26 26 #include "ArrayPrototype.h" 27 #include "ButterflyInlineMethods.h" 27 28 #include "CopiedSpace.h" 28 29 #include "CopiedSpaceInlineMethods.h" … … 31 32 #include "Executable.h" 32 33 #include "GetterSetter.h" 34 #include "IndexingHeaderInlineMethods.h" 33 35 #include "PropertyNameArray.h" 36 #include "Reject.h" 37 #include "SparseArrayValueMapInlineMethods.h" 34 38 #include <wtf/AVLTree.h> 35 39 #include <wtf/Assertions.h> … … 46 50 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray); 47 51 48 // Overview of JSArray49 //50 // Properties of JSArray objects may be stored in one of three locations:51 // * The regular JSObject property map.52 // * A storage vector.53 // * A sparse map of array entries.54 //55 // Properties with non-numeric identifiers, with identifiers that are not representable56 // as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX57 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit58 // integer) are not considered array indices and will be stored in the JSObject property map.59 //60 // All properties with a numeric identifer, representable as an unsigned integer i,61 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the62 // storage vector or the sparse map. An array index i will be handled in the following63 // fashion:64 //65 // * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector,66 // unless the array is in SparseMode in which case all properties go into the map.67 // * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either68 // be stored in the storage vector or in the sparse array, depending on the density of69 // data that would be stored in the vector (a vector being used where at least70 // (1 / minDensityMultiplier) of the entries would be populated).71 // * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored72 // in the sparse array.73 74 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize75 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage76 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +77 // (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).78 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))79 80 // These values have to be macros to be used in max() and min() without introducing81 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.82 #define MIN_SPARSE_ARRAY_INDEX 10000U83 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)84 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.85 #define MAX_ARRAY_INDEX 0xFFFFFFFEU86 87 // The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate88 // for an array that was created with a sepcified length (e.g. a = new Array(123))89 #define BASE_VECTOR_LEN 4U90 91 // The upper bound to the size we'll grow a zero length array when the first element92 // is added.93 #define FIRST_VECTOR_GROW 4U94 95 // Our policy for when to use a vector and when to use a sparse map.96 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.97 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector98 // as long as it is 1/8 full. If more sparse than that, we use a map.99 static const unsigned minDensityMultiplier = 8;100 101 52 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)}; 102 53 103 // We keep track of the size of the last array after it was grown. We use this 104 // as a simple heuristic for as the value to grow the next array from size 0. 105 // This value is capped by the constant FIRST_VECTOR_GROW defined above. 106 static unsigned lastArraySize = 0; 107 108 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) 109 { 110 return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; 111 } 112 113 static bool reject(ExecState* exec, bool throwException, const char* message) 114 { 115 if (throwException) 116 throwTypeError(exec, ASCIILiteral(message)); 117 return false; 118 } 119 120 #if !CHECK_ARRAY_CONSISTENCY 121 122 inline void JSArray::checkConsistency(ConsistencyCheckType) 123 { 124 } 125 54 Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData& globalData, unsigned initialLength) 55 { 56 Butterfly* butterfly = Butterfly::create( 57 globalData, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0)); 58 ArrayStorage* storage = butterfly->arrayStorage(); 59 storage->setLength(initialLength); 60 storage->setVectorLength(0); 61 storage->m_indexBias = 0; 62 storage->m_sparseMap.clear(); 63 storage->m_numValuesInVector = 0; 64 #if CHECK_ARRAY_CONSISTENCY 65 storage->m_initializationIndex = 0; 66 storage->m_inCompactInitialization = 0; 126 67 #endif 127 128 void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength) 129 { 130 Base::finishCreation(globalData); 131 ASSERT(inherits(&s_info)); 132 133 unsigned initialVectorLength = BASE_VECTOR_LEN; 134 unsigned initialStorageSize = storageSize(initialVectorLength); 135 136 void* newStorage = 0; 137 if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage)) 138 CRASH(); 139 140 m_storage = static_cast<ArrayStorage*>(newStorage); 141 m_storage->m_allocBase = m_storage; 142 m_storage->m_length = initialLength; 143 m_vectorLength = initialVectorLength; 144 m_storage->m_numValuesInVector = 0; 145 #if CHECK_ARRAY_CONSISTENCY 146 m_storage->m_inCompactInitialization = false; 147 #endif 148 149 checkConsistency(); 150 } 151 152 JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength) 153 { 154 Base::finishCreation(globalData); 155 ASSERT(inherits(&s_info)); 156 157 // Check for lengths larger than we can handle with a vector. 158 if (initialLength > MAX_STORAGE_VECTOR_LENGTH) 159 return 0; 160 161 unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN); 162 unsigned initialStorageSize = storageSize(initialVectorLength); 163 164 void* newStorage = 0; 165 if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage)) 166 CRASH(); 167 168 m_storage = static_cast<ArrayStorage*>(newStorage); 169 m_storage->m_allocBase = m_storage; 170 m_storage->m_length = initialLength; 171 m_vectorLength = initialVectorLength; 172 m_storage->m_numValuesInVector = initialLength; 173 174 #if CHECK_ARRAY_CONSISTENCY 175 m_storage->m_initializationIndex = 0; 176 m_storage->m_inCompactInitialization = true; 177 #endif 178 179 return this; 180 } 181 182 // This function can be called multiple times on the same object. 183 void JSArray::finalize(JSCell* cell) 184 { 185 JSArray* thisObject = jsCast<JSArray*>(cell); 186 thisObject->checkConsistency(DestructorConsistencyCheck); 187 thisObject->deallocateSparseMap(); 188 } 189 190 inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSArray* array, unsigned i) 191 { 192 SparseArrayEntry entry; 193 entry.setWithoutWriteBarrier(jsUndefined()); 194 195 AddResult result = m_map.add(i, entry); 196 size_t capacity = m_map.capacity(); 197 if (capacity != m_reportedCapacity) { 198 Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>))); 199 m_reportedCapacity = capacity; 200 } 201 return result; 202 } 203 204 inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) 205 { 206 AddResult result = add(array, i); 207 SparseArrayEntry& entry = result.iterator->second; 208 209 // To save a separate find & add, we first always add to the sparse map. 210 // In the uncommon case that this is a new property, and the array is not 211 // extensible, this is not the right thing to have done - so remove again. 212 if (result.isNewEntry && !array->isExtensible()) { 213 remove(result.iterator); 214 if (shouldThrow) 215 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 216 return; 217 } 218 219 if (!(entry.attributes & Accessor)) { 220 if (entry.attributes & ReadOnly) { 221 if (shouldThrow) 222 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 223 return; 224 } 225 226 entry.set(exec->globalData(), array, value); 227 return; 228 } 229 230 JSValue accessor = entry.Base::get(); 231 ASSERT(accessor.isGetterSetter()); 232 JSObject* setter = asGetterSetter(accessor)->setter(); 233 234 if (!setter) { 235 if (shouldThrow) 236 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 237 return; 238 } 239 240 CallData callData; 241 CallType callType = setter->methodTable()->getCallData(setter, callData); 242 MarkedArgumentBuffer args; 243 args.append(value); 244 call(exec, setter, callType, callData, array, args); 245 } 246 247 inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, PutDirectIndexMode mode) 248 { 249 AddResult result = add(array, i); 250 SparseArrayEntry& entry = result.iterator->second; 251 252 // To save a separate find & add, we first always add to the sparse map. 253 // In the uncommon case that this is a new property, and the array is not 254 // extensible, this is not the right thing to have done - so remove again. 255 if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isExtensible()) { 256 remove(result.iterator); 257 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); 258 } 259 260 entry.attributes = 0; 261 entry.set(exec->globalData(), array, value); 262 return true; 263 } 264 265 inline void SparseArrayEntry::get(PropertySlot& slot) const 266 { 267 JSValue value = Base::get(); 268 ASSERT(value); 269 270 if (LIKELY(!value.isGetterSetter())) { 271 slot.setValue(value); 272 return; 273 } 274 275 JSObject* getter = asGetterSetter(value)->getter(); 276 if (!getter) { 277 slot.setUndefined(); 278 return; 279 } 280 281 slot.setGetterSlot(getter); 282 } 283 284 inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const 285 { 286 descriptor.setDescriptor(Base::get(), attributes); 287 } 288 289 inline JSValue SparseArrayEntry::get(ExecState* exec, JSArray* array) const 290 { 291 JSValue result = Base::get(); 292 ASSERT(result); 293 294 if (LIKELY(!result.isGetterSetter())) 295 return result; 296 297 JSObject* getter = asGetterSetter(result)->getter(); 298 if (!getter) 299 return jsUndefined(); 300 301 CallData callData; 302 CallType callType = getter->methodTable()->getCallData(getter, callData); 303 return call(exec, getter, callType, callData, array, exec->emptyList()); 304 } 305 306 inline JSValue SparseArrayEntry::getNonSparseMode() const 307 { 308 ASSERT(!attributes); 309 return Base::get(); 310 } 311 312 inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor) 313 { 314 iterator end = m_map.end(); 315 for (iterator it = m_map.begin(); it != end; ++it) 316 visitor.append(&it->second); 317 } 318 319 void JSArray::allocateSparseMap(JSGlobalData& globalData) 320 { 321 m_sparseValueMap = new SparseArrayValueMap; 322 globalData.heap.addFinalizer(this, finalize); 323 } 324 325 void JSArray::deallocateSparseMap() 326 { 327 delete m_sparseValueMap; 328 m_sparseValueMap = 0; 329 } 330 331 void JSArray::enterDictionaryMode(JSGlobalData& globalData) 332 { 333 ArrayStorage* storage = m_storage; 334 SparseArrayValueMap* map = m_sparseValueMap; 335 336 if (!map) { 337 allocateSparseMap(globalData); 338 map = m_sparseValueMap; 339 } 340 341 if (map->sparseMode()) 342 return; 343 344 map->setSparseMode(); 345 346 unsigned usedVectorLength = min(storage->m_length, m_vectorLength); 347 for (unsigned i = 0; i < usedVectorLength; ++i) { 348 JSValue value = storage->m_vector[i].get(); 349 // This will always be a new entry in the map, so no need to check we can write, 350 // and attributes are default so no need to set them. 351 if (value) 352 map->add(this, i).iterator->second.set(globalData, this, value); 353 } 354 355 void* newRawStorage = 0; 356 if (!globalData.heap.tryAllocateStorage(storageSize(0), &newRawStorage)) 357 CRASH(); 358 359 ArrayStorage* newStorage = static_cast<ArrayStorage*>(newRawStorage); 360 memcpy(newStorage, m_storage, storageSize(0)); 361 newStorage->m_allocBase = newStorage; 362 m_storage = newStorage; 363 m_indexBias = 0; 364 m_vectorLength = 0; 365 } 366 367 void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) 368 { 369 if (descriptor.isDataDescriptor()) { 370 if (descriptor.value()) 371 entryInMap->set(exec->globalData(), this, descriptor.value()); 372 else if (oldDescriptor.isAccessorDescriptor()) 373 entryInMap->set(exec->globalData(), this, jsUndefined()); 374 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; 375 return; 376 } 377 378 if (descriptor.isAccessorDescriptor()) { 379 JSObject* getter = 0; 380 if (descriptor.getterPresent()) 381 getter = descriptor.getterObject(); 382 else if (oldDescriptor.isAccessorDescriptor()) 383 getter = oldDescriptor.getterObject(); 384 JSObject* setter = 0; 385 if (descriptor.setterPresent()) 386 setter = descriptor.setterObject(); 387 else if (oldDescriptor.isAccessorDescriptor()) 388 setter = oldDescriptor.setterObject(); 389 390 GetterSetter* accessor = GetterSetter::create(exec); 391 if (getter) 392 accessor->setGetter(exec->globalData(), getter); 393 if (setter) 394 accessor->setSetter(exec->globalData(), setter); 395 396 entryInMap->set(exec->globalData(), this, accessor); 397 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly; 398 return; 399 } 400 401 ASSERT(descriptor.isGenericDescriptor()); 402 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor); 403 } 404 405 // Defined in ES5.1 8.12.9 406 bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) 407 { 408 ASSERT(index != 0xFFFFFFFF); 409 410 if (!inSparseMode()) { 411 // Fast case: we're putting a regular property to a regular array 412 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false 413 // – however if the property currently exists missing attributes will override from their current 'true' 414 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). 415 if (!descriptor.attributes()) { 416 ASSERT(!descriptor.isAccessorDescriptor()); 417 return putDirectIndex(exec, index, descriptor.value(), throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); 418 } 419 420 enterDictionaryMode(exec->globalData()); 421 } 422 423 SparseArrayValueMap* map = m_sparseValueMap; 424 ASSERT(map); 425 426 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. 427 SparseArrayValueMap::AddResult result = map->add(this, index); 428 SparseArrayEntry* entryInMap = &result.iterator->second; 429 430 // 2. Let extensible be the value of the [[Extensible]] internal property of O. 431 // 3. If current is undefined and extensible is false, then Reject. 432 // 4. If current is undefined and extensible is true, then 433 if (result.isNewEntry) { 434 if (!isExtensible()) { 435 map->remove(result.iterator); 436 return reject(exec, throwException, "Attempting to define property on object that is not extensible."); 437 } 438 439 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property 440 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values 441 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly 442 // created property is set to its default value. 443 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of 444 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by 445 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property 446 // is set to its default value. 447 // 4.c. Return true. 448 449 PropertyDescriptor defaults; 450 entryInMap->setWithoutWriteBarrier(jsUndefined()); 451 entryInMap->attributes = DontDelete | DontEnum | ReadOnly; 452 entryInMap->get(defaults); 453 454 putDescriptor(exec, entryInMap, descriptor, defaults); 455 if (index >= m_storage->m_length) 456 m_storage->m_length = index + 1; 457 return true; 458 } 459 460 // 5. Return true, if every field in Desc is absent. 461 // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12). 462 PropertyDescriptor current; 463 entryInMap->get(current); 464 if (descriptor.isEmpty() || descriptor.equalTo(exec, current)) 465 return true; 466 467 // 7. If the [[Configurable]] field of current is false then 468 if (!current.configurable()) { 469 // 7.a. Reject, if the [[Configurable]] field of Desc is true. 470 if (descriptor.configurablePresent() && descriptor.configurable()) 471 return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); 472 // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other. 473 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable()) 474 return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); 475 } 476 477 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required. 478 if (!descriptor.isGenericDescriptor()) { 479 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then 480 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) { 481 // 9.a. Reject, if the [[Configurable]] field of current is false. 482 if (!current.configurable()) 483 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); 484 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a 485 // data property to an accessor property. Preserve the existing values of the converted property‘s 486 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property‘s attributes to 487 // their default values. 488 // 9.c. Else, convert the property named P of object O from an accessor property to a data property. 489 // Preserve the existing values of the converted property‘s [[Configurable]] and [[Enumerable]] 490 // attributes and set the rest of the property‘s attributes to their default values. 491 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) { 492 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then 493 // 10.a. If the [[Configurable]] field of current is false, then 494 if (!current.configurable() && !current.writable()) { 495 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true. 496 if (descriptor.writable()) 497 return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); 498 // 10.a.ii. If the [[Writable]] field of current is false, then 499 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false. 500 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value())) 501 return reject(exec, throwException, "Attempting to change value of a readonly property."); 502 } 503 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable. 504 } else { 505 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent()); 506 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then 507 if (!current.configurable()) { 508 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false. 509 if (descriptor.setterPresent() && descriptor.setter() != current.setter()) 510 return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property."); 511 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false. 512 if (descriptor.getterPresent() && descriptor.getter() != current.getter()) 513 return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property."); 514 } 515 } 516 } 517 518 // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field. 519 putDescriptor(exec, entryInMap, descriptor, current); 520 // 13. Return true. 521 return true; 68 return butterfly; 522 69 } 523 70 … … 528 75 return; 529 76 530 enterDictionary Mode(exec->globalData());531 532 SparseArrayValueMap* map = m_sparseValueMap;77 enterDictionaryIndexingMode(exec->globalData()); 78 79 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get(); 533 80 ASSERT(map); 534 81 map->setLengthIsReadOnly(); … … 631 178 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true. 632 179 // f. Return true. 633 return array->defineOwnNumericProperty(exec, index, descriptor, throwException); 634 } 635 636 return JSObject::defineOwnProperty(object, exec, propertyName, descriptor, throwException); 637 } 638 639 bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) 640 { 641 JSArray* thisObject = jsCast<JSArray*>(cell); 642 ArrayStorage* storage = thisObject->m_storage; 643 644 if (i >= storage->m_length) { 645 if (i > MAX_ARRAY_INDEX) 646 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); 647 return false; 648 } 649 650 if (i < thisObject->m_vectorLength) { 651 JSValue value = storage->m_vector[i].get(); 652 if (value) { 653 slot.setValue(value); 654 return true; 655 } 656 } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) { 657 SparseArrayValueMap::iterator it = map->find(i); 658 if (it != map->notFound()) { 659 it->second.get(slot); 660 return true; 661 } 662 } 663 664 return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); 180 return array->defineOwnIndexedProperty(exec, index, descriptor, throwException); 181 } 182 183 return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); 665 184 } 666 185 … … 673 192 } 674 193 675 unsigned i = propertyName.asIndex();676 if (i != PropertyName::NotAnIndex)677 return JSArray::getOwnPropertySlotByIndex(thisObject, exec, i, slot);678 679 194 return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); 680 195 } … … 688 203 } 689 204 690 ArrayStorage* storage = thisObject->m_storage;691 692 unsigned i = propertyName.asIndex();693 if (i != PropertyName::NotAnIndex) {694 if (i >= storage->m_length)695 return false;696 if (i < thisObject->m_vectorLength) {697 WriteBarrier<Unknown>& value = storage->m_vector[i];698 if (value) {699 descriptor.setDescriptor(value.get(), 0);700 return true;701 }702 } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {703 SparseArrayValueMap::iterator it = map->find(i);704 if (it != map->notFound()) {705 it->second.get(descriptor);706 return true;707 }708 }709 }710 205 return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); 711 206 } … … 715 210 { 716 211 JSArray* thisObject = jsCast<JSArray*>(cell); 717 unsigned i = propertyName.asIndex();718 if (i != PropertyName::NotAnIndex) {719 putByIndex(thisObject, exec, i, value, slot.isStrictMode());720 return;721 }722 212 723 213 if (propertyName == exec->propertyNames().length) { … … 734 224 } 735 225 736 void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)226 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) 737 227 { 738 228 JSArray* thisObject = jsCast<JSArray*>(cell); 739 thisObject->checkConsistency();740 741 ArrayStorage* storage = thisObject->m_storage;742 743 // Fast case - store to the vector.744 if (i < thisObject->m_vectorLength) {745 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];746 unsigned length = storage->m_length;747 748 // Update m_length & m_numValuesInVector as necessary.749 if (i >= length) {750 length = i + 1;751 storage->m_length = length;752 ++storage->m_numValuesInVector;753 } else if (!valueSlot)754 ++storage->m_numValuesInVector;755 756 valueSlot.set(exec->globalData(), thisObject, value);757 thisObject->checkConsistency();758 return;759 }760 761 // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property.762 if (UNLIKELY(i > MAX_ARRAY_INDEX)) {763 PutPropertySlot slot(shouldThrow);764 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, i), value, slot);765 return;766 }767 768 // For all other cases, call putByIndexBeyondVectorLength.769 thisObject->putByIndexBeyondVectorLength(exec, i, value, shouldThrow);770 thisObject->checkConsistency();771 }772 773 void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)774 {775 JSGlobalData& globalData = exec->globalData();776 777 // i should be a valid array index that is outside of the current vector.778 ASSERT(i >= m_vectorLength);779 ASSERT(i <= MAX_ARRAY_INDEX);780 781 ArrayStorage* storage = m_storage;782 SparseArrayValueMap* map = m_sparseValueMap;783 784 // First, handle cases where we don't currently have a sparse map.785 if (LIKELY(!map)) {786 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.787 ASSERT(isExtensible());788 789 // Update m_length if necessary.790 if (i >= storage->m_length)791 storage->m_length = i + 1;792 793 // Check that it is sensible to still be using a vector, and then try to grow the vector.794 if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {795 // success! - reread m_storage since it has likely been reallocated, and store to the vector.796 storage = m_storage;797 storage->m_vector[i].set(globalData, this, value);798 ++storage->m_numValuesInVector;799 return;800 }801 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.802 allocateSparseMap(exec->globalData());803 map = m_sparseValueMap;804 map->put(exec, this, i, value, shouldThrow);805 return;806 }807 808 // Update m_length if necessary.809 unsigned length = storage->m_length;810 if (i >= length) {811 // Prohibit growing the array if length is not writable.812 if (map->lengthIsReadOnly() || !isExtensible()) {813 if (shouldThrow)814 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));815 return;816 }817 length = i + 1;818 storage->m_length = length;819 }820 821 // We are currently using a map - check whether we still want to be doing so.822 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.823 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();824 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) {825 map->put(exec, this, i, value, shouldThrow);826 return;827 }828 829 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.830 storage = m_storage;831 storage->m_numValuesInVector = numValuesInArray;832 833 // Copy all values from the map into the vector, and delete the map.834 WriteBarrier<Unknown>* vector = storage->m_vector;835 SparseArrayValueMap::const_iterator end = map->end();836 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)837 vector[it->first].set(globalData, this, it->second.getNonSparseMode());838 deallocateSparseMap();839 840 // Store the new property into the vector.841 WriteBarrier<Unknown>& valueSlot = vector[i];842 if (!valueSlot)843 ++storage->m_numValuesInVector;844 valueSlot.set(globalData, this, value);845 }846 847 bool JSArray::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, PutDirectIndexMode mode)848 {849 JSGlobalData& globalData = exec->globalData();850 851 // i should be a valid array index that is outside of the current vector.852 ASSERT(i >= m_vectorLength);853 ASSERT(i <= MAX_ARRAY_INDEX);854 855 ArrayStorage* storage = m_storage;856 SparseArrayValueMap* map = m_sparseValueMap;857 858 // First, handle cases where we don't currently have a sparse map.859 if (LIKELY(!map)) {860 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.861 ASSERT(isExtensible());862 863 // Update m_length if necessary.864 if (i >= storage->m_length)865 storage->m_length = i + 1;866 867 // Check that it is sensible to still be using a vector, and then try to grow the vector.868 if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {869 // success! - reread m_storage since it has likely been reallocated, and store to the vector.870 storage = m_storage;871 storage->m_vector[i].set(globalData, this, value);872 ++storage->m_numValuesInVector;873 return true;874 }875 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.876 allocateSparseMap(exec->globalData());877 map = m_sparseValueMap;878 return map->putDirect(exec, this, i, value, mode);879 }880 881 // Update m_length if necessary.882 unsigned length = storage->m_length;883 if (i >= length) {884 // Prohibit growing the array if length is not writable.885 if (mode != PutDirectIndexLikePutDirect) {886 if (map->lengthIsReadOnly())887 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);888 if (!isExtensible())889 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");890 }891 length = i + 1;892 storage->m_length = length;893 }894 895 // We are currently using a map - check whether we still want to be doing so.896 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.897 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();898 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length))899 return map->putDirect(exec, this, i, value, mode);900 901 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.902 storage = m_storage;903 storage->m_numValuesInVector = numValuesInArray;904 905 // Copy all values from the map into the vector, and delete the map.906 WriteBarrier<Unknown>* vector = storage->m_vector;907 SparseArrayValueMap::const_iterator end = map->end();908 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)909 vector[it->first].set(globalData, this, it->second.getNonSparseMode());910 deallocateSparseMap();911 912 // Store the new property into the vector.913 WriteBarrier<Unknown>& valueSlot = vector[i];914 if (!valueSlot)915 ++storage->m_numValuesInVector;916 valueSlot.set(globalData, this, value);917 return true;918 }919 920 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)921 {922 JSArray* thisObject = jsCast<JSArray*>(cell);923 unsigned i = propertyName.asIndex();924 if (i != PropertyName::NotAnIndex)925 return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i);926 229 927 230 if (propertyName == exec->propertyNames().length) … … 929 232 930 233 return JSObject::deleteProperty(thisObject, exec, propertyName); 931 }932 933 bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)934 {935 JSArray* thisObject = jsCast<JSArray*>(cell);936 thisObject->checkConsistency();937 938 if (i > MAX_ARRAY_INDEX)939 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));940 941 ArrayStorage* storage = thisObject->m_storage;942 943 if (i < thisObject->m_vectorLength) {944 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];945 if (valueSlot) {946 valueSlot.clear();947 --storage->m_numValuesInVector;948 }949 } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {950 SparseArrayValueMap::iterator it = map->find(i);951 if (it != map->notFound()) {952 if (it->second.attributes & DontDelete)953 return false;954 map->remove(it);955 }956 }957 958 thisObject->checkConsistency();959 return true;960 234 } 961 235 … … 967 241 } 968 242 969 void JSArray::getOwn PropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)243 void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 970 244 { 971 245 JSArray* thisObject = jsCast<JSArray*>(object); 972 // FIXME: Filling PropertyNameArray with an identifier for every integer973 // is incredibly inefficient for large arrays. We need a different approach,974 // which almost certainly means a different structure for PropertyNameArray.975 976 ArrayStorage* storage = thisObject->m_storage;977 978 unsigned usedVectorLength = min(storage->m_length, thisObject->m_vectorLength);979 for (unsigned i = 0; i < usedVectorLength; ++i) {980 if (storage->m_vector[i])981 propertyNames.add(Identifier::from(exec, i));982 }983 984 if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {985 Vector<unsigned> keys;986 keys.reserveCapacity(map->size());987 988 SparseArrayValueMap::const_iterator end = map->end();989 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {990 if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum))991 keys.append(static_cast<unsigned>(it->first));992 }993 994 qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);995 for (unsigned i = 0; i < keys.size(); ++i)996 propertyNames.add(Identifier::from(exec, keys[i]));997 }998 246 999 247 if (mode == IncludeDontEnumProperties) 1000 248 propertyNames.add(exec->propertyNames().length); 1001 249 1002 JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); 1003 } 1004 1005 ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength) 1006 { 1007 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH); 1008 1009 unsigned increasedLength; 1010 unsigned maxInitLength = min(m_storage->m_length, 100000U); 1011 1012 if (desiredLength < maxInitLength) 1013 increasedLength = maxInitLength; 1014 else if (!m_vectorLength) 1015 increasedLength = max(desiredLength, lastArraySize); 1016 else { 1017 // Mathematically equivalent to: 1018 // increasedLength = (newLength * 3 + 1) / 2; 1019 // or: 1020 // increasedLength = (unsigned)ceil(newLength * 1.5)); 1021 // This form is not prone to internal overflow. 1022 increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1); 1023 } 1024 1025 ASSERT(increasedLength >= desiredLength); 1026 1027 lastArraySize = min(increasedLength, FIRST_VECTOR_GROW); 1028 1029 return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); 1030 } 1031 1032 bool JSArray::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) 1033 { 1034 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map 1035 // to the vector. Callers have to account for that, because they can do it more efficiently. 1036 if (newLength > MAX_STORAGE_VECTOR_LENGTH) 1037 return false; 1038 1039 ArrayStorage* storage = m_storage; 1040 1041 unsigned vectorLength = m_vectorLength; 1042 ASSERT(newLength > vectorLength); 1043 unsigned newVectorLength = getNewVectorLength(newLength); 1044 1045 // Fast case - there is no precapacity. In these cases a realloc makes sense. 1046 if (LIKELY(!m_indexBias)) { 1047 void* newStorage = storage->m_allocBase; 1048 if (!globalData.heap.tryReallocateStorage(&newStorage, storageSize(vectorLength), storageSize(newVectorLength))) 1049 return false; 1050 1051 storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newStorage)); 1052 m_storage->m_allocBase = newStorage; 1053 ASSERT(m_storage->m_allocBase); 1054 1055 m_vectorLength = newVectorLength; 1056 1057 return true; 1058 } 1059 1060 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length. 1061 unsigned newIndexBias = min(m_indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); 1062 // Calculate new stoarge capcity, allowing room for the pre-capacity. 1063 unsigned newStorageCapacity = newVectorLength + newIndexBias; 1064 void* newAllocBase = 0; 1065 if (!globalData.heap.tryAllocateStorage(storageSize(newStorageCapacity), &newAllocBase)) 1066 return false; 1067 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. 1068 ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias); 1069 1070 m_vectorLength = newVectorLength; 1071 m_indexBias = newIndexBias; 1072 m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias); 1073 1074 // Copy the ArrayStorage header & current contents of the vector. 1075 memmove(m_storage, storage, storageSize(vectorLength)); 1076 1077 // Free the old allocation, update m_allocBase. 1078 m_storage->m_allocBase = newAllocBase; 1079 1080 return true; 250 JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); 1081 251 } 1082 252 … … 1084 254 bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count) 1085 255 { 256 ArrayStorage* storage = ensureArrayStorage(globalData); 257 Butterfly* butterfly = storage->butterfly(); 258 unsigned propertyCapacity = structure()->outOfLineCapacity(); 259 unsigned propertySize = structure()->outOfLineSize(); 260 1086 261 // If not, we should have handled this on the fast path. 1087 ASSERT(count > m_indexBias); 1088 1089 ArrayStorage* storage = m_storage; 262 ASSERT(count > storage->m_indexBias); 1090 263 1091 264 // Step 1: … … 1096 269 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength. 1097 270 1098 unsigned length = storage-> m_length;1099 unsigned usedVectorLength = min( m_vectorLength, length);271 unsigned length = storage->length(); 272 unsigned usedVectorLength = min(storage->vectorLength(), length); 1100 273 ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH); 1101 274 // Check that required vector length is possible, in an overflow-safe fashion. … … 1105 278 ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH); 1106 279 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. 1107 ASSERT( m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >=m_indexBias);1108 unsigned currentCapacity = m_vectorLength +m_indexBias;280 ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias); 281 unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias; 1109 282 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH. 1110 283 unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1); … … 1117 290 // If the current storage array is sufficiently large (but not too large!) then just keep using it. 1118 291 if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) { 1119 newAllocBase = storage->m_allocBase;292 newAllocBase = butterfly->base(structure()); 1120 293 newStorageCapacity = currentCapacity; 1121 294 } else { 1122 if (!globalData.heap.tryAllocateStorage(storageSize(desiredCapacity), &newAllocBase)) 295 size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity)); 296 if (!globalData.heap.tryAllocateStorage(newSize, &newAllocBase)) 1123 297 return false; 1124 298 newStorageCapacity = desiredCapacity; … … 1133 307 // vector with half the post-capacity it had previously. 1134 308 unsigned postCapacity = 0; 1135 if (length < m_vectorLength) {309 if (length < storage->vectorLength()) { 1136 310 // Atomic decay, + the post-capacity cannot be greater than what is available. 1137 postCapacity = min(( m_vectorLength- length) >> 1, newStorageCapacity - requiredVectorLength);311 postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength); 1138 312 // If we're moving contents within the same allocation, the post-capacity is being reduced. 1139 ASSERT(newAllocBase != storage->m_allocBase || postCapacity < m_vectorLength - length); 1140 } 1141 1142 m_vectorLength = requiredVectorLength + postCapacity; 1143 m_indexBias = newStorageCapacity - m_vectorLength; 1144 m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias); 1145 1146 // Step 4: 1147 // Copy array data / header into their new locations, clear post-capacity & free any old allocation. 1148 1149 // If this is being moved within the existing buffer of memory, we are always shifting data 1150 // to the right (since count > m_indexBias). As such this memmove cannot trample the header. 1151 memmove(m_storage->m_vector + count, storage->m_vector, sizeof(WriteBarrier<Unknown>) * usedVectorLength); 1152 memmove(m_storage, storage, storageSize(0)); 1153 1154 // Are we copying into a new allocation? 1155 if (newAllocBase != m_storage->m_allocBase) { 1156 // Free the old allocation, update m_allocBase. 1157 m_storage->m_allocBase = newAllocBase; 1158 } 313 ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length); 314 } 315 316 unsigned newVectorLength = requiredVectorLength + postCapacity; 317 unsigned newIndexBias = newStorageCapacity - newVectorLength; 318 319 Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity); 320 321 memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength); 322 memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); 323 324 newButterfly->arrayStorage()->setVectorLength(newVectorLength); 325 newButterfly->arrayStorage()->m_indexBias = newIndexBias; 326 327 m_butterfly = newButterfly; 1159 328 1160 329 return true; … … 1163 332 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException) 1164 333 { 1165 check Consistency();1166 1167 ArrayStorage* storage = m_storage;1168 unsigned length = storage-> m_length;334 checkIndexingConsistency(); 335 336 ArrayStorage* storage = ensureArrayStorage(exec->globalData()); 337 unsigned length = storage->length(); 1169 338 1170 339 // If the length is read only then we enter sparse mode, so should enter the following 'if'. 1171 ASSERT(isLengthWritable() || m_sparseValueMap);1172 1173 if (SparseArrayValueMap* map = m_sparseValueMap) {340 ASSERT(isLengthWritable() || storage->m_sparseMap); 341 342 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 1174 343 // Fail if the length is not writable. 1175 344 if (map->lengthIsReadOnly()) … … 1198 367 ASSERT(it != map->notFound()); 1199 368 if (it->second.attributes & DontDelete) { 1200 storage-> m_length = index + 1;369 storage->setLength(index + 1); 1201 370 return reject(exec, throwException, "Unable to delete property."); 1202 371 } … … 1207 376 map->remove(keys[i]); 1208 377 if (map->isEmpty()) 1209 deallocateSparse Map();378 deallocateSparseIndexMap(); 1210 379 } 1211 380 } … … 1214 383 if (newLength < length) { 1215 384 // Delete properties from the vector. 1216 unsigned usedVectorLength = min(length, m_vectorLength);385 unsigned usedVectorLength = min(length, storage->vectorLength()); 1217 386 for (unsigned i = newLength; i < usedVectorLength; ++i) { 1218 387 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; … … 1223 392 } 1224 393 1225 storage-> m_length = newLength;1226 1227 check Consistency();394 storage->setLength(newLength); 395 396 checkIndexingConsistency(); 1228 397 return true; 1229 398 } … … 1231 400 JSValue JSArray::pop(ExecState* exec) 1232 401 { 1233 checkConsistency(); 1234 ArrayStorage* storage = m_storage; 1235 1236 unsigned length = storage->m_length; 1237 if (!length) { 1238 if (!isLengthWritable()) 1239 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 402 checkIndexingConsistency(); 403 404 switch (structure()->indexingType()) { 405 case Array: 1240 406 return jsUndefined(); 1241 } 1242 1243 unsigned index = length - 1; 1244 if (index < m_vectorLength) { 1245 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index]; 1246 if (valueSlot) { 1247 --storage->m_numValuesInVector; 1248 JSValue element = valueSlot.get(); 1249 valueSlot.clear(); 407 408 case ArrayWithArrayStorage: { 409 ArrayStorage* storage = m_butterfly->arrayStorage(); 410 411 unsigned length = storage->length(); 412 if (!length) { 413 if (!isLengthWritable()) 414 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 415 return jsUndefined(); 416 } 417 418 unsigned index = length - 1; 419 if (index < storage->vectorLength()) { 420 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index]; 421 if (valueSlot) { 422 --storage->m_numValuesInVector; 423 JSValue element = valueSlot.get(); 424 valueSlot.clear(); 1250 425 1251 ASSERT(isLengthWritable()); 1252 storage->m_length = index; 1253 checkConsistency(); 1254 return element; 1255 } 1256 } 1257 1258 // Let element be the result of calling the [[Get]] internal method of O with argument indx. 1259 JSValue element = get(exec, index); 1260 if (exec->hadException()) 1261 return jsUndefined(); 1262 // Call the [[Delete]] internal method of O with arguments indx and true. 1263 if (!deletePropertyByIndex(this, exec, index)) { 1264 throwTypeError(exec, ASCIILiteral("Unable to delete property.")); 1265 return jsUndefined(); 1266 } 1267 // Call the [[Put]] internal method of O with arguments "length", indx, and true. 1268 setLength(exec, index, true); 1269 // Return element. 1270 checkConsistency(); 1271 return element; 426 ASSERT(isLengthWritable()); 427 storage->setLength(index); 428 checkIndexingConsistency(); 429 return element; 430 } 431 } 432 433 // Let element be the result of calling the [[Get]] internal method of O with argument indx. 434 JSValue element = get(exec, index); 435 if (exec->hadException()) 436 return jsUndefined(); 437 // Call the [[Delete]] internal method of O with arguments indx and true. 438 if (!deletePropertyByIndex(this, exec, index)) { 439 throwTypeError(exec, "Unable to delete property."); 440 return jsUndefined(); 441 } 442 // Call the [[Put]] internal method of O with arguments "length", indx, and true. 443 setLength(exec, index, true); 444 // Return element. 445 checkIndexingConsistency(); 446 return element; 447 } 448 449 default: 450 ASSERT_NOT_REACHED(); 451 return JSValue(); 452 } 1272 453 } 1273 454 … … 1277 458 void JSArray::push(ExecState* exec, JSValue value) 1278 459 { 1279 checkConsistency(); 1280 ArrayStorage* storage = m_storage; 1281 1282 // Fast case - push within vector, always update m_length & m_numValuesInVector. 1283 unsigned length = storage->m_length; 1284 if (length < m_vectorLength) { 1285 storage->m_vector[length].set(exec->globalData(), this, value); 1286 storage->m_length = length + 1; 1287 ++storage->m_numValuesInVector; 1288 checkConsistency(); 1289 return; 1290 } 1291 1292 // Pushing to an array of length 2^32-1 stores the property, but throws a range error. 1293 if (UNLIKELY(storage->m_length == 0xFFFFFFFFu)) { 1294 methodTable()->putByIndex(this, exec, storage->m_length, value, true); 1295 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. 1296 if (!exec->hadException()) 1297 throwError(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); 1298 return; 1299 } 1300 1301 // Handled the same as putIndex. 1302 putByIndexBeyondVectorLength(exec, storage->m_length, value, true); 1303 checkConsistency(); 1304 } 1305 1306 bool JSArray::shiftCount(ExecState*, unsigned count) 460 checkIndexingConsistency(); 461 462 switch (structure()->indexingType()) { 463 case Array: { 464 putByIndexBeyondVectorLengthWithArrayStorage(exec, 0, value, true, createInitialArrayStorage(exec->globalData())); 465 break; 466 } 467 468 case ArrayWithArrayStorage: { 469 ArrayStorage* storage = m_butterfly->arrayStorage(); 470 471 // Fast case - push within vector, always update m_length & m_numValuesInVector. 472 unsigned length = storage->length(); 473 if (length < storage->vectorLength()) { 474 storage->m_vector[length].set(exec->globalData(), this, value); 475 storage->setLength(length + 1); 476 ++storage->m_numValuesInVector; 477 checkIndexingConsistency(); 478 return; 479 } 480 481 // Pushing to an array of length 2^32-1 stores the property, but throws a range error. 482 if (UNLIKELY(storage->length() == 0xFFFFFFFFu)) { 483 methodTable()->putByIndex(this, exec, storage->length(), value, true); 484 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. 485 if (!exec->hadException()) 486 throwError(exec, createRangeError(exec, "Invalid array length")); 487 return; 488 } 489 490 // Handled the same as putIndex. 491 putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage); 492 checkIndexingConsistency(); 493 break; 494 } 495 496 default: 497 ASSERT_NOT_REACHED(); 498 } 499 } 500 501 bool JSArray::shiftCount(ExecState* exec, unsigned count) 1307 502 { 1308 503 ASSERT(count > 0); 1309 504 1310 ArrayStorage* storage = m_storage;1311 1312 unsigned oldLength = storage-> m_length;505 ArrayStorage* storage = ensureArrayStorage(exec->globalData()); 506 507 unsigned oldLength = storage->length(); 1313 508 1314 509 // If the array contains holes or is otherwise in an abnormal state, 1315 510 // use the generic algorithm in ArrayPrototype. 1316 if (oldLength != storage->m_numValuesInVector || inSparse Mode())511 if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode()) 1317 512 return false; 1318 513 … … 1321 516 1322 517 storage->m_numValuesInVector -= count; 1323 storage-> m_length -= count;1324 1325 if (m_vectorLength) {1326 count = min(m_vectorLength, (unsigned)count);1327 1328 m_vectorLength -= count;1329 1330 if (m_vectorLength) {1331 char* newBaseStorage = reinterpret_cast<char*>(storage) + count * sizeof(WriteBarrier<Unknown>);1332 memmove(newBaseStorage, storage, storageSize(0));1333 m_ storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);1334 1335 m_indexBias += count;518 storage->setLength(oldLength - count); 519 520 unsigned vectorLength = storage->vectorLength(); 521 if (vectorLength) { 522 count = min(vectorLength, (unsigned)count); 523 524 vectorLength -= count; 525 storage->setVectorLength(vectorLength); 526 527 if (vectorLength) { 528 m_butterfly = m_butterfly->shift(structure(), count); 529 storage = m_butterfly->arrayStorage(); 530 storage->m_indexBias += count; 1336 531 } 1337 532 } … … 1342 537 bool JSArray::unshiftCount(ExecState* exec, unsigned count) 1343 538 { 1344 ArrayStorage* storage = m_storage;1345 unsigned length = storage-> m_length;539 ArrayStorage* storage = ensureArrayStorage(exec->globalData()); 540 unsigned length = storage->length(); 1346 541 1347 542 // If the array contains holes or is otherwise in an abnormal state, 1348 543 // use the generic algorithm in ArrayPrototype. 1349 if (length != storage->m_numValuesInVector || inSparseMode())544 if (length != storage->m_numValuesInVector || storage->inSparseMode()) 1350 545 return false; 1351 546 1352 if (m_indexBias >= count) { 1353 m_indexBias -= count; 1354 char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(WriteBarrier<Unknown>); 1355 memmove(newBaseStorage, storage, storageSize(0)); 1356 m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); 1357 m_vectorLength += count; 547 if (storage->m_indexBias >= count) { 548 m_butterfly = storage->butterfly()->unshift(structure(), count); 549 storage = m_butterfly->arrayStorage(); 550 storage->m_indexBias -= count; 551 storage->setVectorLength(storage->vectorLength() + count); 1358 552 } else if (!unshiftCountSlowCase(exec->globalData(), count)) { 1359 553 throwOutOfMemoryError(exec); … … 1361 555 } 1362 556 1363 WriteBarrier<Unknown>* vector = m_storage->m_vector;557 WriteBarrier<Unknown>* vector = storage->m_vector; 1364 558 for (unsigned i = 0; i < count; i++) 1365 559 vector[i].clear(); … … 1367 561 } 1368 562 1369 void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor)1370 {1371 JSArray* thisObject = jsCast<JSArray*>(cell);1372 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);1373 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);1374 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());1375 1376 JSNonFinalObject::visitChildren(thisObject, visitor);1377 1378 if (thisObject->m_storage) {1379 MARK_LOG_MESSAGE1("[%u]: ", thisObject->length());1380 1381 ArrayStorage* storage = thisObject->m_storage;1382 void* baseStorage = storage->m_allocBase;1383 1384 visitor.copyAndAppend(reinterpret_cast<void**>(&baseStorage), storageSize(thisObject->m_vectorLength + thisObject->m_indexBias), storage->m_vector->slot(), thisObject->m_vectorLength);1385 1386 if (baseStorage != thisObject->m_storage->m_allocBase) {1387 thisObject->m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + sizeof(JSValue) * thisObject->m_indexBias);1388 thisObject->m_storage->m_allocBase = baseStorage;1389 ASSERT(thisObject->m_storage->m_allocBase);1390 }1391 }1392 1393 if (SparseArrayValueMap* map = thisObject->m_sparseValueMap)1394 map->visitChildren(visitor);1395 }1396 1397 563 static int compareNumbersForQSort(const void* a, const void* b) 1398 564 { … … 1411 577 void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) 1412 578 { 1413 ASSERT(!inSparseMode()); 1414 1415 ArrayStorage* storage = m_storage; 1416 1417 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 1418 if (m_sparseValueMap) { 1419 throwOutOfMemoryError(exec); 1420 return; 1421 } 1422 1423 if (!lengthNotIncludingUndefined) 1424 return; 1425 1426 bool allValuesAreNumbers = true; 1427 size_t size = storage->m_numValuesInVector; 1428 for (size_t i = 0; i < size; ++i) { 1429 if (!storage->m_vector[i].isNumber()) { 1430 allValuesAreNumbers = false; 1431 break; 1432 } 1433 } 1434 1435 if (!allValuesAreNumbers) 1436 return sort(exec, compareFunction, callType, callData); 1437 1438 // For numeric comparison, which is fast, qsort is faster than mergesort. We 1439 // also don't require mergesort's stability, since there's no user visible 1440 // side-effect from swapping the order of equal primitive values. 1441 qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); 1442 1443 checkConsistency(SortConsistencyCheck); 579 ASSERT(!inSparseIndexingMode()); 580 581 switch (structure()->indexingType()) { 582 case Array: 583 return; 584 585 case ArrayWithArrayStorage: { 586 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 587 ArrayStorage* storage = m_butterfly->arrayStorage(); 588 589 if (storage->m_sparseMap.get()) { 590 throwOutOfMemoryError(exec); 591 return; 592 } 593 594 if (!lengthNotIncludingUndefined) 595 return; 596 597 bool allValuesAreNumbers = true; 598 size_t size = storage->m_numValuesInVector; 599 for (size_t i = 0; i < size; ++i) { 600 if (!storage->m_vector[i].isNumber()) { 601 allValuesAreNumbers = false; 602 break; 603 } 604 } 605 606 if (!allValuesAreNumbers) 607 return sort(exec, compareFunction, callType, callData); 608 609 // For numeric comparison, which is fast, qsort is faster than mergesort. We 610 // also don't require mergesort's stability, since there's no user visible 611 // side-effect from swapping the order of equal primitive values. 612 qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); 613 614 checkIndexingConsistency(SortConsistencyCheck); 615 return; 616 } 617 618 default: 619 ASSERT_NOT_REACHED(); 620 } 1444 621 } 1445 622 1446 623 void JSArray::sort(ExecState* exec) 1447 624 { 1448 ASSERT(!inSparseMode()); 1449 1450 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 1451 if (m_sparseValueMap) { 1452 throwOutOfMemoryError(exec); 1453 return; 1454 } 1455 1456 if (!lengthNotIncludingUndefined) 1457 return; 1458 1459 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. 1460 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary 1461 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return 1462 // random or otherwise changing results, effectively making compare function inconsistent. 1463 1464 Vector<ValueStringPair> values(lengthNotIncludingUndefined); 1465 if (!values.begin()) { 1466 throwOutOfMemoryError(exec); 1467 return; 1468 } 1469 1470 Heap::heap(this)->pushTempSortVector(&values); 1471 1472 bool isSortingPrimitiveValues = true; 1473 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { 1474 JSValue value = m_storage->m_vector[i].get(); 1475 ASSERT(!value.isUndefined()); 1476 values[i].first = value; 1477 isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); 1478 } 1479 1480 // FIXME: The following loop continues to call toString on subsequent values even after 1481 // a toString call raises an exception. 1482 1483 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 1484 values[i].second = values[i].first.toWTFStringInline(exec); 1485 1486 if (exec->hadException()) { 625 ASSERT(!inSparseIndexingMode()); 626 627 switch (structure()->indexingType()) { 628 case Array: 629 return; 630 631 case ArrayWithArrayStorage: { 632 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 633 ArrayStorage* storage = m_butterfly->arrayStorage(); 634 if (storage->m_sparseMap.get()) { 635 throwOutOfMemoryError(exec); 636 return; 637 } 638 639 if (!lengthNotIncludingUndefined) 640 return; 641 642 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. 643 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary 644 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return 645 // random or otherwise changing results, effectively making compare function inconsistent. 646 647 Vector<ValueStringPair> values(lengthNotIncludingUndefined); 648 if (!values.begin()) { 649 throwOutOfMemoryError(exec); 650 return; 651 } 652 653 Heap::heap(this)->pushTempSortVector(&values); 654 655 bool isSortingPrimitiveValues = true; 656 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { 657 JSValue value = storage->m_vector[i].get(); 658 ASSERT(!value.isUndefined()); 659 values[i].first = value; 660 isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); 661 } 662 663 // FIXME: The following loop continues to call toString on subsequent values even after 664 // a toString call raises an exception. 665 666 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 667 values[i].second = values[i].first.toWTFStringInline(exec); 668 669 if (exec->hadException()) { 670 Heap::heap(this)->popTempSortVector(&values); 671 return; 672 } 673 674 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather 675 // than O(N log N). 676 677 #if HAVE(MERGESORT) 678 if (isSortingPrimitiveValues) 679 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 680 else 681 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 682 #else 683 // FIXME: The qsort library function is likely to not be a stable sort. 684 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. 685 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 686 #endif 687 688 // If the toString function changed the length of the array or vector storage, 689 // increase the length to handle the orignal number of actual values. 690 if (storage->vectorLength() < lengthNotIncludingUndefined) { 691 increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined); 692 storage = m_butterfly->arrayStorage(); 693 } 694 if (storage->length() < lengthNotIncludingUndefined) 695 storage->setLength(lengthNotIncludingUndefined); 696 697 JSGlobalData& globalData = exec->globalData(); 698 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 699 storage->m_vector[i].set(globalData, this, values[i].first); 700 1487 701 Heap::heap(this)->popTempSortVector(&values); 1488 return; 1489 } 1490 1491 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather 1492 // than O(N log N). 1493 1494 #if HAVE(MERGESORT) 1495 if (isSortingPrimitiveValues) 1496 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 1497 else 1498 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 1499 #else 1500 // FIXME: The qsort library function is likely to not be a stable sort. 1501 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. 1502 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 1503 #endif 1504 1505 // If the toString function changed the length of the array or vector storage, 1506 // increase the length to handle the orignal number of actual values. 1507 if (m_vectorLength < lengthNotIncludingUndefined) 1508 increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined); 1509 if (m_storage->m_length < lengthNotIncludingUndefined) 1510 m_storage->m_length = lengthNotIncludingUndefined; 1511 1512 JSGlobalData& globalData = exec->globalData(); 1513 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 1514 m_storage->m_vector[i].set(globalData, this, values[i].first); 1515 1516 Heap::heap(this)->popTempSortVector(&values); 1517 1518 checkConsistency(SortConsistencyCheck); 702 703 checkIndexingConsistency(SortConsistencyCheck); 704 return; 705 } 706 707 default: 708 ASSERT_NOT_REACHED(); 709 } 1519 710 } 1520 711 … … 1598 789 void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) 1599 790 { 1600 ASSERT(!inSparseMode()); 1601 1602 checkConsistency(); 1603 1604 // FIXME: This ignores exceptions raised in the compare function or in toNumber. 1605 1606 // The maximum tree depth is compiled in - but the caller is clearly up to no good 1607 // if a larger array is passed. 1608 ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); 1609 if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) 1610 return; 1611 1612 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); 1613 unsigned nodeCount = usedVectorLength + (m_sparseValueMap ? m_sparseValueMap->size() : 0); 1614 1615 if (!nodeCount) 1616 return; 1617 1618 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items 1619 tree.abstractor().m_exec = exec; 1620 tree.abstractor().m_compareFunction = compareFunction; 1621 tree.abstractor().m_compareCallType = callType; 1622 tree.abstractor().m_compareCallData = &callData; 1623 tree.abstractor().m_nodes.grow(nodeCount); 1624 1625 if (callType == CallTypeJS) 1626 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2)); 1627 1628 if (!tree.abstractor().m_nodes.begin()) { 1629 throwOutOfMemoryError(exec); 1630 return; 1631 } 1632 1633 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified 1634 // right out from under us while we're building the tree here. 1635 1636 unsigned numDefined = 0; 1637 unsigned numUndefined = 0; 1638 1639 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. 1640 for (; numDefined < usedVectorLength; ++numDefined) { 1641 JSValue v = m_storage->m_vector[numDefined].get(); 1642 if (!v || v.isUndefined()) 1643 break; 1644 tree.abstractor().m_nodes[numDefined].value = v; 1645 tree.insert(numDefined); 1646 } 1647 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 1648 JSValue v = m_storage->m_vector[i].get(); 1649 if (v) { 1650 if (v.isUndefined()) 1651 ++numUndefined; 1652 else { 1653 tree.abstractor().m_nodes[numDefined].value = v; 791 ASSERT(!inSparseIndexingMode()); 792 793 switch (structure()->indexingType()) { 794 case Array: 795 return; 796 797 case ArrayWithArrayStorage: { 798 ArrayStorage* storage = m_butterfly->arrayStorage(); 799 checkIndexingConsistency(); 800 801 // FIXME: This ignores exceptions raised in the compare function or in toNumber. 802 803 // The maximum tree depth is compiled in - but the caller is clearly up to no good 804 // if a larger array is passed. 805 ASSERT(storage->length() <= static_cast<unsigned>(std::numeric_limits<int>::max())); 806 if (storage->length() > static_cast<unsigned>(std::numeric_limits<int>::max())) 807 return; 808 809 unsigned usedVectorLength = min(storage->length(), storage->vectorLength()); 810 unsigned nodeCount = usedVectorLength + (storage->m_sparseMap ? storage->m_sparseMap->size() : 0); 811 812 if (!nodeCount) 813 return; 814 815 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items 816 tree.abstractor().m_exec = exec; 817 tree.abstractor().m_compareFunction = compareFunction; 818 tree.abstractor().m_compareCallType = callType; 819 tree.abstractor().m_compareCallData = &callData; 820 tree.abstractor().m_nodes.grow(nodeCount); 821 822 if (callType == CallTypeJS) 823 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2)); 824 825 if (!tree.abstractor().m_nodes.begin()) { 826 throwOutOfMemoryError(exec); 827 return; 828 } 829 830 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified 831 // right out from under us while we're building the tree here. 832 833 unsigned numDefined = 0; 834 unsigned numUndefined = 0; 835 836 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. 837 for (; numDefined < usedVectorLength; ++numDefined) { 838 JSValue v = storage->m_vector[numDefined].get(); 839 if (!v || v.isUndefined()) 840 break; 841 tree.abstractor().m_nodes[numDefined].value = v; 842 tree.insert(numDefined); 843 } 844 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 845 JSValue v = storage->m_vector[i].get(); 846 if (v) { 847 if (v.isUndefined()) 848 ++numUndefined; 849 else { 850 tree.abstractor().m_nodes[numDefined].value = v; 851 tree.insert(numDefined); 852 ++numDefined; 853 } 854 } 855 } 856 857 unsigned newUsedVectorLength = numDefined + numUndefined; 858 859 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 860 newUsedVectorLength += map->size(); 861 if (newUsedVectorLength > storage->vectorLength()) { 862 // Check that it is possible to allocate an array large enough to hold all the entries. 863 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) { 864 throwOutOfMemoryError(exec); 865 return; 866 } 867 storage = m_butterfly->arrayStorage(); 868 } 869 870 SparseArrayValueMap::const_iterator end = map->end(); 871 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { 872 tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode(); 1654 873 tree.insert(numDefined); 1655 874 ++numDefined; 1656 875 } 1657 } 1658 } 1659 1660 unsigned newUsedVectorLength = numDefined + numUndefined; 1661 1662 if (SparseArrayValueMap* map = m_sparseValueMap) { 1663 newUsedVectorLength += map->size(); 1664 if (newUsedVectorLength > m_vectorLength) { 1665 // Check that it is possible to allocate an array large enough to hold all the entries. 1666 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) { 1667 throwOutOfMemoryError(exec); 1668 return; 876 877 deallocateSparseIndexMap(); 878 } 879 880 ASSERT(tree.abstractor().m_nodes.size() >= numDefined); 881 882 // FIXME: If the compare function changed the length of the array, the following might be 883 // modifying the vector incorrectly. 884 885 // Copy the values back into m_storage. 886 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; 887 iter.start_iter_least(tree); 888 JSGlobalData& globalData = exec->globalData(); 889 for (unsigned i = 0; i < numDefined; ++i) { 890 storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); 891 ++iter; 892 } 893 894 // Put undefined values back in. 895 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 896 storage->m_vector[i].setUndefined(); 897 898 // Ensure that unused values in the vector are zeroed out. 899 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 900 storage->m_vector[i].clear(); 901 902 storage->m_numValuesInVector = newUsedVectorLength; 903 904 checkIndexingConsistency(SortConsistencyCheck); 905 return; 906 } 907 908 default: 909 ASSERT_NOT_REACHED(); 910 } 911 } 912 913 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) 914 { 915 switch (structure()->indexingType()) { 916 case Array: 917 return; 918 919 case ArrayWithArrayStorage: { 920 ArrayStorage* storage = m_butterfly->arrayStorage(); 921 922 WriteBarrier<Unknown>* vector = storage->m_vector; 923 unsigned vectorEnd = min(storage->length(), storage->vectorLength()); 924 unsigned i = 0; 925 for (; i < vectorEnd; ++i) { 926 WriteBarrier<Unknown>& v = vector[i]; 927 if (!v) 928 break; 929 args.append(v.get()); 930 } 931 932 for (; i < storage->length(); ++i) 933 args.append(get(exec, i)); 934 return; 935 } 936 937 default: 938 ASSERT_NOT_REACHED(); 939 } 940 } 941 942 void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) 943 { 944 ASSERT(length == this->length()); 945 switch (structure()->indexingType()) { 946 case Array: 947 return; 948 949 case ArrayWithArrayStorage: { 950 ArrayStorage* storage = m_butterfly->arrayStorage(); 951 unsigned i = 0; 952 WriteBarrier<Unknown>* vector = storage->m_vector; 953 unsigned vectorEnd = min(length, storage->vectorLength()); 954 for (; i < vectorEnd; ++i) { 955 WriteBarrier<Unknown>& v = vector[i]; 956 if (!v) 957 break; 958 callFrame->setArgument(i, v.get()); 959 } 960 961 for (; i < length; ++i) 962 callFrame->setArgument(i, get(exec, i)); 963 return; 964 } 965 966 default: 967 ASSERT_NOT_REACHED(); 968 } 969 } 970 971 unsigned JSArray::compactForSorting(JSGlobalData& globalData) 972 { 973 ASSERT(!inSparseIndexingMode()); 974 975 checkIndexingConsistency(); 976 977 switch (structure()->indexingType()) { 978 case Array: 979 return 0; 980 981 case ArrayWithArrayStorage: { 982 ArrayStorage* storage = m_butterfly->arrayStorage(); 983 984 unsigned usedVectorLength = min(storage->length(), storage->vectorLength()); 985 986 unsigned numDefined = 0; 987 unsigned numUndefined = 0; 988 989 for (; numDefined < usedVectorLength; ++numDefined) { 990 JSValue v = storage->m_vector[numDefined].get(); 991 if (!v || v.isUndefined()) 992 break; 993 } 994 995 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 996 JSValue v = storage->m_vector[i].get(); 997 if (v) { 998 if (v.isUndefined()) 999 ++numUndefined; 1000 else 1001 storage->m_vector[numDefined++].setWithoutWriteBarrier(v); 1669 1002 } 1670 1003 } 1671 1672 SparseArrayValueMap::const_iterator end = map->end(); 1673 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { 1674 tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode(); 1675 tree.insert(numDefined); 1676 ++numDefined; 1677 } 1678 1679 deallocateSparseMap(); 1680 } 1681 1682 ASSERT(tree.abstractor().m_nodes.size() >= numDefined); 1683 1684 // FIXME: If the compare function changed the length of the array, the following might be 1685 // modifying the vector incorrectly. 1686 1687 // Copy the values back into m_storage. 1688 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; 1689 iter.start_iter_least(tree); 1690 JSGlobalData& globalData = exec->globalData(); 1691 for (unsigned i = 0; i < numDefined; ++i) { 1692 m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); 1693 ++iter; 1694 } 1695 1696 // Put undefined values back in. 1697 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 1698 m_storage->m_vector[i].setUndefined(); 1699 1700 // Ensure that unused values in the vector are zeroed out. 1701 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 1702 m_storage->m_vector[i].clear(); 1703 1704 m_storage->m_numValuesInVector = newUsedVectorLength; 1705 1706 checkConsistency(SortConsistencyCheck); 1707 } 1708 1709 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) 1710 { 1711 ArrayStorage* storage = m_storage; 1712 1713 WriteBarrier<Unknown>* vector = storage->m_vector; 1714 unsigned vectorEnd = min(storage->m_length, m_vectorLength); 1715 unsigned i = 0; 1716 for (; i < vectorEnd; ++i) { 1717 WriteBarrier<Unknown>& v = vector[i]; 1718 if (!v) 1719 break; 1720 args.append(v.get()); 1721 } 1722 1723 for (; i < storage->m_length; ++i) 1724 args.append(get(exec, i)); 1725 } 1726 1727 void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) 1728 { 1729 ASSERT(length == this->length()); 1730 UNUSED_PARAM(length); 1731 unsigned i = 0; 1732 WriteBarrier<Unknown>* vector = m_storage->m_vector; 1733 unsigned vectorEnd = min(length, m_vectorLength); 1734 for (; i < vectorEnd; ++i) { 1735 WriteBarrier<Unknown>& v = vector[i]; 1736 if (!v) 1737 break; 1738 callFrame->setArgument(i, v.get()); 1739 } 1740 1741 for (; i < length; ++i) 1742 callFrame->setArgument(i, get(exec, i)); 1743 } 1744 1745 unsigned JSArray::compactForSorting(JSGlobalData& globalData) 1746 { 1747 ASSERT(!inSparseMode()); 1748 1749 checkConsistency(); 1750 1751 ArrayStorage* storage = m_storage; 1752 1753 unsigned usedVectorLength = min(storage->m_length, m_vectorLength); 1754 1755 unsigned numDefined = 0; 1756 unsigned numUndefined = 0; 1757 1758 for (; numDefined < usedVectorLength; ++numDefined) { 1759 JSValue v = storage->m_vector[numDefined].get(); 1760 if (!v || v.isUndefined()) 1761 break; 1762 } 1763 1764 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 1765 JSValue v = storage->m_vector[i].get(); 1766 if (v) { 1767 if (v.isUndefined()) 1768 ++numUndefined; 1769 else 1770 storage->m_vector[numDefined++].setWithoutWriteBarrier(v); 1771 } 1772 } 1773 1774 unsigned newUsedVectorLength = numDefined + numUndefined; 1775 1776 if (SparseArrayValueMap* map = m_sparseValueMap) { 1777 newUsedVectorLength += map->size(); 1778 if (newUsedVectorLength > m_vectorLength) { 1779 // Check that it is possible to allocate an array large enough to hold all the entries - if not, 1780 // exception is thrown by caller. 1781 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength)) 1782 return 0; 1783 1784 storage = m_storage; 1785 } 1786 1787 SparseArrayValueMap::const_iterator end = map->end(); 1788 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1789 storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode()); 1790 1791 deallocateSparseMap(); 1792 } 1793 1794 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 1795 storage->m_vector[i].setUndefined(); 1796 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 1797 storage->m_vector[i].clear(); 1798 1799 storage->m_numValuesInVector = newUsedVectorLength; 1800 1801 checkConsistency(SortConsistencyCheck); 1802 1803 return numDefined; 1804 } 1805 1806 #if CHECK_ARRAY_CONSISTENCY 1807 1808 void JSArray::checkConsistency(ConsistencyCheckType type) 1809 { 1810 ArrayStorage* storage = m_storage; 1811 1812 ASSERT(!storage->m_inCompactInitialization); 1813 1814 ASSERT(storage); 1815 if (type == SortConsistencyCheck) 1816 ASSERT(!m_sparseValueMap); 1817 1818 unsigned numValuesInVector = 0; 1819 for (unsigned i = 0; i < m_vectorLength; ++i) { 1820 if (JSValue value = storage->m_vector[i].get()) { 1821 ASSERT(i < storage->m_length); 1822 if (type != DestructorConsistencyCheck) 1823 value.isUndefined(); // Likely to crash if the object was deallocated. 1824 ++numValuesInVector; 1825 } else { 1826 if (type == SortConsistencyCheck) 1827 ASSERT(i >= storage->m_numValuesInVector); 1828 } 1829 } 1830 ASSERT(numValuesInVector == storage->m_numValuesInVector); 1831 ASSERT(numValuesInVector <= storage->m_length); 1832 1833 if (m_sparseValueMap) { 1834 SparseArrayValueMap::const_iterator end = m_sparseValueMap->end(); 1835 for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) { 1836 unsigned index = it->first; 1837 ASSERT(index < storage->m_length); 1838 ASSERT(index >= m_vectorLength); 1839 ASSERT(index <= MAX_ARRAY_INDEX); 1840 ASSERT(it->second); 1841 if (type != DestructorConsistencyCheck) 1842 it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated. 1843 } 1844 } 1845 } 1846 1847 #endif 1004 1005 unsigned newUsedVectorLength = numDefined + numUndefined; 1006 1007 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 1008 newUsedVectorLength += map->size(); 1009 if (newUsedVectorLength > storage->vectorLength()) { 1010 // Check that it is possible to allocate an array large enough to hold all the entries - if not, 1011 // exception is thrown by caller. 1012 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength)) 1013 return 0; 1014 1015 storage = m_butterfly->arrayStorage(); 1016 } 1017 1018 SparseArrayValueMap::const_iterator end = map->end(); 1019 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1020 storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode()); 1021 1022 deallocateSparseIndexMap(); 1023 } 1024 1025 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 1026 storage->m_vector[i].setUndefined(); 1027 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 1028 storage->m_vector[i].clear(); 1029 1030 storage->m_numValuesInVector = newUsedVectorLength; 1031 1032 checkIndexingConsistency(SortConsistencyCheck); 1033 1034 return numDefined; 1035 } 1036 1037 default: 1038 ASSERT_NOT_REACHED(); 1039 return 0; 1040 } 1041 } 1042 1848 1043 1849 1044 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/JSArray.h
r127349 r128400 1 1 /* 2 2 * Copyright (C) 1999-2000 Harri Porten ([email protected]) 3 * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.3 * Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. 4 4 * 5 5 * This library is free software; you can redistribute it and/or … … 22 22 #define JSArray_h 23 23 24 #include "ArrayConventions.h" 25 #include "ButterflyInlineMethods.h" 24 26 #include "JSObject.h" 25 26 #define CHECK_ARRAY_CONSISTENCY 027 27 28 28 namespace JSC { … … 30 30 class JSArray; 31 31 class LLIntOffsetsExtractor; 32 33 enum PutDirectIndexMode { PutDirectIndexLikePutDirect, PutDirectIndexShouldNotThrow, PutDirectIndexShouldThrow };34 35 struct SparseArrayEntry : public WriteBarrier<Unknown> {36 typedef WriteBarrier<Unknown> Base;37 38 SparseArrayEntry() : attributes(0) {}39 40 JSValue get(ExecState*, JSArray*) const;41 void get(PropertySlot&) const;42 void get(PropertyDescriptor&) const;43 JSValue getNonSparseMode() const;44 45 unsigned attributes;46 };47 48 class SparseArrayValueMap {49 typedef HashMap<uint64_t, SparseArrayEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > Map;50 51 enum Flags {52 Normal = 0,53 SparseMode = 1,54 LengthIsReadOnly = 2,55 };56 57 public:58 typedef Map::iterator iterator;59 typedef Map::const_iterator const_iterator;60 typedef Map::AddResult AddResult;61 62 SparseArrayValueMap()63 : m_flags(Normal)64 , m_reportedCapacity(0)65 {66 }67 68 void visitChildren(SlotVisitor&);69 70 bool sparseMode()71 {72 return m_flags & SparseMode;73 }74 75 void setSparseMode()76 {77 m_flags = static_cast<Flags>(m_flags | SparseMode);78 }79 80 bool lengthIsReadOnly()81 {82 return m_flags & LengthIsReadOnly;83 }84 85 void setLengthIsReadOnly()86 {87 m_flags = static_cast<Flags>(m_flags | LengthIsReadOnly);88 }89 90 // These methods may mutate the contents of the map91 void put(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow);92 bool putDirect(ExecState*, JSArray*, unsigned, JSValue, PutDirectIndexMode);93 AddResult add(JSArray*, unsigned);94 iterator find(unsigned i) { return m_map.find(i); }95 // This should ASSERT the remove is valid (check the result of the find).96 void remove(iterator it) { m_map.remove(it); }97 void remove(unsigned i) { m_map.remove(i); }98 99 // These methods do not mutate the contents of the map.100 iterator notFound() { return m_map.end(); }101 bool isEmpty() const { return m_map.isEmpty(); }102 bool contains(unsigned i) const { return m_map.contains(i); }103 size_t size() const { return m_map.size(); }104 // Only allow const begin/end iteration.105 const_iterator begin() const { return m_map.begin(); }106 const_iterator end() const { return m_map.end(); }107 108 private:109 Map m_map;110 Flags m_flags;111 size_t m_reportedCapacity;112 };113 114 // This struct holds the actual data values of an array. A JSArray object points to it's contained ArrayStorage115 // struct by pointing to m_vector. To access the contained ArrayStorage struct, use the getStorage() and116 // setStorage() methods. It is important to note that there may be space before the ArrayStorage that117 // is used to quick unshift / shift operation. The actual allocated pointer is available by using:118 // getStorage() - m_indexBias * sizeof(JSValue)119 struct ArrayStorage {120 unsigned m_length; // The "length" property on the array121 unsigned m_numValuesInVector;122 void* m_allocBase; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector.123 #if CHECK_ARRAY_CONSISTENCY124 // Needs to be a uintptr_t for alignment purposes.125 uintptr_t m_initializationIndex;126 uintptr_t m_inCompactInitialization;127 #else128 uintptr_t m_padding;129 #endif130 WriteBarrier<Unknown> m_vector[1];131 132 static ptrdiff_t lengthOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_length); }133 static ptrdiff_t numValuesInVectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector); }134 static ptrdiff_t allocBaseOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_allocBase); }135 static ptrdiff_t vectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_vector); }136 };137 32 138 33 class JSArray : public JSNonFinalObject { … … 141 36 friend class JIT; 142 37 38 public: 39 typedef JSNonFinalObject Base; 40 143 41 protected: 144 explicit JSArray(JSGlobalData& globalData, Structure* structure) 145 : JSNonFinalObject(globalData, structure) 146 , m_indexBias(0) 147 , m_storage(0) 148 , m_sparseValueMap(0) 42 explicit JSArray(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly) 43 : JSNonFinalObject(globalData, structure, butterfly) 149 44 { 150 45 } 151 46 152 JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, unsigned initialLength = 0);153 JS_EXPORT_PRIVATE JSArray* tryFinishCreationUninitialized(JSGlobalData&, unsigned initialLength);154 155 47 public: 156 typedef JSNonFinalObject Base;157 158 static void finalize(JSCell*);159 160 48 static JSArray* create(JSGlobalData&, Structure*, unsigned initialLength = 0); 161 49 … … 170 58 171 59 static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); 172 JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);173 60 static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); 174 static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);175 // This is similar to the JSObject::putDirect* methods:176 // - the prototype chain is not consulted177 // - accessors are not called.178 // - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default).179 // This method creates a property with attributes writable, enumerable and configurable all set to true.180 bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, PutDirectIndexMode mode = PutDirectIndexLikePutDirect)181 {182 if (canSetIndex(propertyName)) {183 setIndex(exec->globalData(), propertyName, value);184 return true;185 }186 return putDirectIndexBeyondVectorLength(exec, propertyName, value, mode);187 }188 61 189 62 static JS_EXPORTDATA const ClassInfo s_info; 190 63 191 unsigned length() const { return m_storage->m_length; }64 unsigned length() const { return getArrayLength(); } 192 65 // OK to use on new arrays, but not if it might be a RegExpMatchArray. 193 66 bool setLength(ExecState*, unsigned, bool throwException = false); … … 203 76 bool unshiftCount(ExecState*, unsigned count); 204 77 205 bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; }206 JSValue getIndex(unsigned i)207 {208 ASSERT(canGetIndex(i));209 return m_storage->m_vector[i].get();210 }211 212 bool canSetIndex(unsigned i) { return i < m_vectorLength; }213 void setIndex(JSGlobalData& globalData, unsigned i, JSValue v)214 {215 ASSERT(canSetIndex(i));216 217 WriteBarrier<Unknown>& x = m_storage->m_vector[i];218 if (!x) {219 ArrayStorage *storage = m_storage;220 ++storage->m_numValuesInVector;221 if (i >= storage->m_length)222 storage->m_length = i + 1;223 }224 x.set(globalData, this, v);225 }226 227 inline void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)228 {229 ASSERT(canSetIndex(i));230 ArrayStorage *storage = m_storage;231 #if CHECK_ARRAY_CONSISTENCY232 ASSERT(storage->m_inCompactInitialization);233 // Check that we are initializing the next index in sequence.234 ASSERT(i == storage->m_initializationIndex);235 // tryCreateUninitialized set m_numValuesInVector to the initialLength,236 // check we do not try to initialize more than this number of properties.237 ASSERT(storage->m_initializationIndex < storage->m_numValuesInVector);238 storage->m_initializationIndex++;239 #endif240 ASSERT(i < storage->m_length);241 ASSERT(i < storage->m_numValuesInVector);242 storage->m_vector[i].set(globalData, this, v);243 }244 245 inline void completeInitialization(unsigned newLength)246 {247 // Check that we have initialized as meny properties as we think we have.248 ASSERT_UNUSED(newLength, newLength == m_storage->m_length);249 #if CHECK_ARRAY_CONSISTENCY250 // Check that the number of propreties initialized matches the initialLength.251 ASSERT(m_storage->m_initializationIndex == m_storage->m_numValuesInVector);252 ASSERT(m_storage->m_inCompactInitialization);253 m_storage->m_inCompactInitialization = false;254 #endif255 }256 257 bool inSparseMode()258 {259 SparseArrayValueMap* map = m_sparseValueMap;260 return map && map->sparseMode();261 }262 263 78 void fillArgList(ExecState*, MarkedArgumentBuffer&); 264 79 void copyToArguments(ExecState*, CallFrame*, uint32_t length); … … 266 81 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) 267 82 { 268 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info );83 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage); 269 84 } 270 85 271 static ptrdiff_t storageOffset()272 {273 return OBJECT_OFFSETOF(JSArray, m_storage);274 }275 276 static ptrdiff_t vectorLengthOffset()277 {278 return OBJECT_OFFSETOF(JSArray, m_vectorLength);279 }280 281 JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);282 283 void enterDictionaryMode(JSGlobalData&);284 285 86 protected: 286 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | Overrides VisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;87 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; 287 88 static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); 288 89 289 90 static bool deleteProperty(JSCell*, ExecState*, PropertyName); 290 static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); 291 static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 91 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 292 92 293 93 private: 294 static size_t storageSize(unsigned vectorLength);295 94 bool isLengthWritable() 296 95 { 297 SparseArrayValueMap* map = m_sparseValueMap; 96 ArrayStorage* storage = arrayStorageOrNull(); 97 if (!storage) 98 return false; 99 SparseArrayValueMap* map = storage->m_sparseMap.get(); 298 100 return !map || !map->lengthIsReadOnly(); 299 101 } 300 102 301 103 void setLengthWritable(ExecState*, bool writable); 302 void putDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old); 303 bool defineOwnNumericProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException); 304 void allocateSparseMap(JSGlobalData&); 305 void deallocateSparseMap(); 306 307 void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); 308 JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, PutDirectIndexMode); 309 310 unsigned getNewVectorLength(unsigned desiredLength); 311 bool increaseVectorLength(JSGlobalData&, unsigned newLength); 104 312 105 bool unshiftCountSlowCase(JSGlobalData&, unsigned count); 313 106 314 107 unsigned compactForSorting(JSGlobalData&); 315 316 enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck };317 void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck);318 319 unsigned m_vectorLength; // The valid length of m_vector320 unsigned m_indexBias; // The number of JSValue sized blocks before ArrayStorage.321 ArrayStorage *m_storage;322 323 // FIXME: Maybe SparseArrayValueMap should be put into its own JSCell?324 SparseArrayValueMap* m_sparseValueMap;325 326 static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray, m_sparseValueMap); }327 static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray, m_indexBias); }328 108 }; 329 109 110 inline Butterfly* createArrayButterfly(JSGlobalData& globalData, unsigned initialLength) 111 { 112 Butterfly* butterfly = Butterfly::create( 113 globalData, 0, 0, true, baseIndexingHeaderForArray(initialLength), ArrayStorage::sizeFor(BASE_VECTOR_LEN)); 114 ArrayStorage* storage = butterfly->arrayStorage(); 115 storage->m_indexBias = 0; 116 storage->m_sparseMap.clear(); 117 storage->m_numValuesInVector = 0; 118 #if CHECK_ARRAY_CONSISTENCY 119 storage->m_initializationIndex = 0; 120 storage->m_inCompactInitialization = 0; 121 #endif 122 return butterfly; 123 } 124 125 Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData&, unsigned initialLength); 126 330 127 inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength) 331 128 { 332 JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure); 333 array->finishCreation(globalData, initialLength); 129 Butterfly* butterfly = createArrayButterfly(globalData, initialLength); 130 JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly); 131 array->finishCreation(globalData); 334 132 return array; 335 133 } … … 337 135 inline JSArray* JSArray::tryCreateUninitialized(JSGlobalData& globalData, Structure* structure, unsigned initialLength) 338 136 { 339 JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure); 340 return array->tryFinishCreationUninitialized(globalData, initialLength); 137 unsigned vectorLength = std::max(BASE_VECTOR_LEN, initialLength); 138 if (vectorLength > MAX_STORAGE_VECTOR_LENGTH) 139 return 0; 140 141 void* temp; 142 if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, ArrayStorage::sizeFor(vectorLength)), &temp)) 143 return 0; 144 Butterfly* butterfly = Butterfly::fromBase(temp, 0, 0); 145 *butterfly->indexingHeader() = indexingHeaderForArray(initialLength, vectorLength); 146 ArrayStorage* storage = butterfly->arrayStorage(); 147 storage->m_indexBias = 0; 148 storage->m_sparseMap.clear(); 149 storage->m_numValuesInVector = initialLength; 150 #if CHECK_ARRAY_CONSISTENCY 151 storage->m_initializationIndex = 0; 152 storage->m_inCompactInitialization = true; 153 #endif 154 155 JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly); 156 array->finishCreation(globalData); 157 return array; 341 158 } 342 159 … … 356 173 inline bool isJSArray(JSCell* cell) { return cell->classInfo() == &JSArray::s_info; } 357 174 inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); } 358 359 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize360 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage361 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +362 // (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).363 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))364 365 // These values have to be macros to be used in max() and min() without introducing366 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.367 #define MIN_SPARSE_ARRAY_INDEX 10000U368 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)369 inline size_t JSArray::storageSize(unsigned vectorLength)370 {371 ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);372 373 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)374 // - as asserted above - the following calculation cannot overflow.375 size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>));376 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that377 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).378 ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))));379 380 return size;381 }382 175 383 176 inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values) -
trunk/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
r127191 r128400 46 46 MarkedArgumentBuffer args; 47 47 for (unsigned i = 0; i < boundArgs->length(); ++i) 48 args.append(boundArgs->getIndex (i));48 args.append(boundArgs->getIndexQuickly(i)); 49 49 for (unsigned i = 0; i < exec->argumentCount(); ++i) 50 50 args.append(exec->argument(i)); … … 66 66 MarkedArgumentBuffer args; 67 67 for (unsigned i = 0; i < boundArgs->length(); ++i) 68 args.append(boundArgs->getIndex (i));68 args.append(boundArgs->getIndexQuickly(i)); 69 69 for (unsigned i = 0; i < exec->argumentCount(); ++i) 70 70 args.append(exec->argument(i)); … … 113 113 ASSERT(inherits(&s_info)); 114 114 115 putDirectAccessor(exec ->globalData(), exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);116 putDirectAccessor(exec ->globalData(), exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);115 putDirectAccessor(exec, exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 116 putDirectAccessor(exec, exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 117 117 } 118 118 -
trunk/Source/JavaScriptCore/runtime/JSCell.cpp
r127191 r128400 179 179 } 180 180 181 void JSCell::getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) 182 { 183 ASSERT_NOT_REACHED(); 184 } 185 181 186 String JSCell::className(const JSObject*) 182 187 { -
trunk/Source/JavaScriptCore/runtime/JSCell.h
r128260 r128400 139 139 return &m_structure; 140 140 } 141 141 142 142 #if ENABLE(GC_VALIDATION) 143 143 Structure* unvalidatedStructure() { return m_structure.unvalidatedGet(); } … … 157 157 static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); 158 158 static NO_RETURN_DUE_TO_ASSERT void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 159 static NO_RETURN_DUE_TO_ASSERT void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 159 160 static NO_RETURN_DUE_TO_ASSERT void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 160 161 static String className(const JSObject*); -
trunk/Source/JavaScriptCore/runtime/JSFunction.cpp
r128265 r128400 223 223 bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); 224 224 if (!result) { 225 thisObject->putDirectAccessor(exec ->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);225 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 226 226 result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); 227 227 ASSERT(result); … … 247 247 bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); 248 248 if (!result) { 249 thisObject->putDirectAccessor(exec ->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);249 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 250 250 result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); 251 251 ASSERT(result); … … 276 276 bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); 277 277 if (!result) { 278 thisObject->putDirectAccessor(exec ->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);278 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 279 279 result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); 280 280 ASSERT(result); … … 300 300 bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); 301 301 if (!result) { 302 thisObject->putDirectAccessor(exec ->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);302 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 303 303 result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); 304 304 ASSERT(result); … … 313 313 } 314 314 315 void JSFunction::getOwn PropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)315 void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 316 316 { 317 317 JSFunction* thisObject = jsCast<JSFunction*>(object); … … 326 326 propertyNames.add(exec->propertyNames().name); 327 327 } 328 Base::getOwn PropertyNames(thisObject, exec, propertyNames, mode);328 Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); 329 329 } 330 330 … … 395 395 if (thisObject->jsExecutable()->isStrictMode()) { 396 396 if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor)) 397 thisObject->putDirectAccessor(exec ->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);397 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 398 398 return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); 399 399 } … … 402 402 if (thisObject->jsExecutable()->isStrictMode()) { 403 403 if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor)) 404 thisObject->putDirectAccessor(exec ->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);404 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); 405 405 return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); 406 406 } -
trunk/Source/JavaScriptCore/runtime/JSFunction.h
r128265 r128400 148 148 static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); 149 149 static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); 150 static void getOwn PropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties);150 static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties); 151 151 static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); 152 152 -
trunk/Source/JavaScriptCore/runtime/JSGlobalData.cpp
r127719 r128400 56 56 #include "RegExpCache.h" 57 57 #include "RegExpObject.h" 58 #include "SparseArrayValueMapInlineMethods.h" 58 59 #include "StrictEvalActivation.h" 59 60 #include "StrongInlines.h" … … 214 215 sharedSymbolTableStructure.set(*this, SharedSymbolTable::createStructure(*this, 0, jsNull())); 215 216 structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull())); 217 sparseArrayValueMapStructure.set(*this, SparseArrayValueMap::createStructure(*this, 0, jsNull())); 216 218 217 219 wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable); -
trunk/Source/JavaScriptCore/runtime/JSGlobalData.h
r127719 r128400 245 245 Strong<Structure> sharedSymbolTableStructure; 246 246 Strong<Structure> structureChainStructure; 247 Strong<Structure> sparseArrayValueMapStructure; 247 248 248 249 IdentifierTable* identifierTable; -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
r127958 r128400 214 214 protoAccessor->setGetter(exec->globalData(), JSFunction::create(exec, this, 0, String(), globalFuncProtoGetter)); 215 215 protoAccessor->setSetter(exec->globalData(), JSFunction::create(exec, this, 0, String(), globalFuncProtoSetter)); 216 m_objectPrototype->putDirectAccessor(exec ->globalData(), exec->propertyNames().underscoreProto, protoAccessor, Accessor | DontEnum);216 m_objectPrototype->putDirectAccessor(exec, exec->propertyNames().underscoreProto, protoAccessor, Accessor | DontEnum); 217 217 m_functionPrototype->structure()->setPrototypeWithoutTransition(exec->globalData(), m_objectPrototype.get()); 218 218 -
trunk/Source/JavaScriptCore/runtime/JSONObject.cpp
r127958 r128400 522 522 // Get the value. 523 523 JSValue value; 524 if (m_isJSArray && asArray(m_object.get())->canGetIndex (index))525 value = asArray(m_object.get())->getIndex (index);524 if (m_isJSArray && asArray(m_object.get())->canGetIndexQuickly(index)) 525 value = asArray(m_object.get())->getIndexQuickly(index); 526 526 else { 527 527 PropertySlot slot(m_object.get()); … … 688 688 break; 689 689 } 690 if (isJSArray(array) && array->canGetIndex (index))691 inValue = array->getIndex (index);690 if (isJSArray(array) && array->canGetIndexQuickly(index)) 691 inValue = array->getIndexQuickly(index); 692 692 else { 693 693 PropertySlot slot; -
trunk/Source/JavaScriptCore/runtime/JSObject.cpp
r127505 r128400 2 2 * Copyright (C) 1999-2001 Harri Porten ([email protected]) 3 3 * Copyright (C) 2001 Peter Kelly ([email protected]) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012 Apple Inc. All rights reserved. 5 5 * Copyright (C) 2007 Eric Seidel ([email protected]) 6 6 * … … 25 25 #include "JSObject.h" 26 26 27 #include "ButterflyInlineMethods.h" 27 28 #include "CopiedSpaceInlineMethods.h" 28 29 #include "DatePrototype.h" 29 30 #include "ErrorConstructor.h" 30 31 #include "GetterSetter.h" 32 #include "IndexingHeaderInlineMethods.h" 31 33 #include "JSFunction.h" 32 34 #include "JSGlobalObject.h" … … 39 41 #include "PropertyDescriptor.h" 40 42 #include "PropertyNameArray.h" 43 #include "Reject.h" 41 44 #include "SlotVisitorInlineMethods.h" 45 #include "SparseArrayValueMapInlineMethods.h" 42 46 #include <math.h> 43 47 #include <wtf/Assertions.h> 44 48 45 49 namespace JSC { 50 51 // We keep track of the size of the last array after it was grown. We use this 52 // as a simple heuristic for as the value to grow the next array from size 0. 53 // This value is capped by the constant FIRST_VECTOR_GROW defined above. 54 static unsigned lastArraySize = 0; 46 55 47 56 JSCell* getCallableObjectSlow(JSCell* cell) … … 87 96 } 88 97 89 ALWAYS_INLINE void JSObject::visitOutOfLineStorage(SlotVisitor& visitor, PropertyStorage storage, size_t storageSize) 90 { 91 ASSERT(storage); 92 ASSERT(storageSize); 93 94 size_t capacity = structure()->outOfLineCapacity(); 95 ASSERT(capacity); 96 size_t capacityInBytes = capacity * sizeof(WriteBarrierBase<Unknown>); 97 PropertyStorage baseOfStorage = storage - capacity - 1; 98 if (visitor.checkIfShouldCopyAndPinOtherwise(baseOfStorage, capacityInBytes)) { 99 PropertyStorage newBaseOfStorage = static_cast<PropertyStorage>(visitor.allocateNewSpace(capacityInBytes)); 100 PropertyStorage currentTarget = newBaseOfStorage + capacity; 101 PropertyStorage newStorage = currentTarget + 1; 102 PropertyStorage currentSource = storage - 1; 98 ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize) 99 { 100 ASSERT(butterfly); 101 102 Structure* structure = this->structure(); 103 104 size_t propertyCapacity = structure->outOfLineCapacity(); 105 size_t preCapacity; 106 size_t indexingPayloadSizeInBytes; 107 bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType()); 108 if (UNLIKELY(hasIndexingHeader)) { 109 preCapacity = butterfly->indexingHeader()->preCapacity(structure); 110 indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); 111 } else { 112 preCapacity = 0; 113 indexingPayloadSizeInBytes = 0; 114 } 115 size_t capacityInBytes = Butterfly::totalSize( 116 preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); 117 if (visitor.checkIfShouldCopyAndPinOtherwise( 118 butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) { 119 Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); 120 121 // Mark and copy the properties. 122 PropertyStorage currentTarget = newButterfly->propertyStorage(); 123 PropertyStorage currentSource = butterfly->propertyStorage(); 103 124 for (size_t count = storageSize; count--;) { 104 125 JSValue value = (--currentSource)->get(); … … 107 128 (--currentTarget)->setWithoutWriteBarrier(value); 108 129 } 109 m_outOfLineStorage.set(newStorage, StorageBarrier::Unchecked); 110 } else 111 visitor.appendValues(storage - storageSize - 1, storageSize); 130 131 if (UNLIKELY(hasIndexingHeader)) { 132 *newButterfly->indexingHeader() = *butterfly->indexingHeader(); 133 134 // Mark and copy the array if appropriate. 135 switch (structure->indexingType()) { 136 case ArrayWithArrayStorage: 137 case NonArrayWithArrayStorage: { 138 newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage()); 139 WriteBarrier<Unknown>* currentTarget = newButterfly->arrayStorage()->m_vector; 140 WriteBarrier<Unknown>* currentSource = butterfly->arrayStorage()->m_vector; 141 for (size_t count = newButterfly->arrayStorage()->vectorLength(); count--;) { 142 JSValue value = (currentSource++)->get(); 143 if (value) 144 visitor.appendUnbarrieredValue(&value); 145 (currentTarget++)->setWithoutWriteBarrier(value); 146 } 147 if (newButterfly->arrayStorage()->m_sparseMap) 148 visitor.append(&newButterfly->arrayStorage()->m_sparseMap); 149 break; 150 } 151 default: 152 break; 153 } 154 } 155 156 m_butterfly = newButterfly; 157 } else { 158 // Mark the properties. 159 visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); 160 161 // Mark the array if appropriate. 162 switch (structure->indexingType()) { 163 case ArrayWithArrayStorage: 164 case NonArrayWithArrayStorage: 165 visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); 166 if (butterfly->arrayStorage()->m_sparseMap) 167 visitor.append(&butterfly->arrayStorage()->m_sparseMap); 168 break; 169 default: 170 break; 171 } 172 } 112 173 } 113 174 … … 123 184 JSCell::visitChildren(thisObject, visitor); 124 185 125 PropertyStorage storage = thisObject->outOfLineStorage();126 if ( storage)127 thisObject->visit OutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownNonFinalObject());186 Butterfly* butterfly = thisObject->butterfly(); 187 if (butterfly) 188 thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownNonFinalObject()); 128 189 129 190 #if !ASSERT_DISABLED … … 143 204 JSCell::visitChildren(thisObject, visitor); 144 205 145 PropertyStorage storage = thisObject->outOfLineStorage();146 if ( storage)147 thisObject->visit OutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownFinalObject());206 Butterfly* butterfly = thisObject->butterfly(); 207 if (butterfly) 208 thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownFinalObject()); 148 209 149 210 size_t storageSize = thisObject->structure()->inlineSizeForKnownFinalObject(); … … 162 223 } 163 224 164 bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) 165 { 225 bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) 226 { 227 // NB. The fact that we're directly consulting our indexed storage implies that it is not 228 // legal for anyone to override getOwnPropertySlot() without also overriding 229 // getOwnPropertySlotByIndex(). 230 166 231 JSObject* thisObject = jsCast<JSObject*>(cell); 167 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); 232 233 if (i > MAX_ARRAY_INDEX) 234 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); 235 236 switch (thisObject->structure()->indexingType()) { 237 case NonArray: 238 case Array: 239 break; 240 241 case NonArrayWithArrayStorage: 242 case ArrayWithArrayStorage: { 243 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); 244 if (i >= storage->length()) 245 return false; 246 247 if (i < storage->vectorLength()) { 248 JSValue value = storage->m_vector[i].get(); 249 if (value) { 250 slot.setValue(value); 251 return true; 252 } 253 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 254 SparseArrayValueMap::iterator it = map->find(i); 255 if (it != map->notFound()) { 256 it->second.get(slot); 257 return true; 258 } 259 } 260 break; 261 } 262 263 default: 264 ASSERT_NOT_REACHED(); 265 break; 266 } 267 268 return false; 168 269 } 169 270 … … 175 276 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); 176 277 JSGlobalData& globalData = exec->globalData(); 278 279 // Try indexed put first. This is required for correctness, since loads on property names that appear like 280 // valid indices will never look in the named property storage. 281 unsigned i = propertyName.asIndex(); 282 if (i != PropertyName::NotAnIndex) { 283 putByIndex(thisObject, exec, i, value, slot.isStrictMode()); 284 return; 285 } 177 286 178 287 // Check if there are any setters or getters in the prototype chain … … 236 345 void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) 237 346 { 238 PutPropertySlot slot(shouldThrow);239 347 JSObject* thisObject = jsCast<JSObject*>(cell); 240 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); 348 thisObject->checkIndexingConsistency(); 349 350 if (UNLIKELY(propertyName > MAX_ARRAY_INDEX)) { 351 PutPropertySlot slot(shouldThrow); 352 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); 353 return; 354 } 355 356 switch (thisObject->structure()->indexingType()) { 357 case NonArray: 358 case Array: 359 break; 360 361 case NonArrayWithArrayStorage: 362 case ArrayWithArrayStorage: { 363 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); 364 365 if (propertyName >= storage->vectorLength()) 366 break; 367 368 WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName]; 369 unsigned length = storage->length(); 370 371 // Update length & m_numValuesInVector as necessary. 372 if (propertyName >= length) { 373 length = propertyName + 1; 374 storage->setLength(length); 375 ++storage->m_numValuesInVector; 376 } else if (!valueSlot) 377 ++storage->m_numValuesInVector; 378 379 valueSlot.set(exec->globalData(), thisObject, value); 380 thisObject->checkIndexingConsistency(); 381 return; 382 } 383 384 default: 385 ASSERT_NOT_REACHED(); 386 } 387 388 thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow); 389 thisObject->checkIndexingConsistency(); 390 } 391 392 ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData& globalData, ArrayStorage* storage) 393 { 394 SparseArrayValueMap* map = storage->m_sparseMap.get(); 395 396 if (!map) 397 map = allocateSparseIndexMap(globalData); 398 399 if (map->sparseMode()) 400 return storage; 401 402 map->setSparseMode(); 403 404 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); 405 for (unsigned i = 0; i < usedVectorLength; ++i) { 406 JSValue value = storage->m_vector[i].get(); 407 // This will always be a new entry in the map, so no need to check we can write, 408 // and attributes are default so no need to set them. 409 if (value) 410 map->add(this, i).iterator->second.set(globalData, this, value); 411 } 412 413 Butterfly* newButterfly = storage->butterfly()->resizeArray(globalData, structure(), 0, ArrayStorage::sizeFor(0)); 414 if (!newButterfly) 415 CRASH(); 416 417 m_butterfly = newButterfly; 418 newButterfly->arrayStorage()->m_indexBias = 0; 419 newButterfly->arrayStorage()->setVectorLength(0); 420 newButterfly->arrayStorage()->m_sparseMap.set(globalData, this, map); 421 422 return newButterfly->arrayStorage(); 423 } 424 425 void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) 426 { 427 switch (structure()->indexingType()) { 428 case ArrayWithArrayStorage: 429 case NonArrayWithArrayStorage: 430 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); 431 break; 432 433 default: 434 break; 435 } 436 } 437 438 ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned length, unsigned vectorLength) 439 { 440 IndexingType oldType = structure()->indexingType(); 441 ASSERT_UNUSED(oldType, oldType == NonArray || oldType == Array); 442 Butterfly* newButterfly = m_butterfly->growArrayRight( 443 globalData, structure(), structure()->outOfLineCapacity(), false, 0, 444 ArrayStorage::sizeFor(vectorLength)); 445 if (!newButterfly) 446 CRASH(); 447 ArrayStorage* result = newButterfly->arrayStorage(); 448 result->setLength(length); 449 result->setVectorLength(vectorLength); 450 result->m_sparseMap.clear(); 451 result->m_numValuesInVector = 0; 452 result->m_indexBias = 0; 453 #if CHECK_ARRAY_CONSISTENCY 454 result->m_initializationIndex = 0; 455 result->m_inCompactInitialization = 0; 456 #endif 457 Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateArrayStorage); 458 setButterfly(globalData, newButterfly, newStructure); 459 return result; 460 } 461 462 ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData) 463 { 464 return createArrayStorage(globalData, 0, BASE_VECTOR_LEN); 465 } 466 467 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData) 468 { 469 switch (structure()->indexingType()) { 470 case ArrayWithArrayStorage: 471 case NonArrayWithArrayStorage: 472 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); 473 474 case Array: 475 case NonArray: { 476 createArrayStorage(globalData, 0, 0); 477 SparseArrayValueMap* map = allocateSparseIndexMap(globalData); 478 map->setSparseMode(); 479 return arrayStorage(); 480 } 481 482 default: 483 ASSERT_NOT_REACHED(); 484 return 0; 485 } 241 486 } 242 487 … … 270 515 } 271 516 272 void JSObject::putDirectAccessor( JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)517 void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes) 273 518 { 274 519 ASSERT(value.isGetterSetter() && (attributes & Accessor)); 520 521 unsigned index = propertyName.asIndex(); 522 if (index != PropertyName::NotAnIndex) { 523 putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect); 524 return; 525 } 526 527 JSGlobalData& globalData = exec->globalData(); 275 528 276 529 PutPropertySlot slot; … … 305 558 { 306 559 JSObject* thisObject = jsCast<JSObject*>(cell); 560 561 unsigned i = propertyName.asIndex(); 562 if (i != PropertyName::NotAnIndex) 563 return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i); 307 564 308 565 if (!thisObject->staticFunctionsReified()) … … 333 590 } 334 591 335 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)592 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) 336 593 { 337 594 JSObject* thisObject = jsCast<JSObject*>(cell); 338 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, propertyName)); 595 596 if (i > MAX_ARRAY_INDEX) 597 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i)); 598 599 switch (thisObject->structure()->indexingType()) { 600 case Array: 601 case NonArray: 602 return true; 603 604 case ArrayWithArrayStorage: 605 case NonArrayWithArrayStorage: { 606 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); 607 608 if (i < storage->vectorLength()) { 609 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; 610 if (valueSlot) { 611 valueSlot.clear(); 612 --storage->m_numValuesInVector; 613 } 614 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 615 SparseArrayValueMap::iterator it = map->find(i); 616 if (it != map->notFound()) { 617 if (it->second.attributes & DontDelete) 618 return false; 619 map->remove(it); 620 } 621 } 622 623 thisObject->checkIndexingConsistency(); 624 return true; 625 } 626 627 default: 628 ASSERT_NOT_REACHED(); 629 return false; 630 } 339 631 } 340 632 … … 466 758 void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 467 759 { 760 // Add numeric properties first. That appears to be the accepted convention. 761 // FIXME: Filling PropertyNameArray with an identifier for every integer 762 // is incredibly inefficient for large arrays. We need a different approach, 763 // which almost certainly means a different structure for PropertyNameArray. 764 switch (object->structure()->indexingType()) { 765 case NonArray: 766 case Array: 767 break; 768 769 case NonArrayWithArrayStorage: 770 case ArrayWithArrayStorage: { 771 ArrayStorage* storage = object->m_butterfly->arrayStorage(); 772 773 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); 774 for (unsigned i = 0; i < usedVectorLength; ++i) { 775 if (storage->m_vector[i]) 776 propertyNames.add(Identifier::from(exec, i)); 777 } 778 779 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 780 Vector<unsigned> keys; 781 keys.reserveCapacity(map->size()); 782 783 SparseArrayValueMap::const_iterator end = map->end(); 784 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { 785 if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum)) 786 keys.append(static_cast<unsigned>(it->first)); 787 } 788 789 std::sort(keys.begin(), keys.end()); 790 for (unsigned i = 0; i < keys.size(); ++i) 791 propertyNames.add(Identifier::from(exec, keys[i])); 792 } 793 break; 794 } 795 796 default: 797 ASSERT_NOT_REACHED(); 798 } 799 800 object->methodTable()->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); 801 } 802 803 void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 804 { 468 805 getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified()); 469 806 object->structure()->getPropertyNamesFromStructure(exec->globalData(), propertyNames, mode); … … 516 853 void JSObject::preventExtensions(JSGlobalData& globalData) 517 854 { 518 if (isJSArray(this)) 519 asArray(this)->enterDictionaryMode(globalData); 855 enterDictionaryIndexingMode(globalData); 520 856 if (isExtensible()) 521 857 setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure())); … … 574 910 } 575 911 576 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)577 { 578 if (JSObject* getterFunction = asGetterSetter( location->get())->getter()) {912 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, PropertyOffset offset) 913 { 914 if (JSObject* getterFunction = asGetterSetter(getDirectOffset(offset))->getter()) { 579 915 if (!structure()->isDictionary()) 580 slot.setCacheableGetterSlot(this, getterFunction, offset ForLocation(location));916 slot.setCacheableGetterSlot(this, getterFunction, offset); 581 917 else 582 918 slot.setGetterSlot(getterFunction); … … 604 940 } 605 941 606 PropertyStorage JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) 942 void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) 943 { 944 if (descriptor.isDataDescriptor()) { 945 if (descriptor.value()) 946 entryInMap->set(exec->globalData(), this, descriptor.value()); 947 else if (oldDescriptor.isAccessorDescriptor()) 948 entryInMap->set(exec->globalData(), this, jsUndefined()); 949 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; 950 return; 951 } 952 953 if (descriptor.isAccessorDescriptor()) { 954 JSObject* getter = 0; 955 if (descriptor.getterPresent()) 956 getter = descriptor.getterObject(); 957 else if (oldDescriptor.isAccessorDescriptor()) 958 getter = oldDescriptor.getterObject(); 959 JSObject* setter = 0; 960 if (descriptor.setterPresent()) 961 setter = descriptor.setterObject(); 962 else if (oldDescriptor.isAccessorDescriptor()) 963 setter = oldDescriptor.setterObject(); 964 965 GetterSetter* accessor = GetterSetter::create(exec); 966 if (getter) 967 accessor->setGetter(exec->globalData(), getter); 968 if (setter) 969 accessor->setSetter(exec->globalData(), setter); 970 971 entryInMap->set(exec->globalData(), this, accessor); 972 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly; 973 return; 974 } 975 976 ASSERT(descriptor.isGenericDescriptor()); 977 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor); 978 } 979 980 // Defined in ES5.1 8.12.9 981 bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) 982 { 983 ASSERT(index != 0xFFFFFFFF); 984 985 if (!inSparseIndexingMode()) { 986 // Fast case: we're putting a regular property to a regular array 987 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false 988 // however if the property currently exists missing attributes will override from their current 'true' 989 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). 990 if (!descriptor.attributes()) { 991 ASSERT(!descriptor.isAccessorDescriptor()); 992 return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); 993 } 994 995 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->globalData()); 996 } 997 998 SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get(); 999 ASSERT(map); 1000 1001 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. 1002 SparseArrayValueMap::AddResult result = map->add(this, index); 1003 SparseArrayEntry* entryInMap = &result.iterator->second; 1004 1005 // 2. Let extensible be the value of the [[Extensible]] internal property of O. 1006 // 3. If current is undefined and extensible is false, then Reject. 1007 // 4. If current is undefined and extensible is true, then 1008 if (result.isNewEntry) { 1009 if (!isExtensible()) { 1010 map->remove(result.iterator); 1011 return reject(exec, throwException, "Attempting to define property on object that is not extensible."); 1012 } 1013 1014 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property 1015 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values 1016 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly 1017 // created property is set to its default value. 1018 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of 1019 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by 1020 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property 1021 // is set to its default value. 1022 // 4.c. Return true. 1023 1024 PropertyDescriptor defaults; 1025 entryInMap->setWithoutWriteBarrier(jsUndefined()); 1026 entryInMap->attributes = DontDelete | DontEnum | ReadOnly; 1027 entryInMap->get(defaults); 1028 1029 putIndexedDescriptor(exec, entryInMap, descriptor, defaults); 1030 if (index >= m_butterfly->arrayStorage()->length()) 1031 m_butterfly->arrayStorage()->setLength(index + 1); 1032 return true; 1033 } 1034 1035 // 5. Return true, if every field in Desc is absent. 1036 // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12). 1037 PropertyDescriptor current; 1038 entryInMap->get(current); 1039 if (descriptor.isEmpty() || descriptor.equalTo(exec, current)) 1040 return true; 1041 1042 // 7. If the [[Configurable]] field of current is false then 1043 if (!current.configurable()) { 1044 // 7.a. Reject, if the [[Configurable]] field of Desc is true. 1045 if (descriptor.configurablePresent() && descriptor.configurable()) 1046 return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); 1047 // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other. 1048 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable()) 1049 return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); 1050 } 1051 1052 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required. 1053 if (!descriptor.isGenericDescriptor()) { 1054 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then 1055 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) { 1056 // 9.a. Reject, if the [[Configurable]] field of current is false. 1057 if (!current.configurable()) 1058 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); 1059 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a 1060 // data property to an accessor property. Preserve the existing values of the converted property's 1061 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to 1062 // their default values. 1063 // 9.c. Else, convert the property named P of object O from an accessor property to a data property. 1064 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] 1065 // attributes and set the rest of the property's attributes to their default values. 1066 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) { 1067 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then 1068 // 10.a. If the [[Configurable]] field of current is false, then 1069 if (!current.configurable() && !current.writable()) { 1070 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true. 1071 if (descriptor.writable()) 1072 return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); 1073 // 10.a.ii. If the [[Writable]] field of current is false, then 1074 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false. 1075 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value())) 1076 return reject(exec, throwException, "Attempting to change value of a readonly property."); 1077 } 1078 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable. 1079 } else { 1080 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent()); 1081 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then 1082 if (!current.configurable()) { 1083 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false. 1084 if (descriptor.setterPresent() && descriptor.setter() != current.setter()) 1085 return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property."); 1086 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false. 1087 if (descriptor.getterPresent() && descriptor.getter() != current.getter()) 1088 return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property."); 1089 } 1090 } 1091 } 1092 1093 // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field. 1094 putIndexedDescriptor(exec, entryInMap, descriptor, current); 1095 // 13. Return true. 1096 return true; 1097 } 1098 1099 SparseArrayValueMap* JSObject::allocateSparseIndexMap(JSGlobalData& globalData) 1100 { 1101 SparseArrayValueMap* result = SparseArrayValueMap::create(globalData); 1102 arrayStorage()->m_sparseMap.set(globalData, this, result); 1103 return result; 1104 } 1105 1106 void JSObject::deallocateSparseIndexMap() 1107 { 1108 if (ArrayStorage* arrayStorage = arrayStorageOrNull()) 1109 arrayStorage->m_sparseMap.clear(); 1110 } 1111 1112 void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) 1113 { 1114 JSGlobalData& globalData = exec->globalData(); 1115 1116 // i should be a valid array index that is outside of the current vector. 1117 ASSERT(i <= MAX_ARRAY_INDEX); 1118 ASSERT(i >= storage->vectorLength()); 1119 1120 SparseArrayValueMap* map = storage->m_sparseMap.get(); 1121 1122 // First, handle cases where we don't currently have a sparse map. 1123 if (LIKELY(!map)) { 1124 // If the array is not extensible, we should have entered dictionary mode, and created the spare map. 1125 ASSERT(isExtensible()); 1126 1127 // Update m_length if necessary. 1128 if (i >= storage->length()) 1129 storage->setLength(i + 1); 1130 1131 // Check that it is sensible to still be using a vector, and then try to grow the vector. 1132 if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) { 1133 // success! - reread m_storage since it has likely been reallocated, and store to the vector. 1134 storage = arrayStorage(); 1135 storage->m_vector[i].set(globalData, this, value); 1136 ++storage->m_numValuesInVector; 1137 return; 1138 } 1139 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. 1140 map = allocateSparseIndexMap(exec->globalData()); 1141 map->putEntry(exec, this, i, value, shouldThrow); 1142 return; 1143 } 1144 1145 // Update m_length if necessary. 1146 unsigned length = storage->length(); 1147 if (i >= length) { 1148 // Prohibit growing the array if length is not writable. 1149 if (map->lengthIsReadOnly() || !isExtensible()) { 1150 if (shouldThrow) 1151 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 1152 return; 1153 } 1154 length = i + 1; 1155 storage->setLength(length); 1156 } 1157 1158 // We are currently using a map - check whether we still want to be doing so. 1159 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. 1160 unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); 1161 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) { 1162 map->putEntry(exec, this, i, value, shouldThrow); 1163 return; 1164 } 1165 1166 // Reread m_storage after increaseVectorLength, update m_numValuesInVector. 1167 storage = arrayStorage(); 1168 storage->m_numValuesInVector = numValuesInArray; 1169 1170 // Copy all values from the map into the vector, and delete the map. 1171 WriteBarrier<Unknown>* vector = storage->m_vector; 1172 SparseArrayValueMap::const_iterator end = map->end(); 1173 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1174 vector[it->first].set(globalData, this, it->second.getNonSparseMode()); 1175 deallocateSparseIndexMap(); 1176 1177 // Store the new property into the vector. 1178 WriteBarrier<Unknown>& valueSlot = vector[i]; 1179 if (!valueSlot) 1180 ++storage->m_numValuesInVector; 1181 valueSlot.set(globalData, this, value); 1182 } 1183 1184 void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) 1185 { 1186 JSGlobalData& globalData = exec->globalData(); 1187 1188 // i should be a valid array index that is outside of the current vector. 1189 ASSERT(i <= MAX_ARRAY_INDEX); 1190 1191 switch (structure()->indexingType()) { 1192 case NonArray: 1193 case Array: { 1194 if (indexingShouldBeSparse()) { 1195 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); 1196 break; 1197 } 1198 if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) { 1199 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0)); 1200 break; 1201 } 1202 1203 ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); 1204 storage->m_vector[i].set(globalData, this, value); 1205 storage->m_numValuesInVector = 1; 1206 break; 1207 } 1208 1209 case NonArrayWithArrayStorage: 1210 case ArrayWithArrayStorage: 1211 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage()); 1212 break; 1213 1214 default: 1215 ASSERT_NOT_REACHED(); 1216 } 1217 } 1218 1219 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage) 1220 { 1221 JSGlobalData& globalData = exec->globalData(); 1222 1223 // i should be a valid array index that is outside of the current vector. 1224 ASSERT(i >= storage->vectorLength() || attributes); 1225 ASSERT(i <= MAX_ARRAY_INDEX); 1226 1227 SparseArrayValueMap* map = storage->m_sparseMap.get(); 1228 1229 // First, handle cases where we don't currently have a sparse map. 1230 if (LIKELY(!map)) { 1231 // If the array is not extensible, we should have entered dictionary mode, and created the spare map. 1232 ASSERT(isExtensible()); 1233 1234 // Update m_length if necessary. 1235 if (i >= storage->length()) 1236 storage->setLength(i + 1); 1237 1238 // Check that it is sensible to still be using a vector, and then try to grow the vector. 1239 if (LIKELY( 1240 !attributes 1241 && (isDenseEnoughForVector(i, storage->m_numValuesInVector)) 1242 && increaseVectorLength(globalData, i + 1))) { 1243 // success! - reread m_storage since it has likely been reallocated, and store to the vector. 1244 storage = arrayStorage(); 1245 storage->m_vector[i].set(globalData, this, value); 1246 ++storage->m_numValuesInVector; 1247 return true; 1248 } 1249 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. 1250 map = allocateSparseIndexMap(exec->globalData()); 1251 return map->putDirect(exec, this, i, value, attributes, mode); 1252 } 1253 1254 // Update m_length if necessary. 1255 unsigned length = storage->length(); 1256 if (i >= length) { 1257 if (mode != PutDirectIndexLikePutDirect) { 1258 // Prohibit growing the array if length is not writable. 1259 if (map->lengthIsReadOnly()) 1260 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError); 1261 if (!isExtensible()) 1262 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); 1263 } 1264 length = i + 1; 1265 storage->setLength(length); 1266 } 1267 1268 // We are currently using a map - check whether we still want to be doing so. 1269 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. 1270 unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); 1271 if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) 1272 return map->putDirect(exec, this, i, value, attributes, mode); 1273 1274 // Reread m_storage after increaseVectorLength, update m_numValuesInVector. 1275 storage = arrayStorage(); 1276 storage->m_numValuesInVector = numValuesInArray; 1277 1278 // Copy all values from the map into the vector, and delete the map. 1279 WriteBarrier<Unknown>* vector = storage->m_vector; 1280 SparseArrayValueMap::const_iterator end = map->end(); 1281 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1282 vector[it->first].set(globalData, this, it->second.getNonSparseMode()); 1283 deallocateSparseIndexMap(); 1284 1285 // Store the new property into the vector. 1286 WriteBarrier<Unknown>& valueSlot = vector[i]; 1287 if (!valueSlot) 1288 ++storage->m_numValuesInVector; 1289 valueSlot.set(globalData, this, value); 1290 return true; 1291 } 1292 1293 bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) 1294 { 1295 JSGlobalData& globalData = exec->globalData(); 1296 1297 // i should be a valid array index that is outside of the current vector. 1298 ASSERT(i <= MAX_ARRAY_INDEX); 1299 1300 switch (structure()->indexingType()) { 1301 case NonArray: 1302 case Array: { 1303 if (indexingShouldBeSparse() || attributes) 1304 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); 1305 if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) 1306 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0)); 1307 1308 ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); 1309 storage->m_vector[i].set(globalData, this, value); 1310 storage->m_numValuesInVector = 1; 1311 return true; 1312 } 1313 1314 case NonArrayWithArrayStorage: 1315 case ArrayWithArrayStorage: 1316 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage()); 1317 1318 default: 1319 ASSERT_NOT_REACHED(); 1320 return false; 1321 } 1322 } 1323 1324 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength) 1325 { 1326 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH); 1327 1328 unsigned increasedLength; 1329 unsigned maxInitLength = std::min(currentLength, 100000U); 1330 1331 if (desiredLength < maxInitLength) 1332 increasedLength = maxInitLength; 1333 else if (!currentVectorLength) 1334 increasedLength = std::max(desiredLength, lastArraySize); 1335 else { 1336 // Mathematically equivalent to: 1337 // increasedLength = (newLength * 3 + 1) / 2; 1338 // or: 1339 // increasedLength = (unsigned)ceil(newLength * 1.5)); 1340 // This form is not prone to internal overflow. 1341 increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1); 1342 } 1343 1344 ASSERT(increasedLength >= desiredLength); 1345 1346 lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW); 1347 1348 return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); 1349 } 1350 1351 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) 1352 { 1353 unsigned vectorLength; 1354 unsigned length; 1355 1356 switch (structure()->indexingType()) { 1357 case NonArray: 1358 case Array: 1359 vectorLength = 0; 1360 length = 0; 1361 break; 1362 case NonArrayWithArrayStorage: 1363 case ArrayWithArrayStorage: 1364 vectorLength = m_butterfly->arrayStorage()->vectorLength(); 1365 length = m_butterfly->arrayStorage()->length(); 1366 break; 1367 default: 1368 CRASH(); 1369 return 0; 1370 } 1371 return getNewVectorLength(vectorLength, length, desiredLength); 1372 } 1373 1374 bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) 1375 { 1376 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map 1377 // to the vector. Callers have to account for that, because they can do it more efficiently. 1378 if (newLength > MAX_STORAGE_VECTOR_LENGTH) 1379 return false; 1380 1381 ArrayStorage* storage = arrayStorage(); 1382 1383 unsigned indexBias = storage->m_indexBias; 1384 unsigned vectorLength = storage->vectorLength(); 1385 ASSERT(newLength > vectorLength); 1386 unsigned newVectorLength = getNewVectorLength(newLength); 1387 1388 // Fast case - there is no precapacity. In these cases a realloc makes sense. 1389 if (LIKELY(!indexBias)) { 1390 Butterfly* newButterfly = storage->butterfly()->growArrayRight(globalData, structure(), structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength)); 1391 if (!newButterfly) 1392 return false; 1393 m_butterfly = newButterfly; 1394 newButterfly->arrayStorage()->setVectorLength(newVectorLength); 1395 return true; 1396 } 1397 1398 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length. 1399 unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); 1400 Butterfly* newButterfly = storage->butterfly()->resizeArray( 1401 globalData, 1402 structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), 1403 newIndexBias, true, ArrayStorage::sizeFor(newVectorLength)); 1404 if (!newButterfly) 1405 return false; 1406 1407 m_butterfly = newButterfly; 1408 newButterfly->arrayStorage()->setVectorLength(newVectorLength); 1409 newButterfly->arrayStorage()->m_indexBias = newIndexBias; 1410 return true; 1411 } 1412 1413 #if CHECK_ARRAY_CONSISTENCY 1414 void JSObject::checkIndexingConsistency(ConsistencyCheckType type) 1415 { 1416 ArrayStorage* storage = arrayStorageOrNull(); 1417 if (!storage) 1418 return; 1419 1420 ASSERT(!storage->m_inCompactInitialization); 1421 1422 ASSERT(storage); 1423 if (type == SortConsistencyCheck) 1424 ASSERT(!storage->m_sparseMap); 1425 1426 unsigned numValuesInVector = 0; 1427 for (unsigned i = 0; i < storage->vectorLength(); ++i) { 1428 if (JSValue value = storage->m_vector[i].get()) { 1429 ASSERT(i < storage->length()); 1430 if (type != DestructorConsistencyCheck) 1431 value.isUndefined(); // Likely to crash if the object was deallocated. 1432 ++numValuesInVector; 1433 } else { 1434 if (type == SortConsistencyCheck) 1435 ASSERT(i >= storage->m_numValuesInVector); 1436 } 1437 } 1438 ASSERT(numValuesInVector == storage->m_numValuesInVector); 1439 ASSERT(numValuesInVector <= storage->length()); 1440 1441 if (m_sparseValueMap) { 1442 SparseArrayValueMap::const_iterator end = m_sparseValueMap->end(); 1443 for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) { 1444 unsigned index = it->first; 1445 ASSERT(index < storage->length()); 1446 ASSERT(index >= storage->vectorLength()); 1447 ASSERT(index <= MAX_ARRAY_INDEX); 1448 ASSERT(it->second); 1449 if (type != DestructorConsistencyCheck) 1450 it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated. 1451 } 1452 } 1453 } 1454 #endif // CHECK_ARRAY_CONSISTENCY 1455 1456 Butterfly* JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) 607 1457 { 608 1458 ASSERT(newSize > oldSize); 609 1459 610 // It's important that this function not rely on structure(), since 611 // we might be in the middle of a transition. 612 613 PropertyStorage oldPropertyStorage = m_outOfLineStorage.get(); 614 PropertyStorage newPropertyStorage = 0; 615 616 // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers. 617 void* temp = newPropertyStorage; 618 if (!globalData.heap.tryAllocateStorage(sizeof(WriteBarrierBase<Unknown>) * newSize, &temp)) 619 CRASH(); 620 newPropertyStorage = static_cast<PropertyStorage>(temp) + newSize + 1; 621 622 memcpy(newPropertyStorage - oldSize - 1, oldPropertyStorage - oldSize - 1, sizeof(WriteBarrierBase<Unknown>) * oldSize); 623 624 ASSERT(newPropertyStorage); 625 return newPropertyStorage; 1460 // It's important that this function not rely on structure(), for the property 1461 // capacity, since we might have already mutated the structure in-place. 1462 1463 return m_butterfly->growPropertyStorage(globalData, structure(), oldSize, newSize); 626 1464 } 627 1465 … … 631 1469 JSCell* cell = 0; 632 1470 PropertyOffset offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell); 633 if (offset == invalidOffset) 1471 if (isValidOffset(offset)) { 1472 descriptor.setDescriptor(object->getDirectOffset(offset), attributes); 1473 return true; 1474 } 1475 1476 unsigned i = propertyName.asIndex(); 1477 if (i == PropertyName::NotAnIndex) 634 1478 return false; 635 descriptor.setDescriptor(object->getDirectOffset(offset), attributes); 636 return true; 1479 1480 switch (object->structure()->indexingType()) { 1481 case NonArray: 1482 case Array: 1483 return false; 1484 1485 case NonArrayWithArrayStorage: 1486 case ArrayWithArrayStorage: { 1487 ArrayStorage* storage = object->m_butterfly->arrayStorage(); 1488 if (i >= storage->length()) 1489 return false; 1490 if (i < storage->vectorLength()) { 1491 WriteBarrier<Unknown>& value = storage->m_vector[i]; 1492 if (!value) 1493 return false; 1494 descriptor.setDescriptor(value.get(), 0); 1495 return true; 1496 } 1497 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 1498 SparseArrayValueMap::iterator it = map->find(i); 1499 if (it == map->notFound()) 1500 return false; 1501 it->second.get(descriptor); 1502 return true; 1503 } 1504 return false; 1505 } 1506 1507 default: 1508 ASSERT_NOT_REACHED(); 1509 return false; 1510 } 637 1511 } 638 1512 … … 659 1533 if (oldDescriptor.setterPresent()) 660 1534 accessor->setSetter(exec->globalData(), oldDescriptor.setterObject()); 661 target->putDirectAccessor(exec ->globalData(), propertyName, accessor, attributes | Accessor);1535 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); 662 1536 return true; 663 1537 } … … 684 1558 accessor->setSetter(exec->globalData(), oldDescriptor.setterObject()); 685 1559 686 target->putDirectAccessor(exec ->globalData(), propertyName, accessor, attributes | Accessor);1560 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); 687 1561 return true; 1562 } 1563 1564 void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value) 1565 { 1566 unsigned asIndex = propertyName.asIndex(); 1567 if (asIndex == PropertyName::NotAnIndex) 1568 putDirect(exec->globalData(), propertyName, value); 1569 else 1570 putDirectIndex(exec, asIndex, value); 688 1571 } 689 1572 … … 705 1588 }; 706 1589 707 bool JSObject::defineOwn Property(JSObject* object,ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)1590 bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException) 708 1591 { 709 1592 // Track on the globaldata that we're in define property. … … 712 1595 // DontDelete) properties to be deleted. For now, we can use this flag to make this work. 713 1596 DefineOwnPropertyScope scope(exec); 714 1597 715 1598 // If we have a new property we can just put it on normally 716 1599 PropertyDescriptor current; 717 if (! object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) {1600 if (!methodTable()->getOwnPropertyDescriptor(this, exec, propertyName, current)) { 718 1601 // unless extensions are prevented! 719 if (! object->isExtensible()) {1602 if (!isExtensible()) { 720 1603 if (throwException) 721 1604 throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."))); … … 724 1607 PropertyDescriptor oldDescriptor; 725 1608 oldDescriptor.setValue(jsUndefined()); 726 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);1609 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); 727 1610 } 728 1611 … … 750 1633 if (descriptor.isGenericDescriptor()) { 751 1634 if (!current.attributesEqual(descriptor)) { 752 object->methodTable()->deleteProperty(object, exec, propertyName);753 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);1635 methodTable()->deleteProperty(this, exec, propertyName); 1636 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); 754 1637 } 755 1638 return true; … … 763 1646 return false; 764 1647 } 765 object->methodTable()->deleteProperty(object, exec, propertyName);766 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);1648 methodTable()->deleteProperty(this, exec, propertyName); 1649 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); 767 1650 } 768 1651 … … 785 1668 if (current.attributesEqual(descriptor) && !descriptor.value()) 786 1669 return true; 787 object->methodTable()->deleteProperty(object, exec, propertyName);788 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);1670 methodTable()->deleteProperty(this, exec, propertyName); 1671 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); 789 1672 } 790 1673 … … 803 1686 } 804 1687 } 805 JSValue accessor = object->getDirect(exec->globalData(), propertyName);1688 JSValue accessor = getDirect(exec->globalData(), propertyName); 806 1689 if (!accessor) 807 1690 return false; … … 813 1696 if (current.attributesEqual(descriptor)) 814 1697 return true; 815 object->methodTable()->deleteProperty(object, exec, propertyName);1698 methodTable()->deleteProperty(this, exec, propertyName); 816 1699 unsigned attrs = descriptor.attributesOverridingCurrent(current); 817 object->putDirectAccessor(exec->globalData(), propertyName, getterSetter, attrs | Accessor);1700 putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor); 818 1701 return true; 819 1702 } 820 1703 1704 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException) 1705 { 1706 // If it's an array index, then use the indexed property storage. 1707 unsigned index = propertyName.asIndex(); 1708 if (index != PropertyName::NotAnIndex) { 1709 // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments. 1710 // d. Reject if succeeded is false. 1711 // e. If index >= oldLen 1712 // e.i. Set oldLenDesc.[[Value]] to index + 1. 1713 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true. 1714 // f. Return true. 1715 return object->defineOwnIndexedProperty(exec, index, descriptor, throwException); 1716 } 1717 1718 return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); 1719 } 1720 1721 bool JSObject::getOwnPropertySlotSlow(ExecState* exec, PropertyName propertyName, PropertySlot& slot) 1722 { 1723 unsigned i = propertyName.asIndex(); 1724 if (i != PropertyName::NotAnIndex) 1725 return getOwnPropertySlotByIndex(this, exec, i, slot); 1726 return false; 1727 } 1728 821 1729 JSObject* throwTypeError(ExecState* exec, const String& message) 822 1730 { -
trunk/Source/JavaScriptCore/runtime/JSObject.h
r128146 r128400 2 2 * Copyright (C) 1999-2001 Harri Porten ([email protected]) 3 3 * Copyright (C) 2001 Peter Kelly ([email protected]) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. 5 5 * 6 6 * This library is free software; you can redistribute it and/or … … 25 25 26 26 #include "ArgList.h" 27 #include "ArrayConventions.h" 28 #include "ArrayStorage.h" 29 #include "Butterfly.h" 27 30 #include "ClassInfo.h" 28 31 #include "CommonIdentifiers.h" … … 30 33 #include "JSCell.h" 31 34 #include "PropertySlot.h" 35 #include "PropertyStorage.h" 36 #include "PutDirectIndexMode.h" 32 37 #include "PutPropertySlot.h" 33 38 34 #include "StorageBarrier.h"35 39 #include "Structure.h" 36 40 #include "JSGlobalData.h" 37 41 #include "JSString.h" 42 #include "SparseArrayValueMap.h" 38 43 #include <wtf/StdLibExtras.h> 39 44 … … 80 85 }; 81 86 87 COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute); 88 COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute); 89 COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute); 90 COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute); 91 COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute); 92 COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute); 93 82 94 class JSFinalObject; 83 95 … … 97 109 public: 98 110 typedef JSCell Base; 99 111 100 112 JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); 101 113 … … 121 133 bool allowsAccessFrom(ExecState*); 122 134 135 unsigned getArrayLength() const 136 { 137 switch (structure()->indexingType()) { 138 case NonArray: 139 case Array: 140 return 0; 141 case NonArrayWithArrayStorage: 142 case ArrayWithArrayStorage: 143 return m_butterfly->arrayStorage()->length(); 144 default: 145 ASSERT_NOT_REACHED(); 146 return 0; 147 } 148 } 149 150 unsigned getVectorLength() 151 { 152 switch (structure()->indexingType()) { 153 case NonArray: 154 case Array: 155 return 0; 156 case NonArrayWithArrayStorage: 157 case ArrayWithArrayStorage: 158 return m_butterfly->arrayStorage()->vectorLength(); 159 default: 160 ASSERT_NOT_REACHED(); 161 return 0; 162 } 163 } 164 123 165 JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); 124 166 JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); 167 168 // This is similar to the putDirect* methods: 169 // - the prototype chain is not consulted 170 // - accessors are not called. 171 // - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default). 172 // This method creates a property with attributes writable, enumerable and configurable all set to true. 173 bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode) 174 { 175 if (!attributes && canSetIndexQuickly(propertyName)) { 176 setIndexQuickly(exec->globalData(), propertyName, value); 177 return true; 178 } 179 return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode); 180 } 181 bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value) 182 { 183 return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect); 184 } 185 186 // A non-throwing version of putDirect and putDirectIndex. 187 void putDirectMayBeIndex(ExecState*, PropertyName, JSValue); 188 189 bool canGetIndexQuickly(unsigned i) 190 { 191 switch (structure()->indexingType()) { 192 case NonArray: 193 case Array: 194 return false; 195 case NonArrayWithArrayStorage: 196 case ArrayWithArrayStorage: 197 return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; 198 default: 199 ASSERT_NOT_REACHED(); 200 return false; 201 } 202 } 203 204 JSValue getIndexQuickly(unsigned i) 205 { 206 switch (structure()->indexingType()) { 207 case NonArrayWithArrayStorage: 208 case ArrayWithArrayStorage: 209 return m_butterfly->arrayStorage()->m_vector[i].get(); 210 default: 211 ASSERT_NOT_REACHED(); 212 return JSValue(); 213 } 214 } 215 216 bool canSetIndexQuickly(unsigned i) 217 { 218 switch (structure()->indexingType()) { 219 case NonArray: 220 case Array: 221 return false; 222 case NonArrayWithArrayStorage: 223 case ArrayWithArrayStorage: 224 return i < m_butterfly->arrayStorage()->vectorLength(); 225 default: 226 ASSERT_NOT_REACHED(); 227 return false; 228 } 229 } 230 231 void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v) 232 { 233 switch (structure()->indexingType()) { 234 case NonArrayWithArrayStorage: 235 case ArrayWithArrayStorage: { 236 WriteBarrier<Unknown>& x = m_butterfly->arrayStorage()->m_vector[i]; 237 if (!x) { 238 ArrayStorage* storage = m_butterfly->arrayStorage(); 239 ++storage->m_numValuesInVector; 240 if (i >= storage->length()) 241 storage->setLength(i + 1); 242 } 243 x.set(globalData, this, v); 244 break; 245 } 246 default: 247 ASSERT_NOT_REACHED(); 248 } 249 } 250 251 void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v) 252 { 253 switch (structure()->indexingType()) { 254 case NonArrayWithArrayStorage: 255 case ArrayWithArrayStorage: { 256 ArrayStorage* storage = m_butterfly->arrayStorage(); 257 #if CHECK_ARRAY_CONSISTENCY 258 ASSERT(storage->m_inCompactInitialization); 259 // Check that we are initializing the next index in sequence. 260 ASSERT(i == storage->m_initializationIndex); 261 // tryCreateUninitialized set m_numValuesInVector to the initialLength, 262 // check we do not try to initialize more than this number of properties. 263 ASSERT(storage->m_initializationIndex < storage->m_numValuesInVector); 264 storage->m_initializationIndex++; 265 #endif 266 ASSERT(i < storage->length()); 267 ASSERT(i < storage->m_numValuesInVector); 268 storage->m_vector[i].set(globalData, this, v); 269 break; 270 } 271 default: 272 ASSERT_NOT_REACHED(); 273 } 274 } 275 276 void completeInitialization(unsigned newLength) 277 { 278 switch (structure()->indexingType()) { 279 case NonArrayWithArrayStorage: 280 case ArrayWithArrayStorage: { 281 ArrayStorage* storage = m_butterfly->arrayStorage(); 282 // Check that we have initialized as meny properties as we think we have. 283 UNUSED_PARAM(storage); 284 ASSERT_UNUSED(newLength, newLength == storage->length()); 285 #if CHECK_ARRAY_CONSISTENCY 286 // Check that the number of propreties initialized matches the initialLength. 287 ASSERT(storage->m_initializationIndex == m_storage->m_numValuesInVector); 288 ASSERT(storage->m_inCompactInitialization); 289 storage->m_inCompactInitialization = false; 290 #endif 291 break; 292 } 293 default: 294 ASSERT_NOT_REACHED(); 295 } 296 } 297 298 bool inSparseIndexingMode() 299 { 300 switch (structure()->indexingType()) { 301 case NonArray: 302 case Array: 303 return false; 304 case NonArrayWithArrayStorage: 305 case ArrayWithArrayStorage: 306 return m_butterfly->arrayStorage()->inSparseMode(); 307 default: 308 ASSERT_NOT_REACHED(); 309 return false; 310 } 311 } 312 313 void enterDictionaryIndexingMode(JSGlobalData&); 125 314 126 315 // putDirect is effectively an unchecked vesion of 'defineOwnProperty': … … 128 317 // - accessors are not called. 129 318 // - attributes will be respected (after the call the property will exist with the given attributes) 319 // - the property name is assumed to not be an index. 130 320 JS_EXPORT_PRIVATE static void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes); 131 321 void putDirect(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0); 132 322 void putDirect(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&); 133 323 void putDirectWithoutTransition(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0); 134 void putDirectAccessor( JSGlobalData&, PropertyName, JSValue, unsigned attributes);324 void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes); 135 325 136 326 bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const; … … 148 338 149 339 JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 340 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 150 341 JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 151 342 … … 204 395 } 205 396 206 ConstPropertyStorage outOfLineStorage() const { return m_outOfLineStorage.get(); } 207 PropertyStorage outOfLineStorage() { return m_outOfLineStorage.get(); } 397 const Butterfly* butterfly() const { return m_butterfly; } 398 Butterfly* butterfly() { return m_butterfly; } 399 400 ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); } 401 PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); } 208 402 209 403 const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const … … 228 422 result = offsetInInlineStorage; 229 423 else 230 result = outOfLineStorage() - location + (inlineStorageCapacity - 2);424 result = outOfLineStorage() - location + (inlineStorageCapacity - 1); 231 425 validateOffset(result, structure()->typeInfo().type()); 232 426 return result; … … 266 460 bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); } 267 461 bool isExtensible() { return structure()->isExtensible(); } 462 bool indexingShouldBeSparse() 463 { 464 return !isExtensible() 465 || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); 466 } 268 467 269 468 bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } 270 469 void reifyStaticFunctionsForDelete(ExecState* exec); 271 470 272 JS_EXPORT_PRIVATE PropertyStorage growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize); 273 void setOutOfLineStorage(JSGlobalData&, PropertyStorage, Structure*); 471 JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize); 472 void setButterfly(JSGlobalData&, Butterfly*, Structure*); 473 void setButterflyWithoutChangingStructure(Butterfly*); // You probably don't want to call this. 274 474 275 475 void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*); 276 476 void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, Structure*); 277 278 void* addressOfOutOfLineStorage()279 {280 return &m_outOfLineStorage;281 }282 477 283 478 void flattenDictionaryObject(JSGlobalData& globalData) … … 294 489 295 490 static size_t offsetOfInlineStorage(); 296 static size_t offsetOfOutOfLineStorage(); 491 492 static ptrdiff_t butterflyOffset() 493 { 494 return OBJECT_OFFSETOF(JSObject, m_butterfly); 495 } 496 497 void* butterflyAddress() 498 { 499 return &m_butterfly; 500 } 297 501 298 502 static JS_EXPORTDATA const ClassInfo s_info; … … 317 521 // To instantiate objects you likely want JSFinalObject, below. 318 522 // To create derived types you likely want JSNonFinalObject, below. 319 JSObject(JSGlobalData&, Structure* );523 JSObject(JSGlobalData&, Structure*, Butterfly* = 0); 320 524 321 525 void resetInheritorID(JSGlobalData& globalData) … … 324 528 } 325 529 326 void visitOutOfLineStorage(SlotVisitor&, PropertyStorage, size_t storageSize); 530 void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize); 531 532 // Call this if you know that the object is in a mode where it has array 533 // storage. This will assert otherwise. 534 ArrayStorage* arrayStorage() 535 { 536 ASSERT(structure()->indexingType() | HasArrayStorage); 537 return m_butterfly->arrayStorage(); 538 } 539 540 // Call this if you want to predicate some actions on whether or not the 541 // object is in a mode where it has array storage. 542 ArrayStorage* arrayStorageOrNull() 543 { 544 switch (structure()->indexingType()) { 545 case ArrayWithArrayStorage: 546 case NonArrayWithArrayStorage: 547 return m_butterfly->arrayStorage(); 548 549 default: 550 return 0; 551 } 552 } 553 554 // Ensure that the object is in a mode where it has array storage. Use 555 // this if you're about to perform actions that would have required the 556 // object to be converted to have array storage, if it didn't have it 557 // already. 558 ArrayStorage* ensureArrayStorage(JSGlobalData& globalData) 559 { 560 switch (structure()->indexingType()) { 561 case ArrayWithArrayStorage: 562 case NonArrayWithArrayStorage: 563 return m_butterfly->arrayStorage(); 564 565 case NonArray: 566 case Array: 567 return createInitialArrayStorage(globalData); 568 569 default: 570 ASSERT_NOT_REACHED(); 571 return 0; 572 } 573 } 574 575 ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength); 576 ArrayStorage* createInitialArrayStorage(JSGlobalData&); 577 578 ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&); 579 580 bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException); 581 582 enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck }; 583 #if !CHECK_ARRAY_CONSISTENCY 584 void checkIndexingConsistency(ConsistencyCheckType = NormalConsistencyCheck) { } 585 #else 586 void checkIndexingConsistency(ConsistencyCheckType = NormalConsistencyCheck); 587 #endif 588 589 void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); 590 591 bool increaseVectorLength(JSGlobalData&, unsigned newLength); 592 void deallocateSparseIndexMap(); 593 bool defineOwnIndexedProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException); 594 SparseArrayValueMap* allocateSparseIndexMap(JSGlobalData&); 327 595 328 596 private: … … 337 605 void isString(); 338 606 607 ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*); 608 339 609 template<PutMode> 340 610 bool putDirectInternal(JSGlobalData&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*); 341 611 342 612 bool inlineGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&); 343 JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);613 JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, PropertyOffset); 344 614 345 615 const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const; 346 616 Structure* createInheritorID(JSGlobalData&); 347 348 StorageBarrier m_outOfLineStorage; 617 618 void putIndexedDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old); 619 620 void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); 621 bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*); 622 JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode); 623 624 unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength); 625 unsigned getNewVectorLength(unsigned desiredLength); 626 627 JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&); 628 629 protected: 630 Butterfly* m_butterfly; 349 631 }; 350 632 … … 370 652 371 653 protected: 372 explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure )373 : JSObject(globalData, structure )654 explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly = 0) 655 : JSObject(globalData, structure, butterfly) 374 656 { 375 657 } … … 454 736 } 455 737 456 inline size_t JSObject::offsetOfOutOfLineStorage()457 {458 return OBJECT_OFFSETOF(JSObject, m_outOfLineStorage);459 }460 461 738 inline bool JSObject::isGlobalObject() const 462 739 { … … 489 766 } 490 767 491 inline void JSObject::set OutOfLineStorage(JSGlobalData& globalData, PropertyStorage storage, Structure* structure)768 inline void JSObject::setButterfly(JSGlobalData& globalData, Butterfly* butterfly, Structure* structure) 492 769 { 493 770 ASSERT(structure); 494 if (!storage) { 495 ASSERT(!structure->outOfLineCapacity()); 496 ASSERT(!structure->outOfLineSize()); 497 } else { 498 ASSERT(structure->outOfLineCapacity()); 499 ASSERT(structure->outOfLineSize()); 500 } 771 ASSERT(!butterfly == (!structure->outOfLineCapacity() && !hasIndexingHeader(structure->indexingType()))); 501 772 setStructure(globalData, structure); 502 m_outOfLineStorage.set(globalData, this, storage); 773 m_butterfly = butterfly; 774 } 775 776 inline void JSObject::setButterflyWithoutChangingStructure(Butterfly* butterfly) 777 { 778 m_butterfly = butterfly; 503 779 } 504 780 … … 538 814 } 539 815 540 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure )816 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly) 541 817 : JSCell(globalData, structure) 542 , m_ outOfLineStorage(globalData, this, 0)818 , m_butterfly(butterfly) 543 819 { 544 820 } … … 588 864 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) 589 865 { 590 if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) { 591 if (structure()->hasGetterSetterProperties() && location->isGetterSetter()) 592 fillGetterPropertySlot(slot, location); 866 PropertyOffset offset = structure()->get(exec->globalData(), propertyName); 867 if (LIKELY(isValidOffset(offset))) { 868 JSValue value = getDirectOffset(offset); 869 if (structure()->hasGetterSetterProperties() && value.isGetterSetter()) 870 fillGetterPropertySlot(slot, offset); 593 871 else 594 slot.setValue(this, location->get(), offsetForLocation(location));872 slot.setValue(this, value, offset); 595 873 return true; 596 874 } 597 875 598 return false;876 return getOwnPropertySlotSlow(exec, propertyName, slot); 599 877 } 600 878 … … 682 960 ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); 683 961 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); 962 ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex); 684 963 685 964 if (structure()->isDictionary()) { … … 710 989 return false; 711 990 712 PropertyStorage newStorage = outOfLineStorage();991 Butterfly* newButterfly = m_butterfly; 713 992 if (structure()->putWillGrowOutOfLineStorage()) 714 new Storage= growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());993 newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); 715 994 offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction); 716 set OutOfLineStorage(globalData, newStorage, structure());995 setButterfly(globalData, newButterfly, structure()); 717 996 718 997 validateOffset(offset); … … 728 1007 size_t currentCapacity = structure()->outOfLineCapacity(); 729 1008 if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) { 730 PropertyStorage newStorage = outOfLineStorage();1009 Butterfly* newButterfly = m_butterfly; 731 1010 if (currentCapacity != structure->outOfLineCapacity()) 732 new Storage= growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity());1011 newButterfly = growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity()); 733 1012 734 1013 validateOffset(offset); 735 1014 ASSERT(structure->isValidOffset(offset)); 736 set OutOfLineStorage(globalData, newStorage, structure);1015 setButterfly(globalData, newButterfly, structure); 737 1016 putDirectOffset(globalData, offset, value); 738 1017 // This is a new property; transitions with specific values are not currently cachable, … … 801 1080 } 802 1081 803 PropertyStorage newStorage= growOutOfLineStorage(1082 Butterfly* newButterfly = growOutOfLineStorage( 804 1083 globalData, oldCapacity, newStructure->outOfLineCapacity()); 805 set OutOfLineStorage(globalData, newStorage, newStructure);1084 setButterfly(globalData, newButterfly, newStructure); 806 1085 } 807 1086 … … 837 1116 { 838 1117 ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); 839 PropertyStorage newStorage = outOfLineStorage();1118 Butterfly* newButterfly = m_butterfly; 840 1119 if (structure()->putWillGrowOutOfLineStorage()) 841 new Storage= growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());1120 newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); 842 1121 PropertyOffset offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getCallableObject(value)); 843 set OutOfLineStorage(globalData, newStorage, structure());1122 setButterfly(globalData, newButterfly, structure()); 844 1123 putDirectOffset(globalData, offset, value); 845 1124 } … … 933 1212 } 934 1213 1214 inline size_t offsetInButterfly(PropertyOffset offset) 1215 { 1216 return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); 1217 } 1218 935 1219 // This is a helper for patching code where you want to emit a load or store and 936 1220 // the base is: … … 940 1224 { 941 1225 if (isOutOfLineOffset(offset)) 942 return sizeof(EncodedJSValue) * offsetIn OutOfLineStorage(offset);943 return JSObject::offsetOfInlineStorage() - JSObject:: offsetOfOutOfLineStorage() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset);1226 return sizeof(EncodedJSValue) * offsetInButterfly(offset); 1227 return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset); 944 1228 } 945 1229 … … 947 1231 { 948 1232 if (isOutOfLineOffset(offset)) 949 return offsetInOutOfLineStorage(offset) ;1233 return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); 950 1234 ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue))); 951 1235 return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset); … … 955 1239 { 956 1240 if (isOutOfLineOffset(offset)) 957 return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) ;1241 return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage(); 958 1242 return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue); 959 1243 } -
trunk/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
r126721 r128400 68 68 return jsPropertyNameIterator; 69 69 70 if (hasIndexingHeader(o->structure()->indexingType())) 71 return jsPropertyNameIterator; 72 70 73 size_t count = normalizePrototypeChain(exec, o); 71 74 StructureChain* structureChain = o->structure()->prototypeChain(exec); -
trunk/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp
r126926 r128400 57 57 } 58 58 59 void JSSymbolTableObject::getOwn PropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)59 void JSSymbolTableObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 60 60 { 61 61 JSSymbolTableObject* thisObject = jsCast<JSSymbolTableObject*>(object); … … 66 66 } 67 67 68 JSObject::getOwn PropertyNames(thisObject, exec, propertyNames, mode);68 JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); 69 69 } 70 70 -
trunk/Source/JavaScriptCore/runtime/JSSymbolTableObject.h
r128260 r128400 45 45 46 46 JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); 47 JS_EXPORT_PRIVATE static void getOwn PropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);47 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 48 48 49 49 protected: -
trunk/Source/JavaScriptCore/runtime/JSTypeInfo.h
r117859 r128400 43 43 static const unsigned IsEnvironmentRecord = 1 << 4; 44 44 static const unsigned OverridesGetOwnPropertySlot = 1 << 5; 45 static const unsigned OverridesVisitChildren = 1 << 6; 46 static const unsigned OverridesGetPropertyNames = 1 << 7; 47 static const unsigned ProhibitsPropertyCaching = 1 << 8; 45 static const unsigned InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero = 1 << 6; 46 static const unsigned OverridesVisitChildren = 1 << 7; 47 static const unsigned OverridesGetPropertyNames = 1 << 8; 48 static const unsigned ProhibitsPropertyCaching = 1 << 9; 48 49 49 50 class TypeInfo { … … 76 77 bool implementsDefaultHasInstance() const { return isSetOnFlags1(ImplementsDefaultHasInstance); } 77 78 bool overridesGetOwnPropertySlot() const { return isSetOnFlags1(OverridesGetOwnPropertySlot); } 79 bool interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero() const { return isSetOnFlags1(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero); } 78 80 bool overridesVisitChildren() const { return isSetOnFlags1(OverridesVisitChildren); } 79 bool overridesGetPropertyNames() const { return isSetOnFlags 1(OverridesGetPropertyNames); }81 bool overridesGetPropertyNames() const { return isSetOnFlags2(OverridesGetPropertyNames); } 80 82 bool prohibitsPropertyCaching() const { return isSetOnFlags2(ProhibitsPropertyCaching); } 81 83 -
trunk/Source/JavaScriptCore/runtime/LiteralParser.cpp
r127958 r128400 28 28 #include "LiteralParser.h" 29 29 30 #include "ButterflyInlineMethods.h" 31 #include "CopiedSpaceInlineMethods.h" 30 32 #include "JSArray.h" 31 33 #include "JSString.h" … … 641 643 case DoParseObjectEndExpression: 642 644 { 643 asObject(objectStack.last())->putDirect(m_exec->globalData(), identifierStack.last(), lastValue); 645 JSObject* object = asObject(objectStack.last()); 646 PropertyName ident = identifierStack.last(); 647 unsigned i = ident.asIndex(); 648 if (i != PropertyName::NotAnIndex) 649 object->putDirectIndex(m_exec, i, lastValue); 650 else 651 object->putDirect(m_exec->globalData(), ident, lastValue); 644 652 identifierStack.removeLast(); 645 653 if (m_lexer.currentToken().type == TokComma) -
trunk/Source/JavaScriptCore/runtime/ObjectConstructor.cpp
r127958 r128400 22 22 #include "ObjectConstructor.h" 23 23 24 #include "ButterflyInlineMethods.h" 25 #include "CopiedSpaceInlineMethods.h" 24 26 #include "Error.h" 25 27 #include "ExceptionHelpers.h" -
trunk/Source/JavaScriptCore/runtime/ObjectPrototype.cpp
r127930 r128400 68 68 ObjectPrototype::ObjectPrototype(ExecState* exec, Structure* stucture) 69 69 : JSNonFinalObject(exec->globalData(), stucture) 70 , m_hasNoPropertiesWithUInt32Names(true)71 70 { 72 71 } … … 76 75 Base::finishCreation(globalData); 77 76 ASSERT(inherits(&s_info)); 78 }79 80 void ObjectPrototype::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)81 {82 ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell);83 Base::put(cell, exec, propertyName, value, slot);84 85 if (thisObject->m_hasNoPropertiesWithUInt32Names && propertyName.asIndex() != PropertyName::NotAnIndex)86 thisObject->m_hasNoPropertiesWithUInt32Names = false;87 }88 89 bool ObjectPrototype::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool shouldThrow)90 {91 ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(object);92 bool result = Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);93 94 if (thisObject->m_hasNoPropertiesWithUInt32Names && propertyName.asIndex() != PropertyName::NotAnIndex)95 thisObject->m_hasNoPropertiesWithUInt32Names = false;96 97 return result;98 }99 100 bool ObjectPrototype::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)101 {102 ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell);103 if (thisObject->m_hasNoPropertiesWithUInt32Names)104 return false;105 return Base::getOwnPropertySlotByIndex(thisObject, exec, propertyName, slot);106 77 } 107 78 -
trunk/Source/JavaScriptCore/runtime/ObjectPrototype.h
r116828 r128400 51 51 private: 52 52 ObjectPrototype(ExecState*, Structure*); 53 static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);54 static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);55 56 53 static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); 57 static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);58 54 static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); 59 60 bool m_hasNoPropertiesWithUInt32Names;61 55 }; 62 56 -
trunk/Source/JavaScriptCore/runtime/PropertyOffset.h
r128146 r128400 119 119 validateOffset(offset); 120 120 ASSERT(isOutOfLineOffset(offset)); 121 return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 2;121 return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 1; 122 122 } 123 123 -
trunk/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp
r127349 r128400 27 27 #include "RegExpMatchesArray.h" 28 28 29 #include "ButterflyInlineMethods.h" 30 #include "SparseArrayValueMapInlineMethods.h" 31 29 32 namespace JSC { 30 33 … … 33 36 const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)}; 34 37 38 RegExpMatchesArray::RegExpMatchesArray(JSGlobalData& globalData, Butterfly* butterfly, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result) 39 : JSArray(globalData, globalObject->regExpMatchesArrayStructure(), butterfly) 40 , m_result(result) 41 , m_state(ReifiedNone) 42 { 43 m_input.set(globalData, this, input); 44 m_regExp.set(globalData, this, regExp); 45 } 46 47 RegExpMatchesArray* RegExpMatchesArray::create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result) 48 { 49 ASSERT(result); 50 JSGlobalData& globalData = exec->globalData(); 51 Butterfly* butterfly = createArrayButterfly(globalData, regExp->numSubpatterns() + 1); 52 RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, butterfly, exec->lexicalGlobalObject(), input, regExp, result); 53 array->finishCreation(globalData); 54 return array; 55 } 56 35 57 void RegExpMatchesArray::finishCreation(JSGlobalData& globalData) 36 58 { 37 Base::finishCreation(globalData , m_regExp->numSubpatterns() + 1);59 Base::finishCreation(globalData); 38 60 } 39 61 -
trunk/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
r116828 r128400 29 29 class RegExpMatchesArray : public JSArray { 30 30 private: 31 RegExpMatchesArray(JSGlobalData& globalData, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result) 32 : JSArray(globalData, globalObject->regExpMatchesArrayStructure()) 33 , m_result(result) 34 , m_state(ReifiedNone) 35 { 36 m_input.set(globalData, this, input); 37 m_regExp.set(globalData, this, regExp); 38 } 31 RegExpMatchesArray(JSGlobalData&, Butterfly*, JSGlobalObject*, JSString*, RegExp*, MatchResult); 39 32 40 33 enum ReifiedState { ReifiedNone, ReifiedMatch, ReifiedAll }; … … 43 36 typedef JSArray Base; 44 37 45 static RegExpMatchesArray* create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result) 46 { 47 ASSERT(result); 48 JSGlobalData& globalData = exec->globalData(); 49 RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, exec->lexicalGlobalObject(), input, regExp, result); 50 array->finishCreation(globalData); 51 return array; 52 } 38 static RegExpMatchesArray* create(ExecState*, JSString*, RegExp*, MatchResult); 53 39 54 40 JSString* leftContext(ExecState*); … … 59 45 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) 60 46 { 61 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info );47 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage); 62 48 } 63 49 -
trunk/Source/JavaScriptCore/runtime/RegExpObject.cpp
r127505 r128400 22 22 #include "RegExpObject.h" 23 23 24 #include "ButterflyInlineMethods.h" 25 #include "CopiedSpaceInlineMethods.h" 24 26 #include "Error.h" 25 27 #include "ExceptionHelpers.h" … … 114 116 } 115 117 116 void RegExpObject::getOwn PropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)118 void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 117 119 { 118 120 if (mode == IncludeDontEnumProperties) 119 121 propertyNames.add(exec->propertyNames().lastIndex); 120 Base::getOwn PropertyNames(object, exec, propertyNames, mode);122 Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); 121 123 } 122 124 -
trunk/Source/JavaScriptCore/runtime/RegExpObject.h
r116828 r128400 91 91 92 92 JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); 93 JS_EXPORT_PRIVATE static void getOwn PropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);93 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 94 94 JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 95 95 JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); -
trunk/Source/JavaScriptCore/runtime/StringObject.cpp
r127505 r128400 79 79 } 80 80 81 void StringObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) 82 { 83 StringObject* thisObject = jsCast<StringObject*>(cell); 84 if (thisObject->internalValue()->canGetIndex(propertyName)) { 85 if (shouldThrow) 86 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 87 return; 88 } 89 JSObject::putByIndex(cell, exec, propertyName, value, shouldThrow); 90 } 91 81 92 bool StringObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException) 82 93 { … … 134 145 } 135 146 147 bool StringObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) 148 { 149 StringObject* thisObject = jsCast<StringObject*>(cell); 150 if (thisObject->internalValue()->canGetIndex(i)) 151 return false; 152 return JSObject::deletePropertyByIndex(thisObject, exec, i); 153 } 154 136 155 void StringObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 137 156 { -
trunk/Source/JavaScriptCore/runtime/StringObject.h
r126464 r128400 51 51 52 52 static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); 53 static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); 53 54 54 55 static bool deleteProperty(JSCell*, ExecState*, PropertyName); 56 static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); 55 57 static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); 56 58 static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); … … 67 69 protected: 68 70 JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, JSString*); 69 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSWrapperObject::StructureFlags;71 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | JSWrapperObject::StructureFlags; 70 72 JS_EXPORT_PRIVATE StringObject(JSGlobalData&, Structure*); 71 73 }; -
trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp
r127505 r128400 23 23 #include "StringPrototype.h" 24 24 25 #include "ButterflyInlineMethods.h" 25 26 #include "CachedCall.h" 27 #include "CopiedSpaceInlineMethods.h" 26 28 #include "Error.h" 27 29 #include "Executable.h" -
trunk/Source/JavaScriptCore/runtime/Structure.cpp
r126721 r128400 150 150 } 151 151 152 Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo )152 Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType) 153 153 : JSCell(globalData, globalData.structureStructure.get()) 154 154 , m_typeInfo(typeInfo) 155 , m_indexingType(indexingType) 155 156 , m_globalObject(globalData, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) 156 157 , m_prototype(globalData, this, prototype) … … 177 178 : JSCell(CreatingEarlyCell) 178 179 , m_typeInfo(CompoundType, OverridesVisitChildren) 180 , m_indexingType(0) 179 181 , m_prototype(globalData, this, jsNull()) 180 182 , m_classInfo(&s_info) … … 198 200 : JSCell(globalData, globalData.structureStructure.get()) 199 201 , m_typeInfo(previous->typeInfo()) 202 , m_indexingType(previous->indexingTypeIncludingHistory()) 200 203 , m_prototype(globalData, this, previous->storedPrototype()) 201 204 , m_classInfo(previous->m_classInfo) … … 252 255 for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) { 253 256 structure = structures[i]; 257 if (!structure->m_nameInPrevious) 258 continue; 254 259 PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get()); 255 260 m_propertyTable->add(entry); … … 500 505 transition->pin(); 501 506 507 return transition; 508 } 509 510 Structure* Structure::nonPropertyTransition(JSGlobalData& globalData, Structure* structure, NonPropertyTransition transitionKind) 511 { 512 unsigned attributes = toAttributes(transitionKind); 513 IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); 514 515 if (Structure* existingTransition = structure->m_transitionTable.get(0, attributes)) { 516 ASSERT(existingTransition->m_attributesInPrevious == attributes); 517 ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); 518 return existingTransition; 519 } 520 521 Structure* transition = create(globalData, structure); 522 transition->m_previous.set(globalData, transition, structure); 523 transition->m_attributesInPrevious = attributes; 524 transition->m_indexingType = indexingType; 525 526 if (structure->m_propertyTable) { 527 if (structure->m_isPinnedPropertyTable) 528 transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1); 529 else 530 transition->m_propertyTable = structure->m_propertyTable.release(); 531 } else { 532 if (structure->m_previous) 533 transition->materializePropertyMap(globalData); 534 else 535 transition->createPropertyMap(); 536 } 537 538 structure->m_transitionTable.add(globalData, transition); 502 539 return transition; 503 540 } -
trunk/Source/JavaScriptCore/runtime/Structure.h
r128146 r128400 28 28 29 29 #include "ClassInfo.h" 30 #include "IndexingType.h" 30 31 #include "JSCell.h" 31 32 #include "JSType.h" … … 69 70 typedef JSCell Base; 70 71 71 static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo* );72 static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0); 72 73 73 74 protected: … … 101 102 static Structure* freezeTransition(JSGlobalData&, Structure*); 102 103 static Structure* preventExtensionsTransition(JSGlobalData&, Structure*); 104 static Structure* nonPropertyTransition(JSGlobalData&, Structure*, NonPropertyTransition); 103 105 104 106 bool isSealed(JSGlobalData&); … … 141 143 bool isObject() const { return typeInfo().isObject(); } 142 144 145 IndexingType indexingType() const { return m_indexingType & AllArrayTypes; } 146 IndexingType indexingTypeIncludingHistory() const { return m_indexingType; } 143 147 144 148 JSGlobalObject* globalObject() const { return m_globalObject.get(); } … … 335 339 return OBJECT_OFFSETOF(Structure, m_classInfo); 336 340 } 341 342 static ptrdiff_t indexingTypeOffset() 343 { 344 return OBJECT_OFFSETOF(Structure, m_indexingType); 345 } 337 346 338 347 static Structure* createStructure(JSGlobalData&); … … 364 373 friend class LLIntOffsetsExtractor; 365 374 366 JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo* );375 JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0); 367 376 Structure(JSGlobalData&); 368 377 Structure(JSGlobalData&, const Structure*); … … 417 426 418 427 TypeInfo m_typeInfo; 428 IndexingType m_indexingType; 419 429 420 430 WriteBarrier<JSGlobalObject> m_globalObject; … … 448 458 bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1; 449 459 bool m_hasNonEnumerableProperties : 1; 450 unsigned m_attributesInPrevious : 7;460 unsigned m_attributesInPrevious : 22; 451 461 unsigned m_specificFunctionThrashCount : 2; 452 462 unsigned m_preventExtensions : 1; … … 466 476 } 467 477 468 inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo )478 inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType) 469 479 { 470 480 ASSERT(globalData.structureStructure); 471 481 ASSERT(classInfo); 472 Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo );482 Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo, indexingType); 473 483 structure->finishCreation(globalData); 474 484 return structure; -
trunk/Source/JavaScriptCore/runtime/StructureTransitionTable.h
r127191 r128400 27 27 #define StructureTransitionTable_h 28 28 29 #include "IndexingType.h" 29 30 #include "WeakGCMap.h" 30 31 #include <wtf/HashFunctions.h> … … 38 39 class Structure; 39 40 41 static const unsigned FirstInternalAttribute = 1 << 6; // Use for transitions that don't have to do with property additions. 42 43 // Support for attributes used to indicate transitions not related to properties. 44 // If any of these are used, the string portion of the key should be 0. 45 enum NonPropertyTransition { 46 AllocateArrayStorage 47 }; 48 49 inline unsigned toAttributes(NonPropertyTransition transition) 50 { 51 return transition + FirstInternalAttribute; 52 } 53 54 inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition) 55 { 56 switch (transition) { 57 case AllocateArrayStorage: 58 return oldType | HasArrayStorage; 59 default: 60 ASSERT_NOT_REACHED(); 61 return oldType; 62 } 63 } 64 40 65 class StructureTransitionTable { 41 66 static const intptr_t UsingSingleSlotFlag = 1; … … 45 70 static unsigned hash(const Key& p) 46 71 { 47 return p.first->existingHash(); 72 unsigned result = p.second; 73 if (p.first) 74 result += p.first->existingHash(); 75 return result; 48 76 } 49 77
Note:
See TracChangeset
for help on using the changeset viewer.