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