3e8f6e0255ad2ca7985194c36f78b91f40122122
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003-2017 Apple Inc. All rights reserved.
5  *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public License
18  *  along with this library; see the file COPYING.LIB.  If not, write to
19  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "config.h"
25 #include "JSObject.h"
26
27 #include "ButterflyInlines.h"
28 #include "CatchScope.h"
29 #include "CustomGetterSetter.h"
30 #include "DatePrototype.h"
31 #include "ErrorConstructor.h"
32 #include "Exception.h"
33 #include "GetterSetter.h"
34 #include "HeapSnapshotBuilder.h"
35 #include "IndexingHeaderInlines.h"
36 #include "JSCInlines.h"
37 #include "JSCustomGetterSetterFunction.h"
38 #include "JSFunction.h"
39 #include "JSGlobalObject.h"
40 #include "JSImmutableButterfly.h"
41 #include "Lookup.h"
42 #include "NativeErrorConstructor.h"
43 #include "ObjectPrototype.h"
44 #include "PropertyDescriptor.h"
45 #include "PropertyNameArray.h"
46 #include "ProxyObject.h"
47 #include "SlotVisitorInlines.h"
48 #include "TypeError.h"
49 #include "VMInlines.h"
50 #include <math.h>
51 #include <wtf/Assertions.h>
52
53 namespace JSC {
54
55 // We keep track of the size of the last array after it was grown. We use this
56 // as a simple heuristic for as the value to grow the next array from size 0.
57 // This value is capped by the constant FIRST_VECTOR_GROW defined in
58 // ArrayConventions.h.
59 static unsigned lastArraySize = 0;
60
61 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
62 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
63
64 const ASCIILiteral NonExtensibleObjectPropertyDefineError { "Attempting to define property on object that is not extensible."_s };
65 const ASCIILiteral ReadonlyPropertyWriteError { "Attempted to assign to readonly property."_s };
66 const ASCIILiteral ReadonlyPropertyChangeError { "Attempting to change value of a readonly property."_s };
67 const ASCIILiteral UnableToDeletePropertyError { "Unable to delete property."_s };
68 const ASCIILiteral UnconfigurablePropertyChangeAccessMechanismError { "Attempting to change access mechanism for an unconfigurable property."_s };
69 const ASCIILiteral UnconfigurablePropertyChangeConfigurabilityError { "Attempting to change configurable attribute of unconfigurable property."_s };
70 const ASCIILiteral UnconfigurablePropertyChangeEnumerabilityError { "Attempting to change enumerable attribute of unconfigurable property."_s };
71 const ASCIILiteral UnconfigurablePropertyChangeWritabilityError { "Attempting to change writable attribute of unconfigurable property."_s };
72
73 const ClassInfo JSObject::s_info = { "Object", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSObject) };
74
75 const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSFinalObject) };
76
77 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
78 {
79     VM& vm = exec->vm();
80
81     // Add properties from the static hashtables of properties
82     for (; classInfo; classInfo = classInfo->parentClass) {
83         const HashTable* table = classInfo->staticPropHashTable;
84         if (!table)
85             continue;
86
87         for (auto iter = table->begin(); iter != table->end(); ++iter) {
88             if (!(iter->attributes() & PropertyAttribute::DontEnum) || mode.includeDontEnumProperties())
89                 propertyNames.add(Identifier::fromString(&vm, iter.key()));
90         }
91     }
92 }
93
94 ALWAYS_INLINE void JSObject::markAuxiliaryAndVisitOutOfLineProperties(SlotVisitor& visitor, Butterfly* butterfly, Structure* structure, PropertyOffset lastOffset)
95 {
96     // We call this when we found everything without races.
97     ASSERT(structure);
98     
99     if (!butterfly)
100         return;
101
102     if (isCopyOnWrite(structure->indexingMode())) {
103         visitor.append(bitwise_cast<WriteBarrier<JSCell>>(JSImmutableButterfly::fromButterfly(butterfly)));
104         return;
105     }
106
107     bool hasIndexingHeader = structure->hasIndexingHeader(this);
108     size_t preCapacity;
109     if (hasIndexingHeader)
110         preCapacity = butterfly->indexingHeader()->preCapacity(structure);
111     else
112         preCapacity = 0;
113     
114     HeapCell* base = bitwise_cast<HeapCell*>(
115         butterfly->base(preCapacity, Structure::outOfLineCapacity(lastOffset)));
116     
117     ASSERT(Heap::heap(base) == visitor.heap());
118     
119     visitor.markAuxiliary(base);
120     
121     unsigned outOfLineSize = Structure::outOfLineSize(lastOffset);
122     visitor.appendValuesHidden(butterfly->propertyStorage() - outOfLineSize, outOfLineSize);
123 }
124
125 ALWAYS_INLINE Structure* JSObject::visitButterfly(SlotVisitor& visitor)
126 {
127     static const char* const raceReason = "JSObject::visitButterfly";
128     Structure* result = visitButterflyImpl(visitor);
129     if (!result)
130         visitor.didRace(this, raceReason);
131     return result;
132 }
133
134 ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
135 {
136     VM& vm = visitor.vm();
137     
138     Butterfly* butterfly;
139     Structure* structure;
140     PropertyOffset lastOffset;
141
142     auto visitElements = [&] (IndexingType indexingMode) {
143         switch (indexingMode) {
144         // We don't need to visit the elements for CopyOnWrite butterflies since they we marked the JSImmutableButterfly acting as out butterfly.
145         case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
146             visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
147             break;
148         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
149             visitor.appendValuesHidden(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
150             if (butterfly->arrayStorage()->m_sparseMap)
151                 visitor.append(butterfly->arrayStorage()->m_sparseMap);
152             break;
153         default:
154             break;
155         }
156     };
157
158     if (visitor.mutatorIsStopped()) {
159         butterfly = this->butterfly();
160         structure = this->structure(vm);
161         lastOffset = structure->lastOffset();
162         
163         markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
164         visitElements(structure->indexingMode());
165
166         return structure;
167     }
168     
169     // We want to ensure that we only scan the butterfly if we have an exactly matched structure and an
170     // exactly matched size. The mutator is required to perform the following shenanigans when
171     // reallocating the butterfly with a concurrent collector, with all fencing necessary to ensure
172     // that this executes as if under sequential consistency:
173     //
174     //     object->structure = nuke(object->structure)
175     //     object->butterfly = newButterfly
176     //     structure->m_offset = newLastOffset
177     //     object->structure = newStructure
178     //
179     // It's OK to skip this when reallocating the butterfly in a way that does not affect the m_offset.
180     // We have other protocols in place for that.
181     //
182     // Note that the m_offset can change without the structure changing, but in that case the mutator
183     // will still store null to the structure.
184     //
185     // The collector will ensure that it always sees a matched butterfly/structure by reading the
186     // structure before and after reading the butterfly. For simplicity, let's first consider the case
187     // where the only way to change the outOfLineCapacity is to change the structure. This works
188     // because the mutator performs the following steps sequentially:
189     //
190     //     NukeStructure ChangeButterfly PutNewStructure
191     //
192     // Meanwhile the collector performs the following steps sequentially:
193     //
194     //     ReadStructureEarly ReadButterfly ReadStructureLate
195     //
196     // The collector is allowed to do any of these three things:
197     //
198     // BEFORE: Scan the object with the structure and butterfly *before* the mutator's transition.
199     // AFTER: Scan the object with the structure and butterfly *after* the mutator's transition.
200     // IGNORE: Ignore the butterfly and call didRace to schedule us to be revisted again in the future.
201     //
202     // In other words, the collector will never see any torn structure/butterfly mix. It will
203     // always see the structure/butterfly before the transition or after but not in between.
204     //
205     // We can prove that this is correct by exhaustively considering all interleavings:
206     //
207     // NukeStructure ChangeButterfly PutNewStructure ReadStructureEarly ReadButterfly ReadStructureLate: AFTER, trivially.
208     // NukeStructure ChangeButterfly ReadStructureEarly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
209     // NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
210     // NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
211     // NukeStructure ReadStructureEarly ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
212     // NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
213     // NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
214     // NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
215     // NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
216     // NukeStructure ReadStructureEarly ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read early
217     // ReadStructureEarly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
218     // ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
219     // ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
220     // ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
221     // ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
222     // ReadStructureEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
223     // ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
224     // ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
225     // ReadStructureEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
226     // ReadStructureEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
227     //
228     // But we additionally have to worry about the size changing. We make this work by requiring that
229     // the collector reads the size early and late as well. Lets consider the interleaving of the
230     // mutator changing the size without changing the structure:
231     //
232     //     NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure
233     //
234     // Meanwhile the collector does:
235     //
236     //     ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate
237     //
238     // The collector can detect races by not only comparing the early structure to the late structure
239     // (which will be the same before and after the algorithm runs) but also by comparing the early and
240     // late lastOffsets.  Note: the IGNORE proofs do not cite all of the reasons why the collector will
241     // ignore the case, since we only need to identify one to say that we're in the ignore case.
242     //
243     // NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: AFTER, trivially
244     // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
245     // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
246     // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
247     // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
248     // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
249     // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
250     // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
251     // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
252     // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
253     // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
254     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
255     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
256     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
257     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
258     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
259     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
260     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
261     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
262     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
263     // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
264     // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
265     // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
266     // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
267     // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
268     // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
269     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
270     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
271     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
272     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
273     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
274     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
275     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
276     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
277     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
278     // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
279     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
280     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
281     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
282     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
283     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
284     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
285     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
286     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
287     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
288     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
289     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
290     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
291     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
292     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
293     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
294     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
295     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
296     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
297     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
298     // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
299     // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: AFTER, the ReadStructureEarly sees the same structure as after and everything else runs after.
300     // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: AFTER, as above and the ReadLastOffsetEarly sees the lastOffset after.
301     // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: AFTER, as above and the ReadButterfly sees the right butterfly after.
302     // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure late
303     // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
304     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
305     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
306     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
307     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
308     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
309     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
310     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
311     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
312     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
313     // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
314     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
315     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
316     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
317     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
318     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
319     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
320     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
321     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
322     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
323     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
324     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
325     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
326     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
327     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
328     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
329     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
330     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
331     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
332     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
333     // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
334     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
335     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
336     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure late
337     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
338     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
339     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
340     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
341     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
342     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
343     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
344     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
345     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
346     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
347     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
348     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
349     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
350     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
351     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
352     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
353     // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
354     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
355     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
356     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
357     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
358     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
359     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
360     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure late
361     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
362     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
363     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
364     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
365     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
366     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: BEFORE, reads the offset before, everything else happens before
367     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: BEFORE, reads the offset before, everything else happens before
368     // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure: BEFORE, trivially
369     //
370     // Whew.
371     //
372     // What the collector is doing is just the "double collect" snapshot from "The Unbounded Single-
373     // Writer Algorithm" from Yehuda Afek et al's "Atomic Snapshots of Shared Memory" in JACM 1993,
374     // also available here:
375     //
376     // http://people.csail.mit.edu/shanir/publications/AADGMS.pdf
377     //
378     // Unlike Afek et al's algorithm, ours does not require extra hacks to force wait-freedom (see
379     // "Observation 2" in the paper). This simplifies the whole algorithm. Instead we are happy with
380     // obstruction-freedom, and like any good obstruction-free algorithm, we ensure progress using
381     // scheduling. We also only collect the butterfly once instead of twice; this optimization seems
382     // to hold up in my proofs above and I'm not sure it's part of Afek et al's algos.
383     //
384     // For more background on this kind of madness, I like this paper; it's where I learned about
385     // both the snapshot algorithm and obstruction-freedom:
386     //
387     // Lunchangco, Moir, Shavit. "Nonblocking k-compare-single-swap." SPAA '03
388     // https://pdfs.semanticscholar.org/343f/7182cde7669ca2a7de3dc01127927f384ef7.pdf
389     
390     StructureID structureID = this->structureID();
391     if (isNuked(structureID))
392         return nullptr;
393     structure = vm.getStructure(structureID);
394     lastOffset = structure->lastOffset();
395     IndexingType indexingMode = structure->indexingMode();
396     Dependency indexingModeDependency = Dependency::fence(indexingMode);
397     Locker<JSCellLock> locker(NoLockingNecessary);
398     switch (indexingMode) {
399     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
400         // We need to hold this lock to protect against changes to the innards of the butterfly
401         // that can happen when the butterfly is used for array storage.
402         // We do not need to hold this lock for contiguous butterflies. We do not reuse the existing
403         // butterfly with contiguous shape for new array storage butterfly. When converting the butterfly
404         // with contiguous shape to array storage, we always allocate a new one. Holding this lock for contiguous
405         // butterflies is unnecessary since contiguous shaped butterfly never becomes broken state.
406         locker = holdLock(cellLock());
407         break;
408     default:
409         break;
410     }
411     butterfly = indexingModeDependency.consume(this)->butterfly();
412     Dependency butterflyDependency = Dependency::fence(butterfly);
413     if (!butterfly)
414         return structure;
415     if (butterflyDependency.consume(this)->structureID() != structureID)
416         return nullptr;
417     if (butterflyDependency.consume(structure)->lastOffset() != lastOffset)
418         return nullptr;
419     
420     markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
421     ASSERT(indexingMode == structure->indexingMode());
422     visitElements(indexingMode);
423     
424     return structure;
425 }
426
427 size_t JSObject::estimatedSize(JSCell* cell, VM& vm)
428 {
429     JSObject* thisObject = jsCast<JSObject*>(cell);
430     size_t butterflyOutOfLineSize = thisObject->m_butterfly ? thisObject->structure(vm)->outOfLineSize() : 0;
431     return Base::estimatedSize(cell, vm) + butterflyOutOfLineSize;
432 }
433
434 void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
435 {
436     JSObject* thisObject = jsCast<JSObject*>(cell);
437     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
438 #if !ASSERT_DISABLED
439     bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
440     visitor.m_isCheckingForDefaultMarkViolation = false;
441 #endif
442     
443     JSCell::visitChildren(thisObject, visitor);
444     
445     thisObject->visitButterfly(visitor);
446     
447 #if !ASSERT_DISABLED
448     visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
449 #endif
450 }
451
452 void JSObject::heapSnapshot(JSCell* cell, HeapSnapshotBuilder& builder)
453 {
454     JSObject* thisObject = jsCast<JSObject*>(cell);
455     Base::heapSnapshot(cell, builder);
456
457     Structure* structure = thisObject->structure();
458     for (auto& entry : structure->getPropertiesConcurrently()) {
459         JSValue toValue = thisObject->getDirect(entry.offset);
460         if (toValue && toValue.isCell())
461             builder.appendPropertyNameEdge(thisObject, toValue.asCell(), entry.key);
462     }
463
464     Butterfly* butterfly = thisObject->butterfly();
465     if (butterfly) {
466         WriteBarrier<Unknown>* data = nullptr;
467         uint32_t count = 0;
468
469         switch (thisObject->indexingType()) {
470         case ALL_CONTIGUOUS_INDEXING_TYPES:
471             data = butterfly->contiguous().data();
472             count = butterfly->publicLength();
473             break;
474         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
475             data = butterfly->arrayStorage()->m_vector;
476             count = butterfly->arrayStorage()->vectorLength();
477             break;
478         default:
479             break;
480         }
481
482         for (uint32_t i = 0; i < count; ++i) {
483             JSValue toValue = data[i].get();
484             if (toValue && toValue.isCell())
485                 builder.appendIndexEdge(thisObject, toValue.asCell(), i);
486         }
487     }
488 }
489
490 void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
491 {
492     JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
493     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
494 #if !ASSERT_DISABLED
495     bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
496     visitor.m_isCheckingForDefaultMarkViolation = false;
497 #endif
498     
499     JSCell::visitChildren(thisObject, visitor);
500     
501     if (Structure* structure = thisObject->visitButterfly(visitor)) {
502         if (unsigned storageSize = structure->inlineSize())
503             visitor.appendValuesHidden(thisObject->inlineStorage(), storageSize);
504     }
505     
506 #if !ASSERT_DISABLED
507     visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
508 #endif
509 }
510
511 String JSObject::className(const JSObject* object, VM& vm)
512 {
513     const ClassInfo* info = object->classInfo(vm);
514     ASSERT(info);
515     return info->className;
516 }
517
518 String JSObject::toStringName(const JSObject* object, ExecState* exec)
519 {
520     VM& vm = exec->vm();
521     const ClassInfo* info = object->classInfo(vm);
522     ASSERT(info);
523     return info->className;
524 }
525
526 String JSObject::calculatedClassName(JSObject* object)
527 {
528     String constructorFunctionName;
529     auto* structure = object->structure();
530     auto* globalObject = structure->globalObject();
531     VM& vm = globalObject->vm();
532     auto scope = DECLARE_CATCH_SCOPE(vm);
533     auto* exec = globalObject->globalExec();
534
535     // Check for a display name of obj.constructor.
536     // This is useful to get `Foo` for the `(class Foo).prototype` object.
537     PropertySlot slot(object, PropertySlot::InternalMethodType::VMInquiry);
538     if (object->getOwnPropertySlot(object, exec, vm.propertyNames->constructor, slot)) {
539         EXCEPTION_ASSERT(!scope.exception());
540         if (slot.isValue()) {
541             if (JSObject* ctorObject = jsDynamicCast<JSObject*>(vm, slot.getValue(exec, vm.propertyNames->constructor))) {
542                 if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject))
543                     constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
544                 else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject))
545                     constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
546             }
547         }
548     }
549
550     EXCEPTION_ASSERT(!scope.exception() || constructorFunctionName.isNull());
551     if (UNLIKELY(scope.exception()))
552         scope.clearException();
553
554     // Get the display name of obj.__proto__.constructor.
555     // This is useful to get `Foo` for a `new Foo` object.
556     if (constructorFunctionName.isNull()) {
557         MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
558         if (LIKELY(structure->classInfo()->methodTable.getPrototype == defaultGetPrototype)) {
559             JSValue protoValue = object->getPrototypeDirect(vm);
560             if (protoValue.isObject()) {
561                 JSObject* protoObject = asObject(protoValue);
562                 PropertySlot slot(protoValue, PropertySlot::InternalMethodType::VMInquiry);
563                 if (protoObject->getPropertySlot(exec, vm.propertyNames->constructor, slot)) {
564                     EXCEPTION_ASSERT(!scope.exception());
565                     if (slot.isValue()) {
566                         if (JSObject* ctorObject = jsDynamicCast<JSObject*>(vm, slot.getValue(exec, vm.propertyNames->constructor))) {
567                             if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject))
568                                 constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
569                             else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject))
570                                 constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
571                         }
572                     }
573                 }
574             }
575         }
576     }
577
578     EXCEPTION_ASSERT(!scope.exception() || constructorFunctionName.isNull());
579     if (UNLIKELY(scope.exception()))
580         scope.clearException();
581
582     if (constructorFunctionName.isNull() || constructorFunctionName == "Object") {
583         String tableClassName = object->methodTable(vm)->className(object, vm);
584         if (!tableClassName.isNull() && tableClassName != "Object")
585             return tableClassName;
586
587         String classInfoName = object->classInfo(vm)->className;
588         if (!classInfoName.isNull())
589             return classInfoName;
590
591         if (constructorFunctionName.isNull())
592             return "Object"_s;
593     }
594
595     return constructorFunctionName;
596 }
597
598 bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot)
599 {
600     // NB. The fact that we're directly consulting our indexed storage implies that it is not
601     // legal for anyone to override getOwnPropertySlot() without also overriding
602     // getOwnPropertySlotByIndex().
603     
604     if (i > MAX_ARRAY_INDEX)
605         return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
606     
607     switch (thisObject->indexingType()) {
608     case ALL_BLANK_INDEXING_TYPES:
609     case ALL_UNDECIDED_INDEXING_TYPES:
610         break;
611         
612     case ALL_INT32_INDEXING_TYPES:
613     case ALL_CONTIGUOUS_INDEXING_TYPES: {
614         Butterfly* butterfly = thisObject->butterfly();
615         if (i >= butterfly->vectorLength())
616             return false;
617         
618         JSValue value = butterfly->contiguous().at(thisObject, i).get();
619         if (value) {
620             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), value);
621             return true;
622         }
623         
624         return false;
625     }
626         
627     case ALL_DOUBLE_INDEXING_TYPES: {
628         Butterfly* butterfly = thisObject->butterfly();
629         if (i >= butterfly->vectorLength())
630             return false;
631         
632         double value = butterfly->contiguousDouble().at(thisObject, i);
633         if (value == value) {
634             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), JSValue(JSValue::EncodeAsDouble, value));
635             return true;
636         }
637         
638         return false;
639     }
640         
641     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
642         ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
643         if (i >= storage->length())
644             return false;
645         
646         if (i < storage->vectorLength()) {
647             JSValue value = storage->m_vector[i].get();
648             if (value) {
649                 slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), value);
650                 return true;
651             }
652         } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
653             SparseArrayValueMap::iterator it = map->find(i);
654             if (it != map->notFound()) {
655                 it->value.get(thisObject, slot);
656                 return true;
657             }
658         }
659         break;
660     }
661         
662     default:
663         RELEASE_ASSERT_NOT_REACHED();
664         break;
665     }
666     
667     return false;
668 }
669
670 // https://tc39.github.io/ecma262/#sec-ordinaryset
671 bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyName, JSValue value, JSValue receiver, bool shouldThrow)
672 {
673     // If we find the receiver is not the same to the object, we fall to this slow path.
674     // Currently, there are 3 candidates.
675     // 1. Reflect.set can alter the receiver with an arbitrary value.
676     // 2. Window Proxy.
677     // 3. ES6 Proxy.
678
679     VM& vm = exec->vm();
680     auto scope = DECLARE_THROW_SCOPE(vm);
681     JSObject* current = object;
682     PropertyDescriptor ownDescriptor;
683     while (true) {
684         if (current->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
685             ProxyObject* proxy = jsCast<ProxyObject*>(current);
686             PutPropertySlot slot(receiver, shouldThrow);
687             RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, exec, propertyName, value, slot));
688         }
689
690         // 9.1.9.1-2 Let ownDesc be ? O.[[GetOwnProperty]](P).
691         bool ownDescriptorFound = current->getOwnPropertyDescriptor(exec, propertyName, ownDescriptor);
692         RETURN_IF_EXCEPTION(scope, false);
693
694         if (!ownDescriptorFound) {
695             // 9.1.9.1-3-a Let parent be ? O.[[GetPrototypeOf]]().
696             JSValue prototype = current->getPrototype(vm, exec);
697             RETURN_IF_EXCEPTION(scope, false);
698
699             // 9.1.9.1-3-b If parent is not null, then
700             if (!prototype.isNull()) {
701                 // 9.1.9.1-3-b-i Return ? parent.[[Set]](P, V, Receiver).
702                 current = asObject(prototype);
703                 continue;
704             }
705             // 9.1.9.1-3-c-i Let ownDesc be the PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
706             ownDescriptor = PropertyDescriptor(jsUndefined(), static_cast<unsigned>(PropertyAttribute::None));
707         }
708         break;
709     }
710
711     // 9.1.9.1-4 If IsDataDescriptor(ownDesc) is true, then
712     if (ownDescriptor.isDataDescriptor()) {
713         // 9.1.9.1-4-a If ownDesc.[[Writable]] is false, return false.
714         if (!ownDescriptor.writable())
715             return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
716
717         // 9.1.9.1-4-b If Type(Receiver) is not Object, return false.
718         if (!receiver.isObject())
719             return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
720
721         // In OrdinarySet, the receiver may not be the same to the object.
722         // So, we perform [[GetOwnProperty]] onto the receiver while we already perform [[GetOwnProperty]] onto the object.
723
724         // 9.1.9.1-4-c Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
725         JSObject* receiverObject = asObject(receiver);
726         PropertyDescriptor existingDescriptor;
727         bool existingDescriptorFound = receiverObject->getOwnPropertyDescriptor(exec, propertyName, existingDescriptor);
728         RETURN_IF_EXCEPTION(scope, false);
729
730         // 9.1.9.1-4-d If existingDescriptor is not undefined, then
731         if (existingDescriptorFound) {
732             // 9.1.9.1-4-d-i If IsAccessorDescriptor(existingDescriptor) is true, return false.
733             if (existingDescriptor.isAccessorDescriptor())
734                 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
735
736             // 9.1.9.1-4-d-ii If existingDescriptor.[[Writable]] is false, return false.
737             if (!existingDescriptor.writable())
738                 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
739
740             // 9.1.9.1-4-d-iii Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
741             PropertyDescriptor valueDescriptor;
742             valueDescriptor.setValue(value);
743
744             // 9.1.9.1-4-d-iv Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
745             RELEASE_AND_RETURN(scope, receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, valueDescriptor, shouldThrow));
746         }
747
748         // 9.1.9.1-4-e Else Receiver does not currently have a property P,
749         // 9.1.9.1-4-e-i Return ? CreateDataProperty(Receiver, P, V).
750         RELEASE_AND_RETURN(scope, receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, PropertyDescriptor(value, static_cast<unsigned>(PropertyAttribute::None)), shouldThrow));
751     }
752
753     // 9.1.9.1-5 Assert: IsAccessorDescriptor(ownDesc) is true.
754     ASSERT(ownDescriptor.isAccessorDescriptor());
755
756     // 9.1.9.1-6 Let setter be ownDesc.[[Set]].
757     // 9.1.9.1-7 If setter is undefined, return false.
758     JSValue setter = ownDescriptor.setter();
759     if (!setter.isObject())
760         return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
761
762     // 9.1.9.1-8 Perform ? Call(setter, Receiver, << V >>).
763     JSObject* setterObject = asObject(setter);
764     MarkedArgumentBuffer args;
765     args.append(value);
766     ASSERT(!args.hasOverflowed());
767
768     CallData callData;
769     CallType callType = setterObject->methodTable(vm)->getCallData(setterObject, callData);
770     scope.release();
771     call(exec, setterObject, callType, callData, receiver, args);
772
773     // 9.1.9.1-9 Return true.
774     return true;
775 }
776
777 // ECMA 8.6.2.2
778 bool JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
779 {
780     return putInlineForJSObject(cell, exec, propertyName, value, slot);
781 }
782
783 bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
784 {
785     ASSERT(!isThisValueAltered(slot, this));
786
787     VM& vm = exec->vm();
788     auto scope = DECLARE_THROW_SCOPE(vm);
789
790     JSObject* obj = this;
791     for (;;) {
792         unsigned attributes;
793         PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
794         if (isValidOffset(offset)) {
795             if (attributes & PropertyAttribute::ReadOnly) {
796                 ASSERT(this->prototypeChainMayInterceptStoreTo(vm, propertyName) || obj == this);
797                 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
798             }
799
800             JSValue gs = obj->getDirect(offset);
801             if (gs.isGetterSetter()) {
802                 // We need to make sure that we decide to cache this property before we potentially execute aribitrary JS.
803                 if (!structure(vm)->isDictionary())
804                     slot.setCacheableSetter(obj, offset);
805
806                 bool result = callSetter(exec, slot.thisValue(), gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
807                 RETURN_IF_EXCEPTION(scope, false);
808                 return result;
809             }
810             if (gs.isCustomGetterSetter()) {
811                 // We need to make sure that we decide to cache this property before we potentially execute aribitrary JS.
812                 if (attributes & PropertyAttribute::CustomAccessor)
813                     slot.setCustomAccessor(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
814                 else
815                     slot.setCustomValue(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
816
817                 bool result = callCustomSetter(exec, gs, attributes & PropertyAttribute::CustomAccessor, obj, slot.thisValue(), value);
818                 RETURN_IF_EXCEPTION(scope, false);
819                 return result;
820             }
821             ASSERT(!(attributes & PropertyAttribute::Accessor));
822
823             // If there's an existing property on the object or one of its 
824             // prototypes it should be replaced, so break here.
825             break;
826         }
827         if (!obj->staticPropertiesReified(vm)) {
828             if (obj->classInfo(vm)->hasStaticSetterOrReadonlyProperties()) {
829                 if (auto entry = obj->findPropertyHashEntry(vm, propertyName))
830                     RELEASE_AND_RETURN(scope, putEntry(exec, entry->table->classForThis, entry->value, obj, this, propertyName, value, slot));
831             }
832         }
833         if (obj->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
834             // FIXME: We shouldn't unconditionally perform [[Set]] here.
835             // We need to do more because this is observable behavior.
836             // https://bugs.webkit.org/show_bug.cgi?id=155012
837             ProxyObject* proxy = jsCast<ProxyObject*>(obj);
838             RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, exec, propertyName, value, slot));
839         }
840         JSValue prototype = obj->getPrototype(vm, exec);
841         RETURN_IF_EXCEPTION(scope, false);
842         if (prototype.isNull())
843             break;
844         obj = asObject(prototype);
845     }
846
847     if (!putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot))
848         return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
849     return true;
850 }
851
852 bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
853 {
854     VM& vm = exec->vm();
855     JSObject* thisObject = jsCast<JSObject*>(cell);
856
857     if (propertyName > MAX_ARRAY_INDEX) {
858         PutPropertySlot slot(cell, shouldThrow);
859         return thisObject->methodTable(vm)->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
860     }
861
862     thisObject->ensureWritable(vm);
863
864     switch (thisObject->indexingType()) {
865     case ALL_BLANK_INDEXING_TYPES:
866         break;
867         
868     case ALL_UNDECIDED_INDEXING_TYPES: {
869         thisObject->convertUndecidedForValue(vm, value);
870         // Reloop.
871         return putByIndex(cell, exec, propertyName, value, shouldThrow);
872     }
873         
874     case ALL_INT32_INDEXING_TYPES: {
875         if (!value.isInt32()) {
876             thisObject->convertInt32ForValue(vm, value);
877             return putByIndex(cell, exec, propertyName, value, shouldThrow);
878         }
879         FALLTHROUGH;
880     }
881         
882     case ALL_CONTIGUOUS_INDEXING_TYPES: {
883         Butterfly* butterfly = thisObject->butterfly();
884         if (propertyName >= butterfly->vectorLength())
885             break;
886         butterfly->contiguous().at(thisObject, propertyName).set(vm, thisObject, value);
887         if (propertyName >= butterfly->publicLength())
888             butterfly->setPublicLength(propertyName + 1);
889         return true;
890     }
891         
892     case ALL_DOUBLE_INDEXING_TYPES: {
893         if (!value.isNumber()) {
894             thisObject->convertDoubleToContiguous(vm);
895             // Reloop.
896             return putByIndex(cell, exec, propertyName, value, shouldThrow);
897         }
898
899         double valueAsDouble = value.asNumber();
900         if (valueAsDouble != valueAsDouble) {
901             thisObject->convertDoubleToContiguous(vm);
902             // Reloop.
903             return putByIndex(cell, exec, propertyName, value, shouldThrow);
904         }
905         Butterfly* butterfly = thisObject->butterfly();
906         if (propertyName >= butterfly->vectorLength())
907             break;
908         butterfly->contiguousDouble().at(thisObject, propertyName) = valueAsDouble;
909         if (propertyName >= butterfly->publicLength())
910             butterfly->setPublicLength(propertyName + 1);
911         return true;
912     }
913         
914     case NonArrayWithArrayStorage:
915     case ArrayWithArrayStorage: {
916         ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
917         
918         if (propertyName >= storage->vectorLength())
919             break;
920         
921         WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
922         unsigned length = storage->length();
923         
924         // Update length & m_numValuesInVector as necessary.
925         if (propertyName >= length) {
926             length = propertyName + 1;
927             storage->setLength(length);
928             ++storage->m_numValuesInVector;
929         } else if (!valueSlot)
930             ++storage->m_numValuesInVector;
931         
932         valueSlot.set(vm, thisObject, value);
933         return true;
934     }
935         
936     case NonArrayWithSlowPutArrayStorage:
937     case ArrayWithSlowPutArrayStorage: {
938         ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
939         
940         if (propertyName >= storage->vectorLength())
941             break;
942         
943         WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
944         unsigned length = storage->length();
945         
946         // Update length & m_numValuesInVector as necessary.
947         if (propertyName >= length) {
948             bool putResult = false;
949             if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
950                 return putResult;
951             length = propertyName + 1;
952             storage->setLength(length);
953             ++storage->m_numValuesInVector;
954         } else if (!valueSlot) {
955             bool putResult = false;
956             if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
957                 return putResult;
958             ++storage->m_numValuesInVector;
959         }
960         
961         valueSlot.set(vm, thisObject, value);
962         return true;
963     }
964         
965     default:
966         RELEASE_ASSERT_NOT_REACHED();
967     }
968     
969     return thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
970 }
971
972 ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
973 {
974     SparseArrayValueMap* map = storage->m_sparseMap.get();
975
976     if (!map)
977         map = allocateSparseIndexMap(vm);
978
979     if (map->sparseMode())
980         return storage;
981
982     map->setSparseMode();
983
984     unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
985     for (unsigned i = 0; i < usedVectorLength; ++i) {
986         JSValue value = storage->m_vector[i].get();
987         // This will always be a new entry in the map, so no need to check we can write,
988         // and attributes are default so no need to set them.
989         if (value)
990             map->add(this, i).iterator->value.forceSet(vm, map, value, 0);
991     }
992
993     DeferGC deferGC(vm.heap);
994     Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
995     RELEASE_ASSERT(newButterfly);
996     newButterfly->arrayStorage()->m_indexBias = 0;
997     newButterfly->arrayStorage()->setVectorLength(0);
998     newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
999     setButterfly(vm, newButterfly);
1000     
1001     return newButterfly->arrayStorage();
1002 }
1003
1004 void JSObject::enterDictionaryIndexingMode(VM& vm)
1005 {
1006     switch (indexingType()) {
1007     case ALL_BLANK_INDEXING_TYPES:
1008     case ALL_UNDECIDED_INDEXING_TYPES:
1009     case ALL_INT32_INDEXING_TYPES:
1010     case ALL_DOUBLE_INDEXING_TYPES:
1011     case ALL_CONTIGUOUS_INDEXING_TYPES:
1012         // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
1013         // this case if we ever cared. Note that ensureArrayStorage() can return null if the object
1014         // doesn't support traditional indexed properties. At the time of writing, this just affects
1015         // typed arrays.
1016         if (ArrayStorage* storage = ensureArrayStorageSlow(vm))
1017             enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, storage);
1018         break;
1019     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1020         enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
1021         break;
1022         
1023     default:
1024         break;
1025     }
1026 }
1027
1028 void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
1029 {
1030     if (mayInterceptIndexedAccesses(vm))
1031         return;
1032     
1033     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors));
1034     
1035     if (!mayBePrototype())
1036         return;
1037     
1038     globalObject(vm)->haveABadTime(vm);
1039 }
1040
1041 Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length)
1042 {
1043     ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
1044     IndexingType oldType = indexingType();
1045     ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
1046     ASSERT(!needsSlowPutIndexing(vm));
1047     ASSERT(!indexingShouldBeSparse(vm));
1048     Structure* structure = this->structure(vm);
1049     unsigned propertyCapacity = structure->outOfLineCapacity();
1050     unsigned vectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, length);
1051     Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
1052         butterfly(), vm, this, structure, propertyCapacity, false, 0,
1053         sizeof(EncodedJSValue) * vectorLength);
1054     newButterfly->setPublicLength(length);
1055     newButterfly->setVectorLength(vectorLength);
1056     return newButterfly;
1057 }
1058
1059 Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
1060 {
1061     DeferGC deferGC(vm.heap);
1062     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1063     StructureID oldStructureID = this->structureID();
1064     Structure* oldStructure = vm.getStructure(oldStructureID);
1065     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateUndecided);
1066     nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1067     setStructure(vm, newStructure);
1068     return newButterfly;
1069 }
1070
1071 ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
1072 {
1073     DeferGC deferGC(vm.heap);
1074     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1075     for (unsigned i = newButterfly->vectorLength(); i--;)
1076         newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1077     StructureID oldStructureID = this->structureID();
1078     Structure* oldStructure = vm.getStructure(oldStructureID);
1079     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateInt32);
1080     nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1081     setStructure(vm, newStructure);
1082     return newButterfly->contiguousInt32();
1083 }
1084
1085 ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
1086 {
1087     DeferGC deferGC(vm.heap);
1088     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1089     for (unsigned i = newButterfly->vectorLength(); i--;)
1090         newButterfly->contiguousDouble().at(this, i) = PNaN;
1091     StructureID oldStructureID = this->structureID();
1092     Structure* oldStructure = vm.getStructure(oldStructureID);
1093     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateDouble);
1094     nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1095     setStructure(vm, newStructure);
1096     return newButterfly->contiguousDouble();
1097 }
1098
1099 ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
1100 {
1101     DeferGC deferGC(vm.heap);
1102     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1103     for (unsigned i = newButterfly->vectorLength(); i--;)
1104         newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1105     StructureID oldStructureID = this->structureID();
1106     Structure* oldStructure = vm.getStructure(oldStructureID);
1107     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateContiguous);
1108     nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1109     setStructure(vm, newStructure);
1110     return newButterfly->contiguous();
1111 }
1112
1113 Butterfly* JSObject::createArrayStorageButterfly(VM& vm, JSObject* intendedOwner, Structure* structure, unsigned length, unsigned vectorLength, Butterfly* oldButterfly)
1114 {
1115     Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
1116         oldButterfly, vm, intendedOwner, structure, structure->outOfLineCapacity(), false, 0,
1117         ArrayStorage::sizeFor(vectorLength));
1118     RELEASE_ASSERT(newButterfly);
1119
1120     ArrayStorage* result = newButterfly->arrayStorage();
1121     result->setLength(length);
1122     result->setVectorLength(vectorLength);
1123     result->m_sparseMap.clear();
1124     result->m_numValuesInVector = 0;
1125     result->m_indexBias = 0;
1126     for (size_t i = vectorLength; i--;)
1127         result->m_vector[i].setWithoutWriteBarrier(JSValue());
1128
1129     return newButterfly;
1130 }
1131
1132 ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
1133 {
1134     DeferGC deferGC(vm.heap);
1135     StructureID oldStructureID = this->structureID();
1136     Structure* oldStructure = vm.getStructure(oldStructureID);
1137     IndexingType oldType = indexingType();
1138     ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
1139
1140     Butterfly* newButterfly = createArrayStorageButterfly(vm, this, oldStructure, length, vectorLength, butterfly());
1141     ArrayStorage* result = newButterfly->arrayStorage();
1142     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, suggestedArrayStorageTransition(vm));
1143     nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1144     setStructure(vm, newStructure);
1145     return result;
1146 }
1147
1148 ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
1149 {
1150     return createArrayStorage(
1151         vm, 0, ArrayStorage::optimalVectorLength(0, structure(vm)->outOfLineCapacity(), 0));
1152 }
1153
1154 ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
1155 {
1156     ASSERT(hasUndecided(indexingType()));
1157
1158     Butterfly* butterfly = this->butterfly();
1159     for (unsigned i = butterfly->vectorLength(); i--;)
1160         butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1161
1162     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32));
1163     return m_butterfly->contiguousInt32();
1164 }
1165
1166 ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
1167 {
1168     ASSERT(hasUndecided(indexingType()));
1169
1170     Butterfly* butterfly = m_butterfly.get();
1171     for (unsigned i = butterfly->vectorLength(); i--;)
1172         butterfly->contiguousDouble().at(this, i) = PNaN;
1173     
1174     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
1175     return m_butterfly->contiguousDouble();
1176 }
1177
1178 ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
1179 {
1180     ASSERT(hasUndecided(indexingType()));
1181
1182     Butterfly* butterfly = m_butterfly.get();
1183     for (unsigned i = butterfly->vectorLength(); i--;)
1184         butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1185
1186     WTF::storeStoreFence();
1187     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1188     return m_butterfly->contiguous();
1189 }
1190
1191 ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
1192 {
1193     Structure* structure = this->structure(vm);
1194     unsigned publicLength = m_butterfly->publicLength();
1195     unsigned propertyCapacity = structure->outOfLineCapacity();
1196
1197     Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
1198     
1199     memcpy(
1200         newButterfly->base(0, propertyCapacity),
1201         m_butterfly->base(0, propertyCapacity),
1202         propertyCapacity * sizeof(EncodedJSValue));
1203
1204     ArrayStorage* newStorage = newButterfly->arrayStorage();
1205     newStorage->setVectorLength(neededLength);
1206     newStorage->setLength(publicLength);
1207     newStorage->m_sparseMap.clear();
1208     newStorage->m_indexBias = 0;
1209     newStorage->m_numValuesInVector = 0;
1210     
1211     return newStorage;
1212 }
1213
1214 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
1215 {
1216     DeferGC deferGC(vm.heap);
1217     ASSERT(hasUndecided(indexingType()));
1218
1219     unsigned vectorLength = m_butterfly->vectorLength();
1220     ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1221     
1222     for (unsigned i = vectorLength; i--;)
1223         storage->m_vector[i].setWithoutWriteBarrier(JSValue());
1224     
1225     StructureID oldStructureID = this->structureID();
1226     Structure* oldStructure = vm.getStructure(oldStructureID);
1227     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1228     nukeStructureAndSetButterfly(vm, oldStructureID, storage->butterfly());
1229     setStructure(vm, newStructure);
1230     return storage;
1231 }
1232
1233 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
1234 {
1235     return convertUndecidedToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1236 }
1237
1238 ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
1239 {
1240     ASSERT(hasInt32(indexingType()));
1241     ASSERT(!isCopyOnWrite(indexingMode()));
1242
1243     Butterfly* butterfly = m_butterfly.get();
1244     for (unsigned i = butterfly->vectorLength(); i--;) {
1245         WriteBarrier<Unknown>* current = &butterfly->contiguous().atUnsafe(i);
1246         double* currentAsDouble = bitwise_cast<double*>(current);
1247         JSValue v = current->get();
1248         // NOTE: Since this may be used during initialization, v could be garbage. If it's garbage,
1249         // that means it will be overwritten later.
1250         if (!v.isInt32()) {
1251             *currentAsDouble = PNaN;
1252             continue;
1253         }
1254         *currentAsDouble = v.asInt32();
1255     }
1256     
1257     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
1258     return m_butterfly->contiguousDouble();
1259 }
1260
1261 ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
1262 {
1263     ASSERT(hasInt32(indexingType()));
1264     
1265     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1266     return m_butterfly->contiguous();
1267 }
1268
1269 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
1270 {
1271     DeferGC deferGC(vm.heap);
1272     ASSERT(hasInt32(indexingType()));
1273
1274     unsigned vectorLength = m_butterfly->vectorLength();
1275     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1276     Butterfly* butterfly = m_butterfly.get();
1277     for (unsigned i = 0; i < vectorLength; i++) {
1278         JSValue v = butterfly->contiguous().at(this, i).get();
1279         newStorage->m_vector[i].setWithoutWriteBarrier(v);
1280         if (v)
1281             newStorage->m_numValuesInVector++;
1282     }
1283     
1284     StructureID oldStructureID = this->structureID();
1285     Structure* oldStructure = vm.getStructure(oldStructureID);
1286     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1287     nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1288     setStructure(vm, newStructure);
1289     return newStorage;
1290 }
1291
1292 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
1293 {
1294     return convertInt32ToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1295 }
1296
1297 ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
1298 {
1299     ASSERT(hasDouble(indexingType()));
1300     ASSERT(!isCopyOnWrite(indexingMode()));
1301
1302     Butterfly* butterfly = m_butterfly.get();
1303     for (unsigned i = butterfly->vectorLength(); i--;) {
1304         double* current = &butterfly->contiguousDouble().atUnsafe(i);
1305         WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
1306         double value = *current;
1307         if (value != value) {
1308             currentAsValue->clear();
1309             continue;
1310         }
1311         JSValue v = JSValue(JSValue::EncodeAsDouble, value);
1312         currentAsValue->setWithoutWriteBarrier(v);
1313     }
1314     
1315     WTF::storeStoreFence();
1316     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1317     return m_butterfly->contiguous();
1318 }
1319
1320 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
1321 {
1322     DeferGC deferGC(vm.heap);
1323     ASSERT(hasDouble(indexingType()));
1324
1325     unsigned vectorLength = m_butterfly->vectorLength();
1326     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1327     Butterfly* butterfly = m_butterfly.get();
1328     for (unsigned i = 0; i < vectorLength; i++) {
1329         double value = butterfly->contiguousDouble().at(this, i);
1330         if (value != value) {
1331             newStorage->m_vector[i].clear();
1332             continue;
1333         }
1334         newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
1335         newStorage->m_numValuesInVector++;
1336     }
1337     
1338     StructureID oldStructureID = this->structureID();
1339     Structure* oldStructure = vm.getStructure(oldStructureID);
1340     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1341     nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1342     setStructure(vm, newStructure);
1343     return newStorage;
1344 }
1345
1346 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
1347 {
1348     return convertDoubleToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1349 }
1350
1351 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
1352 {
1353     DeferGC deferGC(vm.heap);
1354     ASSERT(hasContiguous(indexingType()));
1355
1356     unsigned vectorLength = m_butterfly->vectorLength();
1357     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1358     Butterfly* butterfly = m_butterfly.get();
1359     for (unsigned i = 0; i < vectorLength; i++) {
1360         JSValue v = butterfly->contiguous().at(this, i).get();
1361         newStorage->m_vector[i].setWithoutWriteBarrier(v);
1362         if (v)
1363             newStorage->m_numValuesInVector++;
1364     }
1365
1366     // While we modify the butterfly of Contiguous Array, we do not take any cellLock here. This is because
1367     // (1) the old butterfly is not changed and (2) new butterfly is not changed after it is exposed to
1368     // the collector.
1369     // The mutator performs the following operations are sequentially executed by using storeStoreFence.
1370     //
1371     //     CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure
1372     //
1373     // Meanwhile the collector performs the following steps sequentially:
1374     //
1375     //     ReadStructureEarly ReadButterfly ReadStructureLate
1376     //
1377     // We list up all the patterns by writing a tiny script, and ensure all the cases are categorized into BEFORE, AFTER, and IGNORE.
1378     //
1379     // CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureEarly ReadButterfly ReadStructureLate: AFTER, trivially
1380     // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
1381     // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1382     // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1383     // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
1384     // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1385     // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1386     // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1387     // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1388     // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read early
1389     // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
1390     // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1391     // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1392     // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1393     // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1394     // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1395     // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1396     // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1397     // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1398     // CreateNewButterfly ReadStructureEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
1399     // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
1400     // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1401     // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1402     // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1403     // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1404     // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1405     // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1406     // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1407     // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1408     // ReadStructureEarly CreateNewButterfly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
1409     // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1410     // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1411     // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1412     // ReadStructureEarly ReadButterfly CreateNewButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
1413     // ReadStructureEarly ReadButterfly ReadStructureLate CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
1414
1415     ASSERT(newStorage->butterfly() != butterfly);
1416     StructureID oldStructureID = this->structureID();
1417     Structure* oldStructure = vm.getStructure(oldStructureID);
1418     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1419
1420     // Ensure new Butterfly initialization is correctly done before exposing it to the concurrent threads.
1421     if (isX86() || vm.heap.mutatorShouldBeFenced())
1422         WTF::storeStoreFence();
1423     nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1424     setStructure(vm, newStructure);
1425     
1426     return newStorage;
1427 }
1428
1429 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
1430 {
1431     return convertContiguousToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1432 }
1433
1434 void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
1435 {
1436     IndexingType type = indexingTypeForValue(value);
1437     if (type == Int32Shape) {
1438         convertUndecidedToInt32(vm);
1439         return;
1440     }
1441     
1442     if (type == DoubleShape) {
1443         convertUndecidedToDouble(vm);
1444         return;
1445     }
1446
1447     ASSERT(type == ContiguousShape);
1448     convertUndecidedToContiguous(vm);
1449 }
1450
1451 void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
1452 {
1453     if (value.isInt32()) {
1454         createInitialInt32(vm, index + 1).at(this, index).set(vm, this, value);
1455         return;
1456     }
1457     
1458     if (value.isDouble()) {
1459         double doubleValue = value.asNumber();
1460         if (doubleValue == doubleValue) {
1461             createInitialDouble(vm, index + 1).at(this, index) = doubleValue;
1462             return;
1463         }
1464     }
1465     
1466     createInitialContiguous(vm, index + 1).at(this, index).set(vm, this, value);
1467 }
1468
1469 void JSObject::convertInt32ForValue(VM& vm, JSValue value)
1470 {
1471     ASSERT(!value.isInt32());
1472     
1473     if (value.isDouble() && !std::isnan(value.asDouble())) {
1474         convertInt32ToDouble(vm);
1475         return;
1476     }
1477
1478     convertInt32ToContiguous(vm);
1479 }
1480
1481 void JSObject::convertFromCopyOnWrite(VM& vm)
1482 {
1483     ASSERT(isCopyOnWrite(indexingMode()));
1484     ASSERT(structure(vm)->indexingMode() == indexingMode());
1485
1486     const bool hasIndexingHeader = true;
1487     Butterfly* oldButterfly = butterfly();
1488     size_t propertyCapacity = 0;
1489     unsigned newVectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, std::min(oldButterfly->vectorLength() * 2, MAX_STORAGE_VECTOR_LENGTH));
1490     Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, hasIndexingHeader, newVectorLength * sizeof(JSValue));
1491
1492     memcpy(newButterfly->propertyStorage(), oldButterfly->propertyStorage(), oldButterfly->vectorLength() * sizeof(JSValue) + sizeof(IndexingHeader));
1493
1494     WTF::storeStoreFence();
1495     NonPropertyTransition transition = ([&] () {
1496         switch (indexingType()) {
1497         case ArrayWithInt32:
1498             return NonPropertyTransition::AllocateInt32;
1499         case ArrayWithDouble:
1500             return NonPropertyTransition::AllocateDouble;
1501         case ArrayWithContiguous:
1502             return NonPropertyTransition::AllocateContiguous;
1503         default:
1504             RELEASE_ASSERT_NOT_REACHED();
1505             return NonPropertyTransition::AllocateContiguous;
1506         }
1507     })();
1508     StructureID oldStructureID = structureID();
1509     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
1510     nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1511     setStructure(vm, newStructure);
1512 }
1513
1514 void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
1515 {
1516     ASSERT(index < m_butterfly->publicLength());
1517     ASSERT(index < m_butterfly->vectorLength());
1518     convertUndecidedForValue(vm, value);
1519     setIndexQuickly(vm, index, value);
1520 }
1521
1522 void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1523 {
1524     ASSERT(!value.isInt32());
1525     convertInt32ForValue(vm, value);
1526     setIndexQuickly(vm, index, value);
1527 }
1528
1529 void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1530 {
1531     ASSERT(!value.isNumber() || value.asNumber() != value.asNumber());
1532     convertDoubleToContiguous(vm);
1533     setIndexQuickly(vm, index, value);
1534 }
1535
1536 ContiguousJSValues JSObject::tryMakeWritableInt32Slow(VM& vm)
1537 {
1538     ASSERT(inherits(vm, info()));
1539
1540     if (isCopyOnWrite(indexingMode())) {
1541         if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, Int32Shape) == Int32Shape) {
1542             ASSERT(hasInt32(indexingMode()));
1543             convertFromCopyOnWrite(vm);
1544             return butterfly()->contiguousInt32();
1545         }
1546         return ContiguousJSValues();
1547     }
1548
1549     if (structure(vm)->hijacksIndexingHeader())
1550         return ContiguousJSValues();
1551     
1552     switch (indexingType()) {
1553     case ALL_BLANK_INDEXING_TYPES:
1554         if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1555             return ContiguousJSValues();
1556         return createInitialInt32(vm, 0);
1557         
1558     case ALL_UNDECIDED_INDEXING_TYPES:
1559         return convertUndecidedToInt32(vm);
1560         
1561     case ALL_DOUBLE_INDEXING_TYPES:
1562     case ALL_CONTIGUOUS_INDEXING_TYPES:
1563     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1564         return ContiguousJSValues();
1565         
1566     default:
1567         CRASH();
1568         return ContiguousJSValues();
1569     }
1570 }
1571
1572 ContiguousDoubles JSObject::tryMakeWritableDoubleSlow(VM& vm)
1573 {
1574     ASSERT(inherits(vm, info()));
1575
1576     if (isCopyOnWrite(indexingMode())) {
1577         if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, DoubleShape) == DoubleShape) {
1578             convertFromCopyOnWrite(vm);
1579             if (hasDouble(indexingMode()))
1580                 return butterfly()->contiguousDouble();
1581             ASSERT(hasInt32(indexingMode()));
1582         } else
1583             return ContiguousDoubles();
1584     }
1585
1586     if (structure(vm)->hijacksIndexingHeader())
1587         return ContiguousDoubles();
1588     
1589     switch (indexingType()) {
1590     case ALL_BLANK_INDEXING_TYPES:
1591         if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1592             return ContiguousDoubles();
1593         return createInitialDouble(vm, 0);
1594         
1595     case ALL_UNDECIDED_INDEXING_TYPES:
1596         return convertUndecidedToDouble(vm);
1597         
1598     case ALL_INT32_INDEXING_TYPES:
1599         return convertInt32ToDouble(vm);
1600         
1601     case ALL_CONTIGUOUS_INDEXING_TYPES:
1602     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1603         return ContiguousDoubles();
1604         
1605     default:
1606         CRASH();
1607         return ContiguousDoubles();
1608     }
1609 }
1610
1611 ContiguousJSValues JSObject::tryMakeWritableContiguousSlow(VM& vm)
1612 {
1613     ASSERT(inherits(vm, info()));
1614
1615     if (isCopyOnWrite(indexingMode())) {
1616         if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, ContiguousShape) == ContiguousShape) {
1617             convertFromCopyOnWrite(vm);
1618             if (hasContiguous(indexingMode()))
1619                 return butterfly()->contiguous();
1620             ASSERT(hasInt32(indexingMode()) || hasDouble(indexingMode()));
1621         } else
1622             return ContiguousJSValues();
1623     }
1624
1625     if (structure(vm)->hijacksIndexingHeader())
1626         return ContiguousJSValues();
1627     
1628     switch (indexingType()) {
1629     case ALL_BLANK_INDEXING_TYPES:
1630         if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1631             return ContiguousJSValues();
1632         return createInitialContiguous(vm, 0);
1633         
1634     case ALL_UNDECIDED_INDEXING_TYPES:
1635         return convertUndecidedToContiguous(vm);
1636         
1637     case ALL_INT32_INDEXING_TYPES:
1638         return convertInt32ToContiguous(vm);
1639         
1640     case ALL_DOUBLE_INDEXING_TYPES:
1641         return convertDoubleToContiguous(vm);
1642         
1643     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1644         return ContiguousJSValues();
1645         
1646     default:
1647         CRASH();
1648         return ContiguousJSValues();
1649     }
1650 }
1651
1652 ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
1653 {
1654     ASSERT(inherits(vm, info()));
1655
1656     if (structure(vm)->hijacksIndexingHeader())
1657         return nullptr;
1658
1659     ensureWritable(vm);
1660
1661     switch (indexingType()) {
1662     case ALL_BLANK_INDEXING_TYPES:
1663         if (UNLIKELY(indexingShouldBeSparse(vm)))
1664             return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
1665         return createInitialArrayStorage(vm);
1666         
1667     case ALL_UNDECIDED_INDEXING_TYPES:
1668         ASSERT(!indexingShouldBeSparse(vm));
1669         ASSERT(!needsSlowPutIndexing(vm));
1670         return convertUndecidedToArrayStorage(vm);
1671         
1672     case ALL_INT32_INDEXING_TYPES:
1673         ASSERT(!indexingShouldBeSparse(vm));
1674         ASSERT(!needsSlowPutIndexing(vm));
1675         return convertInt32ToArrayStorage(vm);
1676         
1677     case ALL_DOUBLE_INDEXING_TYPES:
1678         ASSERT(!indexingShouldBeSparse(vm));
1679         ASSERT(!needsSlowPutIndexing(vm));
1680         return convertDoubleToArrayStorage(vm);
1681         
1682     case ALL_CONTIGUOUS_INDEXING_TYPES:
1683         ASSERT(!indexingShouldBeSparse(vm));
1684         ASSERT(!needsSlowPutIndexing(vm));
1685         return convertContiguousToArrayStorage(vm);
1686         
1687     default:
1688         RELEASE_ASSERT_NOT_REACHED();
1689         return 0;
1690     }
1691 }
1692
1693 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
1694 {
1695     ensureWritable(vm);
1696
1697     switch (indexingType()) {
1698     case ALL_BLANK_INDEXING_TYPES: {
1699         createArrayStorage(vm, 0, 0);
1700         SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1701         map->setSparseMode();
1702         return arrayStorage();
1703     }
1704         
1705     case ALL_UNDECIDED_INDEXING_TYPES:
1706         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm));
1707         
1708     case ALL_INT32_INDEXING_TYPES:
1709         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm));
1710         
1711     case ALL_DOUBLE_INDEXING_TYPES:
1712         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm));
1713         
1714     case ALL_CONTIGUOUS_INDEXING_TYPES:
1715         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm));
1716         
1717     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1718         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
1719         
1720     default:
1721         CRASH();
1722         return 0;
1723     }
1724 }
1725
1726 void JSObject::switchToSlowPutArrayStorage(VM& vm)
1727 {
1728     ensureWritable(vm);
1729
1730     switch (indexingType()) {
1731     case ArrayClass:
1732         ensureArrayStorage(vm);
1733         RELEASE_ASSERT(hasAnyArrayStorage(indexingType()));
1734         if (hasSlowPutArrayStorage(indexingType()))
1735             return;
1736         switchToSlowPutArrayStorage(vm);
1737         break;
1738
1739     case ALL_UNDECIDED_INDEXING_TYPES:
1740         convertUndecidedToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1741         break;
1742         
1743     case ALL_INT32_INDEXING_TYPES:
1744         convertInt32ToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1745         break;
1746         
1747     case ALL_DOUBLE_INDEXING_TYPES:
1748         convertDoubleToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1749         break;
1750         
1751     case ALL_CONTIGUOUS_INDEXING_TYPES:
1752         convertContiguousToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1753         break;
1754         
1755     case NonArrayWithArrayStorage:
1756     case ArrayWithArrayStorage: {
1757         Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::SwitchToSlowPutArrayStorage);
1758         setStructure(vm, newStructure);
1759         break;
1760     }
1761         
1762     default:
1763         CRASH();
1764         break;
1765     }
1766 }
1767
1768 void JSObject::setPrototypeDirect(VM& vm, JSValue prototype)
1769 {
1770     ASSERT(prototype);
1771     if (prototype.isObject())
1772         asObject(prototype)->didBecomePrototype();
1773     
1774     if (structure(vm)->hasMonoProto()) {
1775         DeferredStructureTransitionWatchpointFire deferred(vm, structure(vm));
1776         Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype, deferred);
1777         setStructure(vm, newStructure);
1778     } else
1779         putDirect(vm, knownPolyProtoOffset, prototype);
1780
1781     if (!anyObjectInChainMayInterceptIndexedAccesses(vm))
1782         return;
1783     
1784     if (mayBePrototype()) {
1785         structure(vm)->globalObject()->haveABadTime(vm);
1786         return;
1787     }
1788     
1789     if (!hasIndexedProperties(indexingType()))
1790         return;
1791     
1792     if (shouldUseSlowPut(indexingType()))
1793         return;
1794     
1795     switchToSlowPutArrayStorage(vm);
1796 }
1797
1798 bool JSObject::setPrototypeWithCycleCheck(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1799 {
1800     auto scope = DECLARE_THROW_SCOPE(vm);
1801
1802     if (this->structure(vm)->isImmutablePrototypeExoticObject()) {
1803         // This implements https://tc39.github.io/ecma262/#sec-set-immutable-prototype.
1804         if (this->getPrototype(vm, exec) == prototype)
1805             return true;
1806
1807         return typeError(exec, scope, shouldThrowIfCantSet, "Cannot set prototype of immutable prototype object"_s);
1808     }
1809
1810     ASSERT(methodTable(vm)->toThis(this, exec, NotStrictMode) == this);
1811
1812     if (this->getPrototypeDirect(vm) == prototype)
1813         return true;
1814
1815     bool isExtensible = this->isExtensible(exec);
1816     RETURN_IF_EXCEPTION(scope, false);
1817
1818     if (!isExtensible)
1819         return typeError(exec, scope, shouldThrowIfCantSet, ReadonlyPropertyWriteError);
1820
1821     JSValue nextPrototype = prototype;
1822     while (nextPrototype && nextPrototype.isObject()) {
1823         if (nextPrototype == this)
1824             return typeError(exec, scope, shouldThrowIfCantSet, "cyclic __proto__ value"_s);
1825         // FIXME: The specification currently says we should check if the [[GetPrototypeOf]] internal method of nextPrototype
1826         // is not the ordinary object internal method. However, we currently restrict this to Proxy objects as it would allow
1827         // for cycles with certain HTML objects (WindowProxy, Location) otherwise.
1828         // https://bugs.webkit.org/show_bug.cgi?id=161534
1829         if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType))
1830             break; // We're done. Set the prototype.
1831         nextPrototype = asObject(nextPrototype)->getPrototypeDirect(vm);
1832     }
1833     setPrototypeDirect(vm, prototype);
1834     return true;
1835 }
1836
1837 bool JSObject::setPrototype(JSObject* object, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1838 {
1839     return object->setPrototypeWithCycleCheck(exec->vm(), exec, prototype, shouldThrowIfCantSet);
1840 }
1841
1842 JSValue JSObject::getPrototype(JSObject* object, ExecState* exec)
1843 {
1844     return object->getPrototypeDirect(exec->vm());
1845 }
1846
1847 bool JSObject::setPrototype(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1848 {
1849     return methodTable(vm)->setPrototype(this, exec, prototype, shouldThrowIfCantSet);
1850 }
1851
1852 bool JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter, unsigned attributes)
1853 {
1854     PropertyDescriptor descriptor;
1855     descriptor.setGetter(getter);
1856
1857     ASSERT(attributes & PropertyAttribute::Accessor);
1858     if (!(attributes & PropertyAttribute::ReadOnly))
1859         descriptor.setConfigurable(true);
1860     if (!(attributes & PropertyAttribute::DontEnum))
1861         descriptor.setEnumerable(true);
1862
1863     return defineOwnProperty(this, exec, propertyName, descriptor, true);
1864 }
1865
1866 bool JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter, unsigned attributes)
1867 {
1868     PropertyDescriptor descriptor;
1869     descriptor.setSetter(setter);
1870
1871     ASSERT(attributes & PropertyAttribute::Accessor);
1872     if (!(attributes & PropertyAttribute::ReadOnly))
1873         descriptor.setConfigurable(true);
1874     if (!(attributes & PropertyAttribute::DontEnum))
1875         descriptor.setEnumerable(true);
1876
1877     return defineOwnProperty(this, exec, propertyName, descriptor, true);
1878 }
1879
1880 bool JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1881 {
1882     ASSERT(attributes & PropertyAttribute::Accessor);
1883
1884     if (Optional<uint32_t> index = parseIndex(propertyName))
1885         return putDirectIndex(exec, index.value(), accessor, attributes, PutDirectIndexLikePutDirect);
1886
1887     return putDirectNonIndexAccessor(exec->vm(), propertyName, accessor, attributes);
1888 }
1889
1890 // FIXME: Introduce a JSObject::putDirectCustomValue() method instead of using
1891 // JSObject::putDirectCustomAccessor() to put CustomValues.
1892 // https://bugs.webkit.org/show_bug.cgi?id=192576
1893 bool JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1894 {
1895     ASSERT(!parseIndex(propertyName));
1896     ASSERT(value.isCustomGetterSetter());
1897     if (!(attributes & PropertyAttribute::CustomAccessor))
1898         attributes |= PropertyAttribute::CustomValue;
1899
1900     PutPropertySlot slot(this);
1901     bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1902
1903     ASSERT(slot.type() == PutPropertySlot::NewProperty);
1904
1905     Structure* structure = this->structure(vm);
1906     if (attributes & PropertyAttribute::ReadOnly)
1907         structure->setContainsReadOnlyProperties();
1908     structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1909     return result;
1910 }
1911
1912 bool JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1913 {
1914     ASSERT(attributes & PropertyAttribute::Accessor);
1915     PutPropertySlot slot(this);
1916     bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, accessor, attributes, slot);
1917
1918     Structure* structure = this->structure(vm);
1919     if (attributes & PropertyAttribute::ReadOnly)
1920         structure->setContainsReadOnlyProperties();
1921
1922     structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1923     return result;
1924 }
1925
1926 void JSObject::putDirectNonIndexAccessorWithoutTransition(VM& vm, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1927 {
1928     ASSERT(attributes & PropertyAttribute::Accessor);
1929     StructureID structureID = this->structureID();
1930     Structure* structure = vm.heap.structureIDTable().get(structureID);
1931     PropertyOffset offset = prepareToPutDirectWithoutTransition(vm, propertyName, attributes, structureID, structure);
1932     putDirect(vm, offset, accessor);
1933     if (attributes & PropertyAttribute::ReadOnly)
1934         structure->setContainsReadOnlyProperties();
1935
1936     structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1937 }
1938
1939 // HasProperty(O, P) from Section 7.3.10 of the spec.
1940 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasproperty
1941 bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
1942 {
1943     return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1944 }
1945
1946 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
1947 {
1948     return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1949 }
1950
1951 bool JSObject::hasPropertyGeneric(ExecState* exec, PropertyName propertyName, PropertySlot::InternalMethodType internalMethodType) const
1952 {
1953     PropertySlot slot(this, internalMethodType);
1954     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1955 }
1956
1957 bool JSObject::hasPropertyGeneric(ExecState* exec, unsigned propertyName, PropertySlot::InternalMethodType internalMethodType) const
1958 {
1959     PropertySlot slot(this, internalMethodType);
1960     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1961 }
1962
1963 // ECMA 8.6.2.5
1964 bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
1965 {
1966     JSObject* thisObject = jsCast<JSObject*>(cell);
1967     VM& vm = exec->vm();
1968     
1969     if (Optional<uint32_t> index = parseIndex(propertyName))
1970         return thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, index.value());
1971
1972     unsigned attributes;
1973
1974     if (!thisObject->staticPropertiesReified(vm)) {
1975         if (auto entry = thisObject->findPropertyHashEntry(vm, propertyName)) {
1976             // If the static table contains a non-configurable (DontDelete) property then we can return early;
1977             // if there is a property in the storage array it too must be non-configurable (the language does
1978             // not allow repacement of a non-configurable property with a configurable one).
1979             if (entry->value->attributes() & PropertyAttribute::DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
1980                 ASSERT(!isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes)) || attributes & PropertyAttribute::DontDelete);
1981                 return false;
1982             }
1983             thisObject->reifyAllStaticProperties(exec);
1984         }
1985     }
1986
1987     Structure* structure = thisObject->structure(vm);
1988
1989     bool propertyIsPresent = isValidOffset(structure->get(vm, propertyName, attributes));
1990     if (propertyIsPresent) {
1991         if (attributes & PropertyAttribute::DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable)
1992             return false;
1993
1994         PropertyOffset offset;
1995         if (structure->isUncacheableDictionary())
1996             offset = structure->removePropertyWithoutTransition(vm, propertyName, [] (const ConcurrentJSLocker&, PropertyOffset) { });
1997         else
1998             thisObject->setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
1999
2000         if (offset != invalidOffset)
2001             thisObject->locationForOffset(offset)->clear();
2002     }
2003
2004     return true;
2005 }
2006
2007 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
2008 {
2009     VM& vm = exec->vm();
2010     JSObject* thisObject = jsCast<JSObject*>(cell);
2011     
2012     if (i > MAX_ARRAY_INDEX)
2013         return thisObject->methodTable(vm)->deleteProperty(thisObject, exec, Identifier::from(exec, i));
2014     
2015     switch (thisObject->indexingMode()) {
2016     case ALL_BLANK_INDEXING_TYPES:
2017     case ALL_UNDECIDED_INDEXING_TYPES:
2018         return true;
2019
2020     case CopyOnWriteArrayWithInt32:
2021     case CopyOnWriteArrayWithContiguous: {
2022         Butterfly* butterfly = thisObject->butterfly();
2023         if (i >= butterfly->vectorLength())
2024             return true;
2025         thisObject->convertFromCopyOnWrite(vm);
2026         FALLTHROUGH;
2027     }
2028
2029     case ALL_WRITABLE_INT32_INDEXING_TYPES:
2030     case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES: {
2031         Butterfly* butterfly = thisObject->butterfly();
2032         if (i >= butterfly->vectorLength())
2033             return true;
2034         butterfly->contiguous().at(thisObject, i).clear();
2035         return true;
2036     }
2037
2038     case CopyOnWriteArrayWithDouble: {
2039         Butterfly* butterfly = thisObject->butterfly();
2040         if (i >= butterfly->vectorLength())
2041             return true;
2042         thisObject->convertFromCopyOnWrite(vm);
2043         FALLTHROUGH;
2044     }
2045
2046     case ALL_WRITABLE_DOUBLE_INDEXING_TYPES: {
2047         Butterfly* butterfly = thisObject->butterfly();
2048         if (i >= butterfly->vectorLength())
2049             return true;
2050         butterfly->contiguousDouble().at(thisObject, i) = PNaN;
2051         return true;
2052     }
2053         
2054     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
2055         ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
2056         
2057         if (i < storage->vectorLength()) {
2058             WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
2059             if (valueSlot) {
2060                 valueSlot.clear();
2061                 --storage->m_numValuesInVector;
2062             }
2063         } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
2064             SparseArrayValueMap::iterator it = map->find(i);
2065             if (it != map->notFound()) {
2066                 if (it->value.attributes() & PropertyAttribute::DontDelete)
2067                     return false;
2068                 map->remove(it);
2069             }
2070         }
2071         
2072         return true;
2073     }
2074         
2075     default:
2076         RELEASE_ASSERT_NOT_REACHED();
2077         return false;
2078     }
2079 }
2080
2081 enum class TypeHintMode { TakesHint, DoesNotTakeHint };
2082
2083 template<TypeHintMode mode = TypeHintMode::DoesNotTakeHint>
2084 static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSObject* object, PropertyName propertyName, PreferredPrimitiveType hint)
2085 {
2086     VM& vm = exec->vm();
2087     auto scope = DECLARE_THROW_SCOPE(vm);
2088
2089     PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
2090     // FIXME: Remove this when we have fixed: rdar://problem/33451840
2091     // https://bugs.webkit.org/show_bug.cgi?id=187109.
2092     constexpr bool debugNullStructure = mode == TypeHintMode::TakesHint;
2093     bool hasProperty = const_cast<JSObject*>(object)->getPropertySlot<debugNullStructure>(exec, propertyName, slot);
2094     RETURN_IF_EXCEPTION(scope, scope.exception());
2095     JSValue function = hasProperty ? slot.getValue(exec, propertyName) : jsUndefined();
2096     RETURN_IF_EXCEPTION(scope, scope.exception());
2097     if (function.isUndefinedOrNull() && mode == TypeHintMode::TakesHint)
2098         return JSValue();
2099     CallData callData;
2100     CallType callType = getCallData(vm, function, callData);
2101     if (callType == CallType::None) {
2102         if (mode == TypeHintMode::TakesHint)
2103             throwTypeError(exec, scope, "Symbol.toPrimitive is not a function, undefined, or null"_s);
2104         return scope.exception();
2105     }
2106
2107     MarkedArgumentBuffer callArgs;
2108     if (mode == TypeHintMode::TakesHint) {
2109         JSString* hintString = nullptr;
2110         switch (hint) {
2111         case NoPreference:
2112             hintString = vm.smallStrings.defaultString();
2113             break;
2114         case PreferNumber:
2115             hintString = vm.smallStrings.numberString();
2116             break;
2117         case PreferString:
2118             hintString = vm.smallStrings.stringString();
2119             break;
2120         }
2121         callArgs.append(hintString);
2122     }
2123     ASSERT(!callArgs.hasOverflowed());
2124
2125     JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), callArgs);
2126     RETURN_IF_EXCEPTION(scope, scope.exception());
2127     ASSERT(!result.isGetterSetter());
2128     if (result.isObject())
2129         return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, scope, "Symbol.toPrimitive returned an object"_s);
2130     return result;
2131 }
2132
2133 // ECMA 7.1.1
2134 JSValue JSObject::ordinaryToPrimitive(ExecState* exec, PreferredPrimitiveType hint) const
2135 {
2136     VM& vm = exec->vm();
2137     auto scope = DECLARE_THROW_SCOPE(vm);
2138
2139     // Make sure that whatever default value methods there are on object's prototype chain are
2140     // being watched.
2141     for (const JSObject* object = this; object; object = object->structure(vm)->storedPrototypeObject(object))
2142         object->structure(vm)->startWatchingInternalPropertiesIfNecessary(vm);
2143
2144     JSValue value;
2145     if (hint == PreferString) {
2146         value = callToPrimitiveFunction(exec, this, vm.propertyNames->toString, hint);
2147         EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2148         if (value)
2149             return value;
2150         value = callToPrimitiveFunction(exec, this, vm.propertyNames->valueOf, hint);
2151         EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2152         if (value)
2153             return value;
2154     } else {
2155         value = callToPrimitiveFunction(exec, this, vm.propertyNames->valueOf, hint);
2156         EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2157         if (value)
2158             return value;
2159         value = callToPrimitiveFunction(exec, this, vm.propertyNames->toString, hint);
2160         EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2161         if (value)
2162             return value;
2163     }
2164
2165     scope.assertNoException();
2166
2167     return throwTypeError(exec, scope, "No default value"_s);
2168 }
2169
2170 JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
2171 {
2172     return object->ordinaryToPrimitive(exec, hint);
2173 }
2174
2175 JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
2176 {
2177     VM& vm = exec->vm();
2178     auto scope = DECLARE_THROW_SCOPE(vm);
2179
2180     JSValue value = callToPrimitiveFunction<TypeHintMode::TakesHint>(exec, this, vm.propertyNames->toPrimitiveSymbol, preferredType);
2181     RETURN_IF_EXCEPTION(scope, { });
2182     if (value)
2183         return value;
2184
2185     RELEASE_AND_RETURN(scope, this->methodTable(vm)->defaultValue(this, exec, preferredType));
2186 }
2187
2188 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
2189 {
2190     VM& vm = exec->vm();
2191     auto scope = DECLARE_THROW_SCOPE(vm);
2192
2193     result = toPrimitive(exec, PreferNumber);
2194     RETURN_IF_EXCEPTION(scope, false);
2195     scope.release();
2196     number = result.toNumber(exec);
2197     return !result.isString();
2198 }
2199
2200 bool JSObject::getOwnStaticPropertySlot(VM& vm, PropertyName propertyName, PropertySlot& slot)
2201 {
2202     for (auto* info = classInfo(vm); info; info = info->parentClass) {
2203         if (auto* table = info->staticPropHashTable) {
2204             if (getStaticPropertySlotFromTable(vm, table->classForThis, *table, this, propertyName, slot))
2205                 return true;
2206         }
2207     }
2208     return false;
2209 }
2210
2211 auto JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const -> Optional<PropertyHashEntry>
2212 {
2213     for (const ClassInfo* info = classInfo(vm); info; info = info->parentClass) {
2214         if (const HashTable* propHashTable = info->staticPropHashTable) {
2215             if (const HashTableValue* entry = propHashTable->entry(propertyName))
2216                 return PropertyHashEntry { propHashTable, entry };
2217         }
2218     }
2219     return WTF::nullopt;
2220 }
2221
2222 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue)
2223 {
2224     VM& vm = exec->vm();
2225     auto scope = DECLARE_THROW_SCOPE(vm);
2226
2227     if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) {
2228         CallData callData;
2229         CallType callType = JSC::getCallData(vm, hasInstanceValue, callData);
2230         if (callType == CallType::None) {
2231             throwException(exec, scope, createInvalidInstanceofParameterErrorHasInstanceValueNotFunction(exec, this));
2232             return false;
2233         }
2234
2235         MarkedArgumentBuffer args;
2236         args.append(value);
2237         ASSERT(!args.hasOverflowed());
2238         JSValue result = call(exec, hasInstanceValue, callType, callData, this, args);
2239         RETURN_IF_EXCEPTION(scope, false);
2240         return result.toBoolean(exec);
2241     }
2242
2243     TypeInfo info = structure(vm)->typeInfo();
2244     if (info.implementsDefaultHasInstance()) {
2245         JSValue prototype = get(exec, vm.propertyNames->prototype);
2246         RETURN_IF_EXCEPTION(scope, false);
2247         RELEASE_AND_RETURN(scope, defaultHasInstance(exec, value, prototype));
2248     }
2249     if (info.implementsHasInstance()) {
2250         if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
2251             throwStackOverflowError(exec, scope);
2252             return false;
2253         }
2254         RELEASE_AND_RETURN(scope, methodTable(vm)->customHasInstance(this, exec, value));
2255     }
2256
2257     throwException(exec, scope, createInvalidInstanceofParameterErrorNotFunction(exec, this));
2258     return false;
2259 }
2260
2261 bool JSObject::hasInstance(ExecState* exec, JSValue value)
2262 {
2263     VM& vm = exec->vm();
2264     auto scope = DECLARE_THROW_SCOPE(vm);
2265     JSValue hasInstanceValue = get(exec, vm.propertyNames->hasInstanceSymbol);
2266     RETURN_IF_EXCEPTION(scope, false);
2267
2268     RELEASE_AND_RETURN(scope, hasInstance(exec, value, hasInstanceValue));
2269 }
2270
2271 bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
2272 {
2273     VM& vm = exec->vm();
2274     auto scope = DECLARE_THROW_SCOPE(vm);
2275
2276     if (!value.isObject())
2277         return false;
2278
2279     if (!proto.isObject()) {
2280         throwTypeError(exec, scope, "instanceof called on an object with an invalid prototype property."_s);
2281         return false;
2282     }
2283
2284     JSObject* object = asObject(value);
2285     while (true) {
2286         JSValue objectValue = object->getPrototype(vm, exec);
2287         RETURN_IF_EXCEPTION(scope, false);
2288         if (!objectValue.isObject())
2289             return false;
2290         object = asObject(objectValue);
2291         if (proto == object)
2292             return true;
2293     }
2294     ASSERT_NOT_REACHED();
2295 }
2296
2297 EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState* exec)
2298 {
2299     JSValue value = exec->uncheckedArgument(0);
2300     JSValue proto = exec->uncheckedArgument(1);
2301
2302     return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
2303 }
2304
2305 void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2306 {
2307     VM& vm = exec->vm();
2308     auto scope = DECLARE_THROW_SCOPE(vm);
2309     object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, mode);
2310     RETURN_IF_EXCEPTION(scope, void());
2311
2312     JSValue nextProto = object->getPrototype(vm, exec);
2313     RETURN_IF_EXCEPTION(scope, void());
2314     if (nextProto.isNull())
2315         return;
2316
2317     JSObject* prototype = asObject(nextProto);
2318     while(1) {
2319         if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
2320             scope.release();
2321             prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
2322             return;
2323         }
2324         prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
2325         RETURN_IF_EXCEPTION(scope, void());
2326         nextProto = prototype->getPrototype(vm, exec);
2327         RETURN_IF_EXCEPTION(scope, void());
2328         if (nextProto.isNull())
2329             break;
2330         prototype = asObject(nextProto);
2331     }
2332 }
2333
2334 void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2335 {
2336     VM& vm = exec->vm();
2337     if (!mode.includeJSObjectProperties()) {
2338         // We still have to get non-indexed properties from any subclasses of JSObject that have them.
2339         object->methodTable(vm)->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
2340         return;
2341     }
2342
2343     if (propertyNames.includeStringProperties()) {
2344         // Add numeric properties first. That appears to be the accepted convention.
2345         // FIXME: Filling PropertyNameArray with an identifier for every integer
2346         // is incredibly inefficient for large arrays. We need a different approach,
2347         // which almost certainly means a different structure for PropertyNameArray.
2348         switch (object->indexingType()) {
2349         case ALL_BLANK_INDEXING_TYPES:
2350         case ALL_UNDECIDED_INDEXING_TYPES:
2351             break;
2352             
2353         case ALL_INT32_INDEXING_TYPES:
2354         case ALL_CONTIGUOUS_INDEXING_TYPES: {
2355             Butterfly* butterfly = object->butterfly();
2356             unsigned usedLength = butterfly->publicLength();
2357             for (unsigned i = 0; i < usedLength; ++i) {
2358                 if (!butterfly->contiguous().at(object, i))
2359                     continue;
2360                 propertyNames.add(i);
2361             }
2362             break;
2363         }
2364             
2365         case ALL_DOUBLE_INDEXING_TYPES: {
2366             Butterfly* butterfly = object->butterfly();
2367             unsigned usedLength = butterfly->publicLength();
2368             for (unsigned i = 0; i < usedLength; ++i) {
2369                 double value = butterfly->contiguousDouble().at(object, i);
2370                 if (value != value)
2371                     continue;
2372                 propertyNames.add(i);
2373             }
2374             break;
2375         }
2376             
2377         case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
2378             ArrayStorage* storage = object->m_butterfly->arrayStorage();
2379             
2380             unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
2381             for (unsigned i = 0; i < usedVectorLength; ++i) {
2382                 if (storage->m_vector[i])
2383                     propertyNames.add(i);
2384             }
2385             
2386             if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
2387                 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
2388                 keys.reserveInitialCapacity(map->size());
2389                 
2390                 SparseArrayValueMap::const_iterator end = map->end();
2391                 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
2392                     if (mode.includeDontEnumProperties() || !(it->value.attributes() & PropertyAttribute::DontEnum))
2393                         keys.uncheckedAppend(static_cast<unsigned>(it->key));
2394                 }
2395                 
2396                 std::sort(keys.begin(), keys.end());
2397                 for (unsigned i = 0; i < keys.size(); ++i)
2398                     propertyNames.add(keys[i]);
2399             }
2400             break;
2401         }
2402             
2403         default:
2404             RELEASE_ASSERT_NOT_REACHED();
2405         }
2406     }
2407
2408     object->methodTable(vm)->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
2409 }
2410
2411 void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2412 {
2413     VM& vm = exec->vm();
2414     if (!object->staticPropertiesReified(vm))
2415         getClassPropertyNames(exec, object->classInfo(vm), propertyNames, mode);
2416
2417     if (!mode.includeJSObjectProperties())
2418         return;
2419     
2420     object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
2421 }
2422
2423 double JSObject::toNumber(ExecState* exec) const
2424 {
2425     VM& vm = exec->vm();
2426     auto scope = DECLARE_THROW_SCOPE(vm);
2427     JSValue primitive = toPrimitive(exec, PreferNumber);
2428     RETURN_IF_EXCEPTION(scope, 0.0); // should be picked up soon in Nodes.cpp
2429     RELEASE_AND_RETURN(scope, primitive.toNumber(exec));
2430 }
2431
2432 JSString* JSObject::toString(ExecState* exec) const
2433 {
2434     VM& vm = exec->vm();
2435     auto scope = DECLARE_THROW_SCOPE(vm);
2436     JSValue primitive = toPrimitive(exec, PreferString);
2437     RETURN_IF_EXCEPTION(scope, jsEmptyString(exec));
2438     RELEASE_AND_RETURN(scope, primitive.toString(exec));
2439 }
2440
2441 JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode)
2442 {
2443     return jsCast<JSObject*>(cell);
2444 }
2445
2446 void JSObject::seal(VM& vm)
2447 {
2448     if (isSealed(vm))
2449         return;
2450     enterDictionaryIndexingMode(vm);
2451     setStructure(vm, Structure::sealTransition(vm, structure(vm)));
2452 }
2453
2454 void JSObject::freeze(VM& vm)
2455 {
2456     if (isFrozen(vm))
2457         return;
2458     enterDictionaryIndexingMode(vm);
2459     setStructure(vm, Structure::freezeTransition(vm, structure(vm)));
2460 }
2461
2462 bool JSObject::preventExtensions(JSObject* object, ExecState* exec)
2463 {
2464     VM& vm = exec->vm();
2465     if (!object->isStructureExtensible(vm)) {
2466         // We've already set the internal [[PreventExtensions]] field to false.
2467         // We don't call the methodTable isExtensible here because it's not defined
2468         // that way in the specification. We are just doing an optimization here.
2469         return true;
2470     }
2471
2472     object->enterDictionaryIndexingMode(vm);
2473     object->setStructure(vm, Structure::preventExtensionsTransition(vm, object->structure(vm)));
2474     return true;
2475 }
2476
2477 bool JSObject::isExtensible(JSObject* obj, ExecState* exec)
2478 {
2479     return obj->isStructureExtensible(exec->vm());
2480 }
2481
2482 bool JSObject::isExtensible(ExecState* exec)
2483
2484     VM& vm = exec->vm();
2485     return methodTable(vm)->isExtensible(this, exec);
2486 }
2487
2488 void JSObject::reifyAllStaticProperties(ExecState* exec)
2489 {
2490     VM& vm = exec->vm();
2491     ASSERT(!staticPropertiesReified(vm));
2492
2493     // If this object's ClassInfo has no static properties, then nothing to reify!
2494     // We can safely set the flag to avoid the expensive check again in the future.
2495     if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags())) {
2496         structure(vm)->setStaticPropertiesReified(true);
2497         return;
2498     }
2499
2500     if (!structure(vm)->isDictionary())
2501         setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure(vm)));
2502
2503     for (const ClassInfo* info = classInfo(vm); info; info = info->parentClass) {
2504         const HashTable* hashTable = info->staticPropHashTable;
2505         if (!hashTable)
2506             continue;
2507
2508         for (auto& value : *hashTable) {
2509             unsigned attributes;
2510             auto key = Identifier::fromString(&vm, value.m_key);
2511             PropertyOffset offset = getDirectOffset(vm, key, attributes);
2512             if (!isValidOffset(offset))
2513                 reifyStaticProperty(vm, hashTable->classForThis, key, value, *this);
2514         }
2515     }
2516
2517     structure(vm)->setStaticPropertiesReified(true);
2518 }
2519
2520 NEVER_INLINE void JSObject::fillGetterPropertySlot(VM& vm, PropertySlot& slot, JSCell* getterSetter, unsigned attributes, PropertyOffset offset)
2521 {
2522     if (structure(vm)->isUncacheableDictionary()) {
2523         slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
2524         return;
2525     }
2526
2527     // This access is cacheable because Structure requires an attributeChangedTransition
2528     // if this property stops being an accessor.
2529     slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
2530 }
2531
2532 static bool putIndexedDescriptor(ExecState* exec, SparseArrayValueMap* map, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
2533 {
2534     VM& vm = exec->vm();
2535
2536     if (descriptor.isDataDescriptor()) {
2537         unsigned attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::Accessor;
2538         if (descriptor.value())
2539             entryInMap->forceSet(vm, map, descriptor.value(), attributes);
2540         else if (oldDescriptor.isAccessorDescriptor())
2541             entryInMap->forceSet(vm, map, jsUndefined(), attributes);
2542         else
2543             entryInMap->forceSet(attributes);
2544         return true;
2545     }
2546
2547     if (descriptor.isAccessorDescriptor()) {
2548         JSObject* getter = nullptr;
2549         if (descriptor.getterPresent())
2550             getter = descriptor.getterObject();
2551         else if (oldDescriptor.isAccessorDescriptor())
2552             getter = oldDescriptor.getterObject();
2553         JSObject* setter = nullptr;
2554         if (descriptor.setterPresent())
2555             setter = descriptor.setterObject();
2556         else if (oldDescriptor.isAccessorDescriptor())
2557             setter = oldDescriptor.setterObject();
2558
2559         GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
2560         entryInMap->forceSet(vm, map, accessor, descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::ReadOnly);
2561         return true;
2562     }
2563
2564     ASSERT(descriptor.isGenericDescriptor());
2565     entryInMap->forceSet(descriptor.attributesOverridingCurrent(oldDescriptor));
2566     return true;
2567 }
2568
2569 ALWAYS_INLINE static bool canDoFastPutDirectIndex(VM& vm, JSObject* object)
2570 {
2571     return (isJSArray(object) && !isCopyOnWrite(object->indexingMode()))
2572         || jsDynamicCast<JSFinalObject*>(vm, object)
2573         || TypeInfo::isArgumentsType(object->type());
2574 }
2575
2576 // Defined in ES5.1 8.12.9
2577 bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException)
2578 {
2579     VM& vm = exec->vm();
2580     auto scope = DECLARE_THROW_SCOPE(vm);
2581
2582     ASSERT(index <= MAX_ARRAY_INDEX);
2583
2584     ensureWritable(vm);
2585
2586     if (!inSparseIndexingMode()) {
2587         // Fast case: we're putting a regular property to a regular array
2588         // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
2589         // however if the property currently exists missing attributes will override from their current 'true'
2590         // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
2591         if (!descriptor.attributes() && descriptor.value() && canDoFastPutDirectIndex(vm, this)) {
2592             ASSERT(!descriptor.isAccessorDescriptor());
2593             RELEASE_AND_RETURN(scope, putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow));
2594         }
2595         
2596         ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
2597     }
2598
2599     if (descriptor.attributes() & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))
2600         notifyPresenceOfIndexedAccessors(vm);
2601
2602     SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
2603     RELEASE_ASSERT(map);
2604     
2605     // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
2606     SparseArrayValueMap::AddResult result = map->add(this, index);
2607     SparseArrayEntry* entryInMap = &result.iterator->value;
2608
2609     // 2. Let extensible be the value of the [[Extensible]] internal property of O.
2610     // 3. If current is undefined and extensible is false, then Reject.
2611     // 4. If current is undefined and extensible is true, then
2612     if (result.isNewEntry) {
2613         if (!isStructureExtensible(vm)) {
2614             map->remove(result.iterator);
2615             return typeError(exec, scope, throwException, NonExtensibleObjectPropertyDefineError);
2616         }
2617
2618         // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
2619         // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
2620         // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
2621         // created property is set to its default value.
2622         // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
2623         // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
2624         // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
2625         // is set to its default value.
2626         // 4.c. Return true.
2627
2628         PropertyDescriptor defaults(jsUndefined(), PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
2629         putIndexedDescriptor(exec, map, entryInMap, descriptor, defaults);
2630         Butterfly* butterfly = m_butterfly.get();
2631         if (index >= butterfly->arrayStorage()->length())
2632             butterfly->arrayStorage()->setLength(index + 1);
2633         return true;
2634     }
2635
2636     // 5. Return true, if every field in Desc is absent.
2637     // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12).
2638     PropertyDescriptor current;
2639     entryInMap->get(current);
2640     bool isEmptyOrEqual = descriptor.isEmpty() || descriptor.equalTo(exec, current);
2641     RETURN_IF_EXCEPTION(scope, false);
2642     if (isEmptyOrEqual)
2643         return true;
2644
2645     // 7. If the [[Configurable]] field of current is false then
2646     if (!current.configurable()) {
2647         // 7.a. Reject, if the [[Configurable]] field of Desc is true.
2648         if (descriptor.configurablePresent() && descriptor.configurable())
2649             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
2650         // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
2651         if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
2652             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
2653     }
2654
2655     // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
2656     if (!descriptor.isGenericDescriptor()) {
2657         // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
2658         if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
2659             // 9.a. Reject, if the [[Configurable]] field of current is false.
2660             if (!current.configurable())
2661                 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
2662             // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
2663             // data property to an accessor property. Preserve the existing values of the converted property's
2664             // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
2665             // their default values.
2666             // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
2667             // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
2668             // attributes and set the rest of the property's attributes to their default values.
2669         } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
2670             // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
2671             // 10.a. If the [[Configurable]] field of current is false, then
2672             if (!current.configurable() && !current.writable()) {
2673                 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
2674                 if (descriptor.writable())
2675                     return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
2676                 // 10.a.ii. If the [[Writable]] field of current is false, then
2677                 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
2678                 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
2679                     return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
2680             }
2681             // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
2682         } else {
2683             ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
2684             // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
2685             if (!current.configurable()) {
2686                 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
2687                 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
2688                     return typeError(exec, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
2689                 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
2690                 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
2691                     return typeError(exec, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
2692             }
2693         }
2694     }
2695
2696     // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field.
2697     putIndexedDescriptor(exec, map, entryInMap, descriptor, current);
2698     // 13. Return true.
2699     return true;
2700 }
2701
2702 SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm)
2703 {
2704     SparseArrayValueMap* result = SparseArrayValueMap::create(vm);
2705     arrayStorage()->m_sparseMap.set(vm, this, result);
2706     return result;
2707 }
2708
2709 void JSObject::deallocateSparseIndexMap()
2710 {
2711     if (ArrayStorage* arrayStorage = arrayStorageOrNull())
2712         arrayStorage->m_sparseMap.clear();
2713 }
2714
2715 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2716 {
2717     VM& vm = exec->vm();
2718     for (JSObject* current = this; ;) {
2719         // This has the same behavior with respect to prototypes as JSObject::put(). It only
2720         // allows a prototype to intercept a put if (a) the prototype declares the property
2721         // we're after rather than intercepting it via an override of JSObject::put(), and
2722         // (b) that property is declared as ReadOnly or Accessor.
2723         
2724         ArrayStorage* storage = current->arrayStorageOrNull();
2725         if (storage && storage->m_sparseMap) {
2726             SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
2727             if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes() & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly))) {
2728                 putResult = iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
2729                 return true;
2730             }
2731         }
2732
2733         if (current->type() == ProxyObjectType) {
2734             ProxyObject* proxy = jsCast<ProxyObject*>(current);
2735             putResult = proxy->putByIndexCommon(exec, thisValue, i, value, shouldThrow);
2736             return true;
2737         }
2738         
2739         JSValue prototypeValue = current->getPrototypeDirect(vm);
2740         if (prototypeValue.isNull())
2741             return false;
2742         
2743         current = asObject(prototypeValue);
2744     }
2745 }
2746
2747 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2748 {
2749     JSValue prototypeValue = getPrototypeDirect(exec->vm());
2750     if (prototypeValue.isNull())
2751         return false;
2752     
2753     return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow, putResult);
2754 }
2755
2756 template<IndexingType indexingShape>
2757 bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value)
2758 {
2759     VM& vm = exec->vm();
2760     auto scope = DECLARE_THROW_SCOPE(vm);
2761
2762     RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
2763     ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
2764     ASSERT(!indexingShouldBeSparse(vm));
2765
2766     Butterfly* butterfly = m_butterfly.get();
2767     
2768     // For us to get here, the index is either greater than the public length, or greater than
2769     // or equal to the vector length.
2770     ASSERT(i >= butterfly->vectorLength());
2771     
2772     if (i > MAX_STORAGE_VECTOR_INDEX
2773         || (i >= MIN_SPARSE_ARRAY_INDEX && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly)))
2774         || indexIsSufficientlyBeyondLengthForSparseMap(i, butterfly->vectorLength())) {
2775         ASSERT(i <= MAX_ARRAY_INDEX);
2776         ensureArrayStorageSlow(vm);
2777         SparseArrayValueMap* map = allocateSparseIndexMap(vm);
2778         bool result = map->putEntry(exec, this, i, value, false);
2779         RETURN_IF_EXCEPTION(scope, false);
2780         ASSERT(i >= arrayStorage()->length());
2781         arrayStorage()->setLength(i + 1);
2782         return result;
2783     }
2784
2785     if (!ensureLength(vm, i + 1)) {
2786         throwOutOfMemoryError(exec, scope);
2787         return false;
2788     }
2789     butterfly = m_butterfly.get();
2790
2791     RELEASE_ASSERT(i < butterfly->vectorLength());
2792     switch (indexingShape) {
2793     case Int32Shape:
2794         ASSERT(value.isInt32());
2795         butterfly->contiguous().at(this, i).setWithoutWriteBarrier(value);
2796         return true;
2797         
2798     case DoubleShape: {
2799         ASSERT(value.isNumber());
2800         double valueAsDouble = value.asNumber();
2801         ASSERT(valueAsDouble == valueAsDouble);
2802         butterfly->contiguousDouble().at(this, i) = valueAsDouble;
2803         return true;
2804     }
2805         
2806     case ContiguousShape:
2807         butterfly->contiguous().at(this, i).set(vm, this, value);
2808         return true;
2809         
2810     default:
2811         CRASH();
2812         return false;
2813     }
2814 }
2815
2816 // Explicit instantiations needed by JSArray.cpp.
2817 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(ExecState*, unsigned, JSValue);
2818 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(ExecState*, unsigned, JSValue);
2819 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(ExecState*, unsigned, JSValue);
2820
2821 bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
2822 {
2823     VM& vm = exec->vm();
2824     auto scope = DECLARE_THROW_SCOPE(vm);
2825
2826     ASSERT(!isCopyOnWrite(indexingMode()));
2827     // i should be a valid array index that is outside of the current vector.
2828     ASSERT(i <= MAX_ARRAY_INDEX);
2829     ASSERT(i >= storage->vectorLength());
2830     
2831     SparseArrayValueMap* map = storage->m_sparseMap.get();
2832     
2833     // First, handle cases where we don't currently have a sparse map.
2834     if (LIKELY(!map)) {
2835         // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
2836         ASSERT(isStructureExtensible(vm));
2837     
2838         // Update m_length if necessary.
2839         if (i >= storage->length())
2840             storage->setLength(i + 1);
2841
2842         // Check that it is sensible to still be using a vector, and then try to grow the vector.
2843         if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()) 
2844             && isDenseEnoughForVector(i, storage->m_numValuesInVector)
2845             && increaseVectorLength(vm, i + 1))) {
2846             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2847             storage = arrayStorage();
2848             storage->m_vector[i].set(vm, this, value);
2849             ++storage->m_numValuesInVector;
2850             return true;
2851         }
2852         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2853         map = allocateSparseIndexMap(vm);
2854         RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
2855     }
2856
2857     // Update m_length if necessary.
2858     unsigned length = storage->length();
2859     if (i >= length) {
2860         // Prohibit growing the array if length is not writable.
2861         if (map->lengthIsReadOnly() || !isStructureExtensible(vm))
2862             return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
2863         length = i + 1;
2864         storage->setLength(length);
2865     }
2866
2867     // We are currently using a map - check whether we still want to be doing so.
2868     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2869     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2870     if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
2871         RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
2872
2873     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2874     storage = arrayStorage();
2875     storage->m_numValuesInVector = numValuesInArray;
2876
2877     // Copy all values from the map into the vector, and delete the map.
2878     WriteBarrier<Unknown>* vector = storage->m_vector;
2879     SparseArrayValueMap::const_iterator end = map->end();
2880     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2881         vector[it->key].set(vm, this, it->value.getNonSparseMode());
2882     deallocateSparseIndexMap();
2883
2884     // Store the new property into the vector.
2885     WriteBarrier<Unknown>& valueSlot = vector[i];
2886     if (!valueSlot)
2887         ++storage->m_numValuesInVector;
2888     valueSlot.set(vm, this, value);
2889     return true;
2890 }
2891
2892 bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
2893 {
2894     VM& vm = exec->vm();
2895
2896     RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
2897
2898     // i should be a valid array index that is outside of the current vector.
2899     ASSERT(i <= MAX_ARRAY_INDEX);
2900     
2901     switch (indexingType()) {
2902     case ALL_BLANK_INDEXING_TYPES: {
2903         if (indexingShouldBeSparse(vm)) {
2904             return putByIndexBeyondVectorLengthWithArrayStorage(
2905                 exec, i, value, shouldThrow,
2906                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2907         }
2908         if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
2909             return putByIndexBeyondVectorLengthWithArrayStorage(
2910                 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
2911         }
2912         if (needsSlowPutIndexing(vm)) {
2913             // Convert the indexing type to the SlowPutArrayStorage and retry.
2914             createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
2915             return putByIndex(this, exec, i, value, shouldThrow);
2916         }
2917         
2918         createInitialForValueAndSet(vm, i, value);
2919         return true;
2920     }
2921         
2922     case ALL_UNDECIDED_INDEXING_TYPES: {
2923         CRASH();
2924         break;
2925     }
2926         
2927     case ALL_INT32_INDEXING_TYPES:
2928         return putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2929         
2930     case ALL_DOUBLE_INDEXING_TYPES:
2931         return putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2932         
2933     case ALL_CONTIGUOUS_INDEXING_TYPES:
2934         return putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2935         
2936     case NonArrayWithSlowPutArrayStorage:
2937     case ArrayWithSlowPutArrayStorage: {
2938         // No own property present in the vector, but there might be in the sparse map!
2939         SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
2940         bool putResult = false;
2941         if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow, putResult))
2942             return putResult;
2943         FALLTHROUGH;
2944     }
2945
2946     case NonArrayWithArrayStorage:
2947     case ArrayWithArrayStorage:
2948         return putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
2949         
2950     default:
2951         RELEASE_ASSERT_NOT_REACHED();
2952     }
2953     return false;
2954 }
2955
2956 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
2957 {
2958     VM& vm = exec->vm();
2959     auto scope = DECLARE_THROW_SCOPE(vm);
2960     
2961     // i should be a valid array index that is outside of the current vector.
2962     ASSERT(hasAnyArrayStorage(indexingType()));
2963     ASSERT(arrayStorage() == storage);
2964     ASSERT(i >= storage->vectorLength() || attributes);
2965     ASSERT(i <= MAX_ARRAY_INDEX);
2966
2967     SparseArrayValueMap* map = storage->m_sparseMap.get();
2968
2969     // First, handle cases where we don't currently have a sparse map.
2970     if (LIKELY(!map)) {
2971         // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2972         ASSERT(isStructureExtensible(vm));
2973     
2974         // Update m_length if necessary.
2975         if (i >= storage->length())
2976             storage->setLength(i + 1);
2977
2978         // Check that it is sensible to still be using a vector, and then try to grow the vector.
2979         if (LIKELY(
2980                 !attributes
2981                 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
2982                 && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
2983                 && increaseVectorLength(vm, i + 1)) {
2984             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2985             storage = arrayStorage();
2986             storage->m_vector[i].set(vm, this, value);
2987             ++storage->m_numValuesInVector;
2988             return true;
2989         }
2990         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2991         map = allocateSparseIndexMap(vm);
2992         RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
2993     }
2994
2995     // Update m_length if necessary.
2996     unsigned length = storage->length();
2997     if (i >= length) {
2998         if (mode != PutDirectIndexLikePutDirect) {
2999             // Prohibit growing the array if length is not writable.
3000             if (map->lengthIsReadOnly())
3001                 return typeError(exec, scope, mode == PutDirectIndexShouldThrow, ReadonlyPropertyWriteError);
3002             if (!isStructureExtensible(vm))
3003                 return typeError(exec, scope, mode == PutDirectIndexShouldThrow, NonExtensibleObjectPropertyDefineError);
3004         }
3005         length = i + 1;
3006         storage->setLength(length);
3007     }
3008
3009     // We are currently using a map - check whether we still want to be doing so.
3010     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
3011     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
3012     if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
3013         RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
3014
3015     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
3016     storage = arrayStorage();
3017     storage->m_numValuesInVector = numValuesInArray;
3018
3019     // Copy all values from the map into the vector, and delete the map.
3020     WriteBarrier<Unknown>* vector = storage->m_vector;
3021     SparseArrayValueMap::const_iterator end = map->end();
3022     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
3023         vector[it->key].set(vm, this, it->value.getNonSparseMode());
3024     deallocateSparseIndexMap();
3025
3026     // Store the new property into the vector.
3027     WriteBarrier<Unknown>& valueSlot = vector[i];
3028     if (!valueSlot)
3029         ++storage->m_numValuesInVector;
3030     valueSlot.set(vm, this, value);
3031     return true;
3032 }
3033
3034 bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
3035 {
3036     VM& vm = exec->vm();
3037     ASSERT(!value.isCustomGetterSetter());
3038
3039     if (!canDoFastPutDirectIndex(vm, this)) {
3040         PropertyDescriptor descriptor;
3041         descriptor.setDescriptor(value, attributes);
3042         return methodTable(vm)->defineOwnProperty(this, exec, Identifier::from(exec, i), descriptor, mode == PutDirectIndexShouldThrow);
3043     }
3044
3045     // i should be a valid array index that is outside of the current vector.
3046     ASSERT(i <= MAX_ARRAY_INDEX);
3047     
3048     if (attributes & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))
3049         notifyPresenceOfIndexedAccessors(vm);
3050     
3051     switch (indexingType()) {
3052     case ALL_BLANK_INDEXING_TYPES: {
3053         if (indexingShouldBeSparse(vm) || attributes) {
3054             return putDirectIndexBeyondVectorLengthWithArrayStorage(
3055                 exec, i, value, attributes, mode,
3056                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3057         }
3058         if (i >= MIN_SPARSE_ARRAY_INDEX) {
3059             return putDirectIndexBeyondVectorLengthWithArrayStorage(
3060                 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
3061         }
3062         if (needsSlowPutIndexing(vm)) {
3063             ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
3064             storage->m_vector[i].set(vm, this, value);
3065             storage->m_numValuesInVector++;
3066             return true;
3067         }
3068         
3069         createInitialForValueAndSet(vm, i, value);
3070         return true;
3071     }
3072         
3073     case ALL_UNDECIDED_INDEXING_TYPES: {
3074         convertUndecidedForValue(vm, value);
3075         // Reloop.
3076         return putDirectIndex(exec, i, value, attributes, mode);
3077     }
3078         
3079     case ALL_INT32_INDEXING_TYPES: {
3080         ASSERT(!indexingShouldBeSparse(vm));
3081         if (attributes)
3082             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3083         if (!value.isInt32()) {
3084             convertInt32ForValue(vm, value);
3085             return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3086         }
3087         putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
3088         return true;
3089     }
3090         
3091     case ALL_DOUBLE_INDEXING_TYPES: {
3092         ASSERT(!indexingShouldBeSparse(vm));
3093         if (attributes)
3094             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3095         if (!value.isNumber()) {
3096             convertDoubleToContiguous(vm);
3097             return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3098         }
3099         double valueAsDouble = value.asNumber();
3100         if (valueAsDouble != valueAsDouble) {
3101             convertDoubleToContiguous(vm);
3102             return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3103         }
3104         putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
3105         return true;
3106     }
3107         
3108     case ALL_CONTIGUOUS_INDEXING_TYPES: {
3109         ASSERT(!indexingShouldBeSparse(vm));
3110         if (attributes)
3111             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3112         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
3113         return true;
3114     }
3115
3116     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
3117         if (attributes)
3118             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3119         return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
3120         
3121     default:
3122         RELEASE_ASSERT_NOT_REACHED();
3123         return false;
3124     }
3125 }
3126
3127 bool JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3128 {
3129     JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
3130     GetterSetter* accessor = GetterSetter::create(vm, globalObject, function, nullptr);
3131     return putDirectNonIndexAccessor(vm, name, accessor, attributes);
3132 }
3133
3134 void JSObject::putDirectNativeIntrinsicGetterWithoutTransition(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3135 {
3136     JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
3137     GetterSetter* accessor = GetterSetter::create(vm, globalObject, function, nullptr);
3138     putDirectNonIndexAccessorWithoutTransition(vm, name, accessor, attributes);
3139 }
3140
3141 bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3142 {
3143     StringImpl* name = propertyName.publicName();
3144     if (!name)
3145         name = vm.propertyNames->anonymous.impl();
3146     ASSERT(name);
3147
3148     JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
3149     return putDirect(vm, propertyName, function, attributes);
3150 }
3151
3152 bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, const DOMJIT::Signature* signature, unsigned attributes)
3153 {
3154     StringImpl* name = propertyName.publicName();
3155     if (!name)
3156         name = vm.propertyNames->anonymous.impl();
3157     ASSERT(name);
3158
3159     JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic, callHostFunctionAsConstructor, signature);
3160     return putDirect(vm, propertyName, function, attributes);
3161 }
3162
3163 void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3164 {
3165     StringImpl* name = propertyName.publicName();
3166     if (!name)
3167         name = vm.propertyNames->anonymous.impl();
3168     ASSERT(name);
3169     JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
3170     putDirectWithoutTransition(vm, propertyName, function, attributes);
3171 }
3172
3173 JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
3174 {
3175     StringImpl* name = propertyName.publicName();
3176     if (!name)
3177         name = vm.propertyNames->anonymous.impl();
3178     ASSERT(name);
3179     JSFunction* function = JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
3180     putDirect(vm, propertyName, function, attributes);
3181     return function;
3182 }
3183
3184 JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
3185 {
3186     JSFunction* function = JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
3187     putDirectWithoutTransition(vm, propertyName, function, attributes);
3188     return function;
3189 }
3190
3191 // NOTE: This method is for ArrayStorage vectors.
3192 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
3193 {
3194     ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
3195
3196     unsigned increasedLength;
3197     unsigned maxInitLength = std::min(currentLength, 100000U);
3198
3199     if (desiredLength < maxInitLength)
3200         increasedLength = maxInitLength;
3201     else if (!currentVectorLength)
3202         increasedLength = std::max(desiredLength, lastArraySize);
3203     else {
3204         increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
3205     }
3206
3207     ASSERT(increasedLength >= desiredLength);
3208
3209     lastArraySize = std::min(increasedLength, FIRST_ARRAY_STORAGE_VECTOR_GROW);
3210
3211     return ArrayStorage::optimalVectorLength(
3212         indexBias, structure(vm)->outOfLineCapacity(),
3213         std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH));
3214 }
3215
3216 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned desiredLength)
3217 {
3218     unsigned indexBias = 0;
3219     unsigned vectorLength = 0;
3220     unsigned length = 0;
3221     
3222     if (hasIndexedProperties(indexingType())) {
3223         if (ArrayStorage* storage = arrayStorageOrNull())
3224             indexBias = storage->m_indexBias;
3225         vectorLength = m_butterfly->vectorLength();
3226         length = m_butterfly->publicLength();
3227     }
3228
3229     return getNewVectorLength(vm, indexBias, vectorLength, length, desiredLength);
3230 }
3231
3232 template<IndexingType indexingShape>
3233 unsigned JSObject::countElements(Butterfly* butterfly)
3234 {
3235     unsigned numValues = 0;
3236     for (unsigned i = butterfly->publicLength(); i--;) {
3237         switch (indexingShape) {
3238         case Int32Shape:
3239         case ContiguousShape:
3240             if (butterfly->contiguous().at(this, i))
3241                 numValues++;
3242             break;
3243             
3244         case DoubleShape: {
3245             double value = butterfly->contiguousDouble().at(this, i);
3246             if (value == value)
3247                 numValues++;
3248             break;
3249         }
3250             
3251         default:
3252             CRASH();
3253         }
3254     }
3255     return numValues;
3256 }
3257
3258 unsigned JSObject::countElements()
3259 {
3260     switch (indexingType()) {
3261     case ALL_BLANK_INDEXING_TYPES:
3262     case ALL_UNDECIDED_INDEXING_TYPES:
3263         return 0;
3264         
3265     case ALL_INT32_INDEXING_TYPES:
3266         return countElements<Int32Shape>(butterfly());
3267         
3268     case ALL_DOUBLE_INDEXING_TYPES:
3269         return countElements<DoubleShape>(butterfly());
3270         
3271     case ALL_CONTIGUOUS_INDEXING_TYPES:
3272         return countElements<ContiguousShape>(butterfly());
3273         
3274     default:
3275         CRASH();
3276         return 0;
3277     }
3278 }
3279
3280 bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
3281 {
3282     ArrayStorage* storage = arrayStorage();
3283     
3284     unsigned vectorLength = storage->vectorLength();
3285     unsigned availableVectorLength = storage->availableVectorLength(structure(vm), vectorLength); 
3286     if (availableVectorLength >= newLength) {
3287         // The cell was already big enough for the desired length!
3288         for (unsigned i = vectorLength; i < availableVectorLength; ++i)
3289             storage->m_vector[i].clear();
3290         storage->setVectorLength(availableVectorLength);
3291         return true;
3292     }
3293     
3294     // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
3295     // to the vector. Callers have to account for that, because they can do it more efficiently.
3296     if (newLength > MAX_STORAGE_VECTOR_LENGTH)
3297         return false;
3298
3299     if (newLength >= MIN_SPARSE_ARRAY_INDEX
3300         && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector))
3301         return false;
3302
3303     unsigned indexBias = storage->m_indexBias;
3304     ASSERT(newLength > vectorLength);
3305     unsigned newVectorLength = getNewVectorLength(vm, newLength);
3306
3307     // Fast case - there is no precapacity. In these cases a realloc makes sense.
3308     Structure* structure = this->structure(vm);
3309     if (LIKELY(!indexBias)) {
3310         DeferGC deferGC(vm.heap);
3311         Butterfly* newButterfly = storage->butterfly()->growArrayRight(
3312             vm, this, structure, structure->outOfLineCapacity(), true,
3313             ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
3314         if (!newButterfly)
3315             return false;
3316         for (unsigned i = vectorLength; i < newVectorLength; ++i)
3317             newButterfly->arrayStorage()->m_vector[i].clear();
3318         newButterfly->arrayStorage()->setVectorLength(newVectorLength);
3319         setButterfly(vm, newButterfly);
3320         return true;
3321     }
3322     
3323     // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
3324     DeferGC deferGC(vm.heap);
3325     unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
3326     Butterfly* newButterfly = storage->butterfly()->resizeArray(
3327         vm, this,
3328         structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
3329         newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
3330     if (!newButterfly)
3331         return false;
3332     for (unsigned i = vectorLength; i < newVectorLength; ++i)
3333         newButterfly->arrayStorage()->m_vector[i].clear();
3334     newButterfly->arrayStorage()->setVectorLength(newVectorLength);
3335     newButterfly->arrayStorage()->m_indexBias = newIndexBias;
3336     setButterfly(vm, newButterfly);
3337     return true;
3338 }
3339
3340 bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
3341 {
3342     if (isCopyOnWrite(indexingMode())) {
3343         convertFromCopyOnWrite(vm);
3344         if (m_butterfly->vectorLength() >= length)
3345             return true;
3346     }
3347
3348     Butterfly* butterfly = this->butterfly();
3349     
3350     ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
3351     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
3352     ASSERT(length > butterfly->vectorLength());
3353
3354     unsigned oldVectorLength = butterfly->vectorLength();
3355     unsigned newVectorLength;
3356     
3357     Structure* structure = this->structure(vm);
3358     unsigned propertyCapacity = structure->outOfLineCapacity();
3359     
3360     unsigned availableOldLength =
3361         Butterfly::availableContiguousVectorLength(propertyCapacity, oldVectorLength);
3362     Butterfly* newButterfly = nullptr;
3363     if (availableOldLength >= length) {
3364         // This is the case where someone else selected a vector length that caused internal
3365         // fragmentation. If we did our jobs right, this would never happen. But I bet we will mess
3366         // this up, so this defense should stay.
3367         newVectorLength = availableOldLength;
3368     } else {
3369         newVectorLength = Butterfly::optimalContiguousVectorLength(
3370             propertyCapacity, std::min(length * 2, MAX_STORAGE_VECTOR_LENGTH));
3371         butterfly = butterfly->growArrayRight(
3372             vm, this, structure, propertyCapacity, true,
3373             oldVectorLength * sizeof(EncodedJSValue),
3374             newVectorLength * sizeof(EncodedJSValue));
3375         if (!butterfly)
3376             return false;
3377         newButterfly = butterfly;
3378     }
3379
3380     if (hasDouble(indexingType())) {
3381         for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
3382             butterfly->indexingPayload<double>()[i] = PNaN;
3383     } else {
3384         for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
3385             butterfly->indexingPayload<WriteBarrier<Unknown>>()[i].clear();
3386     }
3387
3388     if (newButterfly) {
3389         butterfly->setVectorLength(newVectorLength);
3390         WTF::storeStoreFence();
3391         m_butterfly.set(vm, this, newButterfly);
3392     } else {
3393         WTF::storeStoreFence();
3394         butterfly->setVectorLength(newVectorLength);
3395     }
3396
3397     return true;
3398 }
3399
3400 void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length)
3401 {
3402     ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
3403     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
3404     ASSERT(m_butterfly->vectorLength() > length);
3405     ASSERT(!m_butterfly->indexingHeader()->preCapacity(structure(vm)));
3406
3407     DeferGC deferGC(vm.heap);
3408     Butterfly* newButterfly = butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(length));
3409     newButterfly->setVectorLength(length);
3410     newButterfly->setPublicLength(length);
3411     WTF::storeStoreFence();
3412     m_butterfly.set(vm, this, newButterfly);
3413
3414 }
3415
3416 Butterfly* JSObject::allocateMoreOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize)
3417 {
3418     ASSERT(newSize > oldSize);
3419
3420     // It's important that this function not rely on structure(), for the property
3421     // capacity, since we might have already mutated the structure in-place.
3422
3423     return Butterfly::createOrGrowPropertyStorage(butterfly(), vm, this, structure(vm), oldSize, newSize);
3424 }
3425
3426 static JSCustomGetterSetterFunction* getCustomGetterSetterFunctionForGetterSetter(ExecState* exec, PropertyName propertyName, CustomGetterSetter* getterSetter, JSCustomGetterSetterFunction::Type type)
3427 {
3428     VM& vm = exec->vm();
3429     auto key = std::make_pair(getterSetter, (int)type);
3430     JSCustomGetterSetterFunction* customGetterSetterFunction = vm.customGetterSetterFunctionMap.get(key);
3431     if (!customGetterSetterFunction) {
3432         customGetterSetterFunction = JSCustomGetterSetterFunction::create(vm, exec->lexicalGlobalObject(), getterSetter, type, propertyName.publicName());
3433         vm.customGetterSetterFunctionMap.set(key, customGetterSetterFunction);
3434     }
3435     return customGetterSetterFunction;
3436 }
3437
3438 bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
3439 {
3440     VM& vm = exec->vm();
3441     JSC::PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
3442     if (!methodTable(vm)->getOwnPropertySlot(this, exec, propertyName, slot))
3443         return false;
3444
3445     // DebuggerScope::getOwnPropertySlot() (and possibly others) may return attributes from the prototype chain
3446     // but getOwnPropertyDescriptor() should only work for 'own' properties so we exit early if we detect that
3447     // the property is not an own property.
3448     if (slot.slotBase() != this && slot.slotBase()) {
3449         JSProxy* jsProxy = jsDynamicCast<JSProxy*>(vm, this);
3450         if (!jsProxy || jsProxy->target() != slot.slotBase()) {
3451             // Try ProxyObject.
3452             ProxyObject* proxyObject = jsDynamicCast<ProxyObject*>(vm, this);
3453             if (!proxyObject || proxyObject->target() != slot.slotBase())
3454                 return false;
3455         }
3456     }
3457
3458     if (slot.isAccessor())
3459         descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes());
3460     else if (slot.attributes() & PropertyAttribute::CustomAccessor) {
3461         descriptor.setCustomDescriptor(slot.attributes());
3462
3463         JSObject* thisObject = this;
3464         if (auto* proxy = jsDynamicCast<JSProxy*>(vm, this))
3465             thisObject = proxy->target();
3466
3467         CustomGetterSetter* getterSetter;
3468         if (slot.isCustomAccessor())
3469             getterSetter = slot.customGetterSetter();
3470         else {
3471             JSValue maybeGetterSetter = thisObject->getDirect(vm, propertyName);
3472             if (!maybeGetterSetter) {
3473                 thisObject->reifyAllStaticProperties(exec);
3474                 maybeGetterSetter = thisObject->getDirect(vm, propertyName);
3475             }
3476
3477             ASSERT(maybeGetterSetter);
3478             getterSetter = jsDynamicCast<CustomGetterSetter*>(vm, maybeGetterSetter);
3479         }
3480         ASSERT(getterSetter);
3481         if (!getterSetter)
3482             return false;
3483
3484         if (getterSetter->getter())
3485             descriptor.setGetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Getter));
3486         if (getterSetter->setter())
3487             descriptor.setSetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Setter));
3488     } else
3489         descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes());
3490     return true;
3491 }
3492
3493 static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
3494 {
3495     VM& vm = exec->vm();
3496     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
3497         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
3498             JSObject* getter = oldDescriptor.getterPresent() ? oldDescriptor.getterObject() : nullptr;
3499             JSObject* setter = oldDescriptor.setterPresent() ? oldDescriptor.setterObject() : nullptr;
3500             GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
3501             target->putDirectAccessor(exec, propertyName, accessor, attributes | PropertyAttribute::Accessor);
3502             return true;
3503         }
3504         JSValue newValue = jsUndefined();
3505         if (descriptor.value())
3506             newValue = descriptor.value();
3507         else if (oldDescriptor.value())
3508             newValue = oldDescriptor.value();
3509         target->putDirect(vm, propertyName, newValue, attributes & ~PropertyAttribute::Accessor);
3510         if (attributes & PropertyAttribute::ReadOnly)
3511             target->structure(vm)->setContainsReadOnlyProperties();
3512         return true;
3513     }
3514     attributes &= ~PropertyAttribute::ReadOnly;
3515
3516     JSObject* getter = descriptor.getterPresent()
3517         ? descriptor.getterObject() : oldDescriptor.getterPresent()
3518         ? oldDescriptor.getterObject() : nullptr;
3519     JSObject* setter = descriptor.setterPresent()
3520         ? descriptor.setterObject() : oldDescriptor.setterPresent()
3521         ? oldDescriptor.setterObject() : nullptr;
3522     GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
3523
3524     target->putDirectAccessor(exec, propertyName, accessor, attributes | PropertyAttribute::Accessor);
3525     return true;
3526 }
3527
3528 bool JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
3529 {
3530     if (Optional<uint32_t> index = parseIndex(propertyName))
3531         return putDirectIndex(exec, index.value(), value);
3532     return putDirect(exec->vm(), propertyName, value);
3533 }
3534
3535 // 9.1.6.3 of the spec
3536 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-validateandapplypropertydescriptor
3537 bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, PropertyName propertyName, bool isExtensible,
3538     const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException)
3539 {
3540     VM& vm = exec->vm();
3541     auto scope = DECLARE_THROW_SCOPE(vm);
3542
3543     // If we have a new property we can just put it on normally
3544     // Step 2.
3545     if (!isCurrentDefined) {
3546         // unless extensions are prevented!
3547         // Step 2.a
3548         if (!isExtensible)
3549             return typeError(exec, scope, throwException, NonExtensibleObjectPropertyDefineError);
3550         if (!object)
3551             return true;
3552         // Step 2.c/d
3553         PropertyDescriptor oldDescriptor;
3554         oldDescriptor.setValue(jsUndefined());
3555         // FIXME: spec says to always return true here.
3556         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
3557     }
3558     // Step 3.
3559     if (descriptor.isEmpty())
3560         return true;
3561     // Step 4.
3562     bool isEqual = current.equalTo(exec, descriptor);
3563     RETURN_IF_EXCEPTION(scope, false);
3564     if (isEqual)
3565         return true;
3566
3567     // Step 5.
3568     // Filter out invalid changes
3569     if (!current.configurable()) {
3570         if (descriptor.configurable())
3571             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
3572         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable())
3573             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
3574     }
3575     
3576     // Step 6.
3577     // A generic descriptor is simply changing the attributes of an existing property
3578     if (descriptor.isGenericDescriptor()) {
3579         if (!current.attributesEqual(descriptor) && object) {
3580             object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3581             RETURN_IF_EXCEPTION(scope, false);
3582             return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3583         }
3584         return true;
3585     }
3586     
3587     // Step 7.
3588     // Changing between a normal property or an accessor property
3589     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
3590         if (!current.configurable())
3591             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
3592
3593         if (!object)
3594             return true;
3595
3596         object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3597         RETURN_IF_EXCEPTION(scope, false);
3598         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3599     }
3600
3601     // Step 8.
3602     // Changing the value and attributes of an existing property
3603     if (descriptor.isDataDescriptor()) {
3604         if (!current.configurable()) {
3605             if (!current.writable() && descriptor.writable())
3606                 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
3607             if (!current.writable()) {
3608                 if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value()))
3609                     return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
3610             }
3611         }
3612         if (current.attributesEqual(descriptor) && !descriptor.value())
3613             return true;
3614         if (!object)
3615             return true;
3616         object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3617         RETURN_IF_EXCEPTION(scope, false);
3618         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3619     }
3620
3621     // Step 9.
3622     // Changing the accessor functions of an existing accessor property
3623     ASSERT(descriptor.isAccessorDescriptor());
3624     if (!current.configurable()) {
3625         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter())))
3626             return typeError(exec, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
3627         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter())))
3628             return typeError(exec, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
3629         if (current.attributes() & PropertyAttribute::CustomAccessor)
3630             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
3631     }
3632
3633     // Step 10/11.
3634     if (!object)
3635         return true;
3636     JSValue accessor = object->getDirect(vm, propertyName);
3637     if (!accessor)
3638         return false;
3639     JSObject* getter = nullptr;
3640     JSObject* setter = nullptr;
3641     bool getterSetterChanged = false;
3642
3643     if (accessor.isCustomGetterSetter()) {
3644         auto* customGetterSetter = jsCast<CustomGetterSetter*>(accessor);
3645         if (customGetterSetter->setter())
3646             setter = getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Setter);
3647         if (customGetterSetter->getter())
3648             getter = getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Getter);
3649     } else {
3650         ASSERT(accessor.isGetterSetter());
3651         auto* getterSetter = jsCast<GetterSetter*>(accessor);
3652         getter = getterSetter->getter();
3653         setter = getterSetter->setter();
3654     }
3655     if (descriptor.setterPresent()) {
3656         setter = descriptor.setterObject();
3657         getterSetterChanged = true;
3658     }
3659     if (descriptor.getterPresent()) {
3660         getter = descriptor.getterObject();
3661         getterSetterChanged = true;
3662     }
3663
3664     if (current.attributesEqual(descriptor) && !getterSetterChanged)
3665         return true;
3666
3667     GetterSetter* getterSetter = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
3668
3669     object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3670     RETURN_IF_EXCEPTION(scope, false);
3671     unsigned attrs = descriptor.attributesOverridingCurrent(current);
3672     object->putDirectAccessor(exec, propertyName, getterSetter, attrs | PropertyAttribute::Accessor);
3673     return true;
3674 }
3675
3676 bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3677 {
3678     VM& vm  = exec->vm();
3679     auto throwScope = DECLARE_THROW_SCOPE(vm);
3680
3681     // Track on the globaldata that we're in define property.
3682     // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
3683     // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
3684     // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
3685     VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
3686     PropertyDescriptor current;
3687     bool isCurrentDefined = getOwnPropertyDescriptor(exec, propertyName, current);
3688     bool isExtensible = this->isExtensible(exec);
3689     RETURN_IF_EXCEPTION(throwScope, false);
3690     RELEASE_AND_RETURN(throwScope, validateAndApplyPropertyDescriptor(exec, this, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException));
3691 }
3692
3693 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3694 {
3695     // If it's an array index, then use the indexed property storage.
3696     if (Optional<uint32_t> index = parseIndex(propertyName)) {
3697         // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
3698         // d. Reject if succeeded is false.
3699         // e. If index >= oldLen
3700         // e.i. Set oldLenDesc.[[Value]] to index + 1.
3701         // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
3702         // f. Return true.
3703         return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException);
3704     }
3705     
3706     return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
3707 }
3708
3709 void JSObject::convertToDictionary(VM& vm)
3710 {
3711     DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure(vm));
3712     setStructure(
3713         vm, Structure::toCacheableDictionaryTransition(vm, structure(vm), &deferredWatchpointFire));
3714 }
3715
3716 void JSObject::shiftButterflyAfterFlattening(const GCSafeConcurrentJSLocker&, VM& vm, Structure* structure, size_t outOfLineCapacityAfter)
3717 {
3718     // This could interleave visitChildren because some old structure could have been a non
3719     // dictionary structure. We have to be crazy careful. But, we are guaranteed to be holding
3720     // the structure's lock right now, and that helps a bit.
3721
3722     Butterfly* oldButterfly = this->butterfly();
3723     size_t preCapacity;
3724     size_t indexingPayloadSizeInBytes;
3725     bool hasIndexingHeader = this->hasIndexingHeader(vm);
3726     if (UNLIKELY(hasIndexingHeader)) {
3727    &