Remove excessive headers from JavaScriptCore
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSArray.h
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003-2017 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 #pragma once
22
23 #include "ArgList.h"
24 #include "ArrayConventions.h"
25 #include "ButterflyInlines.h"
26 #include "JSCellInlines.h"
27 #include "JSObject.h"
28
29 namespace JSC {
30
31 class JSArray;
32 class LLIntOffsetsExtractor;
33
34 class JSArray : public JSNonFinalObject {
35     friend class LLIntOffsetsExtractor;
36     friend class Walker;
37     friend class JIT;
38
39 public:
40     typedef JSNonFinalObject Base;
41     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
42
43     static size_t allocationSize(Checked<size_t> inlineCapacity)
44     {
45         ASSERT_UNUSED(inlineCapacity, !inlineCapacity);
46         return sizeof(JSArray);
47     }
48         
49 protected:
50     explicit JSArray(VM& vm, Structure* structure, Butterfly* butterfly)
51         : JSNonFinalObject(vm, structure, butterfly)
52     {
53     }
54
55 public:
56     static JSArray* tryCreate(VM&, Structure*, unsigned initialLength = 0);
57     static JSArray* create(VM&, Structure*, unsigned initialLength = 0);
58     static JSArray* createWithButterfly(VM&, GCDeferralContext*, Structure*, Butterfly*);
59
60     // tryCreateUninitializedRestricted is used for fast construction of arrays whose size and
61     // contents are known at time of creation. This is a restricted API for careful use only in
62     // performance critical code paths. If you don't have a good reason to use it, you probably
63     // shouldn't use it. Instead, you should go with
64     //   - JSArray::tryCreate() or JSArray::create() instead of tryCreateUninitializedRestricted(), and
65     //   - putDirectIndex() instead of initializeIndex().
66     //
67     // Clients of this interface must:
68     //   - null-check the result (indicating out of memory, or otherwise unable to allocate vector).
69     //   - call 'initializeIndex' for all properties in sequence, for 0 <= i < initialLength.
70     //   - Provide a valid GCDefferalContext* if they might garbage collect when initializing properties,
71     //     otherwise the caller can provide a null GCDefferalContext*.
72     //   - Provide a local stack instance of ObjectInitializationScope at the call site.
73     //
74     JS_EXPORT_PRIVATE static JSArray* tryCreateUninitializedRestricted(ObjectInitializationScope&, GCDeferralContext*, Structure*, unsigned initialLength);
75     static JSArray* tryCreateUninitializedRestricted(ObjectInitializationScope& scope, Structure* structure, unsigned initialLength)
76     {
77         return tryCreateUninitializedRestricted(scope, nullptr, structure, initialLength);
78     }
79
80     JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool throwException);
81
82     JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
83
84     DECLARE_EXPORT_INFO;
85
86     // OK if we know this is a JSArray, but not if it could be an object of a derived class; for RuntimeArray this always returns 0.
87     unsigned length() const { return getArrayLength(); }
88
89     // OK to use on new arrays, but not if it might be a RegExpMatchArray or RuntimeArray.
90     JS_EXPORT_PRIVATE bool setLength(ExecState*, unsigned, bool throwException = false);
91
92     JS_EXPORT_PRIVATE void push(ExecState*, JSValue);
93     JS_EXPORT_PRIVATE JSValue pop(ExecState*);
94
95     JSArray* fastSlice(ExecState&, unsigned startIndex, unsigned count);
96
97     bool canFastCopy(VM&, JSArray* otherArray);
98     // This function returns NonArray if the indexing types are not compatable for copying.
99     IndexingType mergeIndexingTypeForCopying(IndexingType other);
100     bool appendMemcpy(ExecState*, VM&, unsigned startIndex, JSArray* otherArray);
101
102     enum ShiftCountMode {
103         // This form of shift hints that we're doing queueing. With this assumption in hand,
104         // we convert to ArrayStorage, which has queue optimizations.
105         ShiftCountForShift,
106             
107         // This form of shift hints that we're just doing care and feeding on an array that
108         // is probably typically used for ordinary accesses. With this assumption in hand,
109         // we try to preserve whatever indexing type it has already.
110         ShiftCountForSplice
111     };
112
113     bool shiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count)
114     {
115         return shiftCountWithArrayStorage(exec->vm(), startIndex, count, ensureArrayStorage(exec->vm()));
116     }
117     bool shiftCountForSplice(ExecState* exec, unsigned& startIndex, unsigned count)
118     {
119         return shiftCountWithAnyIndexingType(exec, startIndex, count);
120     }
121     template<ShiftCountMode shiftCountMode>
122     bool shiftCount(ExecState* exec, unsigned& startIndex, unsigned count)
123     {
124         switch (shiftCountMode) {
125         case ShiftCountForShift:
126             return shiftCountForShift(exec, startIndex, count);
127         case ShiftCountForSplice:
128             return shiftCountForSplice(exec, startIndex, count);
129         default:
130             CRASH();
131             return false;
132         }
133     }
134         
135     bool unshiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count)
136     {
137         return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
138     }
139     bool unshiftCountForSplice(ExecState* exec, unsigned startIndex, unsigned count)
140     {
141         return unshiftCountWithAnyIndexingType(exec, startIndex, count);
142     }
143     template<ShiftCountMode shiftCountMode>
144     bool unshiftCount(ExecState* exec, unsigned startIndex, unsigned count)
145     {
146         switch (shiftCountMode) {
147         case ShiftCountForShift:
148             return unshiftCountForShift(exec, startIndex, count);
149         case ShiftCountForSplice:
150             return unshiftCountForSplice(exec, startIndex, count);
151         default:
152             CRASH();
153             return false;
154         }
155     }
156
157     JS_EXPORT_PRIVATE void fillArgList(ExecState*, MarkedArgumentBuffer&);
158     JS_EXPORT_PRIVATE void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length);
159
160     JS_EXPORT_PRIVATE bool isIteratorProtocolFastAndNonObservable();
161
162     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType)
163     {
164         return Structure::create(vm, globalObject, prototype, TypeInfo(ArrayType, StructureFlags), info(), indexingType);
165     }
166         
167 protected:
168     void finishCreation(VM& vm)
169     {
170         Base::finishCreation(vm);
171         ASSERT_WITH_MESSAGE(type() == ArrayType || type() == DerivedArrayType, "Instance inheriting JSArray should have either ArrayType or DerivedArrayType");
172     }
173
174     static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
175
176     static bool deleteProperty(JSCell*, ExecState*, PropertyName);
177     JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
178
179 private:
180     bool isLengthWritable()
181     {
182         ArrayStorage* storage = arrayStorageOrNull();
183         if (!storage)
184             return true;
185         SparseArrayValueMap* map = storage->m_sparseMap.get();
186         return !map || !map->lengthIsReadOnly();
187     }
188         
189     bool shiftCountWithAnyIndexingType(ExecState*, unsigned& startIndex, unsigned count);
190     JS_EXPORT_PRIVATE bool shiftCountWithArrayStorage(VM&, unsigned startIndex, unsigned count, ArrayStorage*);
191
192     bool unshiftCountWithAnyIndexingType(ExecState*, unsigned startIndex, unsigned count);
193     bool unshiftCountWithArrayStorage(ExecState*, unsigned startIndex, unsigned count, ArrayStorage*);
194     bool unshiftCountSlowCase(const AbstractLocker&, VM&, DeferGC&, bool, unsigned);
195
196     bool setLengthWithArrayStorage(ExecState*, unsigned newLength, bool throwException, ArrayStorage*);
197     void setLengthWritable(ExecState*, bool writable);
198 };
199
200 inline Butterfly* tryCreateArrayButterfly(VM& vm, JSCell* intendedOwner, unsigned initialLength)
201 {
202     Butterfly* butterfly = Butterfly::tryCreate(
203         vm, intendedOwner, 0, 0, true, baseIndexingHeaderForArrayStorage(initialLength),
204         ArrayStorage::sizeFor(BASE_ARRAY_STORAGE_VECTOR_LEN));
205     if (!butterfly)
206         return nullptr;
207     ArrayStorage* storage = butterfly->arrayStorage();
208     storage->m_sparseMap.clear();
209     storage->m_indexBias = 0;
210     storage->m_numValuesInVector = 0;
211     return butterfly;
212 }
213
214 Butterfly* createArrayButterflyInDictionaryIndexingMode(
215     VM&, JSCell* intendedOwner, unsigned initialLength);
216
217 inline JSArray* JSArray::tryCreate(VM& vm, Structure* structure, unsigned initialLength)
218 {
219     unsigned outOfLineStorage = structure->outOfLineCapacity();
220
221     Butterfly* butterfly;
222     IndexingType indexingType = structure->indexingType();
223     if (LIKELY(!hasAnyArrayStorage(indexingType))) {
224         ASSERT(
225             hasUndecided(indexingType)
226             || hasInt32(indexingType)
227             || hasDouble(indexingType)
228             || hasContiguous(indexingType));
229
230         if (UNLIKELY(initialLength > MAX_STORAGE_VECTOR_LENGTH))
231             return nullptr;
232
233         unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, initialLength);
234         void* temp = vm.auxiliarySpace.tryAllocate(nullptr, Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)));
235         if (!temp)
236             return nullptr;
237         butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
238         butterfly->setVectorLength(vectorLength);
239         butterfly->setPublicLength(initialLength);
240         if (hasDouble(indexingType))
241             clearArray(butterfly->contiguousDouble().data(), vectorLength);
242         else
243             clearArray(butterfly->contiguous().data(), vectorLength);
244     } else {
245         ASSERT(
246             indexingType == ArrayWithSlowPutArrayStorage
247             || indexingType == ArrayWithArrayStorage);
248         butterfly = tryCreateArrayButterfly(vm, nullptr, initialLength);
249         if (!butterfly)
250             return nullptr;
251         for (unsigned i = 0; i < BASE_ARRAY_STORAGE_VECTOR_LEN; ++i)
252             butterfly->arrayStorage()->m_vector[i].clear();
253     }
254
255     return createWithButterfly(vm, nullptr, structure, butterfly);
256 }
257
258 inline JSArray* JSArray::create(VM& vm, Structure* structure, unsigned initialLength)
259 {
260     JSArray* result = JSArray::tryCreate(vm, structure, initialLength);
261     RELEASE_ASSERT(result);
262
263     return result;
264 }
265
266 inline JSArray* JSArray::createWithButterfly(VM& vm, GCDeferralContext* deferralContext, Structure* structure, Butterfly* butterfly)
267 {
268     JSArray* array = new (NotNull, allocateCell<JSArray>(vm.heap, deferralContext)) JSArray(vm, structure, butterfly);
269     array->finishCreation(vm);
270     return array;
271 }
272
273 JSArray* asArray(JSValue);
274
275 inline JSArray* asArray(JSCell* cell)
276 {
277     ASSERT(cell->inherits(*cell->vm(), JSArray::info()));
278     return jsCast<JSArray*>(cell);
279 }
280
281 inline JSArray* asArray(JSValue value)
282 {
283     return asArray(value.asCell());
284 }
285
286 inline bool isJSArray(JSCell* cell)
287 {
288     ASSERT((cell->classInfo(*cell->vm()) == JSArray::info()) == (cell->type() == ArrayType));
289     return cell->type() == ArrayType;
290 }
291
292 inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); }
293
294 inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values)
295 {
296     VM& vm = exec->vm();
297     unsigned length = values.size();
298     ObjectInitializationScope scope(vm);
299     JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
300
301     // FIXME: we should probably throw an out of memory error here, but
302     // when making this change we should check that all clients of this
303     // function will correctly handle an exception being thrown from here.
304     // https://bugs.webkit.org/show_bug.cgi?id=169786
305     RELEASE_ASSERT(array);
306
307     for (unsigned i = 0; i < length; ++i)
308         array->initializeIndex(scope, i, values.at(i));
309     return array;
310 }
311     
312 inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
313 {
314     VM& vm = exec->vm();
315     ObjectInitializationScope scope(vm);
316     JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
317
318     // FIXME: we should probably throw an out of memory error here, but
319     // when making this change we should check that all clients of this
320     // function will correctly handle an exception being thrown from here.
321     // https://bugs.webkit.org/show_bug.cgi?id=169786
322     RELEASE_ASSERT(array);
323
324     for (unsigned i = 0; i < length; ++i)
325         array->initializeIndex(scope, i, values[i]);
326     return array;
327 }
328
329 inline JSArray* constructArrayNegativeIndexed(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
330 {
331     VM& vm = exec->vm();
332     ObjectInitializationScope scope(vm);
333     JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
334
335     // FIXME: we should probably throw an out of memory error here, but
336     // when making this change we should check that all clients of this
337     // function will correctly handle an exception being thrown from here.
338     // https://bugs.webkit.org/show_bug.cgi?id=169786
339     RELEASE_ASSERT(array);
340
341     for (int i = 0; i < static_cast<int>(length); ++i)
342         array->initializeIndex(scope, i, values[-i]);
343     return array;
344 }
345
346 } // namespace JSC