1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include "config.h"
25#include "JSObject.h"
26
27#include "ButterflyInlines.h"
28#include "CatchScope.h"
29#include "CustomGetterSetter.h"
30#include "DatePrototype.h"
31#include "ErrorConstructor.h"
32#include "Exception.h"
33#include "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 // Update length & m_numValuesInVector as necessary.
948 if (propertyName >= length) {
949 bool putResult = false;
950 if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
951 return putResult;
952 length = propertyName + 1;
953 storage->setLength(length);
954 ++storage->m_numValuesInVector;
955 } else if (!valueSlot) {
956 bool putResult = false;
957 if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
958 return putResult;
959 ++storage->m_numValuesInVector;
960 }
961
962 valueSlot.set(vm, thisObject, value);
963 return true;
964 }
965
966 default:
967 RELEASE_ASSERT_NOT_REACHED();
968 }
969
970 return thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
971}
972
973ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
974{
975 SparseArrayValueMap* map = storage->m_sparseMap.get();
976
977 if (!map)
978 map = allocateSparseIndexMap(vm);
979
980 if (map->sparseMode())
981 return storage;
982
983 map->setSparseMode();
984
985 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
986 for (unsigned i = 0; i < usedVectorLength; ++i) {
987 JSValue value = storage->m_vector[i].get();
988 // This will always be a new entry in the map, so no need to check we can write,
989 // and attributes are default so no need to set them.
990 if (value)
991 map->add(this, i).iterator->value.forceSet(vm, map, value, 0);
992 }
993
994 DeferGC deferGC(vm.heap);
995 Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
996 RELEASE_ASSERT(newButterfly);
997 newButterfly->arrayStorage()->m_indexBias = 0;
998 newButterfly->arrayStorage()->setVectorLength(0);
999 newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
1000 setButterfly(vm, newButterfly);
1001
1002 return newButterfly->arrayStorage();
1003}
1004
1005void JSObject::enterDictionaryIndexingMode(VM& vm)
1006{
1007 switch (indexingType()) {
1008 case ALL_BLANK_INDEXING_TYPES:
1009 case ALL_UNDECIDED_INDEXING_TYPES:
1010 case ALL_INT32_INDEXING_TYPES:
1011 case ALL_DOUBLE_INDEXING_TYPES:
1012 case ALL_CONTIGUOUS_INDEXING_TYPES:
1013 // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
1014 // this case if we ever cared. Note that ensureArrayStorage() can return null if the object
1015 // doesn't support traditional indexed properties. At the time of writing, this just affects
1016 // typed arrays.
1017 if (ArrayStorage* storage = ensureArrayStorageSlow(vm))
1018 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, storage);
1019 break;
1020 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1021 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
1022 break;
1023
1024 default:
1025 break;
1026 }
1027}
1028
1029void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
1030{
1031 if (mayInterceptIndexedAccesses(vm))
1032 return;
1033
1034 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors));
1035
1036 if (!mayBePrototype())
1037 return;
1038
1039 globalObject(vm)->haveABadTime(vm);
1040}
1041
1042Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length)
1043{
1044 ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
1045 IndexingType oldType = indexingType();
1046 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
1047 ASSERT(!needsSlowPutIndexing(vm));
1048 ASSERT(!indexingShouldBeSparse(vm));
1049 Structure* structure = this->structure(vm);
1050 unsigned propertyCapacity = structure->outOfLineCapacity();
1051 unsigned vectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, length);
1052 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
1053 butterfly(), vm, this, structure, propertyCapacity, false, 0,
1054 sizeof(EncodedJSValue) * vectorLength);
1055 newButterfly->setPublicLength(length);
1056 newButterfly->setVectorLength(vectorLength);
1057 return newButterfly;
1058}
1059
1060Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
1061{
1062 DeferGC deferGC(vm.heap);
1063 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1064 StructureID oldStructureID = this->structureID();
1065 Structure* oldStructure = vm.getStructure(oldStructureID);
1066 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateUndecided);
1067 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1068 setStructure(vm, newStructure);
1069 return newButterfly;
1070}
1071
1072ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
1073{
1074 DeferGC deferGC(vm.heap);
1075 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1076 for (unsigned i = newButterfly->vectorLength(); i--;)
1077 newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1078 StructureID oldStructureID = this->structureID();
1079 Structure* oldStructure = vm.getStructure(oldStructureID);
1080 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateInt32);
1081 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1082 setStructure(vm, newStructure);
1083 return newButterfly->contiguousInt32();
1084}
1085
1086ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
1087{
1088 DeferGC deferGC(vm.heap);
1089 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1090 for (unsigned i = newButterfly->vectorLength(); i--;)
1091 newButterfly->contiguousDouble().at(this, i) = PNaN;
1092 StructureID oldStructureID = this->structureID();
1093 Structure* oldStructure = vm.getStructure(oldStructureID);
1094 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateDouble);
1095 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1096 setStructure(vm, newStructure);
1097 return newButterfly->contiguousDouble();
1098}
1099
1100ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
1101{
1102 DeferGC deferGC(vm.heap);
1103 Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
1104 for (unsigned i = newButterfly->vectorLength(); i--;)
1105 newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1106 StructureID oldStructureID = this->structureID();
1107 Structure* oldStructure = vm.getStructure(oldStructureID);
1108 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateContiguous);
1109 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1110 setStructure(vm, newStructure);
1111 return newButterfly->contiguous();
1112}
1113
1114Butterfly* JSObject::createArrayStorageButterfly(VM& vm, JSObject* intendedOwner, Structure* structure, unsigned length, unsigned vectorLength, Butterfly* oldButterfly)
1115{
1116 Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
1117 oldButterfly, vm, intendedOwner, structure, structure->outOfLineCapacity(), false, 0,
1118 ArrayStorage::sizeFor(vectorLength));
1119 RELEASE_ASSERT(newButterfly);
1120
1121 ArrayStorage* result = newButterfly->arrayStorage();
1122 result->setLength(length);
1123 result->setVectorLength(vectorLength);
1124 result->m_sparseMap.clear();
1125 result->m_numValuesInVector = 0;
1126 result->m_indexBias = 0;
1127 for (size_t i = vectorLength; i--;)
1128 result->m_vector[i].setWithoutWriteBarrier(JSValue());
1129
1130 return newButterfly;
1131}
1132
1133ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
1134{
1135 DeferGC deferGC(vm.heap);
1136 StructureID oldStructureID = this->structureID();
1137 Structure* oldStructure = vm.getStructure(oldStructureID);
1138 IndexingType oldType = indexingType();
1139 ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
1140
1141 Butterfly* newButterfly = createArrayStorageButterfly(vm, this, oldStructure, length, vectorLength, butterfly());
1142 ArrayStorage* result = newButterfly->arrayStorage();
1143 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, suggestedArrayStorageTransition(vm));
1144 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1145 setStructure(vm, newStructure);
1146 return result;
1147}
1148
1149ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
1150{
1151 return createArrayStorage(
1152 vm, 0, ArrayStorage::optimalVectorLength(0, structure(vm)->outOfLineCapacity(), 0));
1153}
1154
1155ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
1156{
1157 ASSERT(hasUndecided(indexingType()));
1158
1159 Butterfly* butterfly = this->butterfly();
1160 for (unsigned i = butterfly->vectorLength(); i--;)
1161 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1162
1163 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32));
1164 return m_butterfly->contiguousInt32();
1165}
1166
1167ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
1168{
1169 ASSERT(hasUndecided(indexingType()));
1170
1171 Butterfly* butterfly = m_butterfly.get();
1172 for (unsigned i = butterfly->vectorLength(); i--;)
1173 butterfly->contiguousDouble().at(this, i) = PNaN;
1174
1175 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
1176 return m_butterfly->contiguousDouble();
1177}
1178
1179ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
1180{
1181 ASSERT(hasUndecided(indexingType()));
1182
1183 Butterfly* butterfly = m_butterfly.get();
1184 for (unsigned i = butterfly->vectorLength(); i--;)
1185 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
1186
1187 WTF::storeStoreFence();
1188 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1189 return m_butterfly->contiguous();
1190}
1191
1192ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
1193{
1194 Structure* structure = this->structure(vm);
1195 unsigned publicLength = m_butterfly->publicLength();
1196 unsigned propertyCapacity = structure->outOfLineCapacity();
1197
1198 Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
1199
1200 memcpy(
1201 newButterfly->base(0, propertyCapacity),
1202 m_butterfly->base(0, propertyCapacity),
1203 propertyCapacity * sizeof(EncodedJSValue));
1204
1205 ArrayStorage* newStorage = newButterfly->arrayStorage();
1206 newStorage->setVectorLength(neededLength);
1207 newStorage->setLength(publicLength);
1208 newStorage->m_sparseMap.clear();
1209 newStorage->m_indexBias = 0;
1210 newStorage->m_numValuesInVector = 0;
1211
1212 return newStorage;
1213}
1214
1215ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
1216{
1217 DeferGC deferGC(vm.heap);
1218 ASSERT(hasUndecided(indexingType()));
1219
1220 unsigned vectorLength = m_butterfly->vectorLength();
1221 ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1222
1223 for (unsigned i = vectorLength; i--;)
1224 storage->m_vector[i].setWithoutWriteBarrier(JSValue());
1225
1226 StructureID oldStructureID = this->structureID();
1227 Structure* oldStructure = vm.getStructure(oldStructureID);
1228 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1229 nukeStructureAndSetButterfly(vm, oldStructureID, storage->butterfly());
1230 setStructure(vm, newStructure);
1231 return storage;
1232}
1233
1234ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
1235{
1236 return convertUndecidedToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1237}
1238
1239ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
1240{
1241 ASSERT(hasInt32(indexingType()));
1242 ASSERT(!isCopyOnWrite(indexingMode()));
1243
1244 Butterfly* butterfly = m_butterfly.get();
1245 for (unsigned i = butterfly->vectorLength(); i--;) {
1246 WriteBarrier<Unknown>* current = &butterfly->contiguous().atUnsafe(i);
1247 double* currentAsDouble = bitwise_cast<double*>(current);
1248 JSValue v = current->get();
1249 // NOTE: Since this may be used during initialization, v could be garbage. If it's garbage,
1250 // that means it will be overwritten later.
1251 if (!v.isInt32()) {
1252 *currentAsDouble = PNaN;
1253 continue;
1254 }
1255 *currentAsDouble = v.asInt32();
1256 }
1257
1258 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
1259 return m_butterfly->contiguousDouble();
1260}
1261
1262ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
1263{
1264 ASSERT(hasInt32(indexingType()));
1265
1266 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1267 return m_butterfly->contiguous();
1268}
1269
1270ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
1271{
1272 DeferGC deferGC(vm.heap);
1273 ASSERT(hasInt32(indexingType()));
1274
1275 unsigned vectorLength = m_butterfly->vectorLength();
1276 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1277 Butterfly* butterfly = m_butterfly.get();
1278 for (unsigned i = 0; i < vectorLength; i++) {
1279 JSValue v = butterfly->contiguous().at(this, i).get();
1280 newStorage->m_vector[i].setWithoutWriteBarrier(v);
1281 if (v)
1282 newStorage->m_numValuesInVector++;
1283 }
1284
1285 StructureID oldStructureID = this->structureID();
1286 Structure* oldStructure = vm.getStructure(oldStructureID);
1287 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1288 nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1289 setStructure(vm, newStructure);
1290 return newStorage;
1291}
1292
1293ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
1294{
1295 return convertInt32ToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1296}
1297
1298ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
1299{
1300 ASSERT(hasDouble(indexingType()));
1301 ASSERT(!isCopyOnWrite(indexingMode()));
1302
1303 Butterfly* butterfly = m_butterfly.get();
1304 for (unsigned i = butterfly->vectorLength(); i--;) {
1305 double* current = &butterfly->contiguousDouble().atUnsafe(i);
1306 WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
1307 double value = *current;
1308 if (value != value) {
1309 currentAsValue->clear();
1310 continue;
1311 }
1312 JSValue v = JSValue(JSValue::EncodeAsDouble, value);
1313 currentAsValue->setWithoutWriteBarrier(v);
1314 }
1315
1316 WTF::storeStoreFence();
1317 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
1318 return m_butterfly->contiguous();
1319}
1320
1321ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
1322{
1323 DeferGC deferGC(vm.heap);
1324 ASSERT(hasDouble(indexingType()));
1325
1326 unsigned vectorLength = m_butterfly->vectorLength();
1327 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1328 Butterfly* butterfly = m_butterfly.get();
1329 for (unsigned i = 0; i < vectorLength; i++) {
1330 double value = butterfly->contiguousDouble().at(this, i);
1331 if (value != value) {
1332 newStorage->m_vector[i].clear();
1333 continue;
1334 }
1335 newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
1336 newStorage->m_numValuesInVector++;
1337 }
1338
1339 StructureID oldStructureID = this->structureID();
1340 Structure* oldStructure = vm.getStructure(oldStructureID);
1341 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1342 nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1343 setStructure(vm, newStructure);
1344 return newStorage;
1345}
1346
1347ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
1348{
1349 return convertDoubleToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1350}
1351
1352ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
1353{
1354 DeferGC deferGC(vm.heap);
1355 ASSERT(hasContiguous(indexingType()));
1356
1357 unsigned vectorLength = m_butterfly->vectorLength();
1358 ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
1359 Butterfly* butterfly = m_butterfly.get();
1360 for (unsigned i = 0; i < vectorLength; i++) {
1361 JSValue v = butterfly->contiguous().at(this, i).get();
1362 newStorage->m_vector[i].setWithoutWriteBarrier(v);
1363 if (v)
1364 newStorage->m_numValuesInVector++;
1365 }
1366
1367 // While we modify the butterfly of Contiguous Array, we do not take any cellLock here. This is because
1368 // (1) the old butterfly is not changed and (2) new butterfly is not changed after it is exposed to
1369 // the collector.
1370 // The mutator performs the following operations are sequentially executed by using storeStoreFence.
1371 //
1372 // CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure
1373 //
1374 // Meanwhile the collector performs the following steps sequentially:
1375 //
1376 // ReadStructureEarly ReadButterfly ReadStructureLate
1377 //
1378 // We list up all the patterns by writing a tiny script, and ensure all the cases are categorized into BEFORE, AFTER, and IGNORE.
1379 //
1380 // CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureEarly ReadButterfly ReadStructureLate: AFTER, trivially
1381 // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
1382 // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1383 // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1384 // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
1385 // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1386 // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1387 // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
1388 // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
1389 // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read early
1390 // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
1391 // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1392 // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1393 // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1394 // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1395 // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1396 // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1397 // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1398 // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1399 // CreateNewButterfly ReadStructureEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
1400 // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
1401 // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1402 // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1403 // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1404 // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1405 // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1406 // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1407 // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1408 // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1409 // ReadStructureEarly CreateNewButterfly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
1410 // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
1411 // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
1412 // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
1413 // ReadStructureEarly ReadButterfly CreateNewButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
1414 // ReadStructureEarly ReadButterfly ReadStructureLate CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
1415
1416 ASSERT(newStorage->butterfly() != butterfly);
1417 StructureID oldStructureID = this->structureID();
1418 Structure* oldStructure = vm.getStructure(oldStructureID);
1419 Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
1420
1421 // Ensure new Butterfly initialization is correctly done before exposing it to the concurrent threads.
1422 if (isX86() || vm.heap.mutatorShouldBeFenced())
1423 WTF::storeStoreFence();
1424 nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
1425 setStructure(vm, newStructure);
1426
1427 return newStorage;
1428}
1429
1430ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
1431{
1432 return convertContiguousToArrayStorage(vm, suggestedArrayStorageTransition(vm));
1433}
1434
1435void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
1436{
1437 IndexingType type = indexingTypeForValue(value);
1438 if (type == Int32Shape) {
1439 convertUndecidedToInt32(vm);
1440 return;
1441 }
1442
1443 if (type == DoubleShape) {
1444 convertUndecidedToDouble(vm);
1445 return;
1446 }
1447
1448 ASSERT(type == ContiguousShape);
1449 convertUndecidedToContiguous(vm);
1450}
1451
1452void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
1453{
1454 if (value.isInt32()) {
1455 createInitialInt32(vm, index + 1).at(this, index).set(vm, this, value);
1456 return;
1457 }
1458
1459 if (value.isDouble()) {
1460 double doubleValue = value.asNumber();
1461 if (doubleValue == doubleValue) {
1462 createInitialDouble(vm, index + 1).at(this, index) = doubleValue;
1463 return;
1464 }
1465 }
1466
1467 createInitialContiguous(vm, index + 1).at(this, index).set(vm, this, value);
1468}
1469
1470void JSObject::convertInt32ForValue(VM& vm, JSValue value)
1471{
1472 ASSERT(!value.isInt32());
1473
1474 if (value.isDouble() && !std::isnan(value.asDouble())) {
1475 convertInt32ToDouble(vm);
1476 return;
1477 }
1478
1479 convertInt32ToContiguous(vm);
1480}
1481
1482void JSObject::convertFromCopyOnWrite(VM& vm)
1483{
1484 ASSERT(isCopyOnWrite(indexingMode()));
1485 ASSERT(structure(vm)->indexingMode() == indexingMode());
1486
1487 const bool hasIndexingHeader = true;
1488 Butterfly* oldButterfly = butterfly();
1489 size_t propertyCapacity = 0;
1490 unsigned newVectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, std::min(oldButterfly->vectorLength() * 2, MAX_STORAGE_VECTOR_LENGTH));
1491 Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, hasIndexingHeader, newVectorLength * sizeof(JSValue));
1492
1493 memcpy(newButterfly->propertyStorage(), oldButterfly->propertyStorage(), oldButterfly->vectorLength() * sizeof(JSValue) + sizeof(IndexingHeader));
1494
1495 WTF::storeStoreFence();
1496 NonPropertyTransition transition = ([&] () {
1497 switch (indexingType()) {
1498 case ArrayWithInt32:
1499 return NonPropertyTransition::AllocateInt32;
1500 case ArrayWithDouble:
1501 return NonPropertyTransition::AllocateDouble;
1502 case ArrayWithContiguous:
1503 return NonPropertyTransition::AllocateContiguous;
1504 default:
1505 RELEASE_ASSERT_NOT_REACHED();
1506 return NonPropertyTransition::AllocateContiguous;
1507 }
1508 })();
1509 StructureID oldStructureID = structureID();
1510 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
1511 nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
1512 setStructure(vm, newStructure);
1513}
1514
1515void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
1516{
1517 ASSERT(index < m_butterfly->publicLength());
1518 ASSERT(index < m_butterfly->vectorLength());
1519 convertUndecidedForValue(vm, value);
1520 setIndexQuickly(vm, index, value);
1521}
1522
1523void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1524{
1525 ASSERT(!value.isInt32());
1526 convertInt32ForValue(vm, value);
1527 setIndexQuickly(vm, index, value);
1528}
1529
1530void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1531{
1532 ASSERT(!value.isNumber() || value.asNumber() != value.asNumber());
1533 convertDoubleToContiguous(vm);
1534 setIndexQuickly(vm, index, value);
1535}
1536
1537ContiguousJSValues JSObject::tryMakeWritableInt32Slow(VM& vm)
1538{
1539 ASSERT(inherits(vm, info()));
1540
1541 if (isCopyOnWrite(indexingMode())) {
1542 if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, Int32Shape) == Int32Shape) {
1543 ASSERT(hasInt32(indexingMode()));
1544 convertFromCopyOnWrite(vm);
1545 return butterfly()->contiguousInt32();
1546 }
1547 return ContiguousJSValues();
1548 }
1549
1550 if (structure(vm)->hijacksIndexingHeader())
1551 return ContiguousJSValues();
1552
1553 switch (indexingType()) {
1554 case ALL_BLANK_INDEXING_TYPES:
1555 if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1556 return ContiguousJSValues();
1557 return createInitialInt32(vm, 0);
1558
1559 case ALL_UNDECIDED_INDEXING_TYPES:
1560 return convertUndecidedToInt32(vm);
1561
1562 case ALL_DOUBLE_INDEXING_TYPES:
1563 case ALL_CONTIGUOUS_INDEXING_TYPES:
1564 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1565 return ContiguousJSValues();
1566
1567 default:
1568 CRASH();
1569 return ContiguousJSValues();
1570 }
1571}
1572
1573ContiguousDoubles JSObject::tryMakeWritableDoubleSlow(VM& vm)
1574{
1575 ASSERT(inherits(vm, info()));
1576
1577 if (isCopyOnWrite(indexingMode())) {
1578 if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, DoubleShape) == DoubleShape) {
1579 convertFromCopyOnWrite(vm);
1580 if (hasDouble(indexingMode()))
1581 return butterfly()->contiguousDouble();
1582 ASSERT(hasInt32(indexingMode()));
1583 } else
1584 return ContiguousDoubles();
1585 }
1586
1587 if (structure(vm)->hijacksIndexingHeader())
1588 return ContiguousDoubles();
1589
1590 switch (indexingType()) {
1591 case ALL_BLANK_INDEXING_TYPES:
1592 if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1593 return ContiguousDoubles();
1594 return createInitialDouble(vm, 0);
1595
1596 case ALL_UNDECIDED_INDEXING_TYPES:
1597 return convertUndecidedToDouble(vm);
1598
1599 case ALL_INT32_INDEXING_TYPES:
1600 return convertInt32ToDouble(vm);
1601
1602 case ALL_CONTIGUOUS_INDEXING_TYPES:
1603 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1604 return ContiguousDoubles();
1605
1606 default:
1607 CRASH();
1608 return ContiguousDoubles();
1609 }
1610}
1611
1612ContiguousJSValues JSObject::tryMakeWritableContiguousSlow(VM& vm)
1613{
1614 ASSERT(inherits(vm, info()));
1615
1616 if (isCopyOnWrite(indexingMode())) {
1617 if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, ContiguousShape) == ContiguousShape) {
1618 convertFromCopyOnWrite(vm);
1619 if (hasContiguous(indexingMode()))
1620 return butterfly()->contiguous();
1621 ASSERT(hasInt32(indexingMode()) || hasDouble(indexingMode()));
1622 } else
1623 return ContiguousJSValues();
1624 }
1625
1626 if (structure(vm)->hijacksIndexingHeader())
1627 return ContiguousJSValues();
1628
1629 switch (indexingType()) {
1630 case ALL_BLANK_INDEXING_TYPES:
1631 if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
1632 return ContiguousJSValues();
1633 return createInitialContiguous(vm, 0);
1634
1635 case ALL_UNDECIDED_INDEXING_TYPES:
1636 return convertUndecidedToContiguous(vm);
1637
1638 case ALL_INT32_INDEXING_TYPES:
1639 return convertInt32ToContiguous(vm);
1640
1641 case ALL_DOUBLE_INDEXING_TYPES:
1642 return convertDoubleToContiguous(vm);
1643
1644 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1645 return ContiguousJSValues();
1646
1647 default:
1648 CRASH();
1649 return ContiguousJSValues();
1650 }
1651}
1652
1653ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
1654{
1655 ASSERT(inherits(vm, info()));
1656
1657 if (structure(vm)->hijacksIndexingHeader())
1658 return nullptr;
1659
1660 ensureWritable(vm);
1661
1662 switch (indexingType()) {
1663 case ALL_BLANK_INDEXING_TYPES:
1664 if (UNLIKELY(indexingShouldBeSparse(vm)))
1665 return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
1666 return createInitialArrayStorage(vm);
1667
1668 case ALL_UNDECIDED_INDEXING_TYPES:
1669 ASSERT(!indexingShouldBeSparse(vm));
1670 ASSERT(!needsSlowPutIndexing(vm));
1671 return convertUndecidedToArrayStorage(vm);
1672
1673 case ALL_INT32_INDEXING_TYPES:
1674 ASSERT(!indexingShouldBeSparse(vm));
1675 ASSERT(!needsSlowPutIndexing(vm));
1676 return convertInt32ToArrayStorage(vm);
1677
1678 case ALL_DOUBLE_INDEXING_TYPES:
1679 ASSERT(!indexingShouldBeSparse(vm));
1680 ASSERT(!needsSlowPutIndexing(vm));
1681 return convertDoubleToArrayStorage(vm);
1682
1683 case ALL_CONTIGUOUS_INDEXING_TYPES:
1684 ASSERT(!indexingShouldBeSparse(vm));
1685 ASSERT(!needsSlowPutIndexing(vm));
1686 return convertContiguousToArrayStorage(vm);
1687
1688 default:
1689 RELEASE_ASSERT_NOT_REACHED();
1690 return 0;
1691 }
1692}
1693
1694ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
1695{
1696 ensureWritable(vm);
1697
1698 switch (indexingType()) {
1699 case ALL_BLANK_INDEXING_TYPES: {
1700 createArrayStorage(vm, 0, 0);
1701 SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1702 map->setSparseMode();
1703 return arrayStorage();
1704 }
1705
1706 case ALL_UNDECIDED_INDEXING_TYPES:
1707 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm));
1708
1709 case ALL_INT32_INDEXING_TYPES:
1710 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm));
1711
1712 case ALL_DOUBLE_INDEXING_TYPES:
1713 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm));
1714
1715 case ALL_CONTIGUOUS_INDEXING_TYPES:
1716 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm));
1717
1718 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1719 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
1720
1721 default:
1722 CRASH();
1723 return 0;
1724 }
1725}
1726
1727void JSObject::switchToSlowPutArrayStorage(VM& vm)
1728{
1729 ensureWritable(vm);
1730
1731 switch (indexingType()) {
1732 case ArrayClass:
1733 ensureArrayStorage(vm);
1734 RELEASE_ASSERT(hasAnyArrayStorage(indexingType()));
1735 if (hasSlowPutArrayStorage(indexingType()))
1736 return;
1737 switchToSlowPutArrayStorage(vm);
1738 break;
1739
1740 case ALL_UNDECIDED_INDEXING_TYPES:
1741 convertUndecidedToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1742 break;
1743
1744 case ALL_INT32_INDEXING_TYPES:
1745 convertInt32ToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1746 break;
1747
1748 case ALL_DOUBLE_INDEXING_TYPES:
1749 convertDoubleToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1750 break;
1751
1752 case ALL_CONTIGUOUS_INDEXING_TYPES:
1753 convertContiguousToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1754 break;
1755
1756 case NonArrayWithArrayStorage:
1757 case ArrayWithArrayStorage: {
1758 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::SwitchToSlowPutArrayStorage);
1759 setStructure(vm, newStructure);
1760 break;
1761 }
1762
1763 default:
1764 CRASH();
1765 break;
1766 }
1767}
1768
1769void JSObject::setPrototypeDirect(VM& vm, JSValue prototype)
1770{
1771 ASSERT(prototype);
1772 if (prototype.isObject())
1773 asObject(prototype)->didBecomePrototype();
1774
1775 if (structure(vm)->hasMonoProto()) {
1776 DeferredStructureTransitionWatchpointFire deferred(vm, structure(vm));
1777 Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype, deferred);
1778 setStructure(vm, newStructure);
1779 } else
1780 putDirect(vm, knownPolyProtoOffset, prototype);
1781
1782 if (!anyObjectInChainMayInterceptIndexedAccesses(vm))
1783 return;
1784
1785 if (mayBePrototype()) {
1786 structure(vm)->globalObject()->haveABadTime(vm);
1787 return;
1788 }
1789
1790 if (!hasIndexedProperties(indexingType()))
1791 return;
1792
1793 if (shouldUseSlowPut(indexingType()))
1794 return;
1795
1796 switchToSlowPutArrayStorage(vm);
1797}
1798
1799bool JSObject::setPrototypeWithCycleCheck(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1800{
1801 auto scope = DECLARE_THROW_SCOPE(vm);
1802
1803 if (this->structure(vm)->isImmutablePrototypeExoticObject()) {
1804 // This implements https://tc39.github.io/ecma262/#sec-set-immutable-prototype.
1805 if (this->getPrototype(vm, exec) == prototype)
1806 return true;
1807
1808 return typeError(exec, scope, shouldThrowIfCantSet, "Cannot set prototype of immutable prototype object"_s);
1809 }
1810
1811 ASSERT(methodTable(vm)->toThis(this, exec, NotStrictMode) == this);
1812
1813 if (this->getPrototypeDirect(vm) == prototype)
1814 return true;
1815
1816 bool isExtensible = this->isExtensible(exec);
1817 RETURN_IF_EXCEPTION(scope, false);
1818
1819 if (!isExtensible)
1820 return typeError(exec, scope, shouldThrowIfCantSet, ReadonlyPropertyWriteError);
1821
1822 JSValue nextPrototype = prototype;
1823 while (nextPrototype && nextPrototype.isObject()) {
1824 if (nextPrototype == this)
1825 return typeError(exec, scope, shouldThrowIfCantSet, "cyclic __proto__ value"_s);
1826 // FIXME: The specification currently says we should check if the [[GetPrototypeOf]] internal method of nextPrototype
1827 // is not the ordinary object internal method. However, we currently restrict this to Proxy objects as it would allow
1828 // for cycles with certain HTML objects (WindowProxy, Location) otherwise.
1829 // https://bugs.webkit.org/show_bug.cgi?id=161534
1830 if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType))
1831 break; // We're done. Set the prototype.
1832 nextPrototype = asObject(nextPrototype)->getPrototypeDirect(vm);
1833 }
1834 setPrototypeDirect(vm, prototype);
1835 return true;
1836}
1837
1838bool JSObject::setPrototype(JSObject* object, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1839{
1840 return object->setPrototypeWithCycleCheck(exec->vm(), exec, prototype, shouldThrowIfCantSet);
1841}
1842
1843JSValue JSObject::getPrototype(JSObject* object, ExecState* exec)
1844{
1845 return object->getPrototypeDirect(exec->vm());
1846}
1847
1848bool JSObject::setPrototype(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1849{
1850 return methodTable(vm)->setPrototype(this, exec, prototype, shouldThrowIfCantSet);
1851}
1852
1853bool JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter, unsigned attributes)
1854{
1855 PropertyDescriptor descriptor;
1856 descriptor.setGetter(getter);
1857
1858 ASSERT(attributes & PropertyAttribute::Accessor);
1859 if (!(attributes & PropertyAttribute::ReadOnly))
1860 descriptor.setConfigurable(true);
1861 if (!(attributes & PropertyAttribute::DontEnum))
1862 descriptor.setEnumerable(true);
1863
1864 return defineOwnProperty(this, exec, propertyName, descriptor, true);
1865}
1866
1867bool JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter, unsigned attributes)
1868{
1869 PropertyDescriptor descriptor;
1870 descriptor.setSetter(setter);
1871
1872 ASSERT(attributes & PropertyAttribute::Accessor);
1873 if (!(attributes & PropertyAttribute::ReadOnly))
1874 descriptor.setConfigurable(true);
1875 if (!(attributes & PropertyAttribute::DontEnum))
1876 descriptor.setEnumerable(true);
1877
1878 return defineOwnProperty(this, exec, propertyName, descriptor, true);
1879}
1880
1881bool JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1882{
1883 ASSERT(attributes & PropertyAttribute::Accessor);
1884
1885 if (Optional<uint32_t> index = parseIndex(propertyName))
1886 return putDirectIndex(exec, index.value(), accessor, attributes, PutDirectIndexLikePutDirect);
1887
1888 return putDirectNonIndexAccessor(exec->vm(), propertyName, accessor, attributes);
1889}
1890
1891// FIXME: Introduce a JSObject::putDirectCustomValue() method instead of using
1892// JSObject::putDirectCustomAccessor() to put CustomValues.
1893// https://bugs.webkit.org/show_bug.cgi?id=192576
1894bool JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1895{
1896 ASSERT(!parseIndex(propertyName));
1897 ASSERT(value.isCustomGetterSetter());
1898 if (!(attributes & PropertyAttribute::CustomAccessor))
1899 attributes |= PropertyAttribute::CustomValue;
1900
1901 PutPropertySlot slot(this);
1902 bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1903
1904 ASSERT(slot.type() == PutPropertySlot::NewProperty);
1905
1906 Structure* structure = this->structure(vm);
1907 if (attributes & PropertyAttribute::ReadOnly)
1908 structure->setContainsReadOnlyProperties();
1909 structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1910 return result;
1911}
1912
1913bool JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1914{
1915 ASSERT(attributes & PropertyAttribute::Accessor);
1916 PutPropertySlot slot(this);
1917 bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, accessor, attributes, slot);
1918
1919 Structure* structure = this->structure(vm);
1920 if (attributes & PropertyAttribute::ReadOnly)
1921 structure->setContainsReadOnlyProperties();
1922
1923 structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1924 return result;
1925}
1926
1927void JSObject::putDirectNonIndexAccessorWithoutTransition(VM& vm, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
1928{
1929 ASSERT(attributes & PropertyAttribute::Accessor);
1930 StructureID structureID = this->structureID();
1931 Structure* structure = vm.heap.structureIDTable().get(structureID);
1932 PropertyOffset offset = prepareToPutDirectWithoutTransition(vm, propertyName, attributes, structureID, structure);
1933 putDirect(vm, offset, accessor);
1934 if (attributes & PropertyAttribute::ReadOnly)
1935 structure->setContainsReadOnlyProperties();
1936
1937 structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1938}
1939
1940// HasProperty(O, P) from Section 7.3.10 of the spec.
1941// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasproperty
1942bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
1943{
1944 return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1945}
1946
1947bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
1948{
1949 return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1950}
1951
1952bool JSObject::hasPropertyGeneric(ExecState* exec, PropertyName propertyName, PropertySlot::InternalMethodType internalMethodType) const
1953{
1954 PropertySlot slot(this, internalMethodType);
1955 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1956}
1957
1958bool JSObject::hasPropertyGeneric(ExecState* exec, unsigned propertyName, PropertySlot::InternalMethodType internalMethodType) const
1959{
1960 PropertySlot slot(this, internalMethodType);
1961 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1962}
1963
1964// ECMA 8.6.2.5
1965bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
1966{
1967 JSObject* thisObject = jsCast<JSObject*>(cell);
1968 VM& vm = exec->vm();
1969
1970 if (Optional<uint32_t> index = parseIndex(propertyName))
1971 return thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, index.value());
1972
1973 unsigned attributes;
1974
1975 if (!thisObject->staticPropertiesReified(vm)) {
1976 if (auto entry = thisObject->findPropertyHashEntry(vm, propertyName)) {
1977 // If the static table contains a non-configurable (DontDelete) property then we can return early;
1978 // if there is a property in the storage array it too must be non-configurable (the language does
1979 // not allow repacement of a non-configurable property with a configurable one).
1980 if (entry->value->attributes() & PropertyAttribute::DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
1981 ASSERT(!isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes)) || attributes & PropertyAttribute::DontDelete);
1982 return false;
1983 }
1984 thisObject->reifyAllStaticProperties(exec);
1985 }
1986 }
1987
1988 Structure* structure = thisObject->structure(vm);
1989
1990 bool propertyIsPresent = isValidOffset(structure->get(vm, propertyName, attributes));
1991 if (propertyIsPresent) {
1992 if (attributes & PropertyAttribute::DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable)
1993 return false;
1994
1995 PropertyOffset offset;
1996 if (structure->isUncacheableDictionary())
1997 offset = structure->removePropertyWithoutTransition(vm, propertyName, [] (const ConcurrentJSLocker&, PropertyOffset) { });
1998 else
1999 thisObject->setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
2000
2001 if (offset != invalidOffset)
2002 thisObject->locationForOffset(offset)->clear();
2003 }
2004
2005 return true;
2006}
2007
2008bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
2009{
2010 VM& vm = exec->vm();
2011 JSObject* thisObject = jsCast<JSObject*>(cell);
2012
2013 if (i > MAX_ARRAY_INDEX)
2014 return thisObject->methodTable(vm)->deleteProperty(thisObject, exec, Identifier::from(exec, i));
2015
2016 switch (thisObject->indexingMode()) {
2017 case ALL_BLANK_INDEXING_TYPES:
2018 case ALL_UNDECIDED_INDEXING_TYPES:
2019 return true;
2020
2021 case CopyOnWriteArrayWithInt32:
2022 case CopyOnWriteArrayWithContiguous: {
2023 Butterfly* butterfly = thisObject->butterfly();
2024 if (i >= butterfly->vectorLength())
2025 return true;
2026 thisObject->convertFromCopyOnWrite(vm);
2027 FALLTHROUGH;
2028 }
2029
2030 case ALL_WRITABLE_INT32_INDEXING_TYPES:
2031 case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES: {
2032 Butterfly* butterfly = thisObject->butterfly();
2033 if (i >= butterfly->vectorLength())
2034 return true;
2035 butterfly->contiguous().at(thisObject, i).clear();
2036 return true;
2037 }
2038
2039 case CopyOnWriteArrayWithDouble: {
2040 Butterfly* butterfly = thisObject->butterfly();
2041 if (i >= butterfly->vectorLength())
2042 return true;
2043 thisObject->convertFromCopyOnWrite(vm);
2044 FALLTHROUGH;
2045 }
2046
2047 case ALL_WRITABLE_DOUBLE_INDEXING_TYPES: {
2048 Butterfly* butterfly = thisObject->butterfly();
2049 if (i >= butterfly->vectorLength())
2050 return true;
2051 butterfly->contiguousDouble().at(thisObject, i) = PNaN;
2052 return true;
2053 }
2054
2055 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
2056 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
2057
2058 if (i < storage->vectorLength()) {
2059 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
2060 if (valueSlot) {
2061 valueSlot.clear();
2062 --storage->m_numValuesInVector;
2063 }
2064 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
2065 SparseArrayValueMap::iterator it = map->find(i);
2066 if (it != map->notFound()) {
2067 if (it->value.attributes() & PropertyAttribute::DontDelete)
2068 return false;
2069 map->remove(it);
2070 }
2071 }
2072
2073 return true;
2074 }
2075
2076 default:
2077 RELEASE_ASSERT_NOT_REACHED();
2078 return false;
2079 }
2080}
2081
2082enum class TypeHintMode { TakesHint, DoesNotTakeHint };
2083
2084template<TypeHintMode mode = TypeHintMode::DoesNotTakeHint>
2085static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSObject* object, PropertyName propertyName, PreferredPrimitiveType hint)
2086{
2087 VM& vm = exec->vm();
2088 auto scope = DECLARE_THROW_SCOPE(vm);
2089
2090 PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
2091 // FIXME: Remove this when we have fixed: rdar://problem/33451840
2092 // https://bugs.webkit.org/show_bug.cgi?id=187109.
2093 constexpr bool debugNullStructure = mode == TypeHintMode::TakesHint;
2094 bool hasProperty = const_cast<JSObject*>(object)->getPropertySlot<debugNullStructure>(exec, propertyName, slot);
2095 RETURN_IF_EXCEPTION(scope, scope.exception());
2096 JSValue function = hasProperty ? slot.getValue(exec, propertyName) : jsUndefined();
2097 RETURN_IF_EXCEPTION(scope, scope.exception());
2098 if (function.isUndefinedOrNull() && mode == TypeHintMode::TakesHint)
2099 return JSValue();
2100 CallData callData;
2101 CallType callType = getCallData(vm, function, callData);
2102 if (callType == CallType::None) {
2103 if (mode == TypeHintMode::TakesHint)
2104 throwTypeError(exec, scope, "Symbol.toPrimitive is not a function, undefined, or null"_s);
2105 return scope.exception();
2106 }
2107
2108 MarkedArgumentBuffer callArgs;
2109 if (mode == TypeHintMode::TakesHint) {
2110 JSString* hintString = nullptr;
2111 switch (hint) {
2112 case NoPreference:
2113 hintString = vm.smallStrings.defaultString();
2114 break;
2115 case PreferNumber:
2116 hintString = vm.smallStrings.numberString();
2117 break;
2118 case PreferString:
2119 hintString = vm.smallStrings.stringString();
2120 break;
2121 }
2122 callArgs.append(hintString);
2123 }
2124 ASSERT(!callArgs.hasOverflowed());
2125
2126 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), callArgs);
2127 RETURN_IF_EXCEPTION(scope, scope.exception());
2128 ASSERT(!result.isGetterSetter());
2129 if (result.isObject())
2130 return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, scope, "Symbol.toPrimitive returned an object"_s);
2131 return result;
2132}
2133
2134// ECMA 7.1.1
2135JSValue JSObject::ordinaryToPrimitive(ExecState* exec, PreferredPrimitiveType hint) const
2136{
2137 VM& vm = exec->vm();
2138 auto scope = DECLARE_THROW_SCOPE(vm);
2139
2140 // Make sure that whatever default value methods there are on object's prototype chain are
2141 // being watched.
2142 for (const JSObject* object = this; object; object = object->structure(vm)->storedPrototypeObject(object))
2143 object->structure(vm)->startWatchingInternalPropertiesIfNecessary(vm);
2144
2145 JSValue value;
2146 if (hint == PreferString) {
2147 value = callToPrimitiveFunction(exec, this, vm.propertyNames->toString, hint);
2148 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2149 if (value)
2150 return value;
2151 value = callToPrimitiveFunction(exec, this, vm.propertyNames->valueOf, hint);
2152 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2153 if (value)
2154 return value;
2155 } else {
2156 value = callToPrimitiveFunction(exec, this, vm.propertyNames->valueOf, hint);
2157 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2158 if (value)
2159 return value;
2160 value = callToPrimitiveFunction(exec, this, vm.propertyNames->toString, hint);
2161 EXCEPTION_ASSERT(!scope.exception() || scope.exception() == value.asCell());
2162 if (value)
2163 return value;
2164 }
2165
2166 scope.assertNoException();
2167
2168 return throwTypeError(exec, scope, "No default value"_s);
2169}
2170
2171JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
2172{
2173 return object->ordinaryToPrimitive(exec, hint);
2174}
2175
2176JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
2177{
2178 VM& vm = exec->vm();
2179 auto scope = DECLARE_THROW_SCOPE(vm);
2180
2181 JSValue value = callToPrimitiveFunction<TypeHintMode::TakesHint>(exec, this, vm.propertyNames->toPrimitiveSymbol, preferredType);
2182 RETURN_IF_EXCEPTION(scope, { });
2183 if (value)
2184 return value;
2185
2186 RELEASE_AND_RETURN(scope, this->methodTable(vm)->defaultValue(this, exec, preferredType));
2187}
2188
2189bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
2190{
2191 VM& vm = exec->vm();
2192 auto scope = DECLARE_THROW_SCOPE(vm);
2193
2194 result = toPrimitive(exec, PreferNumber);
2195 RETURN_IF_EXCEPTION(scope, false);
2196 scope.release();
2197 number = result.toNumber(exec);
2198 return !result.isString();
2199}
2200
2201bool JSObject::getOwnStaticPropertySlot(VM& vm, PropertyName propertyName, PropertySlot& slot)
2202{
2203 for (auto* info = classInfo(vm); info; info = info->parentClass) {
2204 if (auto* table = info->staticPropHashTable) {
2205 if (getStaticPropertySlotFromTable(vm, table->classForThis, *table, this, propertyName, slot))
2206 return true;
2207 }
2208 }
2209 return false;
2210}
2211
2212auto JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const -> Optional<PropertyHashEntry>
2213{
2214 for (const ClassInfo* info = classInfo(vm); info; info = info->parentClass) {
2215 if (const HashTable* propHashTable = info->staticPropHashTable) {
2216 if (const HashTableValue* entry = propHashTable->entry(propertyName))
2217 return PropertyHashEntry { propHashTable, entry };
2218 }
2219 }
2220 return WTF::nullopt;
2221}
2222
2223bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue)
2224{
2225 VM& vm = exec->vm();
2226 auto scope = DECLARE_THROW_SCOPE(vm);
2227
2228 if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) {
2229 CallData callData;
2230 CallType callType = JSC::getCallData(vm, hasInstanceValue, callData);
2231 if (callType == CallType::None) {
2232 throwException(exec, scope, createInvalidInstanceofParameterErrorHasInstanceValueNotFunction(exec, this));
2233 return false;
2234 }
2235
2236 MarkedArgumentBuffer args;
2237 args.append(value);
2238 ASSERT(!args.hasOverflowed());
2239 JSValue result = call(exec, hasInstanceValue, callType, callData, this, args);
2240 RETURN_IF_EXCEPTION(scope, false);
2241 return result.toBoolean(exec);
2242 }
2243
2244 TypeInfo info = structure(vm)->typeInfo();
2245 if (info.implementsDefaultHasInstance()) {
2246 JSValue prototype = get(exec, vm.propertyNames->prototype);
2247 RETURN_IF_EXCEPTION(scope, false);
2248 RELEASE_AND_RETURN(scope, defaultHasInstance(exec, value, prototype));
2249 }
2250 if (info.implementsHasInstance()) {
2251 if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
2252 throwStackOverflowError(exec, scope);
2253 return false;
2254 }
2255 RELEASE_AND_RETURN(scope, methodTable(vm)->customHasInstance(this, exec, value));
2256 }
2257
2258 throwException(exec, scope, createInvalidInstanceofParameterErrorNotFunction(exec, this));
2259 return false;
2260}
2261
2262bool JSObject::hasInstance(ExecState* exec, JSValue value)
2263{
2264 VM& vm = exec->vm();
2265 auto scope = DECLARE_THROW_SCOPE(vm);
2266 JSValue hasInstanceValue = get(exec, vm.propertyNames->hasInstanceSymbol);
2267 RETURN_IF_EXCEPTION(scope, false);
2268
2269 RELEASE_AND_RETURN(scope, hasInstance(exec, value, hasInstanceValue));
2270}
2271
2272bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
2273{
2274 VM& vm = exec->vm();
2275 auto scope = DECLARE_THROW_SCOPE(vm);
2276
2277 if (!value.isObject())
2278 return false;
2279
2280 if (!proto.isObject()) {
2281 throwTypeError(exec, scope, "instanceof called on an object with an invalid prototype property."_s);
2282 return false;
2283 }
2284
2285 JSObject* object = asObject(value);
2286 while (true) {
2287 JSValue objectValue = object->getPrototype(vm, exec);
2288 RETURN_IF_EXCEPTION(scope, false);
2289 if (!objectValue.isObject())
2290 return false;
2291 object = asObject(objectValue);
2292 if (proto == object)
2293 return true;
2294 }
2295 ASSERT_NOT_REACHED();
2296}
2297
2298EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState* exec)
2299{
2300 JSValue value = exec->uncheckedArgument(0);
2301 JSValue proto = exec->uncheckedArgument(1);
2302
2303 return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
2304}
2305
2306void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2307{
2308 VM& vm = exec->vm();
2309 auto scope = DECLARE_THROW_SCOPE(vm);
2310 object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, mode);
2311 RETURN_IF_EXCEPTION(scope, void());
2312
2313 JSValue nextProto = object->getPrototype(vm, exec);
2314 RETURN_IF_EXCEPTION(scope, void());
2315 if (nextProto.isNull())
2316 return;
2317
2318 JSObject* prototype = asObject(nextProto);
2319 while(1) {
2320 if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
2321 scope.release();
2322 prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
2323 return;
2324 }
2325 prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
2326 RETURN_IF_EXCEPTION(scope, void());
2327 nextProto = prototype->getPrototype(vm, exec);
2328 RETURN_IF_EXCEPTION(scope, void());
2329 if (nextProto.isNull())
2330 break;
2331 prototype = asObject(nextProto);
2332 }
2333}
2334
2335void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2336{
2337 VM& vm = exec->vm();
2338 if (!mode.includeJSObjectProperties()) {
2339 // We still have to get non-indexed properties from any subclasses of JSObject that have them.
2340 object->methodTable(vm)->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
2341 return;
2342 }
2343
2344 if (propertyNames.includeStringProperties()) {
2345 // Add numeric properties first. That appears to be the accepted convention.
2346 // FIXME: Filling PropertyNameArray with an identifier for every integer
2347 // is incredibly inefficient for large arrays. We need a different approach,
2348 // which almost certainly means a different structure for PropertyNameArray.
2349 switch (object->indexingType()) {
2350 case ALL_BLANK_INDEXING_TYPES:
2351 case ALL_UNDECIDED_INDEXING_TYPES:
2352 break;
2353
2354 case ALL_INT32_INDEXING_TYPES:
2355 case ALL_CONTIGUOUS_INDEXING_TYPES: {
2356 Butterfly* butterfly = object->butterfly();
2357 unsigned usedLength = butterfly->publicLength();
2358 for (unsigned i = 0; i < usedLength; ++i) {
2359 if (!butterfly->contiguous().at(object, i))
2360 continue;
2361 propertyNames.add(i);
2362 }
2363 break;
2364 }
2365
2366 case ALL_DOUBLE_INDEXING_TYPES: {
2367 Butterfly* butterfly = object->butterfly();
2368 unsigned usedLength = butterfly->publicLength();
2369 for (unsigned i = 0; i < usedLength; ++i) {
2370 double value = butterfly->contiguousDouble().at(object, i);
2371 if (value != value)
2372 continue;
2373 propertyNames.add(i);
2374 }
2375 break;
2376 }
2377
2378 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
2379 ArrayStorage* storage = object->m_butterfly->arrayStorage();
2380
2381 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
2382 for (unsigned i = 0; i < usedVectorLength; ++i) {
2383 if (storage->m_vector[i])
2384 propertyNames.add(i);
2385 }
2386
2387 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
2388 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
2389 keys.reserveInitialCapacity(map->size());
2390
2391 SparseArrayValueMap::const_iterator end = map->end();
2392 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
2393 if (mode.includeDontEnumProperties() || !(it->value.attributes() & PropertyAttribute::DontEnum))
2394 keys.uncheckedAppend(static_cast<unsigned>(it->key));
2395 }
2396
2397 std::sort(keys.begin(), keys.end());
2398 for (unsigned i = 0; i < keys.size(); ++i)
2399 propertyNames.add(keys[i]);
2400 }
2401 break;
2402 }
2403
2404 default:
2405 RELEASE_ASSERT_NOT_REACHED();
2406 }
2407 }
2408
2409 object->methodTable(vm)->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
2410}
2411
2412void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
2413{
2414 VM& vm = exec->vm();
2415 if (!object->staticPropertiesReified(vm))
2416 getClassPropertyNames(exec, object->classInfo(vm), propertyNames, mode);
2417
2418 if (!mode.includeJSObjectProperties())
2419 return;
2420
2421 object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
2422}
2423
2424double JSObject::toNumber(ExecState* exec) const
2425{
2426 VM& vm = exec->vm();
2427 auto scope = DECLARE_THROW_SCOPE(vm);
2428 JSValue primitive = toPrimitive(exec, PreferNumber);
2429 RETURN_IF_EXCEPTION(scope, 0.0); // should be picked up soon in Nodes.cpp
2430 RELEASE_AND_RETURN(scope, primitive.toNumber(exec));
2431}
2432
2433JSString* JSObject::toString(ExecState* exec) const
2434{
2435 VM& vm = exec->vm();
2436 auto scope = DECLARE_THROW_SCOPE(vm);
2437 JSValue primitive = toPrimitive(exec, PreferString);
2438 RETURN_IF_EXCEPTION(scope, jsEmptyString(exec));
2439 RELEASE_AND_RETURN(scope, primitive.toString(exec));
2440}
2441
2442JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode)
2443{
2444 return jsCast<JSObject*>(cell);
2445}
2446
2447void JSObject::seal(VM& vm)
2448{
2449 if (isSealed(vm))
2450 return;
2451 enterDictionaryIndexingMode(vm);
2452 setStructure(vm, Structure::sealTransition(vm, structure(vm)));
2453}
2454
2455void JSObject::freeze(VM& vm)
2456{
2457 if (isFrozen(vm))
2458 return;
2459 enterDictionaryIndexingMode(vm);
2460 setStructure(vm, Structure::freezeTransition(vm, structure(vm)));
2461}
2462
2463bool JSObject::preventExtensions(JSObject* object, ExecState* exec)
2464{
2465 VM& vm = exec->vm();
2466 if (!object->isStructureExtensible(vm)) {
2467 // We've already set the internal [[PreventExtensions]] field to false.
2468 // We don't call the methodTable isExtensible here because it's not defined
2469 // that way in the specification. We are just doing an optimization here.
2470 return true;
2471 }
2472
2473 object->enterDictionaryIndexingMode(vm);
2474 object->setStructure(vm, Structure::preventExtensionsTransition(vm, object->structure(vm)));
2475 return true;
2476}
2477
2478bool JSObject::isExtensible(JSObject* obj, ExecState* exec)
2479{
2480 return obj->isStructureExtensible(exec->vm());
2481}
2482
2483bool JSObject::isExtensible(ExecState* exec)
2484{
2485 VM& vm = exec->vm();
2486 return methodTable(vm)->isExtensible(this, exec);
2487}
2488
2489void JSObject::reifyAllStaticProperties(ExecState* exec)
2490{
2491 VM& vm = exec->vm();
2492 ASSERT(!staticPropertiesReified(vm));
2493
2494 // If this object's ClassInfo has no static properties, then nothing to reify!
2495 // We can safely set the flag to avoid the expensive check again in the future.
2496 if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags())) {
2497 structure(vm)->setStaticPropertiesReified(true);
2498 return;
2499 }
2500
2501 if (!structure(vm)->isDictionary())
2502 setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure(vm)));
2503
2504 for (const ClassInfo* info = classInfo(vm); info; info = info->parentClass) {
2505 const HashTable* hashTable = info->staticPropHashTable;
2506 if (!hashTable)
2507 continue;
2508
2509 for (auto& value : *hashTable) {
2510 unsigned attributes;
2511 auto key = Identifier::fromString(&vm, value.m_key);
2512 PropertyOffset offset = getDirectOffset(vm, key, attributes);
2513 if (!isValidOffset(offset))
2514 reifyStaticProperty(vm, hashTable->classForThis, key, value, *this);
2515 }
2516 }
2517
2518 structure(vm)->setStaticPropertiesReified(true);
2519}
2520
2521NEVER_INLINE void JSObject::fillGetterPropertySlot(VM& vm, PropertySlot& slot, JSCell* getterSetter, unsigned attributes, PropertyOffset offset)
2522{
2523 if (structure(vm)->isUncacheableDictionary()) {
2524 slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
2525 return;
2526 }
2527
2528 // This access is cacheable because Structure requires an attributeChangedTransition
2529 // if this property stops being an accessor.
2530 slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
2531}
2532
2533static bool putIndexedDescriptor(ExecState* exec, SparseArrayValueMap* map, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
2534{
2535 VM& vm = exec->vm();
2536
2537 if (descriptor.isDataDescriptor()) {
2538 unsigned attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::Accessor;
2539 if (descriptor.value())
2540 entryInMap->forceSet(vm, map, descriptor.value(), attributes);
2541 else if (oldDescriptor.isAccessorDescriptor())
2542 entryInMap->forceSet(vm, map, jsUndefined(), attributes);
2543 else
2544 entryInMap->forceSet(attributes);
2545 return true;
2546 }
2547
2548 if (descriptor.isAccessorDescriptor()) {
2549 JSObject* getter = nullptr;
2550 if (descriptor.getterPresent())
2551 getter = descriptor.getterObject();
2552 else if (oldDescriptor.isAccessorDescriptor())
2553 getter = oldDescriptor.getterObject();
2554 JSObject* setter = nullptr;
2555 if (descriptor.setterPresent())
2556 setter = descriptor.setterObject();
2557 else if (oldDescriptor.isAccessorDescriptor())
2558 setter = oldDescriptor.setterObject();
2559
2560 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
2561 entryInMap->forceSet(vm, map, accessor, descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::ReadOnly);
2562 return true;
2563 }
2564
2565 ASSERT(descriptor.isGenericDescriptor());
2566 entryInMap->forceSet(descriptor.attributesOverridingCurrent(oldDescriptor));
2567 return true;
2568}
2569
2570ALWAYS_INLINE static bool canDoFastPutDirectIndex(VM& vm, JSObject* object)
2571{
2572 return (isJSArray(object) && !isCopyOnWrite(object->indexingMode()))
2573 || jsDynamicCast<JSFinalObject*>(vm, object)
2574 || TypeInfo::isArgumentsType(object->type());
2575}
2576
2577// Defined in ES5.1 8.12.9
2578bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException)
2579{
2580 VM& vm = exec->vm();
2581 auto scope = DECLARE_THROW_SCOPE(vm);
2582
2583 ASSERT(index <= MAX_ARRAY_INDEX);
2584
2585 ensureWritable(vm);
2586
2587 if (!inSparseIndexingMode()) {
2588 // Fast case: we're putting a regular property to a regular array
2589 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
2590 // however if the property currently exists missing attributes will override from their current 'true'
2591 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
2592 if (!descriptor.attributes() && descriptor.value() && canDoFastPutDirectIndex(vm, this)) {
2593 ASSERT(!descriptor.isAccessorDescriptor());
2594 RELEASE_AND_RETURN(scope, putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow));
2595 }
2596
2597 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
2598 }
2599
2600 if (descriptor.attributes() & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))
2601 notifyPresenceOfIndexedAccessors(vm);
2602
2603 SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
2604 RELEASE_ASSERT(map);
2605
2606 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
2607 SparseArrayValueMap::AddResult result = map->add(this, index);
2608 SparseArrayEntry* entryInMap = &result.iterator->value;
2609
2610 // 2. Let extensible be the value of the [[Extensible]] internal property of O.
2611 // 3. If current is undefined and extensible is false, then Reject.
2612 // 4. If current is undefined and extensible is true, then
2613 if (result.isNewEntry) {
2614 if (!isStructureExtensible(vm)) {
2615 map->remove(result.iterator);
2616 return typeError(exec, scope, throwException, NonExtensibleObjectPropertyDefineError);
2617 }
2618
2619 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
2620 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
2621 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
2622 // created property is set to its default value.
2623 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
2624 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
2625 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
2626 // is set to its default value.
2627 // 4.c. Return true.
2628
2629 PropertyDescriptor defaults(jsUndefined(), PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
2630 putIndexedDescriptor(exec, map, entryInMap, descriptor, defaults);
2631 Butterfly* butterfly = m_butterfly.get();
2632 if (index >= butterfly->arrayStorage()->length())
2633 butterfly->arrayStorage()->setLength(index + 1);
2634 return true;
2635 }
2636
2637 // 5. Return true, if every field in Desc is absent.
2638 // 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).
2639 PropertyDescriptor current;
2640 entryInMap->get(current);
2641 bool isEmptyOrEqual = descriptor.isEmpty() || descriptor.equalTo(exec, current);
2642 RETURN_IF_EXCEPTION(scope, false);
2643 if (isEmptyOrEqual)
2644 return true;
2645
2646 // 7. If the [[Configurable]] field of current is false then
2647 if (!current.configurable()) {
2648 // 7.a. Reject, if the [[Configurable]] field of Desc is true.
2649 if (descriptor.configurablePresent() && descriptor.configurable())
2650 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
2651 // 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.
2652 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
2653 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
2654 }
2655
2656 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
2657 if (!descriptor.isGenericDescriptor()) {
2658 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
2659 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
2660 // 9.a. Reject, if the [[Configurable]] field of current is false.
2661 if (!current.configurable())
2662 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
2663 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
2664 // data property to an accessor property. Preserve the existing values of the converted property's
2665 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
2666 // their default values.
2667 // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
2668 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
2669 // attributes and set the rest of the property's attributes to their default values.
2670 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
2671 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
2672 // 10.a. If the [[Configurable]] field of current is false, then
2673 if (!current.configurable() && !current.writable()) {
2674 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
2675 if (descriptor.writable())
2676 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
2677 // 10.a.ii. If the [[Writable]] field of current is false, then
2678 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
2679 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
2680 return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
2681 }
2682 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
2683 } else {
2684 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
2685 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
2686 if (!current.configurable()) {
2687 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
2688 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
2689 return typeError(exec, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
2690 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
2691 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
2692 return typeError(exec, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
2693 }
2694 }
2695 }
2696
2697 // 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.
2698 putIndexedDescriptor(exec, map, entryInMap, descriptor, current);
2699 // 13. Return true.
2700 return true;
2701}
2702
2703SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm)
2704{
2705 SparseArrayValueMap* result = SparseArrayValueMap::create(vm);
2706 arrayStorage()->m_sparseMap.set(vm, this, result);
2707 return result;
2708}
2709
2710void JSObject::deallocateSparseIndexMap()
2711{
2712 if (ArrayStorage* arrayStorage = arrayStorageOrNull())
2713 arrayStorage->m_sparseMap.clear();
2714}
2715
2716bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2717{
2718 VM& vm = exec->vm();
2719 for (JSObject* current = this; ;) {
2720 // This has the same behavior with respect to prototypes as JSObject::put(). It only
2721 // allows a prototype to intercept a put if (a) the prototype declares the property
2722 // we're after rather than intercepting it via an override of JSObject::put(), and
2723 // (b) that property is declared as ReadOnly or Accessor.
2724
2725 ArrayStorage* storage = current->arrayStorageOrNull();
2726 if (storage && storage->m_sparseMap) {
2727 SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
2728 if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes() & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly))) {
2729 putResult = iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
2730 return true;
2731 }
2732 }
2733
2734 if (current->type() == ProxyObjectType) {
2735 ProxyObject* proxy = jsCast<ProxyObject*>(current);
2736 putResult = proxy->putByIndexCommon(exec, thisValue, i, value, shouldThrow);
2737 return true;
2738 }
2739
2740 JSValue prototypeValue = current->getPrototypeDirect(vm);
2741 if (prototypeValue.isNull())
2742 return false;
2743
2744 current = asObject(prototypeValue);
2745 }
2746}
2747
2748bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2749{
2750 JSValue prototypeValue = getPrototypeDirect(exec->vm());
2751 if (prototypeValue.isNull())
2752 return false;
2753
2754 return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow, putResult);
2755}
2756
2757template<IndexingType indexingShape>
2758bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value)
2759{
2760 VM& vm = exec->vm();
2761 auto scope = DECLARE_THROW_SCOPE(vm);
2762
2763 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
2764 ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
2765 ASSERT(!indexingShouldBeSparse(vm));
2766
2767 Butterfly* butterfly = m_butterfly.get();
2768
2769 // For us to get here, the index is either greater than the public length, or greater than
2770 // or equal to the vector length.
2771 ASSERT(i >= butterfly->vectorLength());
2772
2773 if (i > MAX_STORAGE_VECTOR_INDEX
2774 || (i >= MIN_SPARSE_ARRAY_INDEX && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly)))
2775 || indexIsSufficientlyBeyondLengthForSparseMap(i, butterfly->vectorLength())) {
2776 ASSERT(i <= MAX_ARRAY_INDEX);
2777 ensureArrayStorageSlow(vm);
2778 SparseArrayValueMap* map = allocateSparseIndexMap(vm);
2779 bool result = map->putEntry(exec, this, i, value, false);
2780 RETURN_IF_EXCEPTION(scope, false);
2781 ASSERT(i >= arrayStorage()->length());
2782 arrayStorage()->setLength(i + 1);
2783 return result;
2784 }
2785
2786 if (!ensureLength(vm, i + 1)) {
2787 throwOutOfMemoryError(exec, scope);
2788 return false;
2789 }
2790 butterfly = m_butterfly.get();
2791
2792 RELEASE_ASSERT(i < butterfly->vectorLength());
2793 switch (indexingShape) {
2794 case Int32Shape:
2795 ASSERT(value.isInt32());
2796 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(value);
2797 return true;
2798
2799 case DoubleShape: {
2800 ASSERT(value.isNumber());
2801 double valueAsDouble = value.asNumber();
2802 ASSERT(valueAsDouble == valueAsDouble);
2803 butterfly->contiguousDouble().at(this, i) = valueAsDouble;
2804 return true;
2805 }
2806
2807 case ContiguousShape:
2808 butterfly->contiguous().at(this, i).set(vm, this, value);
2809 return true;
2810
2811 default:
2812 CRASH();
2813 return false;
2814 }
2815}
2816
2817// Explicit instantiations needed by JSArray.cpp.
2818template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(ExecState*, unsigned, JSValue);
2819template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(ExecState*, unsigned, JSValue);
2820template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(ExecState*, unsigned, JSValue);
2821
2822bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
2823{
2824 VM& vm = exec->vm();
2825 auto scope = DECLARE_THROW_SCOPE(vm);
2826
2827 ASSERT(!isCopyOnWrite(indexingMode()));
2828 // i should be a valid array index that is outside of the current vector.
2829 ASSERT(i <= MAX_ARRAY_INDEX);
2830 ASSERT(i >= storage->vectorLength());
2831
2832 SparseArrayValueMap* map = storage->m_sparseMap.get();
2833
2834 // First, handle cases where we don't currently have a sparse map.
2835 if (LIKELY(!map)) {
2836 // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
2837 ASSERT(isStructureExtensible(vm));
2838
2839 // Update m_length if necessary.
2840 if (i >= storage->length())
2841 storage->setLength(i + 1);
2842
2843 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2844 if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength())
2845 && isDenseEnoughForVector(i, storage->m_numValuesInVector)
2846 && increaseVectorLength(vm, i + 1))) {
2847 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2848 storage = arrayStorage();
2849 storage->m_vector[i].set(vm, this, value);
2850 ++storage->m_numValuesInVector;
2851 return true;
2852 }
2853 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2854 map = allocateSparseIndexMap(vm);
2855 RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
2856 }
2857
2858 // Update m_length if necessary.
2859 unsigned length = storage->length();
2860 if (i >= length) {
2861 // Prohibit growing the array if length is not writable.
2862 if (map->lengthIsReadOnly() || !isStructureExtensible(vm))
2863 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
2864 length = i + 1;
2865 storage->setLength(length);
2866 }
2867
2868 // We are currently using a map - check whether we still want to be doing so.
2869 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2870 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2871 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
2872 RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
2873
2874 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2875 storage = arrayStorage();
2876 storage->m_numValuesInVector = numValuesInArray;
2877
2878 // Copy all values from the map into the vector, and delete the map.
2879 WriteBarrier<Unknown>* vector = storage->m_vector;
2880 SparseArrayValueMap::const_iterator end = map->end();
2881 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2882 vector[it->key].set(vm, this, it->value.getNonSparseMode());
2883 deallocateSparseIndexMap();
2884
2885 // Store the new property into the vector.
2886 WriteBarrier<Unknown>& valueSlot = vector[i];
2887 if (!valueSlot)
2888 ++storage->m_numValuesInVector;
2889 valueSlot.set(vm, this, value);
2890 return true;
2891}
2892
2893bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
2894{
2895 VM& vm = exec->vm();
2896
2897 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
2898
2899 // i should be a valid array index that is outside of the current vector.
2900 ASSERT(i <= MAX_ARRAY_INDEX);
2901
2902 switch (indexingType()) {
2903 case ALL_BLANK_INDEXING_TYPES: {
2904 if (indexingShouldBeSparse(vm)) {
2905 return putByIndexBeyondVectorLengthWithArrayStorage(
2906 exec, i, value, shouldThrow,
2907 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2908 }
2909 if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
2910 return putByIndexBeyondVectorLengthWithArrayStorage(
2911 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
2912 }
2913 if (needsSlowPutIndexing(vm)) {
2914 // Convert the indexing type to the SlowPutArrayStorage and retry.
2915 createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
2916 return putByIndex(this, exec, i, value, shouldThrow);
2917 }
2918
2919 createInitialForValueAndSet(vm, i, value);
2920 return true;
2921 }
2922
2923 case ALL_UNDECIDED_INDEXING_TYPES: {
2924 CRASH();
2925 break;
2926 }
2927
2928 case ALL_INT32_INDEXING_TYPES:
2929 return putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2930
2931 case ALL_DOUBLE_INDEXING_TYPES:
2932 return putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2933
2934 case ALL_CONTIGUOUS_INDEXING_TYPES:
2935 return putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2936
2937 case NonArrayWithSlowPutArrayStorage:
2938 case ArrayWithSlowPutArrayStorage: {
2939 // No own property present in the vector, but there might be in the sparse map!
2940 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
2941 bool putResult = false;
2942 if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow, putResult))
2943 return putResult;
2944 FALLTHROUGH;
2945 }
2946
2947 case NonArrayWithArrayStorage:
2948 case ArrayWithArrayStorage:
2949 return putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
2950
2951 default:
2952 RELEASE_ASSERT_NOT_REACHED();
2953 }
2954 return false;
2955}
2956
2957bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
2958{
2959 VM& vm = exec->vm();
2960 auto scope = DECLARE_THROW_SCOPE(vm);
2961
2962 // i should be a valid array index that is outside of the current vector.
2963 ASSERT(hasAnyArrayStorage(indexingType()));
2964 ASSERT(arrayStorage() == storage);
2965 ASSERT(i >= storage->vectorLength() || attributes);
2966 ASSERT(i <= MAX_ARRAY_INDEX);
2967
2968 SparseArrayValueMap* map = storage->m_sparseMap.get();
2969
2970 // First, handle cases where we don't currently have a sparse map.
2971 if (LIKELY(!map)) {
2972 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2973 ASSERT(isStructureExtensible(vm));
2974
2975 // Update m_length if necessary.
2976 if (i >= storage->length())
2977 storage->setLength(i + 1);
2978
2979 // Check that it is sensible to still be using a vector, and then try to grow the vector.
2980 if (LIKELY(
2981 !attributes
2982 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
2983 && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
2984 && increaseVectorLength(vm, i + 1)) {
2985 // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2986 storage = arrayStorage();
2987 storage->m_vector[i].set(vm, this, value);
2988 ++storage->m_numValuesInVector;
2989 return true;
2990 }
2991 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2992 map = allocateSparseIndexMap(vm);
2993 RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
2994 }
2995
2996 // Update m_length if necessary.
2997 unsigned length = storage->length();
2998 if (i >= length) {
2999 if (mode != PutDirectIndexLikePutDirect) {
3000 // Prohibit growing the array if length is not writable.
3001 if (map->lengthIsReadOnly())
3002 return typeError(exec, scope, mode == PutDirectIndexShouldThrow, ReadonlyPropertyWriteError);
3003 if (!isStructureExtensible(vm))
3004 return typeError(exec, scope, mode == PutDirectIndexShouldThrow, NonExtensibleObjectPropertyDefineError);
3005 }
3006 length = i + 1;
3007 storage->setLength(length);
3008 }
3009
3010 // We are currently using a map - check whether we still want to be doing so.
3011 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
3012 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
3013 if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
3014 RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
3015
3016 // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
3017 storage = arrayStorage();
3018 storage->m_numValuesInVector = numValuesInArray;
3019
3020 // Copy all values from the map into the vector, and delete the map.
3021 WriteBarrier<Unknown>* vector = storage->m_vector;
3022 SparseArrayValueMap::const_iterator end = map->end();
3023 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
3024 vector[it->key].set(vm, this, it->value.getNonSparseMode());
3025 deallocateSparseIndexMap();
3026
3027 // Store the new property into the vector.
3028 WriteBarrier<Unknown>& valueSlot = vector[i];
3029 if (!valueSlot)
3030 ++storage->m_numValuesInVector;
3031 valueSlot.set(vm, this, value);
3032 return true;
3033}
3034
3035bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
3036{
3037 VM& vm = exec->vm();
3038 ASSERT(!value.isCustomGetterSetter());
3039
3040 if (!canDoFastPutDirectIndex(vm, this)) {
3041 PropertyDescriptor descriptor;
3042 descriptor.setDescriptor(value, attributes);
3043 return methodTable(vm)->defineOwnProperty(this, exec, Identifier::from(exec, i), descriptor, mode == PutDirectIndexShouldThrow);
3044 }
3045
3046 // i should be a valid array index that is outside of the current vector.
3047 ASSERT(i <= MAX_ARRAY_INDEX);
3048
3049 if (attributes & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))
3050 notifyPresenceOfIndexedAccessors(vm);
3051
3052 switch (indexingType()) {
3053 case ALL_BLANK_INDEXING_TYPES: {
3054 if (indexingShouldBeSparse(vm) || attributes) {
3055 return putDirectIndexBeyondVectorLengthWithArrayStorage(
3056 exec, i, value, attributes, mode,
3057 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3058 }
3059 if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
3060 return putDirectIndexBeyondVectorLengthWithArrayStorage(
3061 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
3062 }
3063 if (needsSlowPutIndexing(vm)) {
3064 ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
3065 storage->m_vector[i].set(vm, this, value);
3066 storage->m_numValuesInVector++;
3067 return true;
3068 }
3069
3070 createInitialForValueAndSet(vm, i, value);
3071 return true;
3072 }
3073
3074 case ALL_UNDECIDED_INDEXING_TYPES: {
3075 convertUndecidedForValue(vm, value);
3076 // Reloop.
3077 return putDirectIndex(exec, i, value, attributes, mode);
3078 }
3079
3080 case ALL_INT32_INDEXING_TYPES: {
3081 ASSERT(!indexingShouldBeSparse(vm));
3082 if (attributes)
3083 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3084 if (!value.isInt32()) {
3085 convertInt32ForValue(vm, value);
3086 return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3087 }
3088 putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
3089 return true;
3090 }
3091
3092 case ALL_DOUBLE_INDEXING_TYPES: {
3093 ASSERT(!indexingShouldBeSparse(vm));
3094 if (attributes)
3095 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3096 if (!value.isNumber()) {
3097 convertDoubleToContiguous(vm);
3098 return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3099 }
3100 double valueAsDouble = value.asNumber();
3101 if (valueAsDouble != valueAsDouble) {
3102 convertDoubleToContiguous(vm);
3103 return putDirectIndexSlowOrBeyondVectorLength(exec, i, value, attributes, mode);
3104 }
3105 putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
3106 return true;
3107 }
3108
3109 case ALL_CONTIGUOUS_INDEXING_TYPES: {
3110 ASSERT(!indexingShouldBeSparse(vm));
3111 if (attributes)
3112 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3113 putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
3114 return true;
3115 }
3116
3117 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
3118 if (attributes)
3119 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
3120 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
3121
3122 default:
3123 RELEASE_ASSERT_NOT_REACHED();
3124 return false;
3125 }
3126}
3127
3128bool JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3129{
3130 JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
3131 GetterSetter* accessor = GetterSetter::create(vm, globalObject, function, nullptr);
3132 return putDirectNonIndexAccessor(vm, name, accessor, attributes);
3133}
3134
3135void JSObject::putDirectNativeIntrinsicGetterWithoutTransition(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3136{
3137 JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
3138 GetterSetter* accessor = GetterSetter::create(vm, globalObject, function, nullptr);
3139 putDirectNonIndexAccessorWithoutTransition(vm, name, accessor, attributes);
3140}
3141
3142bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3143{
3144 StringImpl* name = propertyName.publicName();
3145 if (!name)
3146 name = vm.propertyNames->anonymous.impl();
3147 ASSERT(name);
3148
3149 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
3150 return putDirect(vm, propertyName, function, attributes);
3151}
3152
3153bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, const DOMJIT::Signature* signature, unsigned attributes)
3154{
3155 StringImpl* name = propertyName.publicName();
3156 if (!name)
3157 name = vm.propertyNames->anonymous.impl();
3158 ASSERT(name);
3159
3160 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic, callHostFunctionAsConstructor, signature);
3161 return putDirect(vm, propertyName, function, attributes);
3162}
3163
3164void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
3165{
3166 StringImpl* name = propertyName.publicName();
3167 if (!name)
3168 name = vm.propertyNames->anonymous.impl();
3169 ASSERT(name);
3170 JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
3171 putDirectWithoutTransition(vm, propertyName, function, attributes);
3172}
3173
3174JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
3175{
3176 StringImpl* name = propertyName.publicName();
3177 if (!name)
3178 name = vm.propertyNames->anonymous.impl();
3179 ASSERT(name);
3180 JSFunction* function = JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
3181 putDirect(vm, propertyName, function, attributes);
3182 return function;
3183}
3184
3185JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
3186{
3187 JSFunction* function = JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
3188 putDirectWithoutTransition(vm, propertyName, function, attributes);
3189 return function;
3190}
3191
3192// NOTE: This method is for ArrayStorage vectors.
3193ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
3194{
3195 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
3196
3197 unsigned increasedLength;
3198 unsigned maxInitLength = std::min(currentLength, 100000U);
3199
3200 if (desiredLength < maxInitLength)
3201 increasedLength = maxInitLength;
3202 else if (!currentVectorLength)
3203 increasedLength = std::max(desiredLength, lastArraySize);
3204 else {
3205 increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
3206 }
3207
3208 ASSERT(increasedLength >= desiredLength);
3209
3210 lastArraySize = std::min(increasedLength, FIRST_ARRAY_STORAGE_VECTOR_GROW);
3211
3212 return ArrayStorage::optimalVectorLength(
3213 indexBias, structure(vm)->outOfLineCapacity(),
3214 std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH));
3215}
3216
3217ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned desiredLength)
3218{
3219 unsigned indexBias = 0;
3220 unsigned vectorLength = 0;
3221 unsigned length = 0;
3222
3223 if (hasIndexedProperties(indexingType())) {
3224 if (ArrayStorage* storage = arrayStorageOrNull())
3225 indexBias = storage->m_indexBias;
3226 vectorLength = m_butterfly->vectorLength();
3227 length = m_butterfly->publicLength();
3228 }
3229
3230 return getNewVectorLength(vm, indexBias, vectorLength, length, desiredLength);
3231}
3232
3233template<IndexingType indexingShape>
3234unsigned JSObject::countElements(Butterfly* butterfly)
3235{
3236 unsigned numValues = 0;
3237 for (unsigned i = butterfly->publicLength(); i--;) {
3238 switch (indexingShape) {
3239 case Int32Shape:
3240 case ContiguousShape:
3241 if (butterfly->contiguous().at(this, i))
3242 numValues++;
3243 break;
3244
3245 case DoubleShape: {
3246 double value = butterfly->contiguousDouble().at(this, i);
3247 if (value == value)
3248 numValues++;
3249 break;
3250 }
3251
3252 default:
3253 CRASH();
3254 }
3255 }
3256 return numValues;
3257}
3258
3259unsigned JSObject::countElements()
3260{
3261 switch (indexingType()) {
3262 case ALL_BLANK_INDEXING_TYPES:
3263 case ALL_UNDECIDED_INDEXING_TYPES:
3264 return 0;
3265
3266 case ALL_INT32_INDEXING_TYPES:
3267 return countElements<Int32Shape>(butterfly());
3268
3269 case ALL_DOUBLE_INDEXING_TYPES:
3270 return countElements<DoubleShape>(butterfly());
3271
3272 case ALL_CONTIGUOUS_INDEXING_TYPES:
3273 return countElements<ContiguousShape>(butterfly());
3274
3275 default:
3276 CRASH();
3277 return 0;
3278 }
3279}
3280
3281bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
3282{
3283 ArrayStorage* storage = arrayStorage();
3284
3285 unsigned vectorLength = storage->vectorLength();
3286 unsigned availableVectorLength = storage->availableVectorLength(structure(vm), vectorLength);
3287 if (availableVectorLength >= newLength) {
3288 // The cell was already big enough for the desired length!
3289 for (unsigned i = vectorLength; i < availableVectorLength; ++i)
3290 storage->m_vector[i].clear();
3291 storage->setVectorLength(availableVectorLength);
3292 return true;
3293 }
3294
3295 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
3296 // to the vector. Callers have to account for that, because they can do it more efficiently.
3297 if (newLength > MAX_STORAGE_VECTOR_LENGTH)
3298 return false;
3299
3300 if (newLength >= MIN_SPARSE_ARRAY_INDEX
3301 && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector))
3302 return false;
3303
3304 unsigned indexBias = storage->m_indexBias;
3305 ASSERT(newLength > vectorLength);
3306 unsigned newVectorLength = getNewVectorLength(vm, newLength);
3307
3308 // Fast case - there is no precapacity. In these cases a realloc makes sense.
3309 Structure* structure = this->structure(vm);
3310 if (LIKELY(!indexBias)) {
3311 DeferGC deferGC(vm.heap);
3312 Butterfly* newButterfly = storage->butterfly()->growArrayRight(
3313 vm, this, structure, structure->outOfLineCapacity(), true,
3314 ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
3315 if (!newButterfly)
3316 return false;
3317 for (unsigned i = vectorLength; i < newVectorLength; ++i)
3318 newButterfly->arrayStorage()->m_vector[i].clear();
3319 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
3320 setButterfly(vm, newButterfly);
3321 return true;
3322 }
3323
3324 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
3325 DeferGC deferGC(vm.heap);
3326 unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
3327 Butterfly* newButterfly = storage->butterfly()->resizeArray(
3328 vm, this,
3329 structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
3330 newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
3331 if (!newButterfly)
3332 return false;
3333 for (unsigned i = vectorLength; i < newVectorLength; ++i)
3334 newButterfly->arrayStorage()->m_vector[i].clear();
3335 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
3336 newButterfly->arrayStorage()->m_indexBias = newIndexBias;
3337 setButterfly(vm, newButterfly);
3338 return true;
3339}
3340
3341bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
3342{
3343 if (isCopyOnWrite(indexingMode())) {
3344 convertFromCopyOnWrite(vm);
3345 if (m_butterfly->vectorLength() >= length)
3346 return true;
3347 }
3348
3349 Butterfly* butterfly = this->butterfly();
3350
3351 ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
3352 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
3353 ASSERT(length > butterfly->vectorLength());
3354
3355 unsigned oldVectorLength = butterfly->vectorLength();
3356 unsigned newVectorLength;
3357
3358 Structure* structure = this->structure(vm);
3359 unsigned propertyCapacity = structure->outOfLineCapacity();
3360
3361 GCDeferralContext deferralContext(vm.heap);
3362 DisallowGC disallowGC;
3363 unsigned availableOldLength =
3364 Butterfly::availableContiguousVectorLength(propertyCapacity, oldVectorLength);
3365 Butterfly* newButterfly = nullptr;
3366 if (availableOldLength >= length) {
3367 // This is the case where someone else selected a vector length that caused internal
3368 // fragmentation. If we did our jobs right, this would never happen. But I bet we will mess
3369 // this up, so this defense should stay.
3370 newVectorLength = availableOldLength;
3371 } else {
3372 newVectorLength = Butterfly::optimalContiguousVectorLength(
3373 propertyCapacity, std::min(length * 2, MAX_STORAGE_VECTOR_LENGTH));
3374 butterfly = butterfly->reallocArrayRightIfPossible(
3375 vm, deferralContext, this, structure, propertyCapacity, true,
3376 oldVectorLength * sizeof(EncodedJSValue),
3377 newVectorLength * sizeof(EncodedJSValue));
3378 if (!butterfly)
3379 return false;
3380 newButterfly = butterfly;
3381 }
3382
3383 if (hasDouble(indexingType())) {
3384 for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
3385 butterfly->indexingPayload<double>()[i] = PNaN;
3386 } else {
3387 for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
3388 butterfly->indexingPayload<WriteBarrier<Unknown>>()[i].clear();
3389 }
3390
3391 if (newButterfly) {
3392 butterfly->setVectorLength(newVectorLength);
3393 WTF::storeStoreFence();
3394 m_butterfly.set(vm, this, newButterfly);
3395 } else {
3396 WTF::storeStoreFence();
3397 butterfly->setVectorLength(newVectorLength);
3398 }
3399
3400 return true;
3401}
3402
3403void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length)
3404{
3405 ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
3406 ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
3407 ASSERT(m_butterfly->vectorLength() > length);
3408 ASSERT(!m_butterfly->indexingHeader()->preCapacity(structure(vm)));
3409
3410 DeferGC deferGC(vm.heap);
3411 Butterfly* newButterfly = butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(length));
3412 newButterfly->setVectorLength(length);
3413 newButterfly->setPublicLength(length);
3414 WTF::storeStoreFence();
3415 m_butterfly.set(vm, this, newButterfly);
3416
3417}
3418
3419Butterfly* JSObject::allocateMoreOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize)
3420{
3421 ASSERT(newSize > oldSize);
3422
3423 // It's important that this function not rely on structure(), for the property
3424 // capacity, since we might have already mutated the structure in-place.
3425
3426 return Butterfly::createOrGrowPropertyStorage(butterfly(), vm, this, structure(vm), oldSize, newSize);
3427}
3428
3429static JSCustomGetterSetterFunction* getCustomGetterSetterFunctionForGetterSetter(ExecState* exec, PropertyName propertyName, CustomGetterSetter* getterSetter, JSCustomGetterSetterFunction::Type type)
3430{
3431 VM& vm = exec->vm();
3432 auto key = std::make_pair(getterSetter, (int)type);
3433 JSCustomGetterSetterFunction* customGetterSetterFunction = vm.customGetterSetterFunctionMap.get(key);
3434 if (!customGetterSetterFunction) {
3435 customGetterSetterFunction = JSCustomGetterSetterFunction::create(vm, exec->lexicalGlobalObject(), getterSetter, type, propertyName.publicName());
3436 vm.customGetterSetterFunctionMap.set(key, customGetterSetterFunction);
3437 }
3438 return customGetterSetterFunction;
3439}
3440
3441bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
3442{
3443 VM& vm = exec->vm();
3444 auto scope = DECLARE_THROW_SCOPE(vm);
3445 JSC::PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
3446
3447 bool result = methodTable(vm)->getOwnPropertySlot(this, exec, propertyName, slot);
3448 EXCEPTION_ASSERT(!scope.exception() || !result);
3449 if (!result)
3450 return false;
3451
3452 // DebuggerScope::getOwnPropertySlot() (and possibly others) may return attributes from the prototype chain
3453 // but getOwnPropertyDescriptor() should only work for 'own' properties so we exit early if we detect that
3454 // the property is not an own property.
3455 if (slot.slotBase() != this && slot.slotBase()) {
3456 JSProxy* jsProxy = jsDynamicCast<JSProxy*>(vm, this);
3457 if (!jsProxy || jsProxy->target() != slot.slotBase()) {
3458 // Try ProxyObject.
3459 ProxyObject* proxyObject = jsDynamicCast<ProxyObject*>(vm, this);
3460 if (!proxyObject || proxyObject->target() != slot.slotBase())
3461 return false;
3462 }
3463 }
3464
3465 if (slot.isAccessor())
3466 descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes());
3467 else if (slot.attributes() & PropertyAttribute::CustomAccessor) {
3468 descriptor.setCustomDescriptor(slot.attributes());
3469
3470 JSObject* thisObject = this;
3471 if (auto* proxy = jsDynamicCast<JSProxy*>(vm, this))
3472 thisObject = proxy->target();
3473
3474 CustomGetterSetter* getterSetter;
3475 if (slot.isCustomAccessor())
3476 getterSetter = slot.customGetterSetter();
3477 else {
3478 JSValue maybeGetterSetter = thisObject->getDirect(vm, propertyName);
3479 if (!maybeGetterSetter) {
3480 thisObject->reifyAllStaticProperties(exec);
3481 maybeGetterSetter = thisObject->getDirect(vm, propertyName);
3482 }
3483
3484 ASSERT(maybeGetterSetter);
3485 getterSetter = jsDynamicCast<CustomGetterSetter*>(vm, maybeGetterSetter);
3486 }
3487 ASSERT(getterSetter);
3488 if (!getterSetter)
3489 return false;
3490
3491 if (getterSetter->getter())
3492 descriptor.setGetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Getter));
3493 if (getterSetter->setter())
3494 descriptor.setSetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Setter));
3495 } else {
3496 JSValue value = slot.getValue(exec, propertyName);
3497 RETURN_IF_EXCEPTION(scope, false);
3498 descriptor.setDescriptor(value, slot.attributes());
3499 }
3500
3501 return true;
3502}
3503
3504static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
3505{
3506 VM& vm = exec->vm();
3507 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
3508 if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
3509 JSObject* getter = oldDescriptor.getterPresent() ? oldDescriptor.getterObject() : nullptr;
3510 JSObject* setter = oldDescriptor.setterPresent() ? oldDescriptor.setterObject() : nullptr;
3511 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
3512 target->putDirectAccessor(exec, propertyName, accessor, attributes | PropertyAttribute::Accessor);
3513 return true;
3514 }
3515 JSValue newValue = jsUndefined();
3516 if (descriptor.value())
3517 newValue = descriptor.value();
3518 else if (oldDescriptor.value())
3519 newValue = oldDescriptor.value();
3520 target->putDirect(vm, propertyName, newValue, attributes & ~PropertyAttribute::Accessor);
3521 if (attributes & PropertyAttribute::ReadOnly)
3522 target->structure(vm)->setContainsReadOnlyProperties();
3523 return true;
3524 }
3525 attributes &= ~PropertyAttribute::ReadOnly;
3526
3527 JSObject* getter = descriptor.getterPresent()
3528 ? descriptor.getterObject() : oldDescriptor.getterPresent()
3529 ? oldDescriptor.getterObject() : nullptr;
3530 JSObject* setter = descriptor.setterPresent()
3531 ? descriptor.setterObject() : oldDescriptor.setterPresent()
3532 ? oldDescriptor.setterObject() : nullptr;
3533 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
3534
3535 target->putDirectAccessor(exec, propertyName, accessor, attributes | PropertyAttribute::Accessor);
3536 return true;
3537}
3538
3539bool JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
3540{
3541 if (Optional<uint32_t> index = parseIndex(propertyName))
3542 return putDirectIndex(exec, index.value(), value);
3543 return putDirect(exec->vm(), propertyName, value);
3544}
3545
3546// 9.1.6.3 of the spec
3547// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-validateandapplypropertydescriptor
3548bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, PropertyName propertyName, bool isExtensible,
3549 const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException)
3550{
3551 VM& vm = exec->vm();
3552 auto scope = DECLARE_THROW_SCOPE(vm);
3553
3554 // If we have a new property we can just put it on normally
3555 // Step 2.
3556 if (!isCurrentDefined) {
3557 // unless extensions are prevented!
3558 // Step 2.a
3559 if (!isExtensible)
3560 return typeError(exec, scope, throwException, NonExtensibleObjectPropertyDefineError);
3561 if (!object)
3562 return true;
3563 // Step 2.c/d
3564 PropertyDescriptor oldDescriptor;
3565 oldDescriptor.setValue(jsUndefined());
3566 // FIXME: spec says to always return true here.
3567 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
3568 }
3569 // Step 3.
3570 if (descriptor.isEmpty())
3571 return true;
3572 // Step 4.
3573 bool isEqual = current.equalTo(exec, descriptor);
3574 RETURN_IF_EXCEPTION(scope, false);
3575 if (isEqual)
3576 return true;
3577
3578 // Step 5.
3579 // Filter out invalid changes
3580 if (!current.configurable()) {
3581 if (descriptor.configurable())
3582 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
3583 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable())
3584 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
3585 }
3586
3587 // Step 6.
3588 // A generic descriptor is simply changing the attributes of an existing property
3589 if (descriptor.isGenericDescriptor()) {
3590 if (!current.attributesEqual(descriptor) && object) {
3591 object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3592 RETURN_IF_EXCEPTION(scope, false);
3593 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3594 }
3595 return true;
3596 }
3597
3598 // Step 7.
3599 // Changing between a normal property or an accessor property
3600 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
3601 if (!current.configurable())
3602 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
3603
3604 if (!object)
3605 return true;
3606
3607 object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3608 RETURN_IF_EXCEPTION(scope, false);
3609 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3610 }
3611
3612 // Step 8.
3613 // Changing the value and attributes of an existing property
3614 if (descriptor.isDataDescriptor()) {
3615 if (!current.configurable()) {
3616 if (!current.writable() && descriptor.writable())
3617 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
3618 if (!current.writable()) {
3619 if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value()))
3620 return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
3621 }
3622 }
3623 if (current.attributesEqual(descriptor) && !descriptor.value())
3624 return true;
3625 if (!object)
3626 return true;
3627 object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3628 RETURN_IF_EXCEPTION(scope, false);
3629 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3630 }
3631
3632 // Step 9.
3633 // Changing the accessor functions of an existing accessor property
3634 ASSERT(descriptor.isAccessorDescriptor());
3635 if (!current.configurable()) {
3636 if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter())))
3637 return typeError(exec, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
3638 if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter())))
3639 return typeError(exec, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
3640 if (current.attributes() & PropertyAttribute::CustomAccessor)
3641 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
3642 }
3643
3644 // Step 10/11.
3645 if (!object)
3646 return true;
3647 JSValue accessor = object->getDirect(vm, propertyName);
3648 if (!accessor)
3649 return false;
3650 JSObject* getter = nullptr;
3651 JSObject* setter = nullptr;
3652 bool getterSetterChanged = false;
3653
3654 if (accessor.isCustomGetterSetter()) {
3655 auto* customGetterSetter = jsCast<CustomGetterSetter*>(accessor);
3656 if (customGetterSetter->setter())
3657 setter = getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Setter);
3658 if (customGetterSetter->getter())
3659 getter = getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Getter);
3660 } else {
3661 ASSERT(accessor.isGetterSetter());
3662 auto* getterSetter = jsCast<GetterSetter*>(accessor);
3663 getter = getterSetter->getter();
3664 setter = getterSetter->setter();
3665 }
3666 if (descriptor.setterPresent()) {
3667 setter = descriptor.setterObject();
3668 getterSetterChanged = true;
3669 }
3670 if (descriptor.getterPresent()) {
3671 getter = descriptor.getterObject();
3672 getterSetterChanged = true;
3673 }
3674
3675 if (current.attributesEqual(descriptor) && !getterSetterChanged)
3676 return true;
3677
3678 GetterSetter* getterSetter = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
3679
3680 object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3681 RETURN_IF_EXCEPTION(scope, false);
3682 unsigned attrs = descriptor.attributesOverridingCurrent(current);
3683 object->putDirectAccessor(exec, propertyName, getterSetter, attrs | PropertyAttribute::Accessor);
3684 return true;
3685}
3686
3687bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3688{
3689 VM& vm = exec->vm();
3690 auto throwScope = DECLARE_THROW_SCOPE(vm);
3691
3692 // Track on the globaldata that we're in define property.
3693 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
3694 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
3695 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
3696 VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
3697 PropertyDescriptor current;
3698 bool isCurrentDefined = getOwnPropertyDescriptor(exec, propertyName, current);
3699 bool isExtensible = this->isExtensible(exec);
3700 RETURN_IF_EXCEPTION(throwScope, false);
3701 RELEASE_AND_RETURN(throwScope, validateAndApplyPropertyDescriptor(exec, this, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException));
3702}
3703
3704bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3705{
3706 // If it's an array index, then use the indexed property storage.
3707 if (Optional<uint32_t> index = parseIndex(propertyName)) {
3708 // 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.
3709 // d. Reject if succeeded is false.
3710 // e. If index >= oldLen
3711 // e.i. Set oldLenDesc.[[Value]] to index + 1.
3712 // 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.
3713 // f. Return true.
3714 return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException);
3715 }
3716
3717 return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
3718}
3719
3720void JSObject::convertToDictionary(VM& vm)
3721{
3722 DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure(vm));
3723 setStructure(
3724 vm, Structure::toCacheableDictionaryTransition(vm, structure(vm), &deferredWatchpointFire));
3725}
3726
3727void JSObject::shiftButterflyAfterFlattening(const GCSafeConcurrentJSLocker&, VM& vm, Structure* structure, size_t outOfLineCapacityAfter)
3728{
3729 // This could interleave visitChildren because some old structure could have been a non
3730 // dictionary structure. We have to be crazy careful. But, we are guaranteed to be holding
3731 // the structure's lock right now, and that helps a bit.
3732
3733 Butterfly* oldButterfly = this->butterfly();
3734 size_t preCapacity;
3735 size_t indexingPayloadSizeInBytes;
3736 bool hasIndexingHeader = this->hasIndexingHeader(vm);
3737 if (UNLIKELY(hasIndexingHeader)) {
3738 preCapacity = oldButterfly->indexingHeader()->preCapacity(structure);
3739 indexingPayloadSizeInBytes = oldButterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
3740 } else {
3741 preCapacity = 0;
3742 indexingPayloadSizeInBytes = 0;
3743 }
3744
3745 Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, preCapacity, outOfLineCapacityAfter, hasIndexingHeader, indexingPayloadSizeInBytes);
3746
3747 // No need to copy the precapacity.
3748 void* currentBase = oldButterfly->base(0, outOfLineCapacityAfter);
3749 void* newBase = newButterfly->base(0, outOfLineCapacityAfter);
3750
3751 memcpy(newBase, currentBase, Butterfly::totalSize(0, outOfLineCapacityAfter, hasIndexingHeader, indexingPayloadSizeInBytes));
3752
3753 setButterfly(vm, newButterfly);
3754}
3755
3756uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object)
3757{
3758 VM& vm = exec->vm();
3759 Structure* structure = object->structure(vm);
3760 if (structure->holesMustForwardToPrototype(vm, object))
3761 return 0;
3762 switch (object->indexingType()) {
3763 case ALL_BLANK_INDEXING_TYPES:
3764 case ALL_UNDECIDED_INDEXING_TYPES:
3765 return 0;
3766
3767 case ALL_INT32_INDEXING_TYPES:
3768 case ALL_CONTIGUOUS_INDEXING_TYPES: {
3769 Butterfly* butterfly = object->butterfly();
3770 unsigned usedLength = butterfly->publicLength();
3771 for (unsigned i = 0; i < usedLength; ++i) {
3772 if (!butterfly->contiguous().at(object, i))
3773 return 0;
3774 }
3775 return usedLength;
3776 }
3777
3778 case ALL_DOUBLE_INDEXING_TYPES: {
3779 Butterfly* butterfly = object->butterfly();
3780 unsigned usedLength = butterfly->publicLength();
3781 for (unsigned i = 0; i < usedLength; ++i) {
3782 double value = butterfly->contiguousDouble().at(object, i);
3783 if (value != value)
3784 return 0;
3785 }
3786 return usedLength;
3787 }
3788
3789 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
3790 ArrayStorage* storage = object->m_butterfly->arrayStorage();
3791 if (storage->m_sparseMap.get())
3792 return 0;
3793
3794 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
3795 for (unsigned i = 0; i < usedVectorLength; ++i) {
3796 if (!storage->m_vector[i])
3797 return 0;
3798 }
3799 return usedVectorLength;
3800 }
3801
3802 default:
3803 RELEASE_ASSERT_NOT_REACHED();
3804 return 0;
3805 }
3806}
3807
3808void JSObject::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
3809{
3810 VM& vm = exec->vm();
3811 object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
3812}
3813
3814void JSObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
3815{
3816 VM& vm = exec->vm();
3817 auto scope = DECLARE_THROW_SCOPE(vm);
3818 object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, EnumerationMode(mode, JSObjectPropertiesMode::Exclude));
3819 RETURN_IF_EXCEPTION(scope, void());
3820
3821 JSValue nextProto = object->getPrototype(vm, exec);
3822 RETURN_IF_EXCEPTION(scope, void());
3823 if (nextProto.isNull())
3824 return;
3825
3826 JSObject* prototype = asObject(nextProto);
3827 while (true) {
3828 if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
3829 scope.release();
3830 prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
3831 return;
3832 }
3833 prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
3834 RETURN_IF_EXCEPTION(scope, void());
3835 nextProto = prototype->getPrototype(vm, exec);
3836 RETURN_IF_EXCEPTION(scope, void());
3837 if (nextProto.isNull())
3838 break;
3839 prototype = asObject(nextProto);
3840 }
3841}
3842
3843// Implements GetMethod(O, P) in section 7.3.9 of the spec.
3844// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-getmethod
3845JSValue JSObject::getMethod(ExecState* exec, CallData& callData, CallType& callType, const Identifier& ident, const String& errorMessage)
3846{
3847 VM& vm = exec->vm();
3848 auto scope = DECLARE_THROW_SCOPE(vm);
3849
3850 JSValue method = get(exec, ident);
3851 RETURN_IF_EXCEPTION(scope, JSValue());
3852
3853 if (!method.isCell()) {
3854 if (method.isUndefinedOrNull())
3855 return jsUndefined();
3856
3857 throwVMTypeError(exec, scope, errorMessage);
3858 return jsUndefined();
3859 }
3860
3861 callType = method.asCell()->methodTable(vm)->getCallData(method.asCell(), callData);
3862 if (callType == CallType::None) {
3863 throwVMTypeError(exec, scope, errorMessage);
3864 return jsUndefined();
3865 }
3866
3867 return method;
3868}
3869
3870bool JSObject::anyObjectInChainMayInterceptIndexedAccesses(VM& vm) const
3871{
3872 for (const JSObject* current = this; ;) {
3873 if (current->structure(vm)->mayInterceptIndexedAccesses())
3874 return true;
3875
3876 JSValue prototype = current->getPrototypeDirect(vm);
3877 if (prototype.isNull())
3878 return false;
3879
3880 current = asObject(prototype);
3881 }
3882}
3883
3884bool JSObject::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName)
3885{
3886 if (parseIndex(propertyName))
3887 return anyObjectInChainMayInterceptIndexedAccesses(vm);
3888
3889 for (JSObject* current = this; ;) {
3890 JSValue prototype = current->getPrototypeDirect(vm);
3891 if (prototype.isNull())
3892 return false;
3893
3894 current = asObject(prototype);
3895
3896 unsigned attributes;
3897 PropertyOffset offset = current->structure(vm)->get(vm, propertyName, attributes);
3898 if (!JSC::isValidOffset(offset))
3899 continue;
3900
3901 if (attributes & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))
3902 return true;
3903
3904 return false;
3905 }
3906}
3907
3908bool JSObject::needsSlowPutIndexing(VM& vm) const
3909{
3910 return anyObjectInChainMayInterceptIndexedAccesses(vm) || globalObject(vm)->isHavingABadTime();
3911}
3912
3913NonPropertyTransition JSObject::suggestedArrayStorageTransition(VM& vm) const
3914{
3915 if (needsSlowPutIndexing(vm))
3916 return NonPropertyTransition::AllocateSlowPutArrayStorage;
3917
3918 return NonPropertyTransition::AllocateArrayStorage;
3919}
3920
3921} // namespace JSC
3922