]> git.saurik.com Git - apple/javascriptcore.git/blame_incremental - runtime/JSArray.h
JavaScriptCore-1097.3.tar.gz
[apple/javascriptcore.git] / runtime / JSArray.h
... / ...
CommitLineData
1/*
2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 *
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.
9 *
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.
14 *
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
18 *
19 */
20
21#ifndef JSArray_h
22#define JSArray_h
23
24#include "JSObject.h"
25
26#define CHECK_ARRAY_CONSISTENCY 0
27
28namespace JSC {
29
30 class JSArray;
31 class LLIntOffsetsExtractor;
32
33 struct SparseArrayEntry : public WriteBarrier<Unknown> {
34 typedef WriteBarrier<Unknown> Base;
35
36 SparseArrayEntry() : attributes(0) {}
37
38 JSValue get(ExecState*, JSArray*) const;
39 void get(PropertySlot&) const;
40 void get(PropertyDescriptor&) const;
41 JSValue getNonSparseMode() const;
42
43 unsigned attributes;
44 };
45
46 class SparseArrayValueMap {
47 typedef HashMap<uint64_t, SparseArrayEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > Map;
48
49 enum Flags {
50 Normal = 0,
51 SparseMode = 1,
52 LengthIsReadOnly = 2,
53 };
54
55 public:
56 typedef Map::iterator iterator;
57 typedef Map::const_iterator const_iterator;
58 typedef Map::AddResult AddResult;
59
60 SparseArrayValueMap()
61 : m_flags(Normal)
62 , m_reportedCapacity(0)
63 {
64 }
65
66 void visitChildren(SlotVisitor&);
67
68 bool sparseMode()
69 {
70 return m_flags & SparseMode;
71 }
72
73 void setSparseMode()
74 {
75 m_flags = static_cast<Flags>(m_flags | SparseMode);
76 }
77
78 bool lengthIsReadOnly()
79 {
80 return m_flags & LengthIsReadOnly;
81 }
82
83 void setLengthIsReadOnly()
84 {
85 m_flags = static_cast<Flags>(m_flags | LengthIsReadOnly);
86 }
87
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); }
96
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(); }
105
106 private:
107 Map m_map;
108 Flags m_flags;
109 size_t m_reportedCapacity;
110 };
111
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;
125#else
126 uintptr_t m_padding;
127#endif
128 WriteBarrier<Unknown> m_vector[1];
129
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); }
134 };
135
136 class JSArray : public JSNonFinalObject {
137 friend class LLIntOffsetsExtractor;
138 friend class Walker;
139 friend class JIT;
140
141 protected:
142 explicit JSArray(JSGlobalData& globalData, Structure* structure)
143 : JSNonFinalObject(globalData, structure)
144 , m_indexBias(0)
145 , m_storage(0)
146 , m_sparseValueMap(0)
147 {
148 }
149
150 JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, unsigned initialLength = 0);
151 JS_EXPORT_PRIVATE JSArray* tryFinishCreationUninitialized(JSGlobalData&, unsigned initialLength);
152
153 public:
154 typedef JSNonFinalObject Base;
155
156 static void finalize(JSCell*);
157
158 static JSArray* create(JSGlobalData&, Structure*, unsigned initialLength = 0);
159
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);
166
167 JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&, bool throwException);
168
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)
178 {
179 if (canSetIndex(propertyName)) {
180 setIndex(exec->globalData(), propertyName, value);
181 return true;
182 }
183 return putDirectIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
184 }
185
186 static JS_EXPORTDATA const ClassInfo s_info;
187
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);
191
192 void sort(ExecState*);
193 void sort(ExecState*, JSValue compareFunction, CallType, const CallData&);
194 void sortNumeric(ExecState*, JSValue compareFunction, CallType, const CallData&);
195
196 void push(ExecState*, JSValue);
197 JSValue pop(ExecState*);
198
199 bool shiftCount(ExecState*, unsigned count);
200 bool unshiftCount(ExecState*, unsigned count);
201
202 bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; }
203 JSValue getIndex(unsigned i)
204 {
205 ASSERT(canGetIndex(i));
206 return m_storage->m_vector[i].get();
207 }
208
209 bool canSetIndex(unsigned i) { return i < m_vectorLength; }
210 void setIndex(JSGlobalData& globalData, unsigned i, JSValue v)
211 {
212 ASSERT(canSetIndex(i));
213
214 WriteBarrier<Unknown>& x = m_storage->m_vector[i];
215 if (!x) {
216 ArrayStorage *storage = m_storage;
217 ++storage->m_numValuesInVector;
218 if (i >= storage->m_length)
219 storage->m_length = i + 1;
220 }
221 x.set(globalData, this, v);
222 }
223
224 inline void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)
225 {
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++;
236#endif
237 ASSERT(i < storage->m_length);
238 ASSERT(i < storage->m_numValuesInVector);
239 storage->m_vector[i].set(globalData, this, v);
240 }
241
242 inline void completeInitialization(unsigned newLength)
243 {
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;
251#endif
252 }
253
254 bool inSparseMode()
255 {
256 SparseArrayValueMap* map = m_sparseValueMap;
257 return map && map->sparseMode();
258 }
259
260 void fillArgList(ExecState*, MarkedArgumentBuffer&);
261 void copyToArguments(ExecState*, CallFrame*, uint32_t length);
262
263 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
264 {
265 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
266 }
267
268 static ptrdiff_t storageOffset()
269 {
270 return OBJECT_OFFSETOF(JSArray, m_storage);
271 }
272
273 static ptrdiff_t vectorLengthOffset()
274 {
275 return OBJECT_OFFSETOF(JSArray, m_vectorLength);
276 }
277
278 JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
279
280 void enterDictionaryMode(JSGlobalData&);
281
282 protected:
283 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;
284 static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&);
285
286 static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName);
287 static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
288 static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
289
290 JS_EXPORT_PRIVATE void* subclassData() const;
291 JS_EXPORT_PRIVATE void setSubclassData(void*);
292
293 private:
294 static size_t storageSize(unsigned vectorLength);
295 bool isLengthWritable()
296 {
297 SparseArrayValueMap* map = m_sparseValueMap;
298 return !map || !map->lengthIsReadOnly();
299 }
300
301 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 bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&);
308 void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
309 JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
310
311 unsigned getNewVectorLength(unsigned desiredLength);
312 bool increaseVectorLength(JSGlobalData&, unsigned newLength);
313 bool unshiftCountSlowCase(JSGlobalData&, unsigned count);
314
315 unsigned compactForSorting(JSGlobalData&);
316
317 enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck };
318 void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck);
319
320 unsigned m_vectorLength; // The valid length of m_vector
321 unsigned m_indexBias; // The number of JSValue sized blocks before ArrayStorage.
322 ArrayStorage *m_storage;
323
324 // FIXME: Maybe SparseArrayValueMap should be put into its own JSCell?
325 SparseArrayValueMap* m_sparseValueMap;
326
327 static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray, m_sparseValueMap); }
328 static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray, m_indexBias); }
329 };
330
331 inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength)
332 {
333 JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure);
334 array->finishCreation(globalData, initialLength);
335 return array;
336 }
337
338 inline JSArray* JSArray::tryCreateUninitialized(JSGlobalData& globalData, Structure* structure, unsigned initialLength)
339 {
340 JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure);
341 return array->tryFinishCreationUninitialized(globalData, initialLength);
342 }
343
344 JSArray* asArray(JSValue);
345
346 inline JSArray* asArray(JSCell* cell)
347 {
348 ASSERT(cell->inherits(&JSArray::s_info));
349 return jsCast<JSArray*>(cell);
350 }
351
352 inline JSArray* asArray(JSValue value)
353 {
354 return asArray(value.asCell());
355 }
356
357 inline bool isJSArray(JSCell* cell) { return cell->classInfo() == &JSArray::s_info; }
358 inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); }
359
360 // Rule from ECMA 15.2 about what an array index is.
361 // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1.
362 inline unsigned Identifier::toArrayIndex(bool& ok) const
363 {
364 unsigned i = toUInt32(ok);
365 if (ok && i >= 0xFFFFFFFFU)
366 ok = false;
367 return i;
368 }
369
370// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
371// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
372// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
373// (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
374#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
375
376// These values have to be macros to be used in max() and min() without introducing
377// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
378#define MIN_SPARSE_ARRAY_INDEX 10000U
379#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
380 inline size_t JSArray::storageSize(unsigned vectorLength)
381 {
382 ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
383
384 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
385 // - as asserted above - the following calculation cannot overflow.
386 size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>));
387 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
388 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
389 ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))));
390
391 return size;
392 }
393
394 } // namespace JSC
395
396#endif // JSArray_h