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