1cf09618529f991a31fcc10ff00b9263e8335a3a
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSArrayInlines.h
1 /*
2  *  Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  */
19
20 #pragma once
21
22 #include "Error.h"
23 #include "JSArray.h"
24 #include "JSCellInlines.h"
25 #include "Structure.h"
26
27 namespace JSC {
28
29 inline IndexingType JSArray::mergeIndexingTypeForCopying(IndexingType other)
30 {
31     IndexingType type = indexingType();
32     if (!(type & IsArray && other & IsArray))
33         return NonArray;
34
35     if (hasAnyArrayStorage(type) || hasAnyArrayStorage(other))
36         return NonArray;
37
38     if (type == ArrayWithUndecided)
39         return other;
40
41     if (other == ArrayWithUndecided)
42         return type;
43
44     // We can memcpy an Int32 and a Contiguous into a Contiguous array since
45     // both share the same memory layout for Int32 numbers.
46     if ((type == ArrayWithInt32 || type == ArrayWithContiguous)
47         && (other == ArrayWithInt32 || other == ArrayWithContiguous)) {
48         if (other == ArrayWithContiguous)
49             return other;
50         return type;
51     }
52
53     if (type != other)
54         return NonArray;
55
56     return type;
57 }
58
59 inline bool JSArray::canFastCopy(VM& vm, JSArray* otherArray)
60 {
61     if (otherArray == this)
62         return false;
63     if (hasAnyArrayStorage(indexingType()) || hasAnyArrayStorage(otherArray->indexingType()))
64         return false;
65     // FIXME: We should have a watchpoint for indexed properties on Array.prototype and Object.prototype
66     // instead of walking the prototype chain. https://bugs.webkit.org/show_bug.cgi?id=155592
67     if (structure(vm)->holesMustForwardToPrototype(vm, this)
68         || otherArray->structure(vm)->holesMustForwardToPrototype(vm, otherArray))
69         return false;
70     return true;
71 }
72
73 ALWAYS_INLINE double toLength(ExecState* exec, JSObject* obj)
74 {
75     VM& vm = exec->vm();
76     auto scope = DECLARE_THROW_SCOPE(vm);
77     if (isJSArray(obj))
78         return jsCast<JSArray*>(obj)->length();
79
80     JSValue lengthValue = obj->get(exec, vm.propertyNames->length);
81     RETURN_IF_EXCEPTION(scope, PNaN);
82     scope.release();
83     return lengthValue.toLength(exec);
84 }
85
86 ALWAYS_INLINE void JSArray::pushInline(ExecState* exec, JSValue value)
87 {
88     VM& vm = exec->vm();
89     auto scope = DECLARE_THROW_SCOPE(vm);
90
91 reloop:
92     Butterfly* butterfly = this->butterfly();
93
94     switch (indexingMode()) {
95     case ArrayClass: {
96         createInitialUndecided(vm, 0);
97         FALLTHROUGH;
98     }
99
100     case ArrayWithUndecided: {
101         convertUndecidedForValue(vm, value);
102         scope.release();
103         push(exec, value);
104         return;
105     }
106
107     case ArrayWithInt32: {
108         if (!value.isInt32()) {
109             convertInt32ForValue(vm, value);
110             scope.release();
111             push(exec, value);
112             return;
113         }
114
115         unsigned length = butterfly->publicLength();
116         ASSERT(length <= butterfly->vectorLength());
117         if (length < butterfly->vectorLength()) {
118             butterfly->contiguousInt32().at(this, length).setWithoutWriteBarrier(value);
119             butterfly->setPublicLength(length + 1);
120             return;
121         }
122
123         if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
124             methodTable(vm)->putByIndex(this, exec, length, value, true);
125             if (!scope.exception())
126                 throwException(exec, scope, createRangeError(exec, LengthExceededTheMaximumArrayLengthError));
127             return;
128         }
129
130         scope.release();
131         putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
132         return;
133     }
134
135     case ArrayWithContiguous: {
136         unsigned length = butterfly->publicLength();
137         ASSERT(length <= butterfly->vectorLength());
138         if (length < butterfly->vectorLength()) {
139             butterfly->contiguous().at(this, length).set(vm, this, value);
140             butterfly->setPublicLength(length + 1);
141             return;
142         }
143
144         if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
145             methodTable(vm)->putByIndex(this, exec, length, value, true);
146             if (!scope.exception())
147                 throwException(exec, scope, createRangeError(exec, LengthExceededTheMaximumArrayLengthError));
148             return;
149         }
150
151         scope.release();
152         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
153         return;
154     }
155
156     case ArrayWithDouble: {
157         if (!value.isNumber()) {
158             convertDoubleToContiguous(vm);
159             scope.release();
160             push(exec, value);
161             return;
162         }
163         double valueAsDouble = value.asNumber();
164         if (valueAsDouble != valueAsDouble) {
165             convertDoubleToContiguous(vm);
166             scope.release();
167             push(exec, value);
168             return;
169         }
170
171         unsigned length = butterfly->publicLength();
172         ASSERT(length <= butterfly->vectorLength());
173         if (length < butterfly->vectorLength()) {
174             butterfly->contiguousDouble().at(this, length) = valueAsDouble;
175             butterfly->setPublicLength(length + 1);
176             return;
177         }
178
179         if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
180             methodTable(vm)->putByIndex(this, exec, length, value, true);
181             if (!scope.exception())
182                 throwException(exec, scope, createRangeError(exec, LengthExceededTheMaximumArrayLengthError));
183             return;
184         }
185
186         scope.release();
187         putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
188         return;
189     }
190
191     case ArrayWithSlowPutArrayStorage: {
192         unsigned oldLength = length();
193         bool putResult = false;
194         if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true, putResult)) {
195             if (!scope.exception() && oldLength < 0xFFFFFFFFu) {
196                 scope.release();
197                 setLength(exec, oldLength + 1, true);
198             }
199             return;
200         }
201         FALLTHROUGH;
202     }
203
204     case ArrayWithArrayStorage: {
205         ArrayStorage* storage = butterfly->arrayStorage();
206
207         // Fast case - push within vector, always update m_length & m_numValuesInVector.
208         unsigned length = storage->length();
209         if (length < storage->vectorLength()) {
210             storage->m_vector[length].set(vm, this, value);
211             storage->setLength(length + 1);
212             ++storage->m_numValuesInVector;
213             return;
214         }
215
216         // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
217         if (UNLIKELY(storage->length() > MAX_ARRAY_INDEX)) {
218             methodTable(vm)->putByIndex(this, exec, storage->length(), value, true);
219             // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
220             if (!scope.exception())
221                 throwException(exec, scope, createRangeError(exec, LengthExceededTheMaximumArrayLengthError));
222             return;
223         }
224
225         // Handled the same as putIndex.
226         scope.release();
227         putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
228         return;
229     }
230
231     default: {
232         RELEASE_ASSERT(isCopyOnWrite(indexingMode()));
233         convertFromCopyOnWrite(vm);
234         goto reloop;
235     }
236     }
237 }
238
239 } // namespace JSC