2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009, 2012, 2015 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
24 #include "ArrayConventions.h"
25 #include "ButterflyInlines.h"
31 class LLIntOffsetsExtractor
;
33 class JSArray
: public JSNonFinalObject
{
34 friend class LLIntOffsetsExtractor
;
39 typedef JSNonFinalObject Base
;
40 static const unsigned StructureFlags
= Base::StructureFlags
| OverridesGetOwnPropertySlot
| OverridesGetPropertyNames
;
42 static size_t allocationSize(size_t inlineCapacity
)
44 ASSERT_UNUSED(inlineCapacity
, !inlineCapacity
);
45 return sizeof(JSArray
);
49 explicit JSArray(VM
& vm
, Structure
* structure
, Butterfly
* butterfly
)
50 : JSNonFinalObject(vm
, structure
, butterfly
)
55 static JSArray
* create(VM
&, Structure
*, unsigned initialLength
= 0);
56 static JSArray
* createWithButterfly(VM
&, Structure
*, Butterfly
*);
58 // tryCreateUninitialized is used for fast construction of arrays whose size and
59 // contents are known at time of creation. Clients of this interface must:
60 // - null-check the result (indicating out of memory, or otherwise unable to allocate vector).
61 // - call 'initializeIndex' for all properties in sequence, for 0 <= i < initialLength.
62 static JSArray
* tryCreateUninitialized(VM
&, Structure
*, unsigned initialLength
);
64 JS_EXPORT_PRIVATE
static bool defineOwnProperty(JSObject
*, ExecState
*, PropertyName
, const PropertyDescriptor
&, bool throwException
);
66 JS_EXPORT_PRIVATE
static bool getOwnPropertySlot(JSObject
*, ExecState
*, PropertyName
, PropertySlot
&);
70 // 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.
71 unsigned length() const { return getArrayLength(); }
73 // OK to use on new arrays, but not if it might be a RegExpMatchArray or RuntimeArray.
74 JS_EXPORT_PRIVATE
bool setLength(ExecState
*, unsigned, bool throwException
= false);
76 JS_EXPORT_PRIVATE
void push(ExecState
*, JSValue
);
77 JS_EXPORT_PRIVATE JSValue
pop(ExecState
*);
79 JSArray
* fastSlice(ExecState
&, unsigned startIndex
, unsigned count
);
81 static IndexingType
fastConcatType(VM
& vm
, JSArray
& firstArray
, JSArray
& secondArray
)
83 IndexingType type
= firstArray
.indexingType();
84 if (type
!= secondArray
.indexingType())
86 if (type
!= ArrayWithDouble
&& type
!= ArrayWithInt32
&& type
!= ArrayWithContiguous
)
88 if (firstArray
.structure(vm
)->holesMustForwardToPrototype(vm
)
89 || secondArray
.structure(vm
)->holesMustForwardToPrototype(vm
))
93 EncodedJSValue
fastConcatWith(ExecState
&, JSArray
&);
96 // This form of shift hints that we're doing queueing. With this assumption in hand,
97 // we convert to ArrayStorage, which has queue optimizations.
100 // This form of shift hints that we're just doing care and feeding on an array that
101 // is probably typically used for ordinary accesses. With this assumption in hand,
102 // we try to preserve whatever indexing type it has already.
106 bool shiftCountForShift(ExecState
* exec
, unsigned startIndex
, unsigned count
)
108 return shiftCountWithArrayStorage(exec
->vm(), startIndex
, count
, ensureArrayStorage(exec
->vm()));
110 bool shiftCountForSplice(ExecState
* exec
, unsigned& startIndex
, unsigned count
)
112 return shiftCountWithAnyIndexingType(exec
, startIndex
, count
);
114 template<ShiftCountMode shiftCountMode
>
115 bool shiftCount(ExecState
* exec
, unsigned& startIndex
, unsigned count
)
117 switch (shiftCountMode
) {
118 case ShiftCountForShift
:
119 return shiftCountForShift(exec
, startIndex
, count
);
120 case ShiftCountForSplice
:
121 return shiftCountForSplice(exec
, startIndex
, count
);
128 bool unshiftCountForShift(ExecState
* exec
, unsigned startIndex
, unsigned count
)
130 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
132 bool unshiftCountForSplice(ExecState
* exec
, unsigned startIndex
, unsigned count
)
134 return unshiftCountWithAnyIndexingType(exec
, startIndex
, count
);
136 template<ShiftCountMode shiftCountMode
>
137 bool unshiftCount(ExecState
* exec
, unsigned startIndex
, unsigned count
)
139 switch (shiftCountMode
) {
140 case ShiftCountForShift
:
141 return unshiftCountForShift(exec
, startIndex
, count
);
142 case ShiftCountForSplice
:
143 return unshiftCountForSplice(exec
, startIndex
, count
);
150 JS_EXPORT_PRIVATE
void fillArgList(ExecState
*, MarkedArgumentBuffer
&);
151 JS_EXPORT_PRIVATE
void copyToArguments(ExecState
*, VirtualRegister firstElementDest
, unsigned offset
, unsigned length
);
153 static Structure
* createStructure(VM
& vm
, JSGlobalObject
* globalObject
, JSValue prototype
, IndexingType indexingType
)
155 return Structure::create(vm
, globalObject
, prototype
, TypeInfo(ObjectType
, StructureFlags
), info(), indexingType
);
159 static void put(JSCell
*, ExecState
*, PropertyName
, JSValue
, PutPropertySlot
&);
161 static bool deleteProperty(JSCell
*, ExecState
*, PropertyName
);
162 JS_EXPORT_PRIVATE
static void getOwnNonIndexPropertyNames(JSObject
*, ExecState
*, PropertyNameArray
&, EnumerationMode
);
165 bool isLengthWritable()
167 ArrayStorage
* storage
= arrayStorageOrNull();
170 SparseArrayValueMap
* map
= storage
->m_sparseMap
.get();
171 return !map
|| !map
->lengthIsReadOnly();
174 bool shiftCountWithAnyIndexingType(ExecState
*, unsigned& startIndex
, unsigned count
);
175 JS_EXPORT_PRIVATE
bool shiftCountWithArrayStorage(VM
&, unsigned startIndex
, unsigned count
, ArrayStorage
*);
177 bool unshiftCountWithAnyIndexingType(ExecState
*, unsigned startIndex
, unsigned count
);
178 bool unshiftCountWithArrayStorage(ExecState
*, unsigned startIndex
, unsigned count
, ArrayStorage
*);
179 bool unshiftCountSlowCase(VM
&, bool, unsigned);
181 bool setLengthWithArrayStorage(ExecState
*, unsigned newLength
, bool throwException
, ArrayStorage
*);
182 void setLengthWritable(ExecState
*, bool writable
);
185 inline Butterfly
* createContiguousArrayButterfly(VM
& vm
, JSCell
* intendedOwner
, unsigned length
, unsigned& vectorLength
)
187 IndexingHeader header
;
188 vectorLength
= std::max(length
, BASE_VECTOR_LEN
);
189 header
.setVectorLength(vectorLength
);
190 header
.setPublicLength(length
);
191 Butterfly
* result
= Butterfly::create(
192 vm
, intendedOwner
, 0, 0, true, header
, vectorLength
* sizeof(EncodedJSValue
));
196 inline Butterfly
* createArrayButterfly(VM
& vm
, JSCell
* intendedOwner
, unsigned initialLength
)
198 Butterfly
* butterfly
= Butterfly::create(
199 vm
, intendedOwner
, 0, 0, true, baseIndexingHeaderForArray(initialLength
),
200 ArrayStorage::sizeFor(BASE_VECTOR_LEN
));
201 ArrayStorage
* storage
= butterfly
->arrayStorage();
202 storage
->m_indexBias
= 0;
203 storage
->m_sparseMap
.clear();
204 storage
->m_numValuesInVector
= 0;
208 Butterfly
* createArrayButterflyInDictionaryIndexingMode(
209 VM
&, JSCell
* intendedOwner
, unsigned initialLength
);
211 inline JSArray
* JSArray::create(VM
& vm
, Structure
* structure
, unsigned initialLength
)
213 Butterfly
* butterfly
;
214 if (LIKELY(!hasAnyArrayStorage(structure
->indexingType()))) {
216 hasUndecided(structure
->indexingType())
217 || hasInt32(structure
->indexingType())
218 || hasDouble(structure
->indexingType())
219 || hasContiguous(structure
->indexingType()));
220 unsigned vectorLength
;
221 butterfly
= createContiguousArrayButterfly(vm
, 0, initialLength
, vectorLength
);
222 ASSERT(initialLength
< MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH
);
223 if (hasDouble(structure
->indexingType())) {
224 for (unsigned i
= 0; i
< vectorLength
; ++i
)
225 butterfly
->contiguousDouble()[i
] = PNaN
;
229 structure
->indexingType() == ArrayWithSlowPutArrayStorage
230 || structure
->indexingType() == ArrayWithArrayStorage
);
231 butterfly
= createArrayButterfly(vm
, 0, initialLength
);
234 return createWithButterfly(vm
, structure
, butterfly
);
237 inline JSArray
* JSArray::tryCreateUninitialized(VM
& vm
, Structure
* structure
, unsigned initialLength
)
239 unsigned vectorLength
= std::max(BASE_VECTOR_LEN
, initialLength
);
240 if (vectorLength
> MAX_STORAGE_VECTOR_LENGTH
)
243 Butterfly
* butterfly
;
244 if (LIKELY(!hasAnyArrayStorage(structure
->indexingType()))) {
246 hasUndecided(structure
->indexingType())
247 || hasInt32(structure
->indexingType())
248 || hasDouble(structure
->indexingType())
249 || hasContiguous(structure
->indexingType()));
252 if (!vm
.heap
.tryAllocateStorage(0, Butterfly::totalSize(0, 0, true, vectorLength
* sizeof(EncodedJSValue
)), &temp
))
254 butterfly
= Butterfly::fromBase(temp
, 0, 0);
255 butterfly
->setVectorLength(vectorLength
);
256 butterfly
->setPublicLength(initialLength
);
257 if (hasDouble(structure
->indexingType())) {
258 for (unsigned i
= initialLength
; i
< vectorLength
; ++i
)
259 butterfly
->contiguousDouble()[i
] = PNaN
;
263 if (!vm
.heap
.tryAllocateStorage(0, Butterfly::totalSize(0, 0, true, ArrayStorage::sizeFor(vectorLength
)), &temp
))
265 butterfly
= Butterfly::fromBase(temp
, 0, 0);
266 *butterfly
->indexingHeader() = indexingHeaderForArray(initialLength
, vectorLength
);
267 ArrayStorage
* storage
= butterfly
->arrayStorage();
268 storage
->m_indexBias
= 0;
269 storage
->m_sparseMap
.clear();
270 storage
->m_numValuesInVector
= initialLength
;
273 return createWithButterfly(vm
, structure
, butterfly
);
276 inline JSArray
* JSArray::createWithButterfly(VM
& vm
, Structure
* structure
, Butterfly
* butterfly
)
278 JSArray
* array
= new (NotNull
, allocateCell
<JSArray
>(vm
.heap
)) JSArray(vm
, structure
, butterfly
);
279 array
->finishCreation(vm
);
283 JSArray
* asArray(JSValue
);
285 inline JSArray
* asArray(JSCell
* cell
)
287 ASSERT(cell
->inherits(JSArray::info()));
288 return jsCast
<JSArray
*>(cell
);
291 inline JSArray
* asArray(JSValue value
)
293 return asArray(value
.asCell());
296 inline bool isJSArray(JSCell
* cell
) { return cell
->classInfo() == JSArray::info(); }
297 inline bool isJSArray(JSValue v
) { return v
.isCell() && isJSArray(v
.asCell()); }
299 inline JSArray
* constructArray(ExecState
* exec
, Structure
* arrayStructure
, const ArgList
& values
)
302 unsigned length
= values
.size();
303 JSArray
* array
= JSArray::tryCreateUninitialized(vm
, arrayStructure
, length
);
305 // FIXME: we should probably throw an out of memory error here, but
306 // when making this change we should check that all clients of this
307 // function will correctly handle an exception being thrown from here.
308 RELEASE_ASSERT(array
);
310 for (unsigned i
= 0; i
< length
; ++i
)
311 array
->initializeIndex(vm
, i
, values
.at(i
));
315 inline JSArray
* constructArray(ExecState
* exec
, Structure
* arrayStructure
, const JSValue
* values
, unsigned length
)
318 JSArray
* array
= JSArray::tryCreateUninitialized(vm
, arrayStructure
, length
);
320 // FIXME: we should probably throw an out of memory error here, but
321 // when making this change we should check that all clients of this
322 // function will correctly handle an exception being thrown from here.
323 RELEASE_ASSERT(array
);
325 for (unsigned i
= 0; i
< length
; ++i
)
326 array
->initializeIndex(vm
, i
, values
[i
]);
330 inline JSArray
* constructArrayNegativeIndexed(ExecState
* exec
, Structure
* arrayStructure
, const JSValue
* values
, unsigned length
)
333 JSArray
* array
= JSArray::tryCreateUninitialized(vm
, arrayStructure
, length
);
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 RELEASE_ASSERT(array
);
340 for (int i
= 0; i
< static_cast<int>(length
); ++i
)
341 array
->initializeIndex(vm
, i
, values
[-i
]);