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