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