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