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