2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #define CHECK_ARRAY_CONSISTENCY 0
31 class LLIntOffsetsExtractor
;
33 struct SparseArrayEntry
: public WriteBarrier
<Unknown
> {
34 typedef WriteBarrier
<Unknown
> Base
;
36 SparseArrayEntry() : attributes(0) {}
38 JSValue
get(ExecState
*, JSArray
*) const;
39 void get(PropertySlot
&) const;
40 void get(PropertyDescriptor
&) const;
41 JSValue
getNonSparseMode() const;
46 class SparseArrayValueMap
{
47 typedef HashMap
<uint64_t, SparseArrayEntry
, WTF::IntHash
<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits
<uint64_t> > Map
;
56 typedef Map::iterator iterator
;
57 typedef Map::const_iterator const_iterator
;
58 typedef Map::AddResult AddResult
;
62 , m_reportedCapacity(0)
66 void visitChildren(SlotVisitor
&);
70 return m_flags
& SparseMode
;
75 m_flags
= static_cast<Flags
>(m_flags
| SparseMode
);
78 bool lengthIsReadOnly()
80 return m_flags
& LengthIsReadOnly
;
83 void setLengthIsReadOnly()
85 m_flags
= static_cast<Flags
>(m_flags
| LengthIsReadOnly
);
88 // These methods may mutate the contents of the map
89 void put(ExecState
*, JSArray
*, unsigned, JSValue
, bool shouldThrow
);
90 bool putDirect(ExecState
*, JSArray
*, unsigned, JSValue
, bool shouldThrow
);
91 AddResult
add(JSArray
*, unsigned);
92 iterator
find(unsigned i
) { return m_map
.find(i
); }
93 // This should ASSERT the remove is valid (check the result of the find).
94 void remove(iterator it
) { m_map
.remove(it
); }
95 void remove(unsigned i
) { m_map
.remove(i
); }
97 // These methods do not mutate the contents of the map.
98 iterator
notFound() { return m_map
.end(); }
99 bool isEmpty() const { return m_map
.isEmpty(); }
100 bool contains(unsigned i
) const { return m_map
.contains(i
); }
101 size_t size() const { return m_map
.size(); }
102 // Only allow const begin/end iteration.
103 const_iterator
begin() const { return m_map
.begin(); }
104 const_iterator
end() const { return m_map
.end(); }
109 size_t m_reportedCapacity
;
112 // This struct holds the actual data values of an array. A JSArray object points to it's contained ArrayStorage
113 // struct by pointing to m_vector. To access the contained ArrayStorage struct, use the getStorage() and
114 // setStorage() methods. It is important to note that there may be space before the ArrayStorage that
115 // is used to quick unshift / shift operation. The actual allocated pointer is available by using:
116 // getStorage() - m_indexBias * sizeof(JSValue)
117 struct ArrayStorage
{
118 unsigned m_length
; // The "length" property on the array
119 unsigned m_numValuesInVector
;
120 void* m_allocBase
; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector.
121 #if CHECK_ARRAY_CONSISTENCY
122 // Needs to be a uintptr_t for alignment purposes.
123 uintptr_t m_initializationIndex
;
124 uintptr_t m_inCompactInitialization
;
128 WriteBarrier
<Unknown
> m_vector
[1];
130 static ptrdiff_t lengthOffset() { return OBJECT_OFFSETOF(ArrayStorage
, m_length
); }
131 static ptrdiff_t numValuesInVectorOffset() { return OBJECT_OFFSETOF(ArrayStorage
, m_numValuesInVector
); }
132 static ptrdiff_t allocBaseOffset() { return OBJECT_OFFSETOF(ArrayStorage
, m_allocBase
); }
133 static ptrdiff_t vectorOffset() { return OBJECT_OFFSETOF(ArrayStorage
, m_vector
); }
136 class JSArray
: public JSNonFinalObject
{
137 friend class LLIntOffsetsExtractor
;
142 explicit JSArray(JSGlobalData
& globalData
, Structure
* structure
)
143 : JSNonFinalObject(globalData
, structure
)
146 , m_sparseValueMap(0)
150 JS_EXPORT_PRIVATE
void finishCreation(JSGlobalData
&, unsigned initialLength
= 0);
151 JS_EXPORT_PRIVATE JSArray
* tryFinishCreationUninitialized(JSGlobalData
&, unsigned initialLength
);
154 typedef JSNonFinalObject Base
;
156 static void finalize(JSCell
*);
158 static JSArray
* create(JSGlobalData
&, Structure
*, unsigned initialLength
= 0);
160 // tryCreateUninitialized is used for fast construction of arrays whose size and
161 // contents are known at time of creation. Clients of this interface must:
162 // - null-check the result (indicating out of memory, or otherwise unable to allocate vector).
163 // - call 'initializeIndex' for all properties in sequence, for 0 <= i < initialLength.
164 // - called 'completeInitialization' after all properties have been initialized.
165 static JSArray
* tryCreateUninitialized(JSGlobalData
&, Structure
*, unsigned initialLength
);
167 JS_EXPORT_PRIVATE
static bool defineOwnProperty(JSObject
*, ExecState
*, const Identifier
&, PropertyDescriptor
&, bool throwException
);
169 static bool getOwnPropertySlot(JSCell
*, ExecState
*, const Identifier
&, PropertySlot
&);
170 JS_EXPORT_PRIVATE
static bool getOwnPropertySlotByIndex(JSCell
*, ExecState
*, unsigned propertyName
, PropertySlot
&);
171 static bool getOwnPropertyDescriptor(JSObject
*, ExecState
*, const Identifier
&, PropertyDescriptor
&);
172 static void putByIndex(JSCell
*, ExecState
*, unsigned propertyName
, JSValue
, bool shouldThrow
);
173 // This is similar to the JSObject::putDirect* methods:
174 // - the prototype chain is not consulted
175 // - accessors are not called.
176 // This method creates a property with attributes writable, enumerable and configurable all set to true.
177 bool putDirectIndex(ExecState
* exec
, unsigned propertyName
, JSValue value
, bool shouldThrow
= true)
179 if (canSetIndex(propertyName
)) {
180 setIndex(exec
->globalData(), propertyName
, value
);
183 return putDirectIndexBeyondVectorLength(exec
, propertyName
, value
, shouldThrow
);
186 static JS_EXPORTDATA
const ClassInfo s_info
;
188 unsigned length() const { return m_storage
->m_length
; }
189 // OK to use on new arrays, but not if it might be a RegExpMatchArray.
190 bool setLength(ExecState
*, unsigned, bool throwException
= false);
192 void sort(ExecState
*);
193 void sort(ExecState
*, JSValue compareFunction
, CallType
, const CallData
&);
194 void sortNumeric(ExecState
*, JSValue compareFunction
, CallType
, const CallData
&);
196 void push(ExecState
*, JSValue
);
197 JSValue
pop(ExecState
*);
199 bool shiftCount(ExecState
*, unsigned count
);
200 bool unshiftCount(ExecState
*, unsigned count
);
202 bool canGetIndex(unsigned i
) { return i
< m_vectorLength
&& m_storage
->m_vector
[i
]; }
203 JSValue
getIndex(unsigned i
)
205 ASSERT(canGetIndex(i
));
206 return m_storage
->m_vector
[i
].get();
209 bool canSetIndex(unsigned i
) { return i
< m_vectorLength
; }
210 void setIndex(JSGlobalData
& globalData
, unsigned i
, JSValue v
)
212 ASSERT(canSetIndex(i
));
214 WriteBarrier
<Unknown
>& x
= m_storage
->m_vector
[i
];
216 ArrayStorage
*storage
= m_storage
;
217 ++storage
->m_numValuesInVector
;
218 if (i
>= storage
->m_length
)
219 storage
->m_length
= i
+ 1;
221 x
.set(globalData
, this, v
);
224 inline void initializeIndex(JSGlobalData
& globalData
, unsigned i
, JSValue v
)
226 ASSERT(canSetIndex(i
));
227 ArrayStorage
*storage
= m_storage
;
228 #if CHECK_ARRAY_CONSISTENCY
229 ASSERT(storage
->m_inCompactInitialization
);
230 // Check that we are initializing the next index in sequence.
231 ASSERT(i
== storage
->m_initializationIndex
);
232 // tryCreateUninitialized set m_numValuesInVector to the initialLength,
233 // check we do not try to initialize more than this number of properties.
234 ASSERT(storage
->m_initializationIndex
< storage
->m_numValuesInVector
);
235 storage
->m_initializationIndex
++;
237 ASSERT(i
< storage
->m_length
);
238 ASSERT(i
< storage
->m_numValuesInVector
);
239 storage
->m_vector
[i
].set(globalData
, this, v
);
242 inline void completeInitialization(unsigned newLength
)
244 // Check that we have initialized as meny properties as we think we have.
245 ASSERT_UNUSED(newLength
, newLength
== m_storage
->m_length
);
246 #if CHECK_ARRAY_CONSISTENCY
247 // Check that the number of propreties initialized matches the initialLength.
248 ASSERT(m_storage
->m_initializationIndex
== m_storage
->m_numValuesInVector
);
249 ASSERT(m_storage
->m_inCompactInitialization
);
250 m_storage
->m_inCompactInitialization
= false;
256 return m_sparseValueMap
;
261 SparseArrayValueMap
* map
= m_sparseValueMap
;
262 return map
&& map
->sparseMode();
265 void fillArgList(ExecState
*, MarkedArgumentBuffer
&);
266 void copyToArguments(ExecState
*, CallFrame
*, uint32_t length
);
268 static Structure
* createStructure(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
, JSValue prototype
)
270 return Structure::create(globalData
, globalObject
, prototype
, TypeInfo(ObjectType
, StructureFlags
), &s_info
);
273 static ptrdiff_t storageOffset()
275 return OBJECT_OFFSETOF(JSArray
, m_storage
);
278 static ptrdiff_t vectorLengthOffset()
280 return OBJECT_OFFSETOF(JSArray
, m_vectorLength
);
283 JS_EXPORT_PRIVATE
static void visitChildren(JSCell
*, SlotVisitor
&);
285 void enterDictionaryMode(JSGlobalData
&);
288 static const unsigned StructureFlags
= OverridesGetOwnPropertySlot
| OverridesVisitChildren
| OverridesGetPropertyNames
| JSObject::StructureFlags
;
289 static void put(JSCell
*, ExecState
*, const Identifier
& propertyName
, JSValue
, PutPropertySlot
&);
291 static bool deleteProperty(JSCell
*, ExecState
*, const Identifier
& propertyName
);
292 static bool deletePropertyByIndex(JSCell
*, ExecState
*, unsigned propertyName
);
293 static void getOwnPropertyNames(JSObject
*, ExecState
*, PropertyNameArray
&, EnumerationMode
);
295 JS_EXPORT_PRIVATE
void* subclassData() const;
296 JS_EXPORT_PRIVATE
void setSubclassData(void*);
299 static size_t storageSize(unsigned vectorLength
);
300 bool isLengthWritable()
302 SparseArrayValueMap
* map
= m_sparseValueMap
;
303 return !map
|| !map
->lengthIsReadOnly();
306 void setLengthWritable(ExecState
*, bool writable
);
307 void putDescriptor(ExecState
*, SparseArrayEntry
*, PropertyDescriptor
&, PropertyDescriptor
& old
);
308 bool defineOwnNumericProperty(ExecState
*, unsigned, PropertyDescriptor
&, bool throwException
);
309 void allocateSparseMap(JSGlobalData
&);
310 void deallocateSparseMap();
312 bool getOwnPropertySlotSlowCase(ExecState
*, unsigned propertyName
, PropertySlot
&);
313 void putByIndexBeyondVectorLength(ExecState
*, unsigned propertyName
, JSValue
, bool shouldThrow
);
314 JS_EXPORT_PRIVATE
bool putDirectIndexBeyondVectorLength(ExecState
*, unsigned propertyName
, JSValue
, bool shouldThrow
);
316 unsigned getNewVectorLength(unsigned desiredLength
);
317 bool increaseVectorLength(JSGlobalData
&, unsigned newLength
);
318 bool unshiftCountSlowCase(JSGlobalData
&, unsigned count
);
320 unsigned compactForSorting();
322 enum ConsistencyCheckType
{ NormalConsistencyCheck
, DestructorConsistencyCheck
, SortConsistencyCheck
};
323 void checkConsistency(ConsistencyCheckType
= NormalConsistencyCheck
);
325 unsigned m_vectorLength
; // The valid length of m_vector
326 unsigned m_indexBias
; // The number of JSValue sized blocks before ArrayStorage.
327 ArrayStorage
*m_storage
;
329 // FIXME: Maybe SparseArrayValueMap should be put into its own JSCell?
330 SparseArrayValueMap
* m_sparseValueMap
;
332 static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray
, m_sparseValueMap
); }
333 static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray
, m_indexBias
); }
336 inline JSArray
* JSArray::create(JSGlobalData
& globalData
, Structure
* structure
, unsigned initialLength
)
338 JSArray
* array
= new (NotNull
, allocateCell
<JSArray
>(globalData
.heap
)) JSArray(globalData
, structure
);
339 array
->finishCreation(globalData
, initialLength
);
343 inline JSArray
* JSArray::tryCreateUninitialized(JSGlobalData
& globalData
, Structure
* structure
, unsigned initialLength
)
345 JSArray
* array
= new (NotNull
, allocateCell
<JSArray
>(globalData
.heap
)) JSArray(globalData
, structure
);
346 return array
->tryFinishCreationUninitialized(globalData
, initialLength
);
349 JSArray
* asArray(JSValue
);
351 inline JSArray
* asArray(JSCell
* cell
)
353 ASSERT(cell
->inherits(&JSArray::s_info
));
354 return jsCast
<JSArray
*>(cell
);
357 inline JSArray
* asArray(JSValue value
)
359 return asArray(value
.asCell());
362 inline bool isJSArray(JSCell
* cell
) { return cell
->classInfo() == &JSArray::s_info
; }
363 inline bool isJSArray(JSValue v
) { return v
.isCell() && isJSArray(v
.asCell()); }
365 // Rule from ECMA 15.2 about what an array index is.
366 // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1.
367 inline unsigned Identifier::toArrayIndex(bool& ok
) const
369 unsigned i
= toUInt32(ok
);
370 if (ok
&& i
>= 0xFFFFFFFFU
)
375 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
376 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
377 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
378 // (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
379 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
381 // These values have to be macros to be used in max() and min() without introducing
382 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
383 #define MIN_SPARSE_ARRAY_INDEX 10000U
384 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
385 inline size_t JSArray::storageSize(unsigned vectorLength
)
387 ASSERT(vectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
389 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
390 // - as asserted above - the following calculation cannot overflow.
391 size_t size
= (sizeof(ArrayStorage
) - sizeof(WriteBarrier
<Unknown
>)) + (vectorLength
* sizeof(WriteBarrier
<Unknown
>));
392 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
393 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
394 ASSERT(((size
- (sizeof(ArrayStorage
) - sizeof(WriteBarrier
<Unknown
>))) / sizeof(WriteBarrier
<Unknown
>) == vectorLength
) && (size
>= (sizeof(ArrayStorage
) - sizeof(WriteBarrier
<Unknown
>))));