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