2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
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.
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.
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.
27 #include "ButterflyInlines.h"
28 #include "CatchScope.h"
29 #include "CustomGetterSetter.h"
30 #include "DatePrototype.h"
31 #include "ErrorConstructor.h"
32 #include "Exception.h"
33 #include "GetterSetter.h"
34 #include "HeapSnapshotBuilder.h"
35 #include "IndexingHeaderInlines.h"
36 #include "JSCInlines.h"
37 #include "JSCustomGetterSetterFunction.h"
38 #include "JSFunction.h"
39 #include "JSGlobalObject.h"
40 #include "JSImmutableButterfly.h"
42 #include "NativeErrorConstructor.h"
43 #include "ObjectPrototype.h"
44 #include "PropertyDescriptor.h"
45 #include "PropertyNameArray.h"
46 #include "ProxyObject.h"
47 #include "SlotVisitorInlines.h"
48 #include "TypeError.h"
50 #include <wtf/Assertions.h>
54 // We keep track of the size of the last array after it was grown. We use this
55 // as a simple heuristic for as the value to grow the next array from size 0.
56 // This value is capped by the constant FIRST_VECTOR_GROW defined in
57 // ArrayConventions.h.
58 static unsigned lastArraySize = 0;
60 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
61 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
63 const ASCIILiteral NonExtensibleObjectPropertyDefineError { "Attempting to define property on object that is not extensible."_s };
64 const ASCIILiteral ReadonlyPropertyWriteError { "Attempted to assign to readonly property."_s };
65 const ASCIILiteral ReadonlyPropertyChangeError { "Attempting to change value of a readonly property."_s };
66 const ASCIILiteral UnableToDeletePropertyError { "Unable to delete property."_s };
67 const ASCIILiteral UnconfigurablePropertyChangeAccessMechanismError { "Attempting to change access mechanism for an unconfigurable property."_s };
68 const ASCIILiteral UnconfigurablePropertyChangeConfigurabilityError { "Attempting to change configurable attribute of unconfigurable property."_s };
69 const ASCIILiteral UnconfigurablePropertyChangeEnumerabilityError { "Attempting to change enumerable attribute of unconfigurable property."_s };
70 const ASCIILiteral UnconfigurablePropertyChangeWritabilityError { "Attempting to change writable attribute of unconfigurable property."_s };
72 const ClassInfo JSObject::s_info = { "Object", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSObject) };
74 const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSFinalObject) };
76 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
80 // Add properties from the static hashtables of properties
81 for (; classInfo; classInfo = classInfo->parentClass) {
82 const HashTable* table = classInfo->staticPropHashTable;
86 for (auto iter = table->begin(); iter != table->end(); ++iter) {
87 if (!(iter->attributes() & PropertyAttribute::DontEnum) || mode.includeDontEnumProperties())
88 propertyNames.add(Identifier::fromString(&vm, iter.key()));
93 ALWAYS_INLINE void JSObject::markAuxiliaryAndVisitOutOfLineProperties(SlotVisitor& visitor, Butterfly* butterfly, Structure* structure, PropertyOffset lastOffset)
95 // We call this when we found everything without races.
101 if (isCopyOnWrite(structure->indexingMode())) {
102 visitor.append(bitwise_cast<WriteBarrier<JSCell>>(JSImmutableButterfly::fromButterfly(butterfly)));
106 bool hasIndexingHeader = structure->hasIndexingHeader(this);
108 if (hasIndexingHeader)
109 preCapacity = butterfly->indexingHeader()->preCapacity(structure);
113 HeapCell* base = bitwise_cast<HeapCell*>(
114 butterfly->base(preCapacity, Structure::outOfLineCapacity(lastOffset)));
116 ASSERT(Heap::heap(base) == visitor.heap());
118 visitor.markAuxiliary(base);
120 unsigned outOfLineSize = Structure::outOfLineSize(lastOffset);
121 visitor.appendValuesHidden(butterfly->propertyStorage() - outOfLineSize, outOfLineSize);
124 ALWAYS_INLINE Structure* JSObject::visitButterfly(SlotVisitor& visitor)
126 static const char* raceReason = "JSObject::visitButterfly";
127 Structure* result = visitButterflyImpl(visitor);
129 visitor.didRace(this, raceReason);
133 ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
135 VM& vm = visitor.vm();
137 Butterfly* butterfly;
138 Structure* structure;
139 PropertyOffset lastOffset;
141 auto visitElements = [&] (IndexingType indexingMode) {
142 switch (indexingMode) {
143 // We don't need to visit the elements for CopyOnWrite butterflies since they we marked the JSImmutableButterfly acting as out butterfly.
144 case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
145 visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
147 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
148 visitor.appendValuesHidden(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
149 if (butterfly->arrayStorage()->m_sparseMap)
150 visitor.append(butterfly->arrayStorage()->m_sparseMap);
157 if (visitor.mutatorIsStopped()) {
158 butterfly = this->butterfly();
159 structure = this->structure(vm);
160 lastOffset = structure->lastOffset();
162 markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
163 visitElements(structure->indexingMode());
168 // We want to ensure that we only scan the butterfly if we have an exactly matched structure and an
169 // exactly matched size. The mutator is required to perform the following shenanigans when
170 // reallocating the butterfly with a concurrent collector, with all fencing necessary to ensure
171 // that this executes as if under sequential consistency:
173 // object->structure = nuke(object->structure)
174 // object->butterfly = newButterfly
175 // structure->m_offset = newLastOffset
176 // object->structure = newStructure
178 // It's OK to skip this when reallocating the butterfly in a way that does not affect the m_offset.
179 // We have other protocols in place for that.
181 // Note that the m_offset can change without the structure changing, but in that case the mutator
182 // will still store null to the structure.
184 // The collector will ensure that it always sees a matched butterfly/structure by reading the
185 // structure before and after reading the butterfly. For simplicity, let's first consider the case
186 // where the only way to change the outOfLineCapacity is to change the structure. This works
187 // because the mutator performs the following steps sequentially:
189 // NukeStructure ChangeButterfly PutNewStructure
191 // Meanwhile the collector performs the following steps sequentially:
193 // ReadStructureEarly ReadButterfly ReadStructureLate
195 // The collector is allowed to do any of these three things:
197 // BEFORE: Scan the object with the structure and butterfly *before* the mutator's transition.
198 // AFTER: Scan the object with the structure and butterfly *after* the mutator's transition.
199 // IGNORE: Ignore the butterfly and call didRace to schedule us to be revisted again in the future.
201 // In other words, the collector will never see any torn structure/butterfly mix. It will
202 // always see the structure/butterfly before the transition or after but not in between.
204 // We can prove that this is correct by exhaustively considering all interleavings:
206 // NukeStructure ChangeButterfly PutNewStructure ReadStructureEarly ReadButterfly ReadStructureLate: AFTER, trivially.
207 // NukeStructure ChangeButterfly ReadStructureEarly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
208 // NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
209 // NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
210 // NukeStructure ReadStructureEarly ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
211 // NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
212 // NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
213 // NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
214 // NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
215 // NukeStructure ReadStructureEarly ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read early
216 // ReadStructureEarly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
217 // ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
218 // ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
219 // ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
220 // ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
221 // ReadStructureEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
222 // ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
223 // ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
224 // ReadStructureEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
225 // ReadStructureEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
227 // But we additionally have to worry about the size changing. We make this work by requiring that
228 // the collector reads the size early and late as well. Lets consider the interleaving of the
229 // mutator changing the size without changing the structure:
231 // NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure
233 // Meanwhile the collector does:
235 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate
237 // The collector can detect races by not only comparing the early structure to the late structure
238 // (which will be the same before and after the algorithm runs) but also by comparing the early and
239 // late lastOffsets. Note: the IGNORE proofs do not cite all of the reasons why the collector will
240 // ignore the case, since we only need to identify one to say that we're in the ignore case.
242 // NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: AFTER, trivially
243 // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
244 // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
245 // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
246 // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
247 // NukeStructure ChangeButterfly ChangeLastOffset ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
248 // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
249 // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
250 // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
251 // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
252 // NukeStructure ChangeButterfly ReadStructureEarly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
253 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
254 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
255 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
256 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
257 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
258 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
259 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
260 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
261 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
262 // NukeStructure ChangeButterfly ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
263 // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
264 // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
265 // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
266 // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
267 // NukeStructure ReadStructureEarly ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
268 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
269 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
270 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
271 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
272 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
273 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
274 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
275 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
276 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
277 // NukeStructure ReadStructureEarly ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
278 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
279 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
280 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
281 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
282 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
283 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
284 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
285 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
286 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
287 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
288 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read nuked structure early
289 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
290 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
291 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
292 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
293 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
294 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure early
295 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure early
296 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
297 // NukeStructure ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure early
298 // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate: AFTER, the ReadStructureEarly sees the same structure as after and everything else runs after.
299 // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: AFTER, as above and the ReadLastOffsetEarly sees the lastOffset after.
300 // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: AFTER, as above and the ReadButterfly sees the right butterfly after.
301 // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure late
302 // ReadStructureEarly NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
303 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
304 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
305 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
306 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
307 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
308 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
309 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
310 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
311 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
312 // ReadStructureEarly NukeStructure ChangeButterfly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
313 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
314 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
315 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
316 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
317 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
318 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
319 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
320 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
321 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
322 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ChangeButterfly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
323 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
324 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
325 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
326 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
327 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
328 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
329 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
330 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
331 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
332 // ReadStructureEarly NukeStructure ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
333 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadButterfly ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
334 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset ReadButterfly RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
335 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure late
336 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ChangeLastOffset ReadButterfly ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
337 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
338 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
339 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
340 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
341 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
342 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
343 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
344 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
345 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
346 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
347 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
348 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
349 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
350 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
351 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
352 // ReadStructureEarly ReadLastOffsetEarly NukeStructure ReadButterfly ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
353 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadStructureLate ReadLastOffsetLate: IGNORE, read different offsets
354 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ChangeLastOffset ReadStructureLate RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
355 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ChangeLastOffset ReadStructureLate ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
356 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
357 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
358 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
359 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read nuked structure late
360 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read nuked structure late
361 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
362 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly NukeStructure ReadStructureLate ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: IGNORE, read nuked structure late
363 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure ReadLastOffsetLate: IGNORE, read different offsets
364 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly ChangeLastOffset ReadLastOffsetLate RestoreStructure: IGNORE, read different offsets
365 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly ReadLastOffsetLate ChangeLastOffset RestoreStructure: BEFORE, reads the offset before, everything else happens before
366 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate NukeStructure ReadLastOffsetLate ChangeButterfly ChangeLastOffset RestoreStructure: BEFORE, reads the offset before, everything else happens before
367 // ReadStructureEarly ReadLastOffsetEarly ReadButterfly ReadStructureLate ReadLastOffsetLate NukeStructure ChangeButterfly ChangeLastOffset RestoreStructure: BEFORE, trivially
371 // What the collector is doing is just the "double collect" snapshot from "The Unbounded Single-
372 // Writer Algorithm" from Yehuda Afek et al's "Atomic Snapshots of Shared Memory" in JACM 1993,
373 // also available here:
375 // http://people.csail.mit.edu/shanir/publications/AADGMS.pdf
377 // Unlike Afek et al's algorithm, ours does not require extra hacks to force wait-freedom (see
378 // "Observation 2" in the paper). This simplifies the whole algorithm. Instead we are happy with
379 // obstruction-freedom, and like any good obstruction-free algorithm, we ensure progress using
380 // scheduling. We also only collect the butterfly once instead of twice; this optimization seems
381 // to hold up in my proofs above and I'm not sure it's part of Afek et al's algos.
383 // For more background on this kind of madness, I like this paper; it's where I learned about
384 // both the snapshot algorithm and obstruction-freedom:
386 // Lunchangco, Moir, Shavit. "Nonblocking k-compare-single-swap." SPAA '03
387 // https://pdfs.semanticscholar.org/343f/7182cde7669ca2a7de3dc01127927f384ef7.pdf
389 StructureID structureID = this->structureID();
390 if (isNuked(structureID))
392 structure = vm.getStructure(structureID);
393 lastOffset = structure->lastOffset();
394 IndexingType indexingMode = structure->indexingMode();
395 Dependency indexingModeDependency = Dependency::fence(indexingMode);
396 Locker<JSCellLock> locker(NoLockingNecessary);
397 switch (indexingMode) {
398 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
399 // We need to hold this lock to protect against changes to the innards of the butterfly
400 // that can happen when the butterfly is used for array storage.
401 // We do not need to hold this lock for contiguous butterflies. We do not reuse the existing
402 // butterfly with contiguous shape for new array storage butterfly. When converting the butterfly
403 // with contiguous shape to array storage, we always allocate a new one. Holding this lock for contiguous
404 // butterflies is unnecessary since contiguous shaped butterfly never becomes broken state.
405 locker = holdLock(cellLock());
410 butterfly = indexingModeDependency.consume(this)->butterfly();
411 Dependency butterflyDependency = Dependency::fence(butterfly);
414 if (butterflyDependency.consume(this)->structureID() != structureID)
416 if (butterflyDependency.consume(structure)->lastOffset() != lastOffset)
419 markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
420 ASSERT(indexingMode == structure->indexingMode());
421 visitElements(indexingMode);
426 size_t JSObject::estimatedSize(JSCell* cell, VM& vm)
428 JSObject* thisObject = jsCast<JSObject*>(cell);
429 size_t butterflyOutOfLineSize = thisObject->m_butterfly ? thisObject->structure(vm)->outOfLineSize() : 0;
430 return Base::estimatedSize(cell, vm) + butterflyOutOfLineSize;
433 void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
435 JSObject* thisObject = jsCast<JSObject*>(cell);
436 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
438 bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
439 visitor.m_isCheckingForDefaultMarkViolation = false;
442 JSCell::visitChildren(thisObject, visitor);
444 thisObject->visitButterfly(visitor);
447 visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
451 void JSObject::heapSnapshot(JSCell* cell, HeapSnapshotBuilder& builder)
453 JSObject* thisObject = jsCast<JSObject*>(cell);
454 Base::heapSnapshot(cell, builder);
456 Structure* structure = thisObject->structure();
457 for (auto& entry : structure->getPropertiesConcurrently()) {
458 JSValue toValue = thisObject->getDirect(entry.offset);
459 if (toValue && toValue.isCell())
460 builder.appendPropertyNameEdge(thisObject, toValue.asCell(), entry.key);
463 Butterfly* butterfly = thisObject->butterfly();
465 WriteBarrier<Unknown>* data = nullptr;
468 switch (thisObject->indexingType()) {
469 case ALL_CONTIGUOUS_INDEXING_TYPES:
470 data = butterfly->contiguous().data();
471 count = butterfly->publicLength();
473 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
474 data = butterfly->arrayStorage()->m_vector;
475 count = butterfly->arrayStorage()->vectorLength();
481 for (uint32_t i = 0; i < count; ++i) {
482 JSValue toValue = data[i].get();
483 if (toValue && toValue.isCell())
484 builder.appendIndexEdge(thisObject, toValue.asCell(), i);
489 void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
491 JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
492 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
494 bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
495 visitor.m_isCheckingForDefaultMarkViolation = false;
498 JSCell::visitChildren(thisObject, visitor);
500 if (Structure* structure = thisObject->visitButterfly(visitor)) {
501 if (unsigned storageSize = structure->inlineSize())
502 visitor.appendValuesHidden(thisObject->inlineStorage(), storageSize);
506 visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
510 String JSObject::className(const JSObject* object, VM& vm)
512 const ClassInfo* info = object->classInfo(vm);
514 return info->className;
517 String JSObject::toStringName(const JSObject* object, ExecState* exec)
520 const ClassInfo* info = object->classInfo(vm);
522 return info->methodTable.className(object, vm);
525 String JSObject::calculatedClassName(JSObject* object)
527 String prototypeFunctionName;
528 auto globalObject = object->globalObject();
529 VM& vm = globalObject->vm();
530 auto scope = DECLARE_CATCH_SCOPE(vm);
532 ExecState* exec = globalObject->globalExec();
533 PropertySlot slot(object->getPrototypeDirect(vm), PropertySlot::InternalMethodType::VMInquiry);
534 PropertyName constructor(vm.propertyNames->constructor);
535 if (object->getPropertySlot(exec, constructor, slot)) {
536 EXCEPTION_ASSERT(!scope.exception());
537 if (slot.isValue()) {
538 JSValue constructorValue = slot.getValue(exec, constructor);
539 if (constructorValue.isCell()) {
540 if (JSCell* constructorCell = constructorValue.asCell()) {
541 if (JSObject* ctorObject = constructorCell->getObject()) {
542 if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject))
543 prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
544 else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject))
545 prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
551 EXCEPTION_ASSERT(!scope.exception() || prototypeFunctionName.isNull());
552 if (UNLIKELY(scope.exception()))
553 scope.clearException();
555 if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") {
556 String tableClassName = object->methodTable(vm)->className(object, vm);
557 if (!tableClassName.isNull() && tableClassName != "Object")
558 return tableClassName;
560 String classInfoName = object->classInfo(vm)->className;
561 if (!classInfoName.isNull())
562 return classInfoName;
564 if (prototypeFunctionName.isNull())
568 return prototypeFunctionName;
571 bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot)
573 // NB. The fact that we're directly consulting our indexed storage implies that it is not
574 // legal for anyone to override getOwnPropertySlot() without also overriding
575 // getOwnPropertySlotByIndex().
577 if (i > MAX_ARRAY_INDEX)
578 return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
580 switch (thisObject->indexingType()) {
581 case ALL_BLANK_INDEXING_TYPES:
582 case ALL_UNDECIDED_INDEXING_TYPES:
585 case ALL_INT32_INDEXING_TYPES:
586 case ALL_CONTIGUOUS_INDEXING_TYPES: {
587 Butterfly* butterfly = thisObject->butterfly();
588 if (i >= butterfly->vectorLength())
591 JSValue value = butterfly->contiguous().at(thisObject, i).get();
593 slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), value);
600 case ALL_DOUBLE_INDEXING_TYPES: {
601 Butterfly* butterfly = thisObject->butterfly();
602 if (i >= butterfly->vectorLength())
605 double value = butterfly->contiguousDouble().at(thisObject, i);
606 if (value == value) {
607 slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), JSValue(JSValue::EncodeAsDouble, value));
614 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
615 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
616 if (i >= storage->length())
619 if (i < storage->vectorLength()) {
620 JSValue value = storage->m_vector[i].get();
622 slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), value);
625 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
626 SparseArrayValueMap::iterator it = map->find(i);
627 if (it != map->notFound()) {
628 it->value.get(thisObject, slot);
636 RELEASE_ASSERT_NOT_REACHED();
643 // https://tc39.github.io/ecma262/#sec-ordinaryset
644 bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyName, JSValue value, JSValue receiver, bool shouldThrow)
646 // If we find the receiver is not the same to the object, we fall to this slow path.
647 // Currently, there are 3 candidates.
648 // 1. Reflect.set can alter the receiver with an arbitrary value.
653 auto scope = DECLARE_THROW_SCOPE(vm);
654 JSObject* current = object;
655 PropertyDescriptor ownDescriptor;
657 if (current->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
658 ProxyObject* proxy = jsCast<ProxyObject*>(current);
659 PutPropertySlot slot(receiver, shouldThrow);
660 RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, exec, propertyName, value, slot));
663 // 9.1.9.1-2 Let ownDesc be ? O.[[GetOwnProperty]](P).
664 bool ownDescriptorFound = current->getOwnPropertyDescriptor(exec, propertyName, ownDescriptor);
665 RETURN_IF_EXCEPTION(scope, false);
667 if (!ownDescriptorFound) {
668 // 9.1.9.1-3-a Let parent be ? O.[[GetPrototypeOf]]().
669 JSValue prototype = current->getPrototype(vm, exec);
670 RETURN_IF_EXCEPTION(scope, false);
672 // 9.1.9.1-3-b If parent is not null, then
673 if (!prototype.isNull()) {
674 // 9.1.9.1-3-b-i Return ? parent.[[Set]](P, V, Receiver).
675 current = asObject(prototype);
678 // 9.1.9.1-3-c-i Let ownDesc be the PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
679 ownDescriptor = PropertyDescriptor(jsUndefined(), static_cast<unsigned>(PropertyAttribute::None));
684 // 9.1.9.1-4 If IsDataDescriptor(ownDesc) is true, then
685 if (ownDescriptor.isDataDescriptor()) {
686 // 9.1.9.1-4-a If ownDesc.[[Writable]] is false, return false.
687 if (!ownDescriptor.writable())
688 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
690 // 9.1.9.1-4-b If Type(Receiver) is not Object, return false.
691 if (!receiver.isObject())
692 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
694 // In OrdinarySet, the receiver may not be the same to the object.
695 // So, we perform [[GetOwnProperty]] onto the receiver while we already perform [[GetOwnProperty]] onto the object.
697 // 9.1.9.1-4-c Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
698 JSObject* receiverObject = asObject(receiver);
699 PropertyDescriptor existingDescriptor;
700 bool existingDescriptorFound = receiverObject->getOwnPropertyDescriptor(exec, propertyName, existingDescriptor);
701 RETURN_IF_EXCEPTION(scope, false);
703 // 9.1.9.1-4-d If existingDescriptor is not undefined, then
704 if (existingDescriptorFound) {
705 // 9.1.9.1-4-d-i If IsAccessorDescriptor(existingDescriptor) is true, return false.
706 if (existingDescriptor.isAccessorDescriptor())
707 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
709 // 9.1.9.1-4-d-ii If existingDescriptor.[[Writable]] is false, return false.
710 if (!existingDescriptor.writable())
711 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
713 // 9.1.9.1-4-d-iii Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
714 PropertyDescriptor valueDescriptor;
715 valueDescriptor.setValue(value);
717 // 9.1.9.1-4-d-iv Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
718 RELEASE_AND_RETURN(scope, receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, valueDescriptor, shouldThrow));
721 // 9.1.9.1-4-e Else Receiver does not currently have a property P,
722 // 9.1.9.1-4-e-i Return ? CreateDataProperty(Receiver, P, V).
723 RELEASE_AND_RETURN(scope, receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, PropertyDescriptor(value, static_cast<unsigned>(PropertyAttribute::None)), shouldThrow));
726 // 9.1.9.1-5 Assert: IsAccessorDescriptor(ownDesc) is true.
727 ASSERT(ownDescriptor.isAccessorDescriptor());
729 // 9.1.9.1-6 Let setter be ownDesc.[[Set]].
730 // 9.1.9.1-7 If setter is undefined, return false.
731 JSValue setter = ownDescriptor.setter();
732 if (!setter.isObject())
733 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
735 // 9.1.9.1-8 Perform ? Call(setter, Receiver, << V >>).
736 JSObject* setterObject = asObject(setter);
737 MarkedArgumentBuffer args;
739 ASSERT(!args.hasOverflowed());
742 CallType callType = setterObject->methodTable(vm)->getCallData(setterObject, callData);
744 call(exec, setterObject, callType, callData, receiver, args);
746 // 9.1.9.1-9 Return true.
751 bool JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
753 return putInlineForJSObject(cell, exec, propertyName, value, slot);
756 bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
758 ASSERT(!isThisValueAltered(slot, this));
761 auto scope = DECLARE_THROW_SCOPE(vm);
763 JSObject* obj = this;
766 PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
767 if (isValidOffset(offset)) {
768 if (attributes & PropertyAttribute::ReadOnly) {
769 ASSERT(this->prototypeChainMayInterceptStoreTo(vm, propertyName) || obj == this);
770 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
773 JSValue gs = obj->getDirect(offset);
774 if (gs.isGetterSetter()) {
775 // We need to make sure that we decide to cache this property before we potentially execute aribitrary JS.
776 if (!structure(vm)->isDictionary())
777 slot.setCacheableSetter(obj, offset);
779 bool result = callSetter(exec, slot.thisValue(), gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
780 RETURN_IF_EXCEPTION(scope, false);
783 if (gs.isCustomGetterSetter()) {
784 // We need to make sure that we decide to cache this property before we potentially execute aribitrary JS.
785 if (attributes & PropertyAttribute::CustomAccessor)
786 slot.setCustomAccessor(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
788 slot.setCustomValue(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
790 bool result = callCustomSetter(exec, gs, attributes & PropertyAttribute::CustomAccessor, obj, slot.thisValue(), value);
791 RETURN_IF_EXCEPTION(scope, false);
794 ASSERT(!(attributes & PropertyAttribute::Accessor));
796 // If there's an existing property on the object or one of its
797 // prototypes it should be replaced, so break here.
800 if (!obj->staticPropertiesReified(vm)) {
801 if (obj->classInfo(vm)->hasStaticSetterOrReadonlyProperties()) {
802 if (auto entry = obj->findPropertyHashEntry(vm, propertyName))
803 RELEASE_AND_RETURN(scope, putEntry(exec, entry->table->classForThis, entry->value, obj, this, propertyName, value, slot));
806 if (obj->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
807 // FIXME: We shouldn't unconditionally perform [[Set]] here.
808 // We need to do more because this is observable behavior.
809 // https://bugs.webkit.org/show_bug.cgi?id=155012
810 ProxyObject* proxy = jsCast<ProxyObject*>(obj);
811 RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, exec, propertyName, value, slot));
813 JSValue prototype = obj->getPrototype(vm, exec);
814 RETURN_IF_EXCEPTION(scope, false);
815 if (prototype.isNull())
817 obj = asObject(prototype);
820 if (!putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot))
821 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
825 bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
828 JSObject* thisObject = jsCast<JSObject*>(cell);
830 if (propertyName > MAX_ARRAY_INDEX) {
831 PutPropertySlot slot(cell, shouldThrow);
832 return thisObject->methodTable(vm)->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
835 thisObject->ensureWritable(vm);
837 switch (thisObject->indexingType()) {
838 case ALL_BLANK_INDEXING_TYPES:
841 case ALL_UNDECIDED_INDEXING_TYPES: {
842 thisObject->convertUndecidedForValue(vm, value);
844 return putByIndex(cell, exec, propertyName, value, shouldThrow);
847 case ALL_INT32_INDEXING_TYPES: {
848 if (!value.isInt32()) {
849 thisObject->convertInt32ForValue(vm, value);
850 return putByIndex(cell, exec, propertyName, value, shouldThrow);
855 case ALL_CONTIGUOUS_INDEXING_TYPES: {
856 Butterfly* butterfly = thisObject->butterfly();
857 if (propertyName >= butterfly->vectorLength())
859 butterfly->contiguous().at(thisObject, propertyName).set(vm, thisObject, value);
860 if (propertyName >= butterfly->publicLength())
861 butterfly->setPublicLength(propertyName + 1);
865 case ALL_DOUBLE_INDEXING_TYPES: {
866 if (!value.isNumber()) {
867 thisObject->convertDoubleToContiguous(vm);
869 return putByIndex(cell, exec, propertyName, value, shouldThrow);
872 double valueAsDouble = value.asNumber();
873 if (valueAsDouble != valueAsDouble) {
874 thisObject->convertDoubleToContiguous(vm);
876 return putByIndex(cell, exec, propertyName, value, shouldThrow);
878 Butterfly* butterfly = thisObject->butterfly();
879 if (propertyName >= butterfly->vectorLength())
881 butterfly->contiguousDouble().at(thisObject, propertyName) = valueAsDouble;
882 if (propertyName >= butterfly->publicLength())
883 butterfly->setPublicLength(propertyName + 1);
887 case NonArrayWithArrayStorage:
888 case ArrayWithArrayStorage: {
889 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
891 if (propertyName >= storage->vectorLength())
894 WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
895 unsigned length = storage->length();
897 // Update length & m_numValuesInVector as necessary.
898 if (propertyName >= length) {
899 length = propertyName + 1;
900 storage->setLength(length);
901 ++storage->m_numValuesInVector;
902 } else if (!valueSlot)
903 ++storage->m_numValuesInVector;
905 valueSlot.set(vm, thisObject, value);
909 case NonArrayWithSlowPutArrayStorage:
910 case ArrayWithSlowPutArrayStorage: {
911 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
913 if (propertyName >= storage->vectorLength())
916 WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
917 unsigned length = storage->length();
919 // Update length & m_numValuesInVector as necessary.
920 if (propertyName >= length) {
921 bool putResult = false;
922 if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
924 length = propertyName + 1;
925 storage->setLength(length);
926 ++storage->m_numValuesInVector;
927 } else if (!valueSlot) {
928 bool putResult = false;
929 if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
931 ++storage->m_numValuesInVector;
934 valueSlot.set(vm, thisObject, value);
939 RELEASE_ASSERT_NOT_REACHED();
942 return thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
945 ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
947 SparseArrayValueMap* map = storage->m_sparseMap.get();
950 map = allocateSparseIndexMap(vm);
952 if (map->sparseMode())
955 map->setSparseMode();
957 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
958 for (unsigned i = 0; i < usedVectorLength; ++i) {
959 JSValue value = storage->m_vector[i].get();
960 // This will always be a new entry in the map, so no need to check we can write,
961 // and attributes are default so no need to set them.
963 map->add(this, i).iterator->value.forceSet(vm, map, value, 0);
966 DeferGC deferGC(vm.heap);
967 Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
968 RELEASE_ASSERT(newButterfly);
969 newButterfly->arrayStorage()->m_indexBias = 0;
970 newButterfly->arrayStorage()->setVectorLength(0);
971 newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
972 setButterfly(vm, newButterfly);
974 return newButterfly->arrayStorage();
977 void JSObject::enterDictionaryIndexingMode(VM& vm)
979 switch (indexingType()) {
980 case ALL_BLANK_INDEXING_TYPES:
981 case ALL_UNDECIDED_INDEXING_TYPES:
982 case ALL_INT32_INDEXING_TYPES:
983 case ALL_DOUBLE_INDEXING_TYPES:
984 case ALL_CONTIGUOUS_INDEXING_TYPES:
985 // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
986 // this case if we ever cared. Note that ensureArrayStorage() can return null if the object
987 // doesn't support traditional indexed properties. At the time of writing, this just affects
989 if (ArrayStorage* storage = ensureArrayStorageSlow(vm))
990 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, storage);
992 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
993 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
1001 void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
1003 if (mayInterceptIndexedAccesses(vm))
1006 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors));
1008 if (!mayBePrototype())
1011 globalObject(vm)->haveABadTime(vm);
1014 Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length)
1016 ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
1017 IndexingType oldType = indexingType();
1018 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
1019 ASSERT(!needsSlowPutIndexing(vm));
1020 ASSERT(!indexingShouldBeSparse(vm));
1021 Structure* structure = this->structure(vm);
1022 unsigned propertyCapacity = structure->outOfLineCapacity();
1023 unsigned vectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, length);
1024 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
1025 butterfly(), vm, this, structure, propertyCapacity, false, 0,
1026 sizeof(EncodedJSValue) * vectorLength);
1027 newButterfly->setPublicLength(length);
1028 newButterfly->setVectorLength(vectorLength);
1029 return newButterfly;
1032 Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
1034 DeferGC deferGC(vm.heap);
1035 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1036 StructureID oldStructureID = this->structureID();
1037 Structure* oldStructure = vm.getStructure(oldStructureID);
1038 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateUndecided);
1039 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1040 setStructure(vm, newStructure);
1041 return newButterfly;
1044 ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
1046 DeferGC deferGC(vm.heap);
1047 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1048 for (unsigned i = newButterfly->vectorLength(); i--;)
1049 newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1050 StructureID oldStructureID = this->structureID();
1051 Structure* oldStructure = vm.getStructure(oldStructureID);
1052 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateInt32);
1053 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1054 setStructure(vm, newStructure);
1055 return newButterfly->contiguousInt32();
1058 ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
1060 DeferGC deferGC(vm.heap);
1061 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1062 for (unsigned i = newButterfly->vectorLength(); i--;)
1063 newButterfly->contiguousDouble().at(this, i) = PNaN;
1064 StructureID oldStructureID = this->structureID();
1065 Structure* oldStructure = vm.getStructure(oldStructureID);
1066 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateDouble);
1067 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1068 setStructure(vm, newStructure);
1069 return newButterfly->contiguousDouble();
1072 ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
1074 DeferGC deferGC(vm.heap);
1075 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1076 for (unsigned i = newButterfly->vectorLength(); i--;)
1077 newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1078 StructureID oldStructureID = this->structureID();
1079 Structure* oldStructure = vm.getStructure(oldStructureID);
1080 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateContiguous);
1081 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1082 setStructure(vm, newStructure);
1083 return newButterfly->contiguous();
1086 Butterfly* JSObject::createArrayStorageButterfly(VM& vm, JSObject* intendedOwner, Structure* structure, unsigned length, unsigned vectorLength, Butterfly* oldButterfly)
1088 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
1089 oldButterfly, vm, intendedOwner, structure, structure->outOfLineCapacity(), false, 0,
1090 ArrayStorage::sizeFor(vectorLength));
1091 RELEASE_ASSERT(newButterfly);
1093 ArrayStorage* result = newButterfly->arrayStorage();
1094 result->setLength(length);
1095 result->setVectorLength(vectorLength);
1096 result->m_sparseMap.clear();
1097 result->m_numValuesInVector = 0;
1098 result->m_indexBias = 0;
1099 for (size_t i = vectorLength; i--;)
1100 result->m_vector[i].setWithoutWriteBarrier(JSValue());
1102 return newButterfly;
1105 ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
1107 DeferGC deferGC(vm.heap);
1108 StructureID oldStructureID = this->structureID();
1109 Structure* oldStructure = vm.getStructure(oldStructureID);
1110 IndexingType oldType = indexingType();
1111 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
1113 Butterfly* newButterfly = createArrayStorageButterfly(vm, this, oldStructure, length, vectorLength, butterfly());
1114 ArrayStorage* result = newButterfly->arrayStorage();
1115 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, suggestedArrayStorageTransition(vm));
1116 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1117 setStructure(vm, newStructure);
1121 ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
1123 return createArrayStorage(
1124 vm, 0, ArrayStorage::optimalVectorLength(0, structure(vm)->outOfLineCapacity(), 0));
1127 ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
1129 ASSERT(hasUndecided(indexingType()));
1131 Butterfly* butterfly = this->butterfly();
1132 for (unsigned i = butterfly->vectorLength(); i--;)
1133 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1135 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32));
1136 return m_butterfly->contiguousInt32();
1139 ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
1141 ASSERT(hasUndecided(indexingType()));
1143 Butterfly* butterfly = m_butterfly.get();
1144 for (unsigned i = butterfly->vectorLength(); i--;)
1145 butterfly->contiguousDouble().at(this, i) = PNaN;
1147 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
1148 return m_butterfly->contiguousDouble();
1151 ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
1153 ASSERT(hasUndecided(indexingType()));
1155 Butterfly* butterfly = m_butterfly.get();
1156 for (unsigned i = butterfly->vectorLength(); i--;)
1157 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1159 WTF::storeStoreFence();
1160 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1161 return m_butterfly->contiguous();
1164 ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
1166 Structure* structure = this->structure(vm);
1167 unsigned publicLength = m_butterfly->publicLength();
1168 unsigned propertyCapacity = structure->outOfLineCapacity();
1170 Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
1173 newButterfly->base(0, propertyCapacity),
1174 m_butterfly->base(0, propertyCapacity),
1175 propertyCapacity * sizeof(EncodedJSValue));
1177 ArrayStorage* newStorage = newButterfly->arrayStorage();
1178 newStorage->setVectorLength(neededLength);
1179 newStorage->setLength(publicLength);
1180 newStorage->m_sparseMap.clear();
1181 newStorage->m_indexBias = 0;
1182 newStorage->m_numValuesInVector = 0;
1187 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
1189 DeferGC deferGC(vm.heap);
1190 ASSERT(hasUndecided(indexingType()));
1192 unsigned vectorLength = m_butterfly->vectorLength();
1193 ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1195 for (unsigned i = vectorLength; i--;)
1196 storage->m_vector[i].setWithoutWriteBarrier(JSValue());
1198 StructureID oldStructureID = this->structureID();
1199 Structure* oldStructure = vm.getStructure(oldStructureID);
1200 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1201 nukeStructureAndSetButterfly(vm, oldStructureID, storage->butterfly());
1202 setStructure(vm, newStructure);
1206 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
1208 return convertUndecidedToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1211 ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
1213 ASSERT(hasInt32(indexingType()));
1214 ASSERT(!isCopyOnWrite(indexingMode()));
1216 Butterfly* butterfly = m_butterfly.get();
1217 for (unsigned i = butterfly->vectorLength(); i--;) {
1218 WriteBarrier<Unknown>* current = &butterfly->contiguous().atUnsafe(i);
1219 double* currentAsDouble = bitwise_cast<double*>(current);
1220 JSValue v = current->get();
1221 // NOTE: Since this may be used during initialization, v could be garbage. If it's garbage,
1222 // that means it will be overwritten later.
1224 *currentAsDouble = PNaN;
1227 *currentAsDouble = v.asInt32();
1230 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
1231 return m_butterfly->contiguousDouble();
1234 ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
1236 ASSERT(hasInt32(indexingType()));
1238 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1239 return m_butterfly->contiguous();
1242 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
1244 DeferGC deferGC(vm.heap);
1245 ASSERT(hasInt32(indexingType()));
1247 unsigned vectorLength = m_butterfly->vectorLength();
1248 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1249 Butterfly* butterfly = m_butterfly.get();
1250 for (unsigned i = 0; i < vectorLength; i++) {
1251 JSValue v = butterfly->contiguous().at(this, i).get();
1252 newStorage->m_vector[i].setWithoutWriteBarrier(v);
1254 newStorage->m_numValuesInVector++;
1257 StructureID oldStructureID = this->structureID();
1258 Structure* oldStructure = vm.getStructure(oldStructureID);
1259 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1260 nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1261 setStructure(vm, newStructure);
1265 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
1267 return convertInt32ToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1270 ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
1272 ASSERT(hasDouble(indexingType()));
1273 ASSERT(!isCopyOnWrite(indexingMode()));
1275 Butterfly* butterfly = m_butterfly.get();
1276 for (unsigned i = butterfly->vectorLength(); i--;) {
1277 double* current = &butterfly->contiguousDouble().atUnsafe(i);
1278 WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
1279 double value = *current;
1280 if (value != value) {
1281 currentAsValue->clear();
1284 JSValue v = JSValue(JSValue::EncodeAsDouble, value);
1285 currentAsValue->setWithoutWriteBarrier(v);
1288 WTF::storeStoreFence();
1289 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1290 return m_butterfly->contiguous();
1293 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
1295 DeferGC deferGC(vm.heap);
1296 ASSERT(hasDouble(indexingType()));
1298 unsigned vectorLength = m_butterfly->vectorLength();
1299 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1300 Butterfly* butterfly = m_butterfly.get();
1301 for (unsigned i = 0; i < vectorLength; i++) {
1302 double value = butterfly->contiguousDouble().at(this, i);
1303 if (value != value) {
1304 newStorage->m_vector[i].clear();
1307 newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
1308 newStorage->m_numValuesInVector++;
1311 StructureID oldStructureID = this->structureID();
1312 Structure* oldStructure = vm.getStructure(oldStructureID);
1313 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1314 nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1315 setStructure(vm, newStructure);
1319 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
1321 return convertDoubleToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1324 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
1326 DeferGC deferGC(vm.heap);
1327 ASSERT(hasContiguous(indexingType()));
1329 unsigned vectorLength = m_butterfly->vectorLength();
1330 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1331 Butterfly* butterfly = m_butterfly.get();
1332 for (unsigned i = 0; i < vectorLength; i++) {
1333 JSValue v = butterfly->contiguous().at(this, i).get();
1334 newStorage->m_vector[i].setWithoutWriteBarrier(v);
1336 newStorage->m_numValuesInVector++;
1339 // While we modify the butterfly of Contiguous Array, we do not take any cellLock here. This is because
1340 // (1) the old butterfly is not changed and (2) new butterfly is not changed after it is exposed to
1342 // The mutator performs the following operations are sequentially executed by using storeStoreFence.
1344 // CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure
1346 // Meanwhile the collector performs the following steps sequentially:
1348 // ReadStructureEarly ReadButterfly ReadStructureLate
1350 // We list up all the patterns by writing a tiny script, and ensure all the cases are categorized into BEFORE, AFTER, and IGNORE.
1352 // CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureEarly ReadButterfly ReadStructureLate: AFTER, trivially
1353 // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
1354 // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1355 // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1356 // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
1357 // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1358 // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1359 // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1360 // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1361 // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read early
1362 // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
1363 // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1364 // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1365 // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1366 // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1367 // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1368 // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1369 // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1370 // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1371 // CreateNewButterfly ReadStructureEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
1372 // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
1373 // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1374 // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1375 // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1376 // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1377 // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1378 // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1379 // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1380 // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1381 // ReadStructureEarly CreateNewButterfly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
1382 // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1383 // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1384 // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1385 // ReadStructureEarly ReadButterfly CreateNewButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
1386 // ReadStructureEarly ReadButterfly ReadStructureLate CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
1388 ASSERT(newStorage->butterfly() != butterfly);
1389 StructureID oldStructureID = this->structureID();
1390 Structure* oldStructure = vm.getStructure(oldStructureID);
1391 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1393 // Ensure new Butterfly initialization is correctly done before exposing it to the concurrent threads.
1394 if (isX86() || vm.heap.mutatorShouldBeFenced())
1395 WTF::storeStoreFence();
1396 nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1397 setStructure(vm, newStructure);
1402 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
1404 return convertContiguousToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1407 void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
1409 IndexingType type = indexingTypeForValue(value);
1410 if (type == Int32Shape) {
1411 convertUndecidedToInt32(vm);
1415 if (type == DoubleShape) {
1416 convertUndecidedToDouble(vm);
1420 ASSERT(type == ContiguousShape);
1421 convertUndecidedToContiguous(vm);
1424 void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
1426 if (value.isInt32()) {
1427 createInitialInt32(vm, index + 1).at(this, index).set(vm, this, value);
1431 if (value.isDouble()) {
1432 double doubleValue = value.asNumber();
1433 if (doubleValue == doubleValue) {
1434 createInitialDouble(vm, index + 1).at(this, index) = doubleValue;
1439 createInitialContiguous(vm, index + 1).at(this, index).set(vm, this, value);
1442 void JSObject::convertInt32ForValue(VM& vm, JSValue value)
1444 ASSERT(!value.isInt32());
1446 if (value.isDouble() && !std::isnan(value.asDouble())) {
1447 convertInt32ToDouble(vm);
1451 convertInt32ToContiguous(vm);
1454 void JSObject::convertFromCopyOnWrite(VM& vm)
1456 ASSERT(isCopyOnWrite(indexingMode()));
1457 ASSERT(structure(vm)->indexingMode() == indexingMode());
1459 const bool hasIndexingHeader = true;
1460 Butterfly* oldButterfly = butterfly();
1461 size_t propertyCapacity = 0;
1462 unsigned newVectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, std::min(oldButterfly->vectorLength() * 2, MAX_STORAGE_VECTOR_LENGTH));
1463 Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, hasIndexingHeader, newVectorLength * sizeof(JSValue));
1465 memcpy(newButterfly->propertyStorage(), oldButterfly->propertyStorage(), oldButterfly->vectorLength() * sizeof(JSValue) + sizeof(IndexingHeader));
1467 WTF::storeStoreFence();
1468 NonPropertyTransition transition = ([&] () {
1469 switch (indexingType()) {
1470 case ArrayWithInt32:
1471 return NonPropertyTransition::AllocateInt32;
1472 case ArrayWithDouble:
1473 return NonPropertyTransition::AllocateDouble;
1474 case ArrayWithContiguous:
1475 return NonPropertyTransition::AllocateContiguous;
1477 RELEASE_ASSERT_NOT_REACHED();
1478 return NonPropertyTransition::AllocateContiguous;
1481 StructureID oldStructureID = structureID();
1482 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
1483 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1484 setStructure(vm, newStructure);
1487 void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
1489 ASSERT(index < m_butterfly->publicLength());
1490 ASSERT(index < m_butterfly->vectorLength());
1491 convertUndecidedForValue(vm, value);
1492 setIndexQuickly(vm, index, value);
1495 void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1497 ASSERT(!value.isInt32());
1498 convertInt32ForValue(vm, value);
1499 setIndexQuickly(vm, index, value);
1502 void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1504 ASSERT(!value.isNumber() || value.asNumber() != value.asNumber());
1505 convertDoubleToContiguous(vm);
1506 setIndexQuickly(vm, index, value);
1509 ContiguousJSValues JSObject::tryMakeWritableInt32Slow(VM& vm)
1511 ASSERT(inherits(vm, info()));
1513 if (isCopyOnWrite(indexingMode())) {
1514 if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, Int32Shape) == Int32Shape) {
1515 ASSERT(hasInt32(indexingMode()));
1516 convertFromCopyOnWrite(vm);
1517 return butterfly()->contiguousInt32();
1519 return ContiguousJSValues();
1522 if (structure(vm)->hijacksIndexingHeader())
1523 return ContiguousJSValues();
1525 switch (indexingType()) {
1526 case ALL_BLANK_INDEXING_TYPES:
1527 if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1528 return ContiguousJSValues();
1529 return createInitialInt32(vm, 0);
1531 case ALL_UNDECIDED_INDEXING_TYPES:
1532 return convertUndecidedToInt32(vm);
1534 case ALL_DOUBLE_INDEXING_TYPES:
1535 case ALL_CONTIGUOUS_INDEXING_TYPES:
1536 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1537 return ContiguousJSValues();
1541 return ContiguousJSValues();
1545 ContiguousDoubles JSObject::tryMakeWritableDoubleSlow(VM& vm)
1547 ASSERT(inherits(vm, info()));
1549 if (isCopyOnWrite(indexingMode())) {
1550 if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, DoubleShape) == DoubleShape) {
1551 convertFromCopyOnWrite(vm);
1552 if (hasDouble(indexingMode()))
1553 return butterfly()->contiguousDouble();
1554 ASSERT(hasInt32(indexingMode()));
1556 return ContiguousDoubles();
1559 if (structure(vm)->hijacksIndexingHeader())
1560 return ContiguousDoubles();
1562 switch (indexingType()) {
1563 case ALL_BLANK_INDEXING_TYPES:
1564 if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1565 return ContiguousDoubles();
1566 return createInitialDouble(vm, 0);
1568 case ALL_UNDECIDED_INDEXING_TYPES:
1569 return convertUndecidedToDouble(vm);
1571 case ALL_INT32_INDEXING_TYPES:
1572 return convertInt32ToDouble(vm);
1574 case ALL_CONTIGUOUS_INDEXING_TYPES:
1575 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1576 return ContiguousDoubles();
1580 return ContiguousDoubles();
1584 ContiguousJSValues JSObject::tryMakeWritableContiguousSlow(VM& vm)
1586 ASSERT(inherits(vm, info()));
1588 if (isCopyOnWrite(indexingMode())) {
1589 if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, ContiguousShape) == ContiguousShape) {
1590 convertFromCopyOnWrite(vm);
1591 if (hasContiguous(indexingMode()))
1592 return butterfly()->contiguous();
1593 ASSERT(hasInt32(indexingMode()) || hasDouble(indexingMode()));
1595 return ContiguousJSValues();
1598 if (structure(vm)->hijacksIndexingHeader())
1599 return ContiguousJSValues();
1601 switch (indexingType()) {
1602 case ALL_BLANK_INDEXING_TYPES:
1603 if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1604 return ContiguousJSValues();
1605 return createInitialContiguous(vm, 0);
1607 case ALL_UNDECIDED_INDEXING_TYPES:
1608 return convertUndecidedToContiguous(vm);
1610 case ALL_INT32_INDEXING_TYPES:
1611 return convertInt32ToContiguous(vm);
1613 case ALL_DOUBLE_INDEXING_TYPES:
1614 return convertDoubleToContiguous(vm);
1616 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1617 return ContiguousJSValues();
1621 return ContiguousJSValues();
1625 ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
1627 ASSERT(inherits(vm, info()));
1629 if (structure(vm)->hijacksIndexingHeader())
1634 switch (indexingType()) {
1635 case ALL_BLANK_INDEXING_TYPES:
1636 if (UNLIKELY(indexingShouldBeSparse(vm)))
1637 return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
1638 return createInitialArrayStorage(vm);
1640 case ALL_UNDECIDED_INDEXING_TYPES:
1641 ASSERT(!indexingShouldBeSparse(vm));
1642 ASSERT(!needsSlowPutIndexing(vm));
1643 return convertUndecidedToArrayStorage(vm);
1645 case ALL_INT32_INDEXING_TYPES:
1646 ASSERT(!indexingShouldBeSparse(vm));
1647 ASSERT(!needsSlowPutIndexing(vm));
1648 return convertInt32ToArrayStorage(vm);
1650 case ALL_DOUBLE_INDEXING_TYPES:
1651 ASSERT(!indexingShouldBeSparse(vm));
1652 ASSERT(!needsSlowPutIndexing(vm));
1653 return convertDoubleToArrayStorage(vm);
1655 case ALL_CONTIGUOUS_INDEXING_TYPES:
1656 ASSERT(!indexingShouldBeSparse(vm));
1657 ASSERT(!needsSlowPutIndexing(vm));
1658 return convertContiguousToArrayStorage(vm);
1661 RELEASE_ASSERT_NOT_REACHED();
1666 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
1670 switch (indexingType()) {
1671 case ALL_BLANK_INDEXING_TYPES: {
1672 createArrayStorage(vm, 0, 0);
1673 SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1674 map->setSparseMode();
1675 return arrayStorage();
1678 case ALL_UNDECIDED_INDEXING_TYPES:
1679 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm));
1681 case ALL_INT32_INDEXING_TYPES:
1682 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm));
1684 case ALL_DOUBLE_INDEXING_TYPES:
1685 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm));
1687 case ALL_CONTIGUOUS_INDEXING_TYPES:
1688 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm));
1690 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1691 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
1699 void JSObject::switchToSlowPutArrayStorage(VM& vm)
1703 switch (indexingType()) {
1705 ensureArrayStorage(vm);
1706 RELEASE_ASSERT(hasAnyArrayStorage(indexingType()));
1707 if (hasSlowPutArrayStorage(indexingType()))
1709 switchToSlowPutArrayStorage(vm);
1712 case ALL_UNDECIDED_INDEXING_TYPES:
1713 convertUndecidedToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1716 case ALL_INT32_INDEXING_TYPES:
1717 convertInt32ToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1720 case ALL_DOUBLE_INDEXING_TYPES:
1721 convertDoubleToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1724 case ALL_CONTIGUOUS_INDEXING_TYPES:
1725 convertContiguousToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1728 case NonArrayWithArrayStorage:
1729 case ArrayWithArrayStorage: {
1730 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::SwitchToSlowPutArrayStorage);
1731 setStructure(vm, newStructure);
1741 void JSObject::setPrototypeDirect(VM& vm, JSValue prototype)
1744 if (prototype.isObject())
1745 prototype.asCell()->didBecomePrototype();
1747 if (structure(vm)->hasMonoProto()) {
1748 DeferredStructureTransitionWatchpointFire deferred(vm, structure(vm));
1749 Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype, deferred);
1750 setStructure(vm, newStructure);
1752 putDirect(vm, knownPolyProtoOffset, prototype);
1754 if (!anyObjectInChainMayInterceptIndexedAccesses(vm))
1757 if (mayBePrototype()) {
1758 structure(vm)->globalObject()->haveABadTime(vm);
1762 if (!hasIndexedProperties(indexingType()))
1765 if (shouldUseSlowPut(indexingType()))
1768 switchToSlowPutArrayStorage(vm);
1771 bool JSObject::setPrototypeWithCycleCheck(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1773 auto scope = DECLARE_THROW_SCOPE(vm);
1775 if (this->structure(vm)->isImmutablePrototypeExoticObject()) {
1776 // This implements https://tc39.github.io/ecma262/#sec-set-immutable-prototype.
1777 if (this->getPrototype(vm, exec) == prototype)
1780 return typeError(exec, scope, shouldThrowIfCantSet, "Cannot set prototype of immutable prototype object"_s);
1783 ASSERT(methodTable(vm)->toThis(this, exec, NotStrictMode) == this);
1785 if (this->getPrototypeDirect(vm) == prototype)
1788 bool isExtensible = this->isExtensible(exec);
1789 RETURN_IF_EXCEPTION(scope, false);
1792 return typeError(exec, scope, shouldThrowIfCantSet, ReadonlyPropertyWriteError);
1794 JSValue nextPrototype = prototype;
1795 while (nextPrototype && nextPrototype.isObject()) {
1796 if (nextPrototype == this)
1797 return typeError(exec, scope, shouldThrowIfCantSet, "cyclic __proto__ value"_s);
1798 // FIXME: The specification currently says we should check if the [[GetPrototypeOf]] internal method of nextPrototype
1799 // is not the ordinary object internal method. However, we currently restrict this to Proxy objects as it would allow
1800 // for cycles with certain HTML objects (WindowProxy, Location) otherwise.
1801 // https://bugs.webkit.org/show_bug.cgi?id=161534
1802 if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType))
1803 break; // We're done. Set the prototype.
1804 nextPrototype = asObject(nextPrototype)->getPrototypeDirect(vm);
1806 setPrototypeDirect(vm, prototype);
1810 bool JSObject::setPrototype(JSObject* object, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1812 return object->setPrototypeWithCycleCheck(exec->vm(), exec, prototype, shouldThrowIfCantSet);
1815 JSValue JSObject::getPrototype(JSObject* object, ExecState* exec)
1817 return object->getPrototypeDirect(exec->vm());
1820 bool JSObject::setPrototype(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1822 return methodTable(vm)->setPrototype(this, exec, prototype, shouldThrowIfCantSet);
1825 bool JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter, unsigned attributes)
1827 PropertyDescriptor descriptor;
1828 descriptor.setGetter(getter);
1830 ASSERT(attributes & PropertyAttribute::Accessor);
1831 if (!(attributes & PropertyAttribute::ReadOnly))
1832 descriptor.setConfigurable(true);
1833 if (!(attributes & PropertyAttribute::DontEnum))
1834 descriptor.setEnumerable(true);
1836 return defineOwnProperty(this, exec, propertyName, descriptor, true);
1839 bool JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter, unsigned attributes)
1841 PropertyDescriptor descriptor;
1842 descriptor.setSetter(setter);
1844 ASSERT(attributes & PropertyAttribute::Accessor);
1845 if (!(attributes & PropertyAttribute::ReadOnly))
1846 descriptor.setConfigurable(true);
1847 if (!(attributes & PropertyAttribute::DontEnum))
1848 descriptor.setEnumerable(true);
1850 return defineOwnProperty(this, exec, propertyName, descriptor, true);
1853 bool JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1855 ASSERT(attributes & PropertyAttribute::Accessor);
1857 if (std::optional<uint32_t> index = parseIndex(propertyName))
1858 return putDirectIndex(exec, index.value(), accessor, attributes, PutDirectIndexLikePutDirect);
1860 return putDirectNonIndexAccessor(exec->vm(), propertyName, accessor, attributes);
1863 // FIXME: Introduce a JSObject::putDirectCustomValue() method instead of using
1864 // JSObject::putDirectCustomAccessor() to put CustomValues.
1865 // https://bugs.webkit.org/show_bug.cgi?id=192576
1866 bool JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1868 ASSERT(!parseIndex(propertyName));
1869 ASSERT(value.isCustomGetterSetter());
1870 if (!(attributes & PropertyAttribute::CustomAccessor))
1871 attributes |= PropertyAttribute::CustomValue;
1873 PutPropertySlot slot(this);
1874 bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1876 ASSERT(slot.type() == PutPropertySlot::NewProperty);
1878 Structure* structure = this->structure(vm);
1879 if (attributes & PropertyAttribute::ReadOnly)
1880 structure->setContainsReadOnlyProperties();
1881 structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1885 bool JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1887 ASSERT(attributes & PropertyAttribute::Accessor);
1888 PutPropertySlot slot(this);
1889 bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, accessor, attributes, slot);
1891 Structure* structure = this->structure(vm);
1892 if (attributes & PropertyAttribute::ReadOnly)
1893 structure->setContainsReadOnlyProperties();
1895 structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1899 // HasProperty(O, P) from Section 7.3.10 of the spec.
1900 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasproperty
1901 bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
1903 return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1906 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
1908 return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1911 bool JSObject::hasPropertyGeneric(ExecState* exec, PropertyName propertyName, PropertySlot::InternalMethodType internalMethodType) const
1913 PropertySlot slot(this, internalMethodType);
1914 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1917 bool JSObject::hasPropertyGeneric(ExecState* exec, unsigned propertyName, PropertySlot::InternalMethodType internalMethodType) const
1919 PropertySlot slot(this, internalMethodType);
1920 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1924 bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
1926 JSObject* thisObject = jsCast<JSObject*>(cell);
1927 VM& vm = exec->vm();
1929 if (std::optional<uint32_t> index = parseIndex(propertyName))
1930 return thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, index.value());
1932 unsigned attributes;
1934 if (!thisObject->staticPropertiesReified(vm)) {
1935 if (auto entry = thisObject->findPropertyHashEntry(vm, propertyName)) {
1936 // If the static table contains a non-configurable (DontDelete) property then we can return early;
1937 // if there is a property in the storage array it too must be non-configurable (the language does
1938 // not allow repacement of a non-configurable property with a configurable one).
1939 if (entry->value->attributes() & PropertyAttribute::DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
1940 ASSERT(!isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes)) || attributes & PropertyAttribute::DontDelete);
1943 thisObject->reifyAllStaticProperties(exec);
1947 Structure* structure = thisObject->structure(vm);
1949 bool propertyIsPresent = isValidOffset(structure->get(vm, propertyName, attributes));
1950 if (propertyIsPresent) {
1951 if (attributes & PropertyAttribute::DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable)
1954 PropertyOffset offset;
1955 if (structure->isUncacheableDictionary())
1956 offset = structure->removePropertyWithoutTransition(vm, propertyName, [] (const ConcurrentJSLocker&, PropertyOffset) { });
1958 thisObject->setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
1960 if (offset != invalidOffset)
1961 thisObject->locationForOffset(offset)->clear();
1967 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
1969 VM& vm = exec->vm();
1970 JSObject* thisObject = jsCast<JSObject*>(cell);
1972 if (i > MAX_ARRAY_INDEX)
1973 return thisObject->methodTable(vm)->deleteProperty(thisObject, exec, Identifier::from(exec, i));
1975 switch (thisObject->indexingMode()) {
1976 case ALL_BLANK_INDEXING_TYPES:
1977 case ALL_UNDECIDED_INDEXING_TYPES:
1980 case CopyOnWriteArrayWithInt32:
1981 case CopyOnWriteArrayWithContiguous: {
1982 Butterfly* butterfly = thisObject->butterfly();
1983 if (i >= butterfly->vectorLength())
1985 thisObject->convertFromCopyOnWrite(vm);
1989 case ALL_WRITABLE_INT32_INDEXING_TYPES:
1990 case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES: {
1991 Butterfly* butterfly = thisObject->butterfly();
1992 if (i >= butterfly->vectorLength())
1994 butterfly->contiguous().at(thisObject, i).clear();
1998 case CopyOnWriteArrayWithDouble: {
1999 Butterfly* butterfly = thisObject->butterfly();
2000 if (i >= butterfly->vectorLength())
2002 thisObject->convertFromCopyOnWrite(vm);
2006 case ALL_WRITABLE_DOUBLE_INDEXING_TYPES: {
2007 Butterfly* butterfly = thisObject->butterfly();
2008 if (i >= butterfly->vectorLength())
2010 butterfly->contiguousDouble().at(thisObject, i) = PNaN;
2014 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
2015 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
2017 if (i < storage->vectorLength()) {
2018 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
2021 --storage->m_numValuesInVector;
2023 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
2024 SparseArrayValueMap::iterator it = map->find(i);
2025 if (it != map->notFound()) {
2026 if (it->value.attributes() & PropertyAttribute::DontDelete)
2036 RELEASE_ASSERT_NOT_REACHED();
2041 enum class TypeHintMode { TakesHint, DoesNotTakeHint };
2043 template<TypeHintMode mode = TypeHintMode::DoesNotTakeHint>
2044 static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSObject* object, PropertyName propertyName, PreferredPrimitiveType hint)
2046 VM& vm = exec->vm();
2047 auto scope = DECLARE_THROW_SCOPE(vm);
2049 PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
2050 // FIXME: Remove this when we have fixed: rdar://problem/33451840
2051 // https://bugs.webkit.org/show_bug.cgi?id=187109.
2052 constexpr bool debugNullStructure = mode == TypeHintMode::TakesHint;
2053 bool hasProperty = const_cast<JSObject*>(object)->getPropertySlot<debugNullStructure>(exec, propertyName, slot);
2054 RETURN_IF_EXCEPTION(scope, scope.exception());
2055 JSValue function = hasProperty ? slot.getValue(exec, propertyName) : jsUndefined();
2056 RETURN_IF_EXCEPTION(scope, scope.exception());
2057 if (function.isUndefinedOrNull() && mode == TypeHintMode::TakesHint)
2060 CallType callType = getCallData(vm, function, callData);
2061 if (callType == CallType::None) {
2062 if (mode == TypeHintMode::TakesHint)
2063 throwTypeError(exec, scope, "Symbol.toPrimitive is not a function, undefined, or null"_s);
2064 return scope.exception();
2067 MarkedArgumentBuffer callArgs;
2068 if (mode == TypeHintMode::TakesHint) {
2069 JSString* hintString = nullptr;
2072 hintString = vm.smallStrings.defaultString();
2075 hintString = vm.smallStrings.numberString();
2078 hintString = vm.smallStrings.stringString();
2081 callArgs.append(hintString);
2083 ASSERT(!callArgs.hasOverflowed());
2085 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), callArgs);
2086 RETURN_IF_EXCEPTION(scope, scope.exception());
2087 ASSERT(!result.isGetterSetter());
2088 if (result.isObject())
2089 return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, scope, "Symbol.toPrimitive returned an object"_s);
2094 JSValue JSObject::ordinaryToPrimitive(ExecState* exec, PreferredPrimitiveType hint) const
2096 VM& vm = exec->vm();
2097 auto scope = DECLARE_THROW_SCOPE(vm);
2099 // Make sure that whatever default value methods there are on object's prototype chain are
2101 for (const JSObject* object = this; object; object = object->structure(vm)->storedPrototypeObject(object))
2102 object->structure(vm)->startWatchingInternalPropertiesIfNecessary(vm);
2105 if (hint == PreferString) {
2106 value = callToPrimitiveFunction(exec, this, vm.propertyNames->toString, hint);
2107 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2110 value = callToPrimitiveFunction(exec, this, vm.propertyNames->valueOf, hint);
2111 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2115 value = callToPrimitiveFunction(exec, this, vm.propertyNames->valueOf, hint);
2116 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2119 value = callToPrimitiveFunction(exec, this, vm.propertyNames->toString, hint);
2120 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2125 scope.assertNoException();
2127 return throwTypeError(exec, scope, "No default value"_s);
2130 JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
2132 return object->ordinaryToPrimitive(exec, hint);
2135 JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
2137 VM& vm = exec->vm();
2138 auto scope = DECLARE_THROW_SCOPE(vm);
2140 JSValue value = callToPrimitiveFunction<TypeHintMode::TakesHint>(exec, this, vm.propertyNames->toPrimitiveSymbol, preferredType);
2141 RETURN_IF_EXCEPTION(scope, { });
2145 RELEASE_AND_RETURN(scope, this->methodTable(vm)->defaultValue(this, exec, preferredType));
2148 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
2150 VM& vm = exec->vm();
2151 auto scope = DECLARE_THROW_SCOPE(vm);
2153 result = toPrimitive(exec, PreferNumber);
2154 RETURN_IF_EXCEPTION(scope, false);
2156 number = result.toNumber(exec);
2157 return !result.isString();
2160 bool JSObject::getOwnStaticPropertySlot(VM& vm, PropertyName propertyName, PropertySlot& slot)
2162 for (auto* info = classInfo(vm); info; info = info->parentClass) {
2163 if (auto* table = info->staticPropHashTable) {
2164 if (getStaticPropertySlotFromTable(vm, table->classForThis, *table, this, propertyName, slot))
2171 auto JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const -> std::optional<PropertyHashEntry>
2173 for (const ClassInfo* info = classInfo(vm); info; info = info->parentClass) {
2174 if (const HashTable* propHashTable = info->staticPropHashTable) {
2175 if (const HashTableValue* entry = propHashTable->entry(propertyName))
2176 return PropertyHashEntry { propHashTable, entry };
2179 return std::nullopt;
2182 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue)
2184 VM& vm = exec->vm();
2185 auto scope = DECLARE_THROW_SCOPE(vm);
2187 if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) {
2189 CallType callType = JSC::getCallData(vm, hasInstanceValue, callData);
2190 if (callType == CallType::None) {
2191 throwException(exec, scope, createInvalidInstanceofParameterErrorHasInstanceValueNotFunction(exec, this));
2195 MarkedArgumentBuffer args;
2197 ASSERT(!args.hasOverflowed());
2198 JSValue result = call(exec, hasInstanceValue, callType, callData, this, args);
2199 RETURN_IF_EXCEPTION(scope, false);
2200 return result.toBoolean(exec);
2203 TypeInfo info = structure(vm)->typeInfo();
2204 if (info.implementsDefaultHasInstance()) {
2205 JSValue prototype = get(exec, vm.propertyNames->prototype);
2206 RETURN_IF_EXCEPTION(scope, false);
2207 RELEASE_AND_RETURN(scope, defaultHasInstance(exec, value, prototype));
2209 if (info.implementsHasInstance())
2210 RELEASE_AND_RETURN(scope, methodTable(vm)->customHasInstance(this, exec, value));
2212 throwException(exec, scope, createInvalidInstanceofParameterErrorNotFunction(exec, this));
2216 bool JSObject::hasInstance(ExecState* exec, JSValue value)
2218 VM& vm = exec->vm();
2219 auto scope = DECLARE_THROW_SCOPE(vm);
2220 JSValue hasInstanceValue = get(exec, vm.propertyNames->hasInstanceSymbol);
2221 RETURN_IF_EXCEPTION(scope, false);
2223 RELEASE_AND_RETURN(scope, hasInstance(exec, value, hasInstanceValue));
2226 bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
2228 VM& vm = exec->vm();
2229 auto scope = DECLARE_THROW_SCOPE(vm);
2231 if (!value.isObject())
2234 if (!proto.isObject()) {
2235 throwTypeError(exec, scope, "instanceof called on an object with an invalid prototype property."_s);
2239 JSObject* object = asObject(value);
2241 JSValue objectValue = object->getPrototype(vm, exec);
2242 RETURN_IF_EXCEPTION(scope, false);
2243 if (!objectValue.isObject())
2245 object = asObject(objectValue);
2246 if (proto == object)
2249 ASSERT_NOT_REACHED();
2252 EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState* exec)
2254 JSValue value = exec->uncheckedArgument(0);
2255 JSValue proto = exec->uncheckedArgument(1);
2257 return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
2260 void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2262 VM& vm = exec->vm();
2263 auto scope = DECLARE_THROW_SCOPE(vm);
2264 object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, mode);
2265 RETURN_IF_EXCEPTION(scope, void());
2267 JSValue nextProto = object->getPrototype(vm, exec);
2268 RETURN_IF_EXCEPTION(scope, void());
2269 if (nextProto.isNull())
2272 JSObject* prototype = asObject(nextProto);
2274 if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
2276 prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
2279 prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
2280 RETURN_IF_EXCEPTION(scope, void());
2281 nextProto = prototype->getPrototype(vm, exec);
2282 RETURN_IF_EXCEPTION(scope, void());
2283 if (nextProto.isNull())
2285 prototype = asObject(nextProto);
2289 void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2291 VM& vm = exec->vm();
2292 if (!mode.includeJSObjectProperties()) {
2293 // We still have to get non-indexed properties from any subclasses of JSObject that have them.
2294 object->methodTable(vm)->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
2298 if (propertyNames.includeStringProperties()) {
2299 // Add numeric properties first. That appears to be the accepted convention.
2300 // FIXME: Filling PropertyNameArray with an identifier for every integer
2301 // is incredibly inefficient for large arrays. We need a different approach,
2302 // which almost certainly means a different structure for PropertyNameArray.
2303 switch (object->indexingType()) {
2304 case ALL_BLANK_INDEXING_TYPES:
2305 case ALL_UNDECIDED_INDEXING_TYPES:
2308 case ALL_INT32_INDEXING_TYPES:
2309 case ALL_CONTIGUOUS_INDEXING_TYPES: {
2310 Butterfly* butterfly = object->butterfly();
2311 unsigned usedLength = butterfly->publicLength();
2312 for (unsigned i = 0; i < usedLength; ++i) {
2313 if (!butterfly->contiguous().at(object, i))
2315 propertyNames.add(i);
2320 case ALL_DOUBLE_INDEXING_TYPES: {
2321 Butterfly* butterfly = object->butterfly();
2322 unsigned usedLength = butterfly->publicLength();
2323 for (unsigned i = 0; i < usedLength; ++i) {
2324 double value = butterfly->contiguousDouble().at(object, i);
2327 propertyNames.add(i);
2332 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
2333 ArrayStorage* storage = object->m_butterfly->arrayStorage();
2335 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
2336 for (unsigned i = 0; i < usedVectorLength; ++i) {
2337 if (storage->m_vector[i])
2338 propertyNames.add(i);
2341 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
2342 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
2343 keys.reserveInitialCapacity(map->size());
2345 SparseArrayValueMap::const_iterator end = map->end();
2346 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
2347 if (mode.includeDontEnumProperties() || !(it->value.attributes() & PropertyAttribute::DontEnum))
2348 keys.uncheckedAppend(static_cast<unsigned>(it->key));
2351 std::sort(keys.begin(), keys.end());
2352 for (unsigned i = 0; i < keys.size(); ++i)
2353 propertyNames.add(keys[i]);
2359 RELEASE_ASSERT_NOT_REACHED();
2363 object->methodTable(vm)->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
2366 void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2368 VM& vm = exec->vm();
2369 if (!object->staticPropertiesReified(vm))
2370 getClassPropertyNames(exec, object->classInfo(vm), propertyNames, mode);
2372 if (!mode.includeJSObjectProperties())
2375 object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
2378 double JSObject::toNumber(ExecState* exec) const
2380 VM& vm = exec->vm();
2381 auto scope = DECLARE_THROW_SCOPE(vm);
2382 JSValue primitive = toPrimitive(exec, PreferNumber);
2383 RETURN_IF_EXCEPTION(scope, 0.0); // should be picked up soon in Nodes.cpp
2384 RELEASE_AND_RETURN(scope, primitive.toNumber(exec));
2387 JSString* JSObject::toString(ExecState* exec) const
2389 VM& vm = exec->vm();
2390 auto scope = DECLARE_THROW_SCOPE(vm);
2391 JSValue primitive = toPrimitive(exec, PreferString);
2392 RETURN_IF_EXCEPTION(scope, jsEmptyString(exec));
2393 return primitive.toString(exec);
2396 JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode)
2398 return jsCast<JSObject*>(cell);
2401 void JSObject::seal(VM& vm)
2405 enterDictionaryIndexingMode(vm);
2406 setStructure(vm, Structure::sealTransition(vm, structure(vm)));
2409 void JSObject::freeze(VM& vm)
2413 enterDictionaryIndexingMode(vm);
2414 setStructure(vm, Structure::freezeTransition(vm, structure(vm)));
2417 bool JSObject::preventExtensions(JSObject* object, ExecState* exec)
2419 VM& vm = exec->vm();
2420 if (!object->isStructureExtensible(vm)) {
2421 // We've already set the internal [[PreventExtensions]] field to false.
2422 // We don't call the methodTable isExtensible here because it's not defined
2423 // that way in the specification. We are just doing an optimization here.
2427 object->enterDictionaryIndexingMode(vm);
2428 object->setStructure(vm, Structure::preventExtensionsTransition(vm, object->structure(vm)));
2432 bool JSObject::isExtensible(JSObject* obj, ExecState* exec)
2434 return obj->isExtensibleImpl(exec->vm());
2437 bool JSObject::isExtensible(ExecState* exec)
2439 VM& vm = exec->vm();
2440 return methodTable(vm)->isExtensible(this, exec);
2443 void JSObject::reifyAllStaticProperties(ExecState* exec)
2445 VM& vm = exec->vm();
2446 ASSERT(!staticPropertiesReified(vm));
2448 // If this object's ClassInfo has no static properties, then nothing to reify!
2449 // We can safely set the flag to avoid the expensive check again in the future.
2450 if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags())) {
2451 structure(vm)->setStaticPropertiesReified(true);
2455 if (!structure(vm)->isDictionary())
2456 setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure(vm)));
2458 for (const ClassInfo* info = classInfo(vm); info; info = info->parentClass) {
2459 const HashTable* hashTable = info->staticPropHashTable;
2463 for (auto& value : *hashTable) {
2464 unsigned attributes;
2465 auto key = Identifier::fromString(&vm, value.m_key);
2466 PropertyOffset offset = getDirectOffset(vm, key, attributes);
2467 if (!isValidOffset(offset))
2468 reifyStaticProperty(vm, hashTable->classForThis, key, value, *this);
2472 structure(vm)->setStaticPropertiesReified(true);
2475 NEVER_INLINE void JSObject::fillGetterPropertySlot(VM& vm, PropertySlot& slot, JSCell* getterSetter, unsigned attributes, PropertyOffset offset)
2477 if (structure(vm)->isUncacheableDictionary()) {
2478 slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
2482 // This access is cacheable because Structure requires an attributeChangedTransition
2483 // if this property stops being an accessor.
2484 slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
2487 static bool putIndexedDescriptor(ExecState* exec, SparseArrayValueMap* map, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
2489 VM& vm = exec->vm();
2491 if (descriptor.isDataDescriptor()) {
2492 unsigned attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::Accessor;
2493 if (descriptor.value())
2494 entryInMap->forceSet(vm, map, descriptor.value(), attributes);
2495 else if (oldDescriptor.isAccessorDescriptor())
2496 entryInMap->forceSet(vm, map, jsUndefined(), attributes);
2498 entryInMap->forceSet(attributes);
2502 if (descriptor.isAccessorDescriptor()) {
2503 JSObject* getter = nullptr;
2504 if (descriptor.getterPresent())
2505 getter = descriptor.getterObject();
2506 else if (oldDescriptor.isAccessorDescriptor())
2507 getter = oldDescriptor.getterObject();
2508 JSObject* setter = nullptr;
2509 if (descriptor.setterPresent())
2510 setter = descriptor.setterObject();
2511 else if (oldDescriptor.isAccessorDescriptor())
2512 setter = oldDescriptor.setterObject();
2514 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
2515 entryInMap->forceSet(vm, map, accessor, descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::ReadOnly);
2519 ASSERT(descriptor.isGenericDescriptor());
2520 entryInMap->forceSet(descriptor.attributesOverridingCurrent(oldDescriptor));
2524 ALWAYS_INLINE static bool canDoFastPutDirectIndex(VM& vm, JSObject* object)
2526 return (isJSArray(object) && !isCopyOnWrite(object->indexingMode()))
2527 || jsDynamicCast<JSFinalObject*>(vm, object)
2528 || TypeInfo::isArgumentsType(object->type());
2531 // Defined in ES5.1 8.12.9
2532 bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException)
2534 VM& vm = exec->vm();
2535 auto scope = DECLARE_THROW_SCOPE(vm);
2537 ASSERT(index <= MAX_ARRAY_INDEX);
2541 if (!inSparseIndexingMode()) {
2542 // Fast case: we're putting a regular property to a regular array
2543 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
2544 // however if the property currently exists missing attributes will override from their current 'true'
2545 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
2546 if (!descriptor.attributes() && descriptor.value() && canDoFastPutDirectIndex(vm, this)) {
2547 ASSERT(!descriptor.isAccessorDescriptor());
2548 RELEASE_AND_RETURN(scope, putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow));
2551 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
2554 if (descriptor.attributes() & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))
2555 notifyPresenceOfIndexedAccessors(vm);
2557 SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
2558 RELEASE_ASSERT(map);
2560 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
2561 SparseArrayValueMap::AddResult result = map->add(this, index);
2562 SparseArrayEntry* entryInMap = &result.iterator->value;
2564 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
2565 // 3. If current is undefined and extensible is false, then Reject.
2566 // 4. If current is undefined and extensible is true, then
2567 if (result.isNewEntry) {
2568 if (!isStructureExtensible(vm)) {
2569 map->remove(result.iterator);
2570 return typeError(exec, scope, throwException, NonExtensibleObjectPropertyDefineError);
2573 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
2574 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
2575 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
2576 // created property is set to its default value.
2577 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
2578 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
2579 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
2580 // is set to its default value.
2581 // 4.c. Return true.
2583 PropertyDescriptor defaults(jsUndefined(), PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
2584 putIndexedDescriptor(exec, map, entryInMap, descriptor, defaults);
2585 Butterfly* butterfly = m_butterfly.get();
2586 if (index >= butterfly->arrayStorage()->length())
2587 butterfly->arrayStorage()->setLength(index + 1);
2591 // 5. Return true, if every field in Desc is absent.
2592 // 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).
2593 PropertyDescriptor current;
2594 entryInMap->get(current);
2595 bool isEmptyOrEqual = descriptor.isEmpty() || descriptor.equalTo(exec, current);
2596 RETURN_IF_EXCEPTION(scope, false);
2600 // 7. If the [[Configurable]] field of current is false then
2601 if (!current.configurable()) {
2602 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
2603 if (descriptor.configurablePresent() && descriptor.configurable())
2604 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
2605 // 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.
2606 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
2607 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
2610 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
2611 if (!descriptor.isGenericDescriptor()) {
2612 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
2613 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
2614 // 9.a. Reject, if the [[Configurable]] field of current is false.
2615 if (!current.configurable())
2616 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
2617 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
2618 // data property to an accessor property. Preserve the existing values of the converted property's
2619 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
2620 // their default values.
2621 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
2622 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
2623 // attributes and set the rest of the property's attributes to their default values.
2624 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
2625 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
2626 // 10.a. If the [[Configurable]] field of current is false, then
2627 if (!current.configurable() && !current.writable()) {
2628 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
2629 if (descriptor.writable())
2630 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
2631 // 10.a.ii. If the [[Writable]] field of current is false, then
2632 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
2633 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
2634 return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
2636 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
2638 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
2639 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
2640 if (!current.configurable()) {
2641 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
2642 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
2643 return typeError(exec, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
2644 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
2645 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
2646 return typeError(exec, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
2651 // 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.
2652 putIndexedDescriptor(exec, map, entryInMap, descriptor, current);
2657 SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm)
2659 SparseArrayValueMap* result = SparseArrayValueMap::create(vm);
2660 arrayStorage()->m_sparseMap.set(vm, this, result);
2664 void JSObject::deallocateSparseIndexMap()
2666 if (ArrayStorage* arrayStorage = arrayStorageOrNull())
2667 arrayStorage->m_sparseMap.clear();
2670 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2672 VM& vm = exec->vm();
2673 for (JSObject* current = this; ;) {
2674 // This has the same behavior with respect to prototypes as JSObject::put(). It only
2675 // allows a prototype to intercept a put if (a) the prototype declares the property
2676 // we're after rather than intercepting it via an override of JSObject::put(), and
2677 // (b) that property is declared as ReadOnly or Accessor.
2679 ArrayStorage* storage = current->arrayStorageOrNull();
2680 if (storage && storage->m_sparseMap) {
2681 SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
2682 if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes() & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly))) {
2683 putResult = iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
2688 if (current->type() == ProxyObjectType) {
2689 ProxyObject* proxy = jsCast<ProxyObject*>(current);
2690 putResult = proxy->putByIndexCommon(exec, thisValue, i, value, shouldThrow);
2694 JSValue prototypeValue = current->getPrototypeDirect(vm);
2695 if (prototypeValue.isNull())
2698 current = asObject(prototypeValue);
2702 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2704 JSValue prototypeValue = getPrototypeDirect(exec->vm());
2705 if (prototypeValue.isNull())
2708 return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow, putResult);
2711 template<IndexingType indexingShape>
2712 bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value)
2714 VM& vm = exec->vm();
2715 auto scope = DECLARE_THROW_SCOPE(vm);
2717 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
2718 ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
2719 ASSERT(!indexingShouldBeSparse(vm));
2721 Butterfly* butterfly = m_butterfly.get();
2723 // For us to get here, the index is either greater than the public length, or greater than
2724 // or equal to the vector length.
2725 ASSERT(i >= butterfly->vectorLength());
2727 if (i > MAX_STORAGE_VECTOR_INDEX
2728 || (i >= MIN_SPARSE_ARRAY_INDEX && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly)))
2729 || indexIsSufficientlyBeyondLengthForSparseMap(i, butterfly->vectorLength())) {
2730 ASSERT(i <= MAX_ARRAY_INDEX);
2731 ensureArrayStorageSlow(vm);
2732 SparseArrayValueMap* map = allocateSparseIndexMap(vm);
2733 bool result = map->putEntry(exec, this, i, value, false);
2734 RETURN_IF_EXCEPTION(scope, false);
2735 ASSERT(i >= arrayStorage()->length());
2736 arrayStorage()->setLength(i + 1);
2740 if (!ensureLength(vm, i + 1)) {
2741 throwOutOfMemoryError(exec, scope);
2744 butterfly = m_butterfly.get();
2746 RELEASE_ASSERT(i < butterfly->vectorLength());
2747 switch (indexingShape) {
2749 ASSERT(value.isInt32());
2750 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(value);
2754 ASSERT(value.isNumber());
2755 double valueAsDouble = value.asNumber();
2756 ASSERT(valueAsDouble == valueAsDouble);
2757 butterfly->contiguousDouble().at(this, i) = valueAsDouble;
2761 case ContiguousShape:
2762 butterfly->contiguous().at(this, i).set(vm, this, value);
2771 // Explicit instantiations needed by JSArray.cpp.
2772 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(ExecState*, unsigned, JSValue);
2773 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(ExecState*, unsigned, JSValue);
2774 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(ExecState*, unsigned, JSValue);
2776 bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
2778 VM& vm = exec->vm();
2779 auto scope = DECLARE_THROW_SCOPE(vm);
2781 ASSERT(!isCopyOnWrite(indexingMode()));
2782 // i should be a valid array index that is outside of the current vector.
2783 ASSERT(i <= MAX_ARRAY_INDEX);
2784 ASSERT(i >= storage->vectorLength());
2786 SparseArrayValueMap* map = storage->m_sparseMap.get();
2788 // First, handle cases where we don't currently have a sparse map.
2790 // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
2791 ASSERT(isStructureExtensible(vm));
2793 // Update m_length if necessary.
2794 if (i >= storage->length())
2795 storage->setLength(i + 1);
2797 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2798 if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength())
2799 && isDenseEnoughForVector(i, storage->m_numValuesInVector)
2800 && increaseVectorLength(vm, i + 1))) {
2801 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2802 storage = arrayStorage();
2803 storage->m_vector[i].set(vm, this, value);
2804 ++storage->m_numValuesInVector;
2807 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2808 map = allocateSparseIndexMap(vm);
2809 RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
2812 // Update m_length if necessary.
2813 unsigned length = storage->length();
2815 // Prohibit growing the array if length is not writable.
2816 if (map->lengthIsReadOnly() || !isStructureExtensible(vm))
2817 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
2819 storage->setLength(length);
2822 // We are currently using a map - check whether we still want to be doing so.
2823 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2824 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2825 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
2826 RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
2828 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2829 storage = arrayStorage();
2830 storage->m_numValuesInVector = numValuesInArray;
2832 // Copy all values from the map into the vector, and delete the map.
2833 WriteBarrier<Unknown>* vector = storage->m_vector;
2834 SparseArrayValueMap::const_iterator end = map->end();
2835 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2836 vector[it->key].set(vm, this, it->value.getNonSparseMode());
2837 deallocateSparseIndexMap();
2839 // Store the new property into the vector.
2840 WriteBarrier<Unknown>& valueSlot = vector[i];
2842 ++storage->m_numValuesInVector;
2843 valueSlot.set(vm, this, value);
2847 bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
2849 VM& vm = exec->vm();
2851 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
2853 // i should be a valid array index that is outside of the current vector.
2854 ASSERT(i <= MAX_ARRAY_INDEX);
2856 switch (indexingType()) {
2857 case ALL_BLANK_INDEXING_TYPES: {
2858 if (indexingShouldBeSparse(vm)) {
2859 return putByIndexBeyondVectorLengthWithArrayStorage(
2860 exec, i, value, shouldThrow,
2861 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2863 if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
2864 return putByIndexBeyondVectorLengthWithArrayStorage(
2865 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
2867 if (needsSlowPutIndexing(vm)) {
2868 // Convert the indexing type to the SlowPutArrayStorage and retry.
2869 createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
2870 return putByIndex(this, exec, i, value, shouldThrow);
2873 createInitialForValueAndSet(vm, i, value);
2877 case ALL_UNDECIDED_INDEXING_TYPES: {
2882 case ALL_INT32_INDEXING_TYPES:
2883 return putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2885 case ALL_DOUBLE_INDEXING_TYPES:
2886 return putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2888 case ALL_CONTIGUOUS_INDEXING_TYPES:
2889 return putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2891 case NonArrayWithSlowPutArrayStorage:
2892 case ArrayWithSlowPutArrayStorage: {
2893 // No own property present in the vector, but there might be in the sparse map!
2894 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
2895 bool putResult = false;
2896 if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow, putResult))
2901 case NonArrayWithArrayStorage:
2902 case ArrayWithArrayStorage:
2903 return putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
2906 RELEASE_ASSERT_NOT_REACHED();
2911 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
2913 VM& vm = exec->vm();
2914 auto scope = DECLARE_THROW_SCOPE(vm);
2916 // i should be a valid array index that is outside of the current vector.
2917 ASSERT(hasAnyArrayStorage(indexingType()));
2918 ASSERT(arrayStorage() == storage);
2919 ASSERT(i >= storage->vectorLength() || attributes);
2920 ASSERT(i <= MAX_ARRAY_INDEX);
2922 SparseArrayValueMap* map = storage->m_sparseMap.get();
2924 // First, handle cases where we don't currently have a sparse map.
2926 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2927 ASSERT(isStructureExtensible(vm));
2929 // Update m_length if necessary.
2930 if (i >= storage->length())
2931 storage->setLength(i + 1);
2933 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2936 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
2937 && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
2938 && increaseVectorLength(vm, i + 1)) {
2939 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2940 storage = arrayStorage();
2941 storage->m_vector[i].set(vm, this, value);
2942 ++storage->m_numValuesInVector;
2945 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2946 map = allocateSparseIndexMap(vm);
2947 RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
2950 // Update m_length if necessary.
2951 unsigned length = storage->length();
2953 if (mode != PutDirectIndexLikePutDirect) {
2954 // Prohibit growing the array if length is not writable.
2955 if (map->lengthIsReadOnly())
2956 return typeError(exec, scope, mode == PutDirectIndexShouldThrow, ReadonlyPropertyWriteError);
2957 if (!isStructureExtensible(vm))
2958 return typeError(exec, scope, mode == PutDirectIndexShouldThrow, NonExtensibleObjectPropertyDefineError);
2961 storage->setLength(length);
2964 // We are currently using a map - check whether we still want to be doing so.
2965 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2966 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2967 if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
2968 RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
2970 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2971 storage = arrayStorage();
2972 storage->m_numValuesInVector = numValuesInArray;
2974 // Copy all values from the map into the vector, and delete the map.
2975 WriteBarrier<Unknown>* vector = storage->m_vector;
2976 SparseArrayValueMap::const_iterator end = map->end();
2977 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2978 vector[it->key].set(vm, this, it->value.getNonSparseMode());
2979 deallocateSparseIndexMap();
2981 // Store the new property into the vector.
2982 WriteBarrier<Unknown>& valueSlot = vector[i];
2984 ++storage->m_numValuesInVector;
2985 valueSlot.set(vm, this, value);
2989 bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
2991 VM& vm = exec->vm();
2992 ASSERT(!value.isCustomGetterSetter());
2994 if (!canDoFastPutDirectIndex(vm, this)) {
2995 PropertyDescriptor descriptor;
2996 descriptor.setDescriptor(value, attributes);
2997 return methodTable(vm)->defineOwnProperty(this, exec, Identifier::from(exec, i), descriptor, mode == PutDirectIndexShouldThrow);
3000 // i should be a valid array index that is outside of the current vector.
3001 ASSERT(i <= MAX_ARRAY_INDEX);
3003 if (attributes & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))
3004 notifyPresenceOfIndexedAccessors(vm);
3006 switch (indexingType()) {
3007 case ALL_BLANK_INDEXING_TYPES: {
3008 if (indexingShouldBeSparse(vm) || attributes) {
3009 return putDirectIndexBeyondVectorLengthWithArrayStorage(
3010 exec, i, value, attributes, mode,
3011 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3013 if (i >= MIN_SPARSE_ARRAY_INDEX) {
3014 return putDirectIndexBeyondVectorLengthWithArrayStorage(
3015 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
3017 if (needsSlowPutIndexing(vm)) {
3018 ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
3019 storage->m_vector[i].set(vm, this, value);
3020 storage->m_numValuesInVector++;
3024 createInitialForValueAndSet(vm, i, value);
3028 case ALL_UNDECIDED_INDEXING_TYPES: {
3029 convertUndecidedForValue(vm, value);
3031 return putDirectIndex(exec, i, value, attributes, mode);
3034 case ALL_INT32_INDEXING_TYPES: {
3035 ASSERT(!indexingShouldBeSparse(vm));
3037 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3038 if (!value.isInt32()) {
3039 convertInt32ForValue(vm, value);
3040 return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3042 putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
3046 case ALL_DOUBLE_INDEXING_TYPES: {
3047 ASSERT(!indexingShouldBeSparse(vm));
3049 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3050 if (!value.isNumber()) {
3051 convertDoubleToContiguous(vm);
3052 return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3054 double valueAsDouble = value.asNumber();
3055 if (valueAsDouble != valueAsDouble) {
3056 convertDoubleToContiguous(vm);
3057 return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3059 putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
3063 case ALL_CONTIGUOUS_INDEXING_TYPES: {
3064 ASSERT(!indexingShouldBeSparse(vm));
3066 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3067 putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
3071 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
3073 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3074 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
3077 RELEASE_ASSERT_NOT_REACHED();
3082 bool JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3084 JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
3085 GetterSetter* accessor = GetterSetter::create(vm, globalObject, function, nullptr);
3086 return putDirectNonIndexAccessor(vm, name, accessor, attributes);
3089 bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3091 StringImpl* name = propertyName.publicName();
3093 name = vm.propertyNames->anonymous.impl();
3096 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
3097 return putDirect(vm, propertyName, function, attributes);
3100 bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, const DOMJIT::Signature* signature, unsigned attributes)
3102 StringImpl* name = propertyName.publicName();
3104 name = vm.propertyNames->anonymous.impl();
3107 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic, callHostFunctionAsConstructor, signature);
3108 return putDirect(vm, propertyName, function, attributes);
3111 void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3113 StringImpl* name = propertyName.publicName();
3115 name = vm.propertyNames->anonymous.impl();
3117 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
3118 putDirectWithoutTransition(vm, propertyName, function, attributes);
3121 JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
3123 StringImpl* name = propertyName.publicName();
3125 name = vm.propertyNames->anonymous.impl();
3127 JSFunction* function = JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
3128 putDirect(vm, propertyName, function, attributes);
3132 JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
3134 JSFunction* function = JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
3135 putDirectWithoutTransition(vm, propertyName, function, attributes);
3139 // NOTE: This method is for ArrayStorage vectors.
3140 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
3142 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
3144 unsigned increasedLength;
3145 unsigned maxInitLength = std::min(currentLength, 100000U);
3147 if (desiredLength < maxInitLength)
3148 increasedLength = maxInitLength;
3149 else if (!currentVectorLength)
3150 increasedLength = std::max(desiredLength, lastArraySize);
3152 increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
3155 ASSERT(increasedLength >= desiredLength);
3157 lastArraySize = std::min(increasedLength, FIRST_ARRAY_STORAGE_VECTOR_GROW);
3159 return ArrayStorage::optimalVectorLength(
3160 indexBias, structure(vm)->outOfLineCapacity(),
3161 std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH));
3164 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned desiredLength)
3166 unsigned indexBias = 0;
3167 unsigned vectorLength = 0;
3168 unsigned length = 0;
3170 if (hasIndexedProperties(indexingType())) {
3171 if (ArrayStorage* storage = arrayStorageOrNull())
3172 indexBias = storage->m_indexBias;
3173 vectorLength = m_butterfly->vectorLength();
3174 length = m_butterfly->publicLength();
3177 return getNewVectorLength(vm, indexBias, vectorLength, length, desiredLength);
3180 template<IndexingType indexingShape>
3181 unsigned JSObject::countElements(Butterfly* butterfly)
3183 unsigned numValues = 0;
3184 for (unsigned i = butterfly->publicLength(); i--;) {
3185 switch (indexingShape) {
3187 case ContiguousShape:
3188 if (butterfly->contiguous().at(this, i))
3193 double value = butterfly->contiguousDouble().at(this, i);
3206 unsigned JSObject::countElements()
3208 switch (indexingType()) {
3209 case ALL_BLANK_INDEXING_TYPES:
3210 case ALL_UNDECIDED_INDEXING_TYPES:
3213 case ALL_INT32_INDEXING_TYPES:
3214 return countElements<Int32Shape>(butterfly());
3216 case ALL_DOUBLE_INDEXING_TYPES:
3217 return countElements&l