Make MarkedBlock state tracking support overlapped allocation and marking state
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003-2006, 2008-2009, 2012-2016 Apple Inc. All rights reserved.
5  *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public License
18  *  along with this library; see the file COPYING.LIB.  If not, write to
19  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "config.h"
25 #include "JSObject.h"
26
27 #include "ButterflyInlines.h"
28 #include "CustomGetterSetter.h"
29 #include "DatePrototype.h"
30 #include "ErrorConstructor.h"
31 #include "Exception.h"
32 #include "Executable.h"
33 #include "GetterSetter.h"
34 #include "HeapSnapshotBuilder.h"
35 #include "IndexingHeaderInlines.h"
36 #include "JSCInlines.h"
37 #include "JSCustomGetterSetterFunction.h"
38 #include "JSFunction.h"
39 #include "JSGlobalObject.h"
40 #include "Lookup.h"
41 #include "NativeErrorConstructor.h"
42 #include "Nodes.h"
43 #include "ObjectPrototype.h"
44 #include "PropertyDescriptor.h"
45 #include "PropertyNameArray.h"
46 #include "PrototypeMapInlines.h"
47 #include "ProxyObject.h"
48 #include "Reject.h"
49 #include "SlotVisitorInlines.h"
50 #include <math.h>
51 #include <wtf/Assertions.h>
52
53 namespace JSC {
54
55 // We keep track of the size of the last array after it was grown. We use this
56 // as a simple heuristic for as the value to grow the next array from size 0.
57 // This value is capped by the constant FIRST_VECTOR_GROW defined in
58 // ArrayConventions.h.
59 static unsigned lastArraySize = 0;
60
61 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
62 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
63
64 const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
65 const char* UnconfigurablePropertyChangeAccessMechanismError = "Attempting to change access mechanism for an unconfigurable property.";
66
67 const ClassInfo JSObject::s_info = { "Object", 0, 0, CREATE_METHOD_TABLE(JSObject) };
68
69 const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(JSFinalObject) };
70
71 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
72 {
73     VM& vm = exec->vm();
74
75     // Add properties from the static hashtables of properties
76     for (; classInfo; classInfo = classInfo->parentClass) {
77         const HashTable* table = classInfo->staticPropHashTable;
78         if (!table)
79             continue;
80
81         for (auto iter = table->begin(); iter != table->end(); ++iter) {
82             if (!(iter->attributes() & DontEnum) || mode.includeDontEnumProperties())
83                 propertyNames.add(Identifier::fromString(&vm, iter.key()));
84         }
85     }
86 }
87
88 ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, Structure* structure)
89 {
90     ASSERT(butterfly);
91     
92     size_t storageSize = structure->outOfLineSize();
93     size_t propertyCapacity = structure->outOfLineCapacity();
94     size_t preCapacity;
95     bool hasIndexingHeader = this->hasIndexingHeader();
96     if (UNLIKELY(hasIndexingHeader))
97         preCapacity = butterfly->indexingHeader()->preCapacity(structure);
98     else
99         preCapacity = 0;
100     
101     HeapCell* base = bitwise_cast<HeapCell*>(butterfly->base(preCapacity, propertyCapacity));
102     
103     ASSERT(Heap::heap(base) == visitor.heap());
104
105     // Keep the butterfly alive.
106     visitor.markAuxiliary(base);
107     
108     // Mark the properties.
109     visitor.appendValuesHidden(butterfly->propertyStorage() - storageSize, storageSize);
110     
111     // Mark the array if appropriate.
112     switch (this->indexingType()) {
113     case ALL_CONTIGUOUS_INDEXING_TYPES:
114         visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
115         break;
116     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
117         visitor.appendValuesHidden(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
118         if (butterfly->arrayStorage()->m_sparseMap)
119             visitor.append(&butterfly->arrayStorage()->m_sparseMap);
120         break;
121     default:
122         break;
123     }
124 }
125
126 size_t JSObject::estimatedSize(JSCell* cell)
127 {
128     JSObject* thisObject = jsCast<JSObject*>(cell);
129     size_t butterflyOutOfLineSize = thisObject->m_butterfly ? thisObject->structure()->outOfLineSize() : 0;
130     return Base::estimatedSize(cell) + butterflyOutOfLineSize;
131 }
132
133 void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
134 {
135     JSObject* thisObject = jsCast<JSObject*>(cell);
136     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
137 #if !ASSERT_DISABLED
138     bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
139     visitor.m_isCheckingForDefaultMarkViolation = false;
140 #endif
141     
142     JSCell::visitChildren(thisObject, visitor);
143
144     Butterfly* butterfly = thisObject->m_butterfly.get();
145     if (butterfly)
146         thisObject->visitButterfly(visitor, butterfly, thisObject->structure(visitor.vm()));
147
148 #if !ASSERT_DISABLED
149     visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
150 #endif
151 }
152
153 void JSObject::heapSnapshot(JSCell* cell, HeapSnapshotBuilder& builder)
154 {
155     JSObject* thisObject = jsCast<JSObject*>(cell);
156     Base::heapSnapshot(cell, builder);
157
158     Structure* structure = thisObject->structure();
159     for (auto& entry : structure->getPropertiesConcurrently()) {
160         JSValue toValue = thisObject->getDirect(entry.offset);
161         if (toValue && toValue.isCell())
162             builder.appendPropertyNameEdge(thisObject, toValue.asCell(), entry.key);
163     }
164
165     Butterfly* butterfly = thisObject->m_butterfly.get();
166     if (butterfly) {
167         WriteBarrier<Unknown>* data = nullptr;
168         uint32_t count = 0;
169
170         switch (thisObject->indexingType()) {
171         case ALL_CONTIGUOUS_INDEXING_TYPES:
172             data = butterfly->contiguous().data();
173             count = butterfly->publicLength();
174             break;
175         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
176             data = butterfly->arrayStorage()->m_vector;
177             count = butterfly->arrayStorage()->vectorLength();
178             break;
179         default:
180             break;
181         }
182
183         for (uint32_t i = 0; i < count; ++i) {
184             JSValue toValue = data[i].get();
185             if (toValue && toValue.isCell())
186                 builder.appendIndexEdge(thisObject, toValue.asCell(), i);
187         }
188     }
189 }
190
191 void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
192 {
193     JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
194     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
195 #if !ASSERT_DISABLED
196     bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
197     visitor.m_isCheckingForDefaultMarkViolation = false;
198 #endif
199     
200     JSCell::visitChildren(thisObject, visitor);
201
202     Structure* structure = thisObject->structure(visitor.vm());
203     Butterfly* butterfly = thisObject->butterfly();
204     if (butterfly)
205         thisObject->visitButterfly(visitor, butterfly, structure);
206
207     size_t storageSize = structure->inlineSize();
208     if (storageSize)
209         visitor.appendValuesHidden(thisObject->inlineStorage(), storageSize);
210
211 #if !ASSERT_DISABLED
212     visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
213 #endif
214 }
215
216 String JSObject::className(const JSObject* object)
217 {
218     const ClassInfo* info = object->classInfo();
219     ASSERT(info);
220     return info->className;
221 }
222
223 String JSObject::toStringName(const JSObject* object, ExecState*)
224 {
225     const ClassInfo* info = object->classInfo();
226     ASSERT(info);
227     return info->methodTable.className(object);
228 }
229
230 String JSObject::calculatedClassName(JSObject* object)
231 {
232     String prototypeFunctionName;
233     ExecState* exec = object->globalObject()->globalExec();
234     PropertySlot slot(object->getPrototypeDirect(), PropertySlot::InternalMethodType::VMInquiry);
235     PropertyName constructor(exec->propertyNames().constructor);
236     if (object->getPropertySlot(exec, constructor, slot)) {
237         if (slot.isValue()) {
238             JSValue constructorValue = slot.getValue(exec, constructor);
239             if (constructorValue.isCell()) {
240                 if (JSCell* constructorCell = constructorValue.asCell()) {
241                     if (JSObject* ctorObject = constructorCell->getObject()) {
242                         VM& vm = exec->vm();
243                         if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(ctorObject))
244                             prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
245                         else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(ctorObject))
246                             prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
247                     }
248                 }
249             }
250         }
251     }
252
253     if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") {
254         String tableClassName = object->methodTable()->className(object);
255         if (!tableClassName.isNull() && tableClassName != "Object")
256             return tableClassName;
257
258         String classInfoName = object->classInfo()->className;
259         if (!classInfoName.isNull())
260             return classInfoName;
261
262         if (prototypeFunctionName.isNull())
263             return ASCIILiteral("Object");
264     }
265
266     return prototypeFunctionName;
267 }
268
269 bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot)
270 {
271     // NB. The fact that we're directly consulting our indexed storage implies that it is not
272     // legal for anyone to override getOwnPropertySlot() without also overriding
273     // getOwnPropertySlotByIndex().
274     
275     if (i > MAX_ARRAY_INDEX)
276         return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
277     
278     switch (thisObject->indexingType()) {
279     case ALL_BLANK_INDEXING_TYPES:
280     case ALL_UNDECIDED_INDEXING_TYPES:
281         break;
282         
283     case ALL_INT32_INDEXING_TYPES:
284     case ALL_CONTIGUOUS_INDEXING_TYPES: {
285         Butterfly* butterfly = thisObject->butterfly();
286         if (i >= butterfly->vectorLength())
287             return false;
288         
289         JSValue value = butterfly->contiguous()[i].get();
290         if (value) {
291             slot.setValue(thisObject, None, value);
292             return true;
293         }
294         
295         return false;
296     }
297         
298     case ALL_DOUBLE_INDEXING_TYPES: {
299         Butterfly* butterfly = thisObject->butterfly();
300         if (i >= butterfly->vectorLength())
301             return false;
302         
303         double value = butterfly->contiguousDouble()[i];
304         if (value == value) {
305             slot.setValue(thisObject, None, JSValue(JSValue::EncodeAsDouble, value));
306             return true;
307         }
308         
309         return false;
310     }
311         
312     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
313         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
314         if (i >= storage->length())
315             return false;
316         
317         if (i < storage->vectorLength()) {
318             JSValue value = storage->m_vector[i].get();
319             if (value) {
320                 slot.setValue(thisObject, None, value);
321                 return true;
322             }
323         } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
324             SparseArrayValueMap::iterator it = map->find(i);
325             if (it != map->notFound()) {
326                 it->value.get(thisObject, slot);
327                 return true;
328             }
329         }
330         break;
331     }
332         
333     default:
334         RELEASE_ASSERT_NOT_REACHED();
335         break;
336     }
337     
338     return false;
339 }
340
341 // https://tc39.github.io/ecma262/#sec-ordinaryset
342 bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyName, JSValue value, JSValue receiver, bool shouldThrow)
343 {
344     // If we find the receiver is not the same to the object, we fall to this slow path.
345     // Currently, there are 3 candidates.
346     // 1. Reflect.set can alter the receiver with an arbitrary value.
347     // 2. Window Proxy.
348     // 3. ES6 Proxy.
349
350     VM& vm = exec->vm();
351     auto scope = DECLARE_THROW_SCOPE(vm);
352     JSObject* current = object;
353     PropertyDescriptor ownDescriptor;
354     while (true) {
355         if (current->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
356             ProxyObject* proxy = jsCast<ProxyObject*>(current);
357             PutPropertySlot slot(receiver, shouldThrow);
358             return proxy->ProxyObject::put(proxy, exec, propertyName, value, slot);
359         }
360
361         // 9.1.9.1-2 Let ownDesc be ? O.[[GetOwnProperty]](P).
362         bool ownDescriptorFound = current->getOwnPropertyDescriptor(exec, propertyName, ownDescriptor);
363         if (UNLIKELY(scope.exception()))
364             return false;
365
366         if (!ownDescriptorFound) {
367             // 9.1.9.1-3-a Let parent be ? O.[[GetPrototypeOf]]().
368             JSValue prototype = current->getPrototype(vm, exec);
369             if (UNLIKELY(scope.exception()))
370                 return false;
371
372             // 9.1.9.1-3-b If parent is not null, then
373             if (!prototype.isNull()) {
374                 // 9.1.9.1-3-b-i Return ? parent.[[Set]](P, V, Receiver).
375                 current = asObject(prototype);
376                 continue;
377             }
378             // 9.1.9.1-3-c-i Let ownDesc be the PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
379             ownDescriptor = PropertyDescriptor(jsUndefined(), None);
380         }
381         break;
382     }
383
384     // 9.1.9.1-4 If IsDataDescriptor(ownDesc) is true, then
385     if (ownDescriptor.isDataDescriptor()) {
386         // 9.1.9.1-4-a If ownDesc.[[Writable]] is false, return false.
387         if (!ownDescriptor.writable())
388             return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError);
389
390         // 9.1.9.1-4-b If Type(Receiver) is not Object, return false.
391         if (!receiver.isObject())
392             return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError);
393
394         // In OrdinarySet, the receiver may not be the same to the object.
395         // So, we perform [[GetOwnProperty]] onto the receiver while we already perform [[GetOwnProperty]] onto the object.
396
397         // 9.1.9.1-4-c Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
398         JSObject* receiverObject = asObject(receiver);
399         PropertyDescriptor existingDescriptor;
400         bool existingDescriptorFound = receiverObject->getOwnPropertyDescriptor(exec, propertyName, existingDescriptor);
401         if (UNLIKELY(scope.exception()))
402             return false;
403
404         // 9.1.9.1-4-d If existingDescriptor is not undefined, then
405         if (existingDescriptorFound) {
406             // 9.1.9.1-4-d-i If IsAccessorDescriptor(existingDescriptor) is true, return false.
407             if (existingDescriptor.isAccessorDescriptor())
408                 return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError);
409
410             // 9.1.9.1-4-d-ii If existingDescriptor.[[Writable]] is false, return false.
411             if (!existingDescriptor.writable())
412                 return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError);
413
414             // 9.1.9.1-4-d-iii Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
415             PropertyDescriptor valueDescriptor;
416             valueDescriptor.setValue(value);
417
418             // 9.1.9.1-4-d-iv Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
419             return receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, valueDescriptor, shouldThrow);
420         }
421
422         // 9.1.9.1-4-e Else Receiver does not currently have a property P,
423         // 9.1.9.1-4-e-i Return ? CreateDataProperty(Receiver, P, V).
424         return receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, PropertyDescriptor(value, None), shouldThrow);
425     }
426
427     // 9.1.9.1-5 Assert: IsAccessorDescriptor(ownDesc) is true.
428     ASSERT(ownDescriptor.isAccessorDescriptor());
429
430     // 9.1.9.1-6 Let setter be ownDesc.[[Set]].
431     // 9.1.9.1-7 If setter is undefined, return false.
432     JSValue setter = ownDescriptor.setter();
433     if (!setter.isObject())
434         return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError);
435
436     // 9.1.9.1-8 Perform ? Call(setter, Receiver, << V >>).
437     JSObject* setterObject = asObject(setter);
438     MarkedArgumentBuffer args;
439     args.append(value);
440
441     CallData callData;
442     CallType callType = setterObject->methodTable(exec->vm())->getCallData(setterObject, callData);
443     call(exec, setterObject, callType, callData, receiver, args);
444
445     // 9.1.9.1-9 Return true.
446     return true;
447 }
448
449 // ECMA 8.6.2.2
450 bool JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
451 {
452     return putInline(cell, exec, propertyName, value, slot);
453 }
454
455 bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
456 {
457     ASSERT(!isThisValueAltered(slot, this));
458
459     VM& vm = exec->vm();
460
461     JSObject* obj = this;
462     for (;;) {
463         unsigned attributes;
464         PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
465         if (isValidOffset(offset)) {
466             if (attributes & ReadOnly) {
467                 ASSERT(structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == this);
468                 return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
469             }
470
471             JSValue gs = obj->getDirect(offset);
472             if (gs.isGetterSetter()) {
473                 bool result = callSetter(exec, slot.thisValue(), gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
474                 if (!structure()->isDictionary())
475                     slot.setCacheableSetter(obj, offset);
476                 return result;
477             }
478             if (gs.isCustomGetterSetter()) {
479                 bool result = callCustomSetter(exec, gs, attributes & CustomAccessor, obj, slot.thisValue(), value);
480                 if (attributes & CustomAccessor)
481                     slot.setCustomAccessor(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
482                 else
483                     slot.setCustomValue(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
484                 return result;
485             }
486             ASSERT(!(attributes & Accessor));
487
488             // If there's an existing property on the object or one of its 
489             // prototypes it should be replaced, so break here.
490             break;
491         }
492         if (!obj->staticPropertiesReified()) {
493             if (obj->classInfo()->hasStaticSetterOrReadonlyProperties()) {
494                 if (auto* entry = obj->findPropertyHashEntry(propertyName))
495                     return putEntry(exec, entry, obj, this, propertyName, value, slot);
496             }
497         }
498         if (obj->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
499             // FIXME: We shouldn't unconditionally perform [[Set]] here.
500             // We need to do more because this is observable behavior.
501             // https://bugs.webkit.org/show_bug.cgi?id=155012
502             ProxyObject* proxy = jsCast<ProxyObject*>(obj);
503             return proxy->ProxyObject::put(proxy, exec, propertyName, value, slot);
504         }
505         JSValue prototype = obj->getPrototypeDirect();
506         if (prototype.isNull())
507             break;
508         obj = asObject(prototype);
509     }
510
511     ASSERT(!structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == this);
512     if (!putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot))
513         return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
514     return true;
515 }
516
517 bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
518 {
519     JSObject* thisObject = jsCast<JSObject*>(cell);
520     
521     if (propertyName > MAX_ARRAY_INDEX) {
522         PutPropertySlot slot(cell, shouldThrow);
523         return thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
524     }
525     
526     switch (thisObject->indexingType()) {
527     case ALL_BLANK_INDEXING_TYPES:
528         break;
529         
530     case ALL_UNDECIDED_INDEXING_TYPES: {
531         thisObject->convertUndecidedForValue(exec->vm(), value);
532         // Reloop.
533         return putByIndex(cell, exec, propertyName, value, shouldThrow);
534     }
535         
536     case ALL_INT32_INDEXING_TYPES: {
537         if (!value.isInt32()) {
538             thisObject->convertInt32ForValue(exec->vm(), value);
539             return putByIndex(cell, exec, propertyName, value, shouldThrow);
540         }
541         FALLTHROUGH;
542     }
543         
544     case ALL_CONTIGUOUS_INDEXING_TYPES: {
545         Butterfly* butterfly = thisObject->butterfly();
546         if (propertyName >= butterfly->vectorLength())
547             break;
548         butterfly->contiguous()[propertyName].set(exec->vm(), thisObject, value);
549         if (propertyName >= butterfly->publicLength())
550             butterfly->setPublicLength(propertyName + 1);
551         return true;
552     }
553         
554     case ALL_DOUBLE_INDEXING_TYPES: {
555         if (!value.isNumber()) {
556             thisObject->convertDoubleToContiguous(exec->vm());
557             // Reloop.
558             return putByIndex(cell, exec, propertyName, value, shouldThrow);
559         }
560         double valueAsDouble = value.asNumber();
561         if (valueAsDouble != valueAsDouble) {
562             thisObject->convertDoubleToContiguous(exec->vm());
563             // Reloop.
564             return putByIndex(cell, exec, propertyName, value, shouldThrow);
565         }
566         Butterfly* butterfly = thisObject->butterfly();
567         if (propertyName >= butterfly->vectorLength())
568             break;
569         butterfly->contiguousDouble()[propertyName] = valueAsDouble;
570         if (propertyName >= butterfly->publicLength())
571             butterfly->setPublicLength(propertyName + 1);
572         return true;
573     }
574         
575     case NonArrayWithArrayStorage:
576     case ArrayWithArrayStorage: {
577         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
578         
579         if (propertyName >= storage->vectorLength())
580             break;
581         
582         WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
583         unsigned length = storage->length();
584         
585         // Update length & m_numValuesInVector as necessary.
586         if (propertyName >= length) {
587             length = propertyName + 1;
588             storage->setLength(length);
589             ++storage->m_numValuesInVector;
590         } else if (!valueSlot)
591             ++storage->m_numValuesInVector;
592         
593         valueSlot.set(exec->vm(), thisObject, value);
594         return true;
595     }
596         
597     case NonArrayWithSlowPutArrayStorage:
598     case ArrayWithSlowPutArrayStorage: {
599         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
600         
601         if (propertyName >= storage->vectorLength())
602             break;
603         
604         WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
605         unsigned length = storage->length();
606         
607         // Update length & m_numValuesInVector as necessary.
608         if (propertyName >= length) {
609             bool putResult = false;
610             if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
611                 return putResult;
612             length = propertyName + 1;
613             storage->setLength(length);
614             ++storage->m_numValuesInVector;
615         } else if (!valueSlot) {
616             bool putResult = false;
617             if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
618                 return putResult;
619             ++storage->m_numValuesInVector;
620         }
621         
622         valueSlot.set(exec->vm(), thisObject, value);
623         return true;
624     }
625         
626     default:
627         RELEASE_ASSERT_NOT_REACHED();
628     }
629     
630     return thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
631 }
632
633 ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
634 {
635     SparseArrayValueMap* map = storage->m_sparseMap.get();
636
637     if (!map)
638         map = allocateSparseIndexMap(vm);
639
640     if (map->sparseMode())
641         return storage;
642
643     map->setSparseMode();
644
645     unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
646     for (unsigned i = 0; i < usedVectorLength; ++i) {
647         JSValue value = storage->m_vector[i].get();
648         // This will always be a new entry in the map, so no need to check we can write,
649         // and attributes are default so no need to set them.
650         if (value)
651             map->add(this, i).iterator->value.set(vm, map, value);
652     }
653
654     DeferGC deferGC(vm.heap);
655     Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
656     RELEASE_ASSERT(newButterfly);
657     newButterfly->arrayStorage()->m_indexBias = 0;
658     newButterfly->arrayStorage()->setVectorLength(0);
659     newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
660     setButterflyWithoutChangingStructure(vm, newButterfly);
661     
662     return newButterfly->arrayStorage();
663 }
664
665 void JSObject::enterDictionaryIndexingMode(VM& vm)
666 {
667     switch (indexingType()) {
668     case ALL_BLANK_INDEXING_TYPES:
669     case ALL_UNDECIDED_INDEXING_TYPES:
670     case ALL_INT32_INDEXING_TYPES:
671     case ALL_DOUBLE_INDEXING_TYPES:
672     case ALL_CONTIGUOUS_INDEXING_TYPES:
673         // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
674         // this case if we ever cared. Note that ensureArrayStorage() can return null if the object
675         // doesn't support traditional indexed properties. At the time of writing, this just affects
676         // typed arrays.
677         if (ArrayStorage* storage = ensureArrayStorageSlow(vm))
678             enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, storage);
679         break;
680     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
681         enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly.get()->arrayStorage());
682         break;
683         
684     default:
685         break;
686     }
687 }
688
689 void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
690 {
691     if (mayInterceptIndexedAccesses())
692         return;
693     
694     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors));
695     
696     if (!vm.prototypeMap.isPrototype(this))
697         return;
698     
699     globalObject(vm)->haveABadTime(vm);
700 }
701
702 Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length)
703 {
704     ASSERT(length < MAX_ARRAY_INDEX);
705     IndexingType oldType = indexingType();
706     ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
707     ASSERT(!structure()->needsSlowPutIndexing());
708     ASSERT(!indexingShouldBeSparse());
709     Structure* structure = this->structure(vm);
710     unsigned propertyCapacity = structure->outOfLineCapacity();
711     unsigned vectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, length);
712     Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
713         m_butterfly.get(), vm, this, structure, propertyCapacity, false, 0,
714         sizeof(EncodedJSValue) * vectorLength);
715     newButterfly->setPublicLength(length);
716     newButterfly->setVectorLength(vectorLength);
717     return newButterfly;
718 }
719
720 Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
721 {
722     DeferGC deferGC(vm.heap);
723     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
724     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateUndecided);
725     setStructureAndButterfly(vm, newStructure, newButterfly);
726     return newButterfly;
727 }
728
729 ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
730 {
731     DeferGC deferGC(vm.heap);
732     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
733     for (unsigned i = newButterfly->vectorLength(); i--;)
734         newButterfly->contiguousInt32()[i].setWithoutWriteBarrier(JSValue());
735     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32);
736     setStructureAndButterfly(vm, newStructure, newButterfly);
737     return newButterfly->contiguousInt32();
738 }
739
740 ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
741 {
742     DeferGC deferGC(vm.heap);
743     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
744     for (unsigned i = newButterfly->vectorLength(); i--;)
745         newButterfly->contiguousDouble()[i] = PNaN;
746     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble);
747     setStructureAndButterfly(vm, newStructure, newButterfly);
748     return newButterfly->contiguousDouble();
749 }
750
751 ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
752 {
753     DeferGC deferGC(vm.heap);
754     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
755     for (unsigned i = newButterfly->vectorLength(); i--;)
756         newButterfly->contiguous()[i].setWithoutWriteBarrier(JSValue());
757     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous);
758     setStructureAndButterfly(vm, newStructure, newButterfly);
759     return newButterfly->contiguous();
760 }
761
762 ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
763 {
764     DeferGC deferGC(vm.heap);
765     Structure* structure = this->structure(vm);
766     IndexingType oldType = indexingType();
767     ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
768     Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
769         m_butterfly.get(), vm, this, structure, structure->outOfLineCapacity(), false, 0,
770         ArrayStorage::sizeFor(vectorLength));
771     RELEASE_ASSERT(newButterfly);
772
773     ArrayStorage* result = newButterfly->arrayStorage();
774     result->setLength(length);
775     result->setVectorLength(vectorLength);
776     result->m_sparseMap.clear();
777     result->m_numValuesInVector = 0;
778     result->m_indexBias = 0;
779     for (size_t i = vectorLength; i--;)
780         result->m_vector[i].setWithoutWriteBarrier(JSValue());
781     Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition());
782     setStructureAndButterfly(vm, newStructure, newButterfly);
783     return result;
784 }
785
786 ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
787 {
788     return createArrayStorage(
789         vm, 0, ArrayStorage::optimalVectorLength(0, structure(vm)->outOfLineCapacity(), 0));
790 }
791
792 ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
793 {
794     ASSERT(hasUndecided(indexingType()));
795
796     Butterfly* butterfly = m_butterfly.get();
797     for (unsigned i = butterfly->vectorLength(); i--;)
798         butterfly->contiguousInt32()[i].setWithoutWriteBarrier(JSValue());
799
800     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32));
801     return m_butterfly.get()->contiguousInt32();
802 }
803
804 ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
805 {
806     ASSERT(hasUndecided(indexingType()));
807
808     Butterfly* butterfly = m_butterfly.get();
809     for (unsigned i = butterfly->vectorLength(); i--;)
810         butterfly->contiguousDouble()[i] = PNaN;
811     
812     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
813     return m_butterfly.get()->contiguousDouble();
814 }
815
816 ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
817 {
818     ASSERT(hasUndecided(indexingType()));
819
820     Butterfly* butterfly = m_butterfly.get();
821     for (unsigned i = butterfly->vectorLength(); i--;)
822         butterfly->contiguous()[i].setWithoutWriteBarrier(JSValue());
823
824     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
825     return m_butterfly.get()->contiguous();
826 }
827
828 ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
829 {
830     Structure* structure = this->structure(vm);
831     unsigned publicLength = m_butterfly.get()->publicLength();
832     unsigned propertyCapacity = structure->outOfLineCapacity();
833     unsigned propertySize = structure->outOfLineSize();
834     
835     Butterfly* newButterfly = Butterfly::createUninitialized(
836         vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
837     
838     memcpy(
839         newButterfly->propertyStorage() - propertySize,
840         m_butterfly.get()->propertyStorage() - propertySize,
841         propertySize * sizeof(EncodedJSValue));
842     
843     ArrayStorage* newStorage = newButterfly->arrayStorage();
844     newStorage->setVectorLength(neededLength);
845     newStorage->setLength(publicLength);
846     newStorage->m_sparseMap.clear();
847     newStorage->m_indexBias = 0;
848     newStorage->m_numValuesInVector = 0;
849     
850     return newStorage;
851 }
852
853 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
854 {
855     DeferGC deferGC(vm.heap);
856     ASSERT(hasUndecided(indexingType()));
857
858     unsigned vectorLength = m_butterfly.get()->vectorLength();
859     ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
860     
861     for (unsigned i = vectorLength; i--;)
862         storage->m_vector[i].setWithoutWriteBarrier(JSValue());
863     
864     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
865     setStructureAndButterfly(vm, newStructure, storage->butterfly());
866     return storage;
867 }
868
869 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
870 {
871     return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
872 }
873
874 ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
875 {
876     ASSERT(hasInt32(indexingType()));
877
878     Butterfly* butterfly = m_butterfly.get();
879     for (unsigned i = butterfly->vectorLength(); i--;) {
880         WriteBarrier<Unknown>* current = &butterfly->contiguousInt32()[i];
881         double* currentAsDouble = bitwise_cast<double*>(current);
882         JSValue v = current->get();
883         // NOTE: Since this may be used during initialization, v could be garbage. If it's garbage,
884         // that means it will be overwritten later.
885         if (!v.isInt32()) {
886             *currentAsDouble = PNaN;
887             continue;
888         }
889         *currentAsDouble = v.asInt32();
890     }
891     
892     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
893     return m_butterfly.get()->contiguousDouble();
894 }
895
896 ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
897 {
898     ASSERT(hasInt32(indexingType()));
899     
900     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
901     return m_butterfly.get()->contiguous();
902 }
903
904 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
905 {
906     DeferGC deferGC(vm.heap);
907     ASSERT(hasInt32(indexingType()));
908
909     unsigned vectorLength = m_butterfly.get()->vectorLength();
910     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
911     Butterfly* butterfly = m_butterfly.get();
912     for (unsigned i = 0; i < vectorLength; i++) {
913         JSValue v = butterfly->contiguous()[i].get();
914         newStorage->m_vector[i].setWithoutWriteBarrier(v);
915         if (v)
916             newStorage->m_numValuesInVector++;
917     }
918     
919     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
920     setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
921     return newStorage;
922 }
923
924 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
925 {
926     return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
927 }
928
929 ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
930 {
931     ASSERT(hasDouble(indexingType()));
932
933     Butterfly* butterfly = m_butterfly.get();
934     for (unsigned i = butterfly->vectorLength(); i--;) {
935         double* current = &butterfly->contiguousDouble()[i];
936         WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
937         double value = *current;
938         if (value != value) {
939             currentAsValue->clear();
940             continue;
941         }
942         JSValue v = JSValue(JSValue::EncodeAsDouble, value);
943         currentAsValue->setWithoutWriteBarrier(v);
944     }
945     
946     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
947     return m_butterfly.get()->contiguous();
948 }
949
950 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
951 {
952     DeferGC deferGC(vm.heap);
953     ASSERT(hasDouble(indexingType()));
954
955     unsigned vectorLength = m_butterfly.get()->vectorLength();
956     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
957     Butterfly* butterfly = m_butterfly.get();
958     for (unsigned i = 0; i < vectorLength; i++) {
959         double value = butterfly->contiguousDouble()[i];
960         newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
961         if (value == value)
962             newStorage->m_numValuesInVector++;
963     }
964     
965     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
966     setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
967     return newStorage;
968 }
969
970 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
971 {
972     return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
973 }
974
975 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
976 {
977     DeferGC deferGC(vm.heap);
978     ASSERT(hasContiguous(indexingType()));
979
980     unsigned vectorLength = m_butterfly.get()->vectorLength();
981     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
982     Butterfly* butterfly = m_butterfly.get();
983     for (unsigned i = 0; i < vectorLength; i++) {
984         JSValue v = butterfly->contiguous()[i].get();
985         newStorage->m_vector[i].setWithoutWriteBarrier(v);
986         if (v)
987             newStorage->m_numValuesInVector++;
988     }
989     
990     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
991     setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
992     return newStorage;
993 }
994
995 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
996 {
997     return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
998 }
999
1000 void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
1001 {
1002     IndexingType type = indexingTypeForValue(value);
1003     if (type == Int32Shape) {
1004         convertUndecidedToInt32(vm);
1005         return;
1006     }
1007     
1008     if (type == DoubleShape) {
1009         convertUndecidedToDouble(vm);
1010         return;
1011     }
1012
1013     ASSERT(type == ContiguousShape);
1014     convertUndecidedToContiguous(vm);
1015 }
1016
1017 void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
1018 {
1019     if (value.isInt32()) {
1020         createInitialInt32(vm, index + 1)[index].set(vm, this, value);
1021         return;
1022     }
1023     
1024     if (value.isDouble()) {
1025         double doubleValue = value.asNumber();
1026         if (doubleValue == doubleValue) {
1027             createInitialDouble(vm, index + 1)[index] = doubleValue;
1028             return;
1029         }
1030     }
1031     
1032     createInitialContiguous(vm, index + 1)[index].set(vm, this, value);
1033 }
1034
1035 void JSObject::convertInt32ForValue(VM& vm, JSValue value)
1036 {
1037     ASSERT(!value.isInt32());
1038     
1039     if (value.isDouble() && !std::isnan(value.asDouble())) {
1040         convertInt32ToDouble(vm);
1041         return;
1042     }
1043     
1044     convertInt32ToContiguous(vm);
1045 }
1046
1047 void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
1048 {
1049     ASSERT(index < m_butterfly.get()->publicLength());
1050     ASSERT(index < m_butterfly.get()->vectorLength());
1051     convertUndecidedForValue(vm, value);
1052     setIndexQuickly(vm, index, value);
1053 }
1054
1055 void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1056 {
1057     ASSERT(!value.isInt32());
1058     convertInt32ForValue(vm, value);
1059     setIndexQuickly(vm, index, value);
1060 }
1061
1062 void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1063 {
1064     ASSERT(!value.isNumber() || value.asNumber() != value.asNumber());
1065     convertDoubleToContiguous(vm);
1066     setIndexQuickly(vm, index, value);
1067 }
1068
1069 ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
1070 {
1071     ASSERT(inherits(info()));
1072     
1073     if (structure(vm)->hijacksIndexingHeader())
1074         return ContiguousJSValues();
1075     
1076     switch (indexingType()) {
1077     case ALL_BLANK_INDEXING_TYPES:
1078         if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1079             return ContiguousJSValues();
1080         return createInitialInt32(vm, 0);
1081         
1082     case ALL_UNDECIDED_INDEXING_TYPES:
1083         return convertUndecidedToInt32(vm);
1084         
1085     case ALL_DOUBLE_INDEXING_TYPES:
1086     case ALL_CONTIGUOUS_INDEXING_TYPES:
1087     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1088         return ContiguousJSValues();
1089         
1090     default:
1091         CRASH();
1092         return ContiguousJSValues();
1093     }
1094 }
1095
1096 ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
1097 {
1098     ASSERT(inherits(info()));
1099     
1100     if (structure(vm)->hijacksIndexingHeader())
1101         return ContiguousDoubles();
1102     
1103     switch (indexingType()) {
1104     case ALL_BLANK_INDEXING_TYPES:
1105         if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1106             return ContiguousDoubles();
1107         return createInitialDouble(vm, 0);
1108         
1109     case ALL_UNDECIDED_INDEXING_TYPES:
1110         return convertUndecidedToDouble(vm);
1111         
1112     case ALL_INT32_INDEXING_TYPES:
1113         return convertInt32ToDouble(vm);
1114         
1115     case ALL_CONTIGUOUS_INDEXING_TYPES:
1116     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1117         return ContiguousDoubles();
1118         
1119     default:
1120         CRASH();
1121         return ContiguousDoubles();
1122     }
1123 }
1124
1125 ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
1126 {
1127     ASSERT(inherits(info()));
1128     
1129     if (structure(vm)->hijacksIndexingHeader())
1130         return ContiguousJSValues();
1131     
1132     switch (indexingType()) {
1133     case ALL_BLANK_INDEXING_TYPES:
1134         if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1135             return ContiguousJSValues();
1136         return createInitialContiguous(vm, 0);
1137         
1138     case ALL_UNDECIDED_INDEXING_TYPES:
1139         return convertUndecidedToContiguous(vm);
1140         
1141     case ALL_INT32_INDEXING_TYPES:
1142         return convertInt32ToContiguous(vm);
1143         
1144     case ALL_DOUBLE_INDEXING_TYPES:
1145         return convertDoubleToContiguous(vm);
1146         
1147     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1148         return ContiguousJSValues();
1149         
1150     default:
1151         CRASH();
1152         return ContiguousJSValues();
1153     }
1154 }
1155
1156 ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
1157 {
1158     ASSERT(inherits(info()));
1159
1160     if (structure(vm)->hijacksIndexingHeader())
1161         return nullptr;
1162     
1163     switch (indexingType()) {
1164     case ALL_BLANK_INDEXING_TYPES:
1165         if (UNLIKELY(indexingShouldBeSparse()))
1166             return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
1167         return createInitialArrayStorage(vm);
1168         
1169     case ALL_UNDECIDED_INDEXING_TYPES:
1170         ASSERT(!indexingShouldBeSparse());
1171         ASSERT(!structure(vm)->needsSlowPutIndexing());
1172         return convertUndecidedToArrayStorage(vm);
1173         
1174     case ALL_INT32_INDEXING_TYPES:
1175         ASSERT(!indexingShouldBeSparse());
1176         ASSERT(!structure(vm)->needsSlowPutIndexing());
1177         return convertInt32ToArrayStorage(vm);
1178         
1179     case ALL_DOUBLE_INDEXING_TYPES:
1180         ASSERT(!indexingShouldBeSparse());
1181         ASSERT(!structure(vm)->needsSlowPutIndexing());
1182         return convertDoubleToArrayStorage(vm);
1183         
1184     case ALL_CONTIGUOUS_INDEXING_TYPES:
1185         ASSERT(!indexingShouldBeSparse());
1186         ASSERT(!structure(vm)->needsSlowPutIndexing());
1187         return convertContiguousToArrayStorage(vm);
1188         
1189     default:
1190         RELEASE_ASSERT_NOT_REACHED();
1191         return 0;
1192     }
1193 }
1194
1195 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
1196 {
1197     switch (indexingType()) {
1198     case ALL_BLANK_INDEXING_TYPES: {
1199         createArrayStorage(vm, 0, 0);
1200         SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1201         map->setSparseMode();
1202         return arrayStorage();
1203     }
1204         
1205     case ALL_UNDECIDED_INDEXING_TYPES:
1206         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm));
1207         
1208     case ALL_INT32_INDEXING_TYPES:
1209         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm));
1210         
1211     case ALL_DOUBLE_INDEXING_TYPES:
1212         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm));
1213         
1214     case ALL_CONTIGUOUS_INDEXING_TYPES:
1215         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm));
1216         
1217     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1218         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly.get()->arrayStorage());
1219         
1220     default:
1221         CRASH();
1222         return 0;
1223     }
1224 }
1225
1226 void JSObject::switchToSlowPutArrayStorage(VM& vm)
1227 {
1228     switch (indexingType()) {
1229     case ALL_UNDECIDED_INDEXING_TYPES:
1230         convertUndecidedToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1231         break;
1232         
1233     case ALL_INT32_INDEXING_TYPES:
1234         convertInt32ToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1235         break;
1236         
1237     case ALL_DOUBLE_INDEXING_TYPES:
1238         convertDoubleToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1239         break;
1240         
1241     case ALL_CONTIGUOUS_INDEXING_TYPES:
1242         convertContiguousToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1243         break;
1244         
1245     case NonArrayWithArrayStorage:
1246     case ArrayWithArrayStorage: {
1247         Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::SwitchToSlowPutArrayStorage);
1248         setStructure(vm, newStructure);
1249         break;
1250     }
1251         
1252     default:
1253         CRASH();
1254         break;
1255     }
1256 }
1257
1258 void JSObject::setPrototypeDirect(VM& vm, JSValue prototype)
1259 {
1260     ASSERT(prototype);
1261     if (prototype.isObject())
1262         vm.prototypeMap.addPrototype(asObject(prototype));
1263     
1264     Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype);
1265     setStructure(vm, newStructure);
1266     
1267     if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses())
1268         return;
1269     
1270     if (vm.prototypeMap.isPrototype(this)) {
1271         newStructure->globalObject()->haveABadTime(vm);
1272         return;
1273     }
1274     
1275     if (!hasIndexedProperties(indexingType()))
1276         return;
1277     
1278     if (shouldUseSlowPut(indexingType()))
1279         return;
1280     
1281     switchToSlowPutArrayStorage(vm);
1282 }
1283
1284 bool JSObject::setPrototypeWithCycleCheck(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1285 {
1286     auto scope = DECLARE_THROW_SCOPE(vm);
1287     ASSERT(methodTable(vm)->toThis(this, exec, NotStrictMode) == this);
1288
1289     if (this->getPrototypeDirect() == prototype)
1290         return true;
1291
1292     bool isExtensible = this->isExtensible(exec);
1293     if (UNLIKELY(scope.exception()))
1294         return false;
1295
1296     if (!isExtensible) {
1297         if (shouldThrowIfCantSet)
1298             throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError);
1299         return false;
1300     }
1301
1302     JSValue nextPrototype = prototype;
1303     while (nextPrototype && nextPrototype.isObject()) {
1304         if (nextPrototype == this) {
1305             if (shouldThrowIfCantSet)
1306                 throwTypeError(exec, scope, ASCIILiteral("cyclic __proto__ value"));
1307             return false;
1308         }
1309         // FIXME: The specification currently says we should check if the [[GetPrototypeOf]] internal method of nextPrototype
1310         // is not the ordinary object internal method. However, we currently restrict this to Proxy objects as it would allow
1311         // for cycles with certain HTML objects (WindowProxy, Location) otherwise.
1312         // https://bugs.webkit.org/show_bug.cgi?id=161534
1313         if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType))
1314             break; // We're done. Set the prototype.
1315         nextPrototype = asObject(nextPrototype)->getPrototypeDirect();
1316     }
1317     setPrototypeDirect(vm, prototype);
1318     return true;
1319 }
1320
1321 bool JSObject::setPrototype(JSObject* object, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1322 {
1323     return object->setPrototypeWithCycleCheck(exec->vm(), exec, prototype, shouldThrowIfCantSet);
1324 }
1325
1326 JSValue JSObject::getPrototype(JSObject* object, ExecState*)
1327 {
1328     return object->getPrototypeDirect();
1329 }
1330
1331 bool JSObject::setPrototype(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1332 {
1333     return methodTable(vm)->setPrototype(this, exec, prototype, shouldThrowIfCantSet);
1334 }
1335
1336 bool JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter, unsigned attributes)
1337 {
1338     PropertyDescriptor descriptor;
1339     descriptor.setGetter(getter);
1340
1341     ASSERT(attributes & Accessor);
1342     if (!(attributes & ReadOnly))
1343         descriptor.setConfigurable(true);
1344     if (!(attributes & DontEnum))
1345         descriptor.setEnumerable(true);
1346
1347     return defineOwnProperty(this, exec, propertyName, descriptor, false);
1348 }
1349
1350 bool JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter, unsigned attributes)
1351 {
1352     PropertyDescriptor descriptor;
1353     descriptor.setSetter(setter);
1354
1355     ASSERT(attributes & Accessor);
1356     if (!(attributes & ReadOnly))
1357         descriptor.setConfigurable(true);
1358     if (!(attributes & DontEnum))
1359         descriptor.setEnumerable(true);
1360
1361     return defineOwnProperty(this, exec, propertyName, descriptor, false);
1362 }
1363
1364 bool JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
1365 {
1366     ASSERT(value.isGetterSetter() && (attributes & Accessor));
1367
1368     if (Optional<uint32_t> index = parseIndex(propertyName))
1369         return putDirectIndex(exec, index.value(), value, attributes, PutDirectIndexLikePutDirect);
1370
1371     return putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes);
1372 }
1373
1374 bool JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1375 {
1376     ASSERT(!parseIndex(propertyName));
1377
1378     PutPropertySlot slot(this);
1379     bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1380
1381     ASSERT(slot.type() == PutPropertySlot::NewProperty);
1382
1383     Structure* structure = this->structure(vm);
1384     if (attributes & ReadOnly)
1385         structure->setContainsReadOnlyProperties();
1386     structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1387     return result;
1388 }
1389
1390 bool JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1391 {
1392     PutPropertySlot slot(this);
1393     bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1394
1395     Structure* structure = this->structure(vm);
1396     if (attributes & ReadOnly)
1397         structure->setContainsReadOnlyProperties();
1398
1399     structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1400     return result;
1401 }
1402
1403 // HasProperty(O, P) from Section 7.3.10 of the spec.
1404 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasproperty
1405 bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
1406 {
1407     return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1408 }
1409
1410 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
1411 {
1412     return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1413 }
1414
1415 bool JSObject::hasPropertyGeneric(ExecState* exec, PropertyName propertyName, PropertySlot::InternalMethodType internalMethodType) const
1416 {
1417     PropertySlot slot(this, internalMethodType);
1418     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1419 }
1420
1421 bool JSObject::hasPropertyGeneric(ExecState* exec, unsigned propertyName, PropertySlot::InternalMethodType internalMethodType) const
1422 {
1423     PropertySlot slot(this, internalMethodType);
1424     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1425 }
1426
1427 // ECMA 8.6.2.5
1428 bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
1429 {
1430     JSObject* thisObject = jsCast<JSObject*>(cell);
1431     VM& vm = exec->vm();
1432     
1433     if (Optional<uint32_t> index = parseIndex(propertyName))
1434         return thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, index.value());
1435
1436     unsigned attributes;
1437
1438     if (!thisObject->staticPropertiesReified()) {
1439         if (auto* entry = thisObject->findPropertyHashEntry(propertyName)) {
1440             // If the static table contains a non-configurable (DontDelete) property then we can return early;
1441             // if there is a property in the storage array it too must be non-configurable (the language does
1442             // not allow repacement of a non-configurable property with a configurable one).
1443             if (entry->attributes() & DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
1444                 ASSERT(!isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes)) || attributes & DontDelete);
1445                 return false;
1446             }
1447             thisObject->reifyAllStaticProperties(exec);
1448         }
1449     }
1450
1451     Structure* structure = thisObject->structure(vm);
1452
1453     bool propertyIsPresent = isValidOffset(structure->get(vm, propertyName, attributes));
1454     if (propertyIsPresent) {
1455         if (attributes & DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable)
1456             return false;
1457
1458         PropertyOffset offset;
1459         if (structure->isUncacheableDictionary())
1460             offset = structure->removePropertyWithoutTransition(vm, propertyName);
1461         else
1462             thisObject->setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
1463
1464         if (offset != invalidOffset)
1465             thisObject->putDirectUndefined(offset);
1466     }
1467
1468     return true;
1469 }
1470
1471 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
1472 {
1473     JSObject* thisObject = jsCast<JSObject*>(cell);
1474     
1475     if (i > MAX_ARRAY_INDEX)
1476         return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i));
1477     
1478     switch (thisObject->indexingType()) {
1479     case ALL_BLANK_INDEXING_TYPES:
1480     case ALL_UNDECIDED_INDEXING_TYPES:
1481         return true;
1482         
1483     case ALL_INT32_INDEXING_TYPES:
1484     case ALL_CONTIGUOUS_INDEXING_TYPES: {
1485         Butterfly* butterfly = thisObject->butterfly();
1486         if (i >= butterfly->vectorLength())
1487             return true;
1488         butterfly->contiguous()[i].clear();
1489         return true;
1490     }
1491         
1492     case ALL_DOUBLE_INDEXING_TYPES: {
1493         Butterfly* butterfly = thisObject->butterfly();
1494         if (i >= butterfly->vectorLength())
1495             return true;
1496         butterfly->contiguousDouble()[i] = PNaN;
1497         return true;
1498     }
1499         
1500     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
1501         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
1502         
1503         if (i < storage->vectorLength()) {
1504             WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
1505             if (valueSlot) {
1506                 valueSlot.clear();
1507                 --storage->m_numValuesInVector;
1508             }
1509         } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
1510             SparseArrayValueMap::iterator it = map->find(i);
1511             if (it != map->notFound()) {
1512                 if (it->value.attributes & DontDelete)
1513                     return false;
1514                 map->remove(it);
1515             }
1516         }
1517         
1518         return true;
1519     }
1520         
1521     default:
1522         RELEASE_ASSERT_NOT_REACHED();
1523         return false;
1524     }
1525 }
1526
1527 enum class TypeHintMode { TakesHint, DoesNotTakeHint };
1528
1529 template<TypeHintMode mode = TypeHintMode::DoesNotTakeHint>
1530 static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSObject* object, PropertyName propertyName, PreferredPrimitiveType hint)
1531 {
1532     VM& vm = exec->vm();
1533     auto scope = DECLARE_THROW_SCOPE(vm);
1534
1535     JSValue function = object->get(exec, propertyName);
1536     if (UNLIKELY(scope.exception()))
1537         return scope.exception();
1538     if (function.isUndefined() && mode == TypeHintMode::TakesHint)
1539         return JSValue();
1540     CallData callData;
1541     CallType callType = getCallData(function, callData);
1542     if (callType == CallType::None)
1543         return scope.exception();
1544
1545     MarkedArgumentBuffer callArgs;
1546     if (mode == TypeHintMode::TakesHint) {
1547         JSString* hintString = nullptr;
1548         switch (hint) {
1549         case NoPreference:
1550             hintString = vm.smallStrings.defaultString();
1551             break;
1552         case PreferNumber:
1553             hintString = vm.smallStrings.numberString();
1554             break;
1555         case PreferString:
1556             hintString = vm.smallStrings.stringString();
1557             break;
1558         }
1559         callArgs.append(hintString);
1560     }
1561
1562     JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), callArgs);
1563     ASSERT(!result.isGetterSetter());
1564     if (UNLIKELY(scope.exception()))
1565         return scope.exception();
1566     if (result.isObject())
1567         return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, scope, ASCIILiteral("Symbol.toPrimitive returned an object"));
1568     return result;
1569 }
1570
1571 // ECMA 7.1.1
1572 JSValue JSObject::ordinaryToPrimitive(ExecState* exec, PreferredPrimitiveType hint) const
1573 {
1574     VM& vm = exec->vm();
1575     auto scope = DECLARE_THROW_SCOPE(vm);
1576
1577     // Make sure that whatever default value methods there are on object's prototype chain are
1578     // being watched.
1579     this->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(vm);
1580
1581     JSValue value;
1582     if (hint == PreferString) {
1583         value = callToPrimitiveFunction(exec, this, exec->propertyNames().toString, hint);
1584         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1585         if (value)
1586             return value;
1587         value = callToPrimitiveFunction(exec, this, exec->propertyNames().valueOf, hint);
1588         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1589         if (value)
1590             return value;
1591     } else {
1592         value = callToPrimitiveFunction(exec, this, exec->propertyNames().valueOf, hint);
1593         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1594         if (value)
1595             return value;
1596         value = callToPrimitiveFunction(exec, this, exec->propertyNames().toString, hint);
1597         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1598         if (value)
1599             return value;
1600     }
1601
1602     ASSERT(!scope.exception());
1603
1604     return throwTypeError(exec, scope, ASCIILiteral("No default value"));
1605 }
1606
1607 JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
1608 {
1609     return object->ordinaryToPrimitive(exec, hint);
1610 }
1611
1612 JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
1613 {
1614     JSValue value = callToPrimitiveFunction<TypeHintMode::TakesHint>(exec, this, exec->propertyNames().toPrimitiveSymbol, preferredType);
1615     if (value)
1616         return value;
1617
1618     return this->methodTable(exec->vm())->defaultValue(this, exec, preferredType);
1619 }
1620
1621 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
1622 {
1623     result = toPrimitive(exec, PreferNumber);
1624     number = result.toNumber(exec);
1625     return !result.isString();
1626 }
1627
1628 bool JSObject::getOwnStaticPropertySlot(VM& vm, PropertyName propertyName, PropertySlot& slot)
1629 {
1630     for (auto* info = classInfo(); info; info = info->parentClass) {
1631         if (auto* table = info->staticPropHashTable) {
1632             if (getStaticPropertySlotFromTable(vm, *table, this, propertyName, slot))
1633                 return true;
1634         }
1635     }
1636     return false;
1637 }
1638
1639 const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName) const
1640 {
1641     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
1642         if (const HashTable* propHashTable = info->staticPropHashTable) {
1643             if (const HashTableValue* entry = propHashTable->entry(propertyName))
1644                 return entry;
1645         }
1646     }
1647     return 0;
1648 }
1649
1650 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue)
1651 {
1652     VM& vm = exec->vm();
1653     auto scope = DECLARE_THROW_SCOPE(vm);
1654     
1655
1656     if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) {
1657         CallData callData;
1658         CallType callType = JSC::getCallData(hasInstanceValue, callData);
1659         if (callType == CallType::None) {
1660             throwException(exec, scope, createInvalidInstanceofParameterErrorhasInstanceValueNotFunction(exec, this));
1661             return false;
1662         }
1663
1664         MarkedArgumentBuffer args;
1665         args.append(value);
1666         JSValue result = call(exec, hasInstanceValue, callType, callData, this, args);
1667         return result.toBoolean(exec);
1668     }
1669
1670     TypeInfo info = structure(vm)->typeInfo();
1671     if (info.implementsDefaultHasInstance())
1672         return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
1673     if (info.implementsHasInstance())
1674         return methodTable(vm)->customHasInstance(this, exec, value);
1675     throwException(exec, scope, createInvalidInstanceofParameterErrorNotFunction(exec, this));
1676     return false;
1677 }
1678
1679 bool JSObject::hasInstance(ExecState* exec, JSValue value)
1680 {
1681     JSValue hasInstanceValue = get(exec, exec->propertyNames().hasInstanceSymbol);
1682
1683     return hasInstance(exec, value, hasInstanceValue);
1684 }
1685
1686 bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
1687 {
1688     VM& vm = exec->vm();
1689     auto scope = DECLARE_THROW_SCOPE(vm);
1690
1691     if (!value.isObject())
1692         return false;
1693
1694     if (!proto.isObject()) {
1695         throwTypeError(exec, scope, ASCIILiteral("instanceof called on an object with an invalid prototype property."));
1696         return false;
1697     }
1698
1699     JSObject* object = asObject(value);
1700     while (true) {
1701         JSValue objectValue = object->getPrototype(vm, exec);
1702         if (UNLIKELY(scope.exception()))
1703             return false;
1704         if (!objectValue.isObject())
1705             return false;
1706         object = asObject(objectValue);
1707         if (proto == object)
1708             return true;
1709     }
1710     ASSERT_NOT_REACHED();
1711 }
1712
1713 EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState* exec)
1714 {
1715     JSValue value = exec->uncheckedArgument(0);
1716     JSValue proto = exec->uncheckedArgument(1);
1717
1718     return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
1719 }
1720
1721 void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1722 {
1723     VM& vm = exec->vm();
1724     auto scope = DECLARE_THROW_SCOPE(vm);
1725     object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, mode);
1726     if (UNLIKELY(scope.exception()))
1727         return;
1728
1729     JSValue nextProto = object->getPrototype(vm, exec);
1730     if (UNLIKELY(scope.exception()))
1731         return;
1732     if (nextProto.isNull())
1733         return;
1734
1735     JSObject* prototype = asObject(nextProto);
1736     while(1) {
1737         if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
1738             prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
1739             break;
1740         }
1741         prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
1742         if (UNLIKELY(scope.exception()))
1743             return;
1744         nextProto = prototype->getPrototype(vm, exec);
1745         if (UNLIKELY(scope.exception()))
1746             return;
1747         if (nextProto.isNull())
1748             break;
1749         prototype = asObject(nextProto);
1750     }
1751 }
1752
1753 void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1754 {
1755     if (!mode.includeJSObjectProperties()) {
1756         // We still have to get non-indexed properties from any subclasses of JSObject that have them.
1757         object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
1758         return;
1759     }
1760
1761     if (propertyNames.includeStringProperties()) {
1762         // Add numeric properties first. That appears to be the accepted convention.
1763         // FIXME: Filling PropertyNameArray with an identifier for every integer
1764         // is incredibly inefficient for large arrays. We need a different approach,
1765         // which almost certainly means a different structure for PropertyNameArray.
1766         switch (object->indexingType()) {
1767         case ALL_BLANK_INDEXING_TYPES:
1768         case ALL_UNDECIDED_INDEXING_TYPES:
1769             break;
1770             
1771         case ALL_INT32_INDEXING_TYPES:
1772         case ALL_CONTIGUOUS_INDEXING_TYPES: {
1773             Butterfly* butterfly = object->butterfly();
1774             unsigned usedLength = butterfly->publicLength();
1775             for (unsigned i = 0; i < usedLength; ++i) {
1776                 if (!butterfly->contiguous()[i])
1777                     continue;
1778                 propertyNames.add(i);
1779             }
1780             break;
1781         }
1782             
1783         case ALL_DOUBLE_INDEXING_TYPES: {
1784             Butterfly* butterfly = object->butterfly();
1785             unsigned usedLength = butterfly->publicLength();
1786             for (unsigned i = 0; i < usedLength; ++i) {
1787                 double value = butterfly->contiguousDouble()[i];
1788                 if (value != value)
1789                     continue;
1790                 propertyNames.add(i);
1791             }
1792             break;
1793         }
1794             
1795         case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
1796             ArrayStorage* storage = object->m_butterfly.get()->arrayStorage();
1797             
1798             unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
1799             for (unsigned i = 0; i < usedVectorLength; ++i) {
1800                 if (storage->m_vector[i])
1801                     propertyNames.add(i);
1802             }
1803             
1804             if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
1805                 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
1806                 keys.reserveInitialCapacity(map->size());
1807                 
1808                 SparseArrayValueMap::const_iterator end = map->end();
1809                 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
1810                     if (mode.includeDontEnumProperties() || !(it->value.attributes & DontEnum))
1811                         keys.uncheckedAppend(static_cast<unsigned>(it->key));
1812                 }
1813                 
1814                 std::sort(keys.begin(), keys.end());
1815                 for (unsigned i = 0; i < keys.size(); ++i)
1816                     propertyNames.add(keys[i]);
1817             }
1818             break;
1819         }
1820             
1821         default:
1822             RELEASE_ASSERT_NOT_REACHED();
1823         }
1824     }
1825
1826     object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
1827 }
1828
1829 void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1830 {
1831     if (!object->staticPropertiesReified())
1832         getClassPropertyNames(exec, object->classInfo(), propertyNames, mode);
1833
1834     if (!mode.includeJSObjectProperties())
1835         return;
1836     
1837     VM& vm = exec->vm();
1838     object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
1839 }
1840
1841 double JSObject::toNumber(ExecState* exec) const
1842 {
1843     VM& vm = exec->vm();
1844     auto scope = DECLARE_THROW_SCOPE(vm);
1845     JSValue primitive = toPrimitive(exec, PreferNumber);
1846     if (UNLIKELY(scope.exception())) // should be picked up soon in Nodes.cpp
1847         return 0.0;
1848     return primitive.toNumber(exec);
1849 }
1850
1851 JSString* JSObject::toString(ExecState* exec) const
1852 {
1853     VM& vm = exec->vm();
1854     auto scope = DECLARE_THROW_SCOPE(vm);
1855     JSValue primitive = toPrimitive(exec, PreferString);
1856     if (UNLIKELY(scope.exception()))
1857         return jsEmptyString(exec);
1858     return primitive.toString(exec);
1859 }
1860
1861 JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode)
1862 {
1863     return jsCast<JSObject*>(cell);
1864 }
1865
1866 void JSObject::seal(VM& vm)
1867 {
1868     if (isSealed(vm))
1869         return;
1870     enterDictionaryIndexingMode(vm);
1871     setStructure(vm, Structure::sealTransition(vm, structure(vm)));
1872 }
1873
1874 void JSObject::freeze(VM& vm)
1875 {
1876     if (isFrozen(vm))
1877         return;
1878     enterDictionaryIndexingMode(vm);
1879     setStructure(vm, Structure::freezeTransition(vm, structure(vm)));
1880 }
1881
1882 bool JSObject::preventExtensions(JSObject* object, ExecState* exec)
1883 {
1884     if (!object->isStructureExtensible()) {
1885         // We've already set the internal [[PreventExtensions]] field to false.
1886         // We don't call the methodTable isExtensible here because it's not defined
1887         // that way in the specification. We are just doing an optimization here.
1888         return true;
1889     }
1890
1891     VM& vm = exec->vm();
1892     object->enterDictionaryIndexingMode(vm);
1893     object->setStructure(vm, Structure::preventExtensionsTransition(vm, object->structure(vm)));
1894     return true;
1895 }
1896
1897 bool JSObject::isExtensible(JSObject* obj, ExecState*)
1898 {
1899     return obj->isExtensibleImpl();
1900 }
1901
1902 bool JSObject::isExtensible(ExecState* exec)
1903
1904     VM& vm = exec->vm();
1905     return methodTable(vm)->isExtensible(this, exec);
1906 }
1907
1908 void JSObject::reifyAllStaticProperties(ExecState* exec)
1909 {
1910     ASSERT(!staticPropertiesReified());
1911     VM& vm = exec->vm();
1912
1913     // If this object's ClassInfo has no static properties, then nothing to reify!
1914     // We can safely set the flag to avoid the expensive check again in the future.
1915     if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags())) {
1916         structure(vm)->setStaticPropertiesReified(true);
1917         return;
1918     }
1919
1920     if (!structure(vm)->isDictionary())
1921         setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure(vm)));
1922
1923     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
1924         const HashTable* hashTable = info->staticPropHashTable;
1925         if (!hashTable)
1926             continue;
1927
1928         for (auto& value : *hashTable) {
1929             unsigned attributes;
1930             auto key = Identifier::fromString(&vm, value.m_key);
1931             PropertyOffset offset = getDirectOffset(vm, key, attributes);
1932             if (!isValidOffset(offset))
1933                 reifyStaticProperty(vm, key, value, *this);
1934         }
1935     }
1936
1937     structure(vm)->setStaticPropertiesReified(true);
1938 }
1939
1940 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue getterSetter, unsigned attributes, PropertyOffset offset)
1941 {
1942     if (structure()->isUncacheableDictionary()) {
1943         slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
1944         return;
1945     }
1946
1947     // This access is cacheable because Structure requires an attributeChangedTransition
1948     // if this property stops being an accessor.
1949     slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
1950 }
1951
1952 bool JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
1953 {
1954     VM& vm = exec->vm();
1955     auto map = m_butterfly.get()->arrayStorage()->m_sparseMap.get();
1956
1957     if (descriptor.isDataDescriptor()) {
1958         if (descriptor.value())
1959             entryInMap->set(vm, map, descriptor.value());
1960         else if (oldDescriptor.isAccessorDescriptor())
1961             entryInMap->set(vm, map, jsUndefined());
1962         entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
1963         return true;
1964     }
1965
1966     if (descriptor.isAccessorDescriptor()) {
1967         JSObject* getter = 0;
1968         if (descriptor.getterPresent())
1969             getter = descriptor.getterObject();
1970         else if (oldDescriptor.isAccessorDescriptor())
1971             getter = oldDescriptor.getterObject();
1972         JSObject* setter = 0;
1973         if (descriptor.setterPresent())
1974             setter = descriptor.setterObject();
1975         else if (oldDescriptor.isAccessorDescriptor())
1976             setter = oldDescriptor.setterObject();
1977
1978         GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
1979         if (getter)
1980             accessor->setGetter(vm, exec->lexicalGlobalObject(), getter);
1981         if (setter)
1982             accessor->setSetter(vm, exec->lexicalGlobalObject(), setter);
1983
1984         entryInMap->set(vm, map, accessor);
1985         entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
1986         return true;
1987     }
1988
1989     ASSERT(descriptor.isGenericDescriptor());
1990     entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
1991     return true;
1992 }
1993
1994 // Defined in ES5.1 8.12.9
1995 bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException)
1996 {
1997     ASSERT(index <= MAX_ARRAY_INDEX);
1998
1999     if (!inSparseIndexingMode()) {
2000         // Fast case: we're putting a regular property to a regular array
2001         // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
2002         // however if the property currently exists missing attributes will override from their current 'true'
2003         // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
2004         if (!descriptor.attributes() && descriptor.value()) {
2005             ASSERT(!descriptor.isAccessorDescriptor());
2006             return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
2007         }
2008         
2009         ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->vm());
2010     }
2011
2012     if (descriptor.attributes() & (ReadOnly | Accessor))
2013         notifyPresenceOfIndexedAccessors(exec->vm());
2014
2015     SparseArrayValueMap* map = m_butterfly.get()->arrayStorage()->m_sparseMap.get();
2016     RELEASE_ASSERT(map);
2017
2018     // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
2019     SparseArrayValueMap::AddResult result = map->add(this, index);
2020     SparseArrayEntry* entryInMap = &result.iterator->value;
2021
2022     // 2. Let extensible be the value of the [[Extensible]] internal property of O.
2023     // 3. If current is undefined and extensible is false, then Reject.
2024     // 4. If current is undefined and extensible is true, then
2025     if (result.isNewEntry) {
2026         if (!isStructureExtensible()) {
2027             map->remove(result.iterator);
2028             return reject(exec, throwException, "Attempting to define property on object that is not extensible.");
2029         }
2030
2031         // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
2032         // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
2033         // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
2034         // created property is set to its default value.
2035         // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
2036         // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
2037         // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
2038         // is set to its default value.
2039         // 4.c. Return true.
2040
2041         PropertyDescriptor defaults;
2042         entryInMap->setWithoutWriteBarrier(jsUndefined());
2043         entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
2044         entryInMap->get(defaults);
2045
2046         putIndexedDescriptor(exec, entryInMap, descriptor, defaults);
2047         Butterfly* butterfly = m_butterfly.get();
2048         if (index >= butterfly->arrayStorage()->length())
2049             butterfly->arrayStorage()->setLength(index + 1);
2050         return true;
2051     }
2052
2053     // 5. Return true, if every field in Desc is absent.
2054     // 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).
2055     PropertyDescriptor current;
2056     entryInMap->get(current);
2057     if (descriptor.isEmpty() || descriptor.equalTo(exec, current))
2058         return true;
2059
2060     // 7. If the [[Configurable]] field of current is false then
2061     if (!current.configurable()) {
2062         // 7.a. Reject, if the [[Configurable]] field of Desc is true.
2063         if (descriptor.configurablePresent() && descriptor.configurable())
2064             return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
2065         // 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.
2066         if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
2067             return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
2068     }
2069
2070     // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
2071     if (!descriptor.isGenericDescriptor()) {
2072         // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
2073         if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
2074             // 9.a. Reject, if the [[Configurable]] field of current is false.
2075             if (!current.configurable())
2076                 return reject(exec, throwException, UnconfigurablePropertyChangeAccessMechanismError);
2077             // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
2078             // data property to an accessor property. Preserve the existing values of the converted property's
2079             // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
2080             // their default values.
2081             // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
2082             // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
2083             // attributes and set the rest of the property's attributes to their default values.
2084         } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
2085             // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
2086             // 10.a. If the [[Configurable]] field of current is false, then
2087             if (!current.configurable() && !current.writable()) {
2088                 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
2089                 if (descriptor.writable())
2090                     return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
2091                 // 10.a.ii. If the [[Writable]] field of current is false, then
2092                 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
2093                 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
2094                     return reject(exec, throwException, "Attempting to change value of a readonly property.");
2095             }
2096             // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
2097         } else {
2098             ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
2099             // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
2100             if (!current.configurable()) {
2101                 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
2102                 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
2103                     return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property.");
2104                 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
2105                 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
2106                     return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property.");
2107             }
2108         }
2109     }
2110
2111     // 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.
2112     putIndexedDescriptor(exec, entryInMap, descriptor, current);
2113     // 13. Return true.
2114     return true;
2115 }
2116
2117 SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm)
2118 {
2119     SparseArrayValueMap* result = SparseArrayValueMap::create(vm);
2120     arrayStorage()->m_sparseMap.set(vm, this, result);
2121     return result;
2122 }
2123
2124 void JSObject::deallocateSparseIndexMap()
2125 {
2126     if (ArrayStorage* arrayStorage = arrayStorageOrNull())
2127         arrayStorage->m_sparseMap.clear();
2128 }
2129
2130 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2131 {
2132     for (JSObject* current = this; ;) {
2133         // This has the same behavior with respect to prototypes as JSObject::put(). It only
2134         // allows a prototype to intercept a put if (a) the prototype declares the property
2135         // we're after rather than intercepting it via an override of JSObject::put(), and
2136         // (b) that property is declared as ReadOnly or Accessor.
2137         
2138         ArrayStorage* storage = current->arrayStorageOrNull();
2139         if (storage && storage->m_sparseMap) {
2140             SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
2141             if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (Accessor | ReadOnly))) {
2142                 putResult = iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
2143                 return true;
2144             }
2145         }
2146
2147         if (current->type() == ProxyObjectType) {
2148             ProxyObject* proxy = jsCast<ProxyObject*>(current);
2149             putResult = proxy->putByIndexCommon(exec, thisValue, i, value, shouldThrow);
2150             return true;
2151         }
2152         
2153         JSValue prototypeValue = current->getPrototypeDirect();
2154         if (prototypeValue.isNull())
2155             return false;
2156         
2157         current = asObject(prototypeValue);
2158     }
2159 }
2160
2161 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2162 {
2163     JSValue prototypeValue = getPrototypeDirect();
2164     if (prototypeValue.isNull())
2165         return false;
2166     
2167     return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow, putResult);
2168 }
2169
2170 template<IndexingType indexingShape>
2171 bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value)
2172 {
2173     VM& vm = exec->vm();
2174     auto scope = DECLARE_THROW_SCOPE(vm);
2175
2176     ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
2177     ASSERT(!indexingShouldBeSparse());
2178
2179     Butterfly* butterfly = m_butterfly.get();
2180     
2181     // For us to get here, the index is either greater than the public length, or greater than
2182     // or equal to the vector length.
2183     ASSERT(i >= butterfly->vectorLength());
2184     
2185     if (i > MAX_STORAGE_VECTOR_INDEX
2186         || (i >= MIN_SPARSE_ARRAY_INDEX && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly)))
2187         || indexIsSufficientlyBeyondLengthForSparseMap(i, butterfly->vectorLength())) {
2188         ASSERT(i <= MAX_ARRAY_INDEX);
2189         ensureArrayStorageSlow(vm);
2190         SparseArrayValueMap* map = allocateSparseIndexMap(vm);
2191         bool result = map->putEntry(exec, this, i, value, false);
2192         ASSERT(i >= arrayStorage()->length());
2193         arrayStorage()->setLength(i + 1);
2194         return result;
2195     }
2196
2197     if (!ensureLength(vm, i + 1)) {
2198         throwOutOfMemoryError(exec, scope);
2199         return false;
2200     }
2201     butterfly = m_butterfly.get();
2202
2203     RELEASE_ASSERT(i < butterfly->vectorLength());
2204     switch (indexingShape) {
2205     case Int32Shape:
2206         ASSERT(value.isInt32());
2207         butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value);
2208         return true;
2209         
2210     case DoubleShape: {
2211         ASSERT(value.isNumber());
2212         double valueAsDouble = value.asNumber();
2213         ASSERT(valueAsDouble == valueAsDouble);
2214         butterfly->contiguousDouble()[i] = valueAsDouble;
2215         return true;
2216     }
2217         
2218     case ContiguousShape:
2219         butterfly->contiguous()[i].set(vm, this, value);
2220         return true;
2221         
2222     default:
2223         CRASH();
2224         return false;
2225     }
2226 }
2227
2228 // Explicit instantiations needed by JSArray.cpp.
2229 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(ExecState*, unsigned, JSValue);
2230 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(ExecState*, unsigned, JSValue);
2231 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(ExecState*, unsigned, JSValue);
2232
2233 bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
2234 {
2235     VM& vm = exec->vm();
2236     auto scope = DECLARE_THROW_SCOPE(vm);
2237
2238     // i should be a valid array index that is outside of the current vector.
2239     ASSERT(i <= MAX_ARRAY_INDEX);
2240     ASSERT(i >= storage->vectorLength());
2241     
2242     SparseArrayValueMap* map = storage->m_sparseMap.get();
2243     
2244     // First, handle cases where we don't currently have a sparse map.
2245     if (LIKELY(!map)) {
2246         // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
2247         ASSERT(isStructureExtensible());
2248     
2249         // Update m_length if necessary.
2250         if (i >= storage->length())
2251             storage->setLength(i + 1);
2252
2253         // Check that it is sensible to still be using a vector, and then try to grow the vector.
2254         if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()) 
2255             && isDenseEnoughForVector(i, storage->m_numValuesInVector)
2256             && increaseVectorLength(vm, i + 1))) {
2257             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2258             storage = arrayStorage();
2259             storage->m_vector[i].set(vm, this, value);
2260             ++storage->m_numValuesInVector;
2261             return true;
2262         }
2263         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2264         map = allocateSparseIndexMap(vm);
2265         return map->putEntry(exec, this, i, value, shouldThrow);
2266     }
2267
2268     // Update m_length if necessary.
2269     unsigned length = storage->length();
2270     if (i >= length) {
2271         // Prohibit growing the array if length is not writable.
2272         if (map->lengthIsReadOnly() || !isStructureExtensible()) {
2273             if (shouldThrow)
2274                 throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError);
2275             return false;
2276         }
2277         length = i + 1;
2278         storage->setLength(length);
2279     }
2280
2281     // We are currently using a map - check whether we still want to be doing so.
2282     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2283     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2284     if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
2285         return map->putEntry(exec, this, i, value, shouldThrow);
2286
2287     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2288     storage = arrayStorage();
2289     storage->m_numValuesInVector = numValuesInArray;
2290
2291     // Copy all values from the map into the vector, and delete the map.
2292     WriteBarrier<Unknown>* vector = storage->m_vector;
2293     SparseArrayValueMap::const_iterator end = map->end();
2294     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2295         vector[it->key].set(vm, this, it->value.getNonSparseMode());
2296     deallocateSparseIndexMap();
2297
2298     // Store the new property into the vector.
2299     WriteBarrier<Unknown>& valueSlot = vector[i];
2300     if (!valueSlot)
2301         ++storage->m_numValuesInVector;
2302     valueSlot.set(vm, this, value);
2303     return true;
2304 }
2305
2306 bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
2307 {
2308     VM& vm = exec->vm();
2309
2310     // i should be a valid array index that is outside of the current vector.
2311     ASSERT(i <= MAX_ARRAY_INDEX);
2312     
2313     switch (indexingType()) {
2314     case ALL_BLANK_INDEXING_TYPES: {
2315         if (indexingShouldBeSparse()) {
2316             return putByIndexBeyondVectorLengthWithArrayStorage(
2317                 exec, i, value, shouldThrow,
2318                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2319         }
2320         if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
2321             return putByIndexBeyondVectorLengthWithArrayStorage(
2322                 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
2323         }
2324         if (structure(vm)->needsSlowPutIndexing()) {
2325             // Convert the indexing type to the SlowPutArrayStorage and retry.
2326             createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, 0, i + 1));
2327             return putByIndex(this, exec, i, value, shouldThrow);
2328         }
2329         
2330         createInitialForValueAndSet(vm, i, value);
2331         return true;
2332     }
2333         
2334     case ALL_UNDECIDED_INDEXING_TYPES: {
2335         CRASH();
2336         break;
2337     }
2338         
2339     case ALL_INT32_INDEXING_TYPES:
2340         return putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2341         
2342     case ALL_DOUBLE_INDEXING_TYPES:
2343         return putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2344         
2345     case ALL_CONTIGUOUS_INDEXING_TYPES:
2346         return putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2347         
2348     case NonArrayWithSlowPutArrayStorage:
2349     case ArrayWithSlowPutArrayStorage: {
2350         // No own property present in the vector, but there might be in the sparse map!
2351         SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
2352         bool putResult = false;
2353         if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow, putResult))
2354             return putResult;
2355         FALLTHROUGH;
2356     }
2357
2358     case NonArrayWithArrayStorage:
2359     case ArrayWithArrayStorage:
2360         return putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
2361         
2362     default:
2363         RELEASE_ASSERT_NOT_REACHED();
2364     }
2365     return false;
2366 }
2367
2368 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
2369 {
2370     VM& vm = exec->vm();
2371     
2372     // i should be a valid array index that is outside of the current vector.
2373     ASSERT(hasAnyArrayStorage(indexingType()));
2374     ASSERT(arrayStorage() == storage);
2375     ASSERT(i >= storage->vectorLength() || attributes);
2376     ASSERT(i <= MAX_ARRAY_INDEX);
2377
2378     SparseArrayValueMap* map = storage->m_sparseMap.get();
2379
2380     // First, handle cases where we don't currently have a sparse map.
2381     if (LIKELY(!map)) {
2382         // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2383         ASSERT(isStructureExtensible());
2384     
2385         // Update m_length if necessary.
2386         if (i >= storage->length())
2387             storage->setLength(i + 1);
2388
2389         // Check that it is sensible to still be using a vector, and then try to grow the vector.
2390         if (LIKELY(
2391                 !attributes
2392                 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
2393                 && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
2394                 && increaseVectorLength(vm, i + 1)) {
2395             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2396             storage = arrayStorage();
2397             storage->m_vector[i].set(vm, this, value);
2398             ++storage->m_numValuesInVector;
2399             return true;
2400         }
2401         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2402         map = allocateSparseIndexMap(exec->vm());
2403         return map->putDirect(exec, this, i, value, attributes, mode);
2404     }
2405
2406     // Update m_length if necessary.
2407     unsigned length = storage->length();
2408     if (i >= length) {
2409         if (mode != PutDirectIndexLikePutDirect) {
2410             // Prohibit growing the array if length is not writable.
2411             if (map->lengthIsReadOnly())
2412                 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);
2413             if (!isStructureExtensible())
2414                 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
2415         }
2416         length = i + 1;
2417         storage->setLength(length);
2418     }
2419
2420     // We are currently using a map - check whether we still want to be doing so.
2421     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2422     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2423     if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length))
2424         return map->putDirect(exec, this, i, value, attributes, mode);
2425
2426     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2427     storage = arrayStorage();
2428     storage->m_numValuesInVector = numValuesInArray;
2429
2430     // Copy all values from the map into the vector, and delete the map.
2431     WriteBarrier<Unknown>* vector = storage->m_vector;
2432     SparseArrayValueMap::const_iterator end = map->end();
2433     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2434         vector[it->key].set(vm, this, it->value.getNonSparseMode());
2435     deallocateSparseIndexMap();
2436
2437     // Store the new property into the vector.
2438     WriteBarrier<Unknown>& valueSlot = vector[i];
2439     if (!valueSlot)
2440         ++storage->m_numValuesInVector;
2441     valueSlot.set(vm, this, value);
2442     return true;
2443 }
2444
2445 bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
2446 {
2447     VM& vm = exec->vm();
2448
2449     // i should be a valid array index that is outside of the current vector.
2450     ASSERT(i <= MAX_ARRAY_INDEX);
2451     
2452     if (attributes & (ReadOnly | Accessor))
2453         notifyPresenceOfIndexedAccessors(vm);
2454     
2455     switch (indexingType()) {
2456     case ALL_BLANK_INDEXING_TYPES: {
2457         if (indexingShouldBeSparse() || attributes) {
2458             return putDirectIndexBeyondVectorLengthWithArrayStorage(
2459                 exec, i, value, attributes, mode,
2460                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2461         }
2462         if (i >= MIN_SPARSE_ARRAY_INDEX) {
2463             return putDirectIndexBeyondVectorLengthWithArrayStorage(
2464                 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
2465         }
2466         if (structure(vm)->needsSlowPutIndexing()) {
2467             ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, 0, i + 1));
2468             storage->m_vector[i].set(vm, this, value);
2469             storage->m_numValuesInVector++;
2470             return true;
2471         }
2472         
2473         createInitialForValueAndSet(vm, i, value);
2474         return true;
2475     }
2476         
2477     case ALL_UNDECIDED_INDEXING_TYPES: {
2478         convertUndecidedForValue(exec->vm(), value);
2479         // Reloop.
2480         return putDirectIndex(exec, i, value, attributes, mode);
2481     }
2482         
2483     case ALL_INT32_INDEXING_TYPES: {
2484         if (attributes) {
2485             if (i < m_butterfly.get()->vectorLength())
2486                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2487             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm));
2488         }
2489         if (!value.isInt32()) {
2490             convertInt32ForValue(vm, value);
2491             return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2492         }
2493         putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2494         return true;
2495     }
2496         
2497     case ALL_DOUBLE_INDEXING_TYPES: {
2498         if (attributes) {
2499             if (i < m_butterfly.get()->vectorLength())
2500                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2501             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm));
2502         }
2503         if (!value.isNumber()) {
2504             convertDoubleToContiguous(vm);
2505             return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2506         }
2507         double valueAsDouble = value.asNumber();
2508         if (valueAsDouble != valueAsDouble) {
2509             convertDoubleToContiguous(vm);
2510             return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2511         }
2512         putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2513         return true;
2514     }
2515         
2516     case ALL_CONTIGUOUS_INDEXING_TYPES: {
2517         if (attributes) {
2518             if (i < m_butterfly.get()->vectorLength())
2519                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2520             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm));
2521         }
2522         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2523         return true;
2524     }
2525
2526     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
2527         if (attributes) {
2528             if (i < m_butterfly.get()->vectorLength())
2529                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2530         }
2531         return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
2532         
2533     default:
2534         RELEASE_ASSERT_NOT_REACHED();
2535         return false;
2536     }
2537 }
2538
2539 bool JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2540 {
2541     GetterSetter* accessor = GetterSetter::create(vm, globalObject);
2542     JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
2543     accessor->setGetter(vm, globalObject, function);
2544     return putDirectNonIndexAccessor(vm, name, accessor, attributes);
2545 }
2546
2547 bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2548 {
2549     StringImpl* name = propertyName.publicName();
2550     if (!name)
2551         name = vm.propertyNames->anonymous.impl();
2552     ASSERT(name);
2553
2554     JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2555     return putDirect(vm, propertyName, function, attributes);
2556 }
2557
2558 JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2559 {
2560     StringImpl* name = propertyName.publicName();
2561     if (!name)
2562         name = vm.propertyNames->anonymous.impl();
2563     ASSERT(name);
2564     JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2565     putDirect(vm, propertyName, function, attributes);
2566     return function;
2567 }
2568
2569 JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2570 {
2571     JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2572     putDirectWithoutTransition(vm, propertyName, function, attributes);
2573     return function;
2574 }
2575
2576 void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2577 {
2578     StringImpl* name = propertyName.publicName();
2579     if (!name)
2580         name = vm.propertyNames->anonymous.impl();
2581     ASSERT(name);
2582     JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2583     putDirectWithoutTransition(vm, propertyName, function, attributes);
2584 }
2585
2586 // NOTE: This method is for ArrayStorage vectors.
2587 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
2588 {
2589     ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
2590
2591     unsigned increasedLength;
2592     unsigned maxInitLength = std::min(currentLength, 100000U);
2593
2594     if (desiredLength < maxInitLength)
2595         increasedLength = maxInitLength;
2596     else if (!currentVectorLength)
2597         increasedLength = std::max(desiredLength, lastArraySize);
2598     else {
2599         increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
2600     }
2601
2602     ASSERT(increasedLength >= desiredLength);
2603
2604     lastArraySize = std::min(increasedLength, FIRST_ARRAY_STORAGE_VECTOR_GROW);
2605
2606     return ArrayStorage::optimalVectorLength(
2607         indexBias, structure()->outOfLineCapacity(),
2608         std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH));
2609 }
2610
2611 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
2612 {
2613     unsigned indexBias = 0;
2614     unsigned vectorLength = 0;
2615     unsigned length = 0;
2616     
2617     if (hasIndexedProperties(indexingType())) {
2618         if (ArrayStorage* storage = arrayStorageOrNull())
2619             indexBias = storage->m_indexBias;
2620         vectorLength = m_butterfly.get()->vectorLength();
2621         length = m_butterfly.get()->publicLength();
2622     }
2623
2624     return getNewVectorLength(indexBias, vectorLength, length, desiredLength);
2625 }
2626
2627 template<IndexingType indexingShape>
2628 unsigned JSObject::countElements(Butterfly* butterfly)
2629 {
2630     unsigned numValues = 0;
2631     for (unsigned i = butterfly->publicLength(); i--;) {
2632         switch (indexingShape) {
2633         case Int32Shape:
2634         case ContiguousShape:
2635             if (butterfly->contiguous()[i])
2636                 numValues++;
2637             break;
2638             
2639         case DoubleShape: {
2640             double value = butterfly->contiguousDouble()[i];
2641             if (value == value)
2642                 numValues++;
2643             break;
2644         }
2645             
2646         default:
2647             CRASH();
2648         }
2649     }
2650     return numValues;
2651 }
2652
2653 unsigned JSObject::countElements()
2654 {
2655     switch (indexingType()) {
2656     case ALL_BLANK_INDEXING_TYPES:
2657     case ALL_UNDECIDED_INDEXING_TYPES:
2658         return 0;
2659         
2660     case ALL_INT32_INDEXING_TYPES:
2661         return countElements<Int32Shape>(butterfly());
2662         
2663     case ALL_DOUBLE_INDEXING_TYPES:
2664         return countElements<DoubleShape>(butterfly());
2665         
2666     case ALL_CONTIGUOUS_INDEXING_TYPES:
2667         return countElements<ContiguousShape>(butterfly());
2668         
2669     default:
2670         CRASH();
2671         return 0;
2672     }
2673 }
2674
2675 bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
2676 {
2677     ArrayStorage* storage = arrayStorage();
2678     
2679     unsigned vectorLength = storage->vectorLength();
2680     unsigned availableVectorLength = storage->availableVectorLength(structure(vm), vectorLength); 
2681     if (availableVectorLength >= newLength) {
2682         // The cell was already big enough for the desired length!
2683         for (unsigned i = vectorLength; i < availableVectorLength; ++i)
2684             storage->m_vector[i].clear();
2685         storage->setVectorLength(availableVectorLength);
2686         return true;
2687     }
2688     
2689     // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2690     // to the vector. Callers have to account for that, because they can do it more efficiently.
2691     if (newLength > MAX_STORAGE_VECTOR_LENGTH)
2692         return false;
2693
2694     if (newLength >= MIN_SPARSE_ARRAY_INDEX
2695         && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector))
2696         return false;
2697
2698     unsigned indexBias = storage->m_indexBias;
2699     ASSERT(newLength > vectorLength);
2700     unsigned newVectorLength = getNewVectorLength(newLength);
2701
2702     // Fast case - there is no precapacity. In these cases a realloc makes sense.
2703     Structure* structure = this->structure(vm);
2704     if (LIKELY(!indexBias)) {
2705         DeferGC deferGC(vm.heap);
2706         Butterfly* newButterfly = storage->butterfly()->growArrayRight(
2707             vm, this, structure, structure->outOfLineCapacity(), true,
2708             ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
2709         if (!newButterfly)
2710             return false;
2711         for (unsigned i = vectorLength; i < newVectorLength; ++i)
2712             newButterfly->arrayStorage()->m_vector[i].clear();
2713         newButterfly->arrayStorage()->setVectorLength(newVectorLength);
2714         setButterflyWithoutChangingStructure(vm, newButterfly);
2715         return true;
2716     }
2717     
2718     // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
2719     DeferGC deferGC(vm.heap);
2720     unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
2721     Butterfly* newButterfly = storage->butterfly()->resizeArray(
2722         vm, this,
2723         structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
2724         newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
2725     if (!newButterfly)
2726         return false;
2727     for (unsigned i = vectorLength; i < newVectorLength; ++i)
2728         newButterfly->arrayStorage()->m_vector[i].clear();
2729     newButterfly->arrayStorage()->setVectorLength(newVectorLength);
2730     newButterfly->arrayStorage()->m_indexBias = newIndexBias;
2731     setButterflyWithoutChangingStructure(vm, newButterfly);
2732     return true;
2733 }
2734
2735 bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
2736 {
2737     Butterfly* butterfly = m_butterfly.get();
2738     
2739     ASSERT(length < MAX_ARRAY_INDEX);
2740     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2741     ASSERT(length > butterfly->vectorLength());
2742     
2743     unsigned oldVectorLength = butterfly->vectorLength();
2744     unsigned newVectorLength;
2745     
2746     Structure* structure = this->structure(vm);
2747     unsigned propertyCapacity = structure->outOfLineCapacity();
2748     
2749     unsigned availableOldLength =
2750         Butterfly::availableContiguousVectorLength(propertyCapacity, oldVectorLength);
2751     if (availableOldLength >= length) {
2752         // This is the case where someone else selected a vector length that caused internal
2753         // fragmentation. If we did our jobs right, this would never happen. But I bet we will mess
2754         // this up, so this defense should stay.
2755         newVectorLength = availableOldLength;
2756     } else {
2757         newVectorLength = Butterfly::optimalContiguousVectorLength(
2758             propertyCapacity, std::min(length << 1, MAX_STORAGE_VECTOR_LENGTH));
2759         butterfly = butterfly->growArrayRight(
2760             vm, this, structure, propertyCapacity, true,
2761             oldVectorLength * sizeof(EncodedJSValue),
2762             newVectorLength * sizeof(EncodedJSValue));
2763         if (!butterfly)
2764             return false;
2765         m_butterfly.set(vm, this, butterfly);
2766     }
2767
2768     butterfly->setVectorLength(newVectorLength);
2769
2770     if (hasDouble(indexingType())) {
2771         for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
2772             butterfly->contiguousDouble()[i] = PNaN;
2773     } else {
2774         for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
2775             butterfly->contiguous()[i].clear();
2776     }
2777
2778     return true;
2779 }
2780
2781 void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length)
2782 {
2783     ASSERT(length < MAX_ARRAY_INDEX);
2784     ASSERT(length < MAX_STORAGE_VECTOR_LENGTH);
2785     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2786     ASSERT(m_butterfly.get()->vectorLength() > length);
2787     ASSERT(!m_butterfly.get()->indexingHeader()->preCapacity(structure()));
2788
2789     DeferGC deferGC(vm.heap);
2790     Butterfly* newButterfly = m_butterfly.get()->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(length));
2791     m_butterfly.set(vm, this, newButterfly);
2792     newButterfly->setVectorLength(length);
2793     newButterfly->setPublicLength(length);
2794 }
2795
2796 Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize)
2797 {
2798     ASSERT(newSize > oldSize);
2799
2800     // It's important that this function not rely on structure(), for the property
2801     // capacity, since we might have already mutated the structure in-place.
2802
2803     return Butterfly::createOrGrowPropertyStorage(m_butterfly.get(), vm, this, structure(vm), oldSize, newSize);
2804 }
2805
2806 static JSCustomGetterSetterFunction* getCustomGetterSetterFunctionForGetterSetter(ExecState* exec, PropertyName propertyName, CustomGetterSetter* getterSetter, JSCustomGetterSetterFunction::Type type)
2807 {
2808     auto key = std::make_pair(getterSetter, (int)type);
2809     JSCustomGetterSetterFunction* customGetterSetterFunction = exec->vm().customGetterSetterFunctionMap.get(key);
2810     if (!customGetterSetterFunction) {
2811         customGetterSetterFunction = JSCustomGetterSetterFunction::create(exec->vm(), exec->lexicalGlobalObject(), getterSetter, type, propertyName.publicName());
2812         exec->vm().customGetterSetterFunctionMap.set(key, customGetterSetterFunction);
2813     }
2814     return customGetterSetterFunction;
2815 }
2816
2817 bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
2818 {
2819     JSC::PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
2820     if (!methodTable(exec->vm())->getOwnPropertySlot(this, exec, propertyName, slot))
2821         return false;
2822
2823     // DebuggerScope::getOwnPropertySlot() (and possibly others) may return attributes from the prototype chain
2824     // but getOwnPropertyDescriptor() should only work for 'own' properties so we exit early if we detect that
2825     // the property is not an own property.
2826     if (slot.slotBase() != this && slot.slotBase()) {
2827         JSProxy* jsProxy = jsDynamicCast<JSProxy*>(this);
2828         if (!jsProxy || jsProxy->target() != slot.slotBase()) {
2829             // Try ProxyObject.
2830             ProxyObject* proxyObject = jsDynamicCast<ProxyObject*>(this);
2831             if (!proxyObject || proxyObject->target() != slot.slotBase())
2832                 return false;
2833         }
2834     }
2835
2836     if (slot.isAccessor())
2837         descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes());
2838     else if (slot.attributes() & CustomAccessor) {
2839         descriptor.setCustomDescriptor(slot.attributes());
2840
2841         JSObject* thisObject = this;
2842         if (auto* proxy = jsDynamicCast<JSProxy*>(this))
2843             thisObject = proxy->target();
2844
2845         JSValue maybeGetterSetter = thisObject->getDirect(exec->vm(), propertyName);
2846         if (!maybeGetterSetter) {
2847             thisObject->reifyAllStaticProperties(exec);
2848             maybeGetterSetter = thisObject->getDirect(exec->vm(), propertyName);
2849         }
2850
2851         ASSERT(maybeGetterSetter);
2852         auto* getterSetter = jsCast<CustomGetterSetter*>(maybeGetterSetter);
2853         if (getterSetter->getter())
2854             descriptor.setGetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Getter));
2855         if (getterSetter->setter())
2856             descriptor.setSetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Setter));
2857     } else
2858         descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes());
2859     return true;
2860 }
2861
2862 static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
2863 {
2864     VM& vm = exec->vm();
2865     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
2866         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
2867             GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
2868             if (oldDescriptor.getterPresent())
2869                 accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
2870             if (oldDescriptor.setterPresent())
2871                 accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
2872             target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
2873             return true;
2874         }
2875         JSValue newValue = jsUndefined();
2876         if (descriptor.value())
2877             newValue = descriptor.value();
2878         else if (oldDescriptor.value())
2879             newValue = oldDescriptor.value();
2880         target->putDirect(vm, propertyName, newValue, attributes & ~Accessor);
2881         if (attributes & ReadOnly)
2882             target->structure(vm)->setContainsReadOnlyProperties();
2883         return true;
2884     }
2885     attributes &= ~ReadOnly;
2886     GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
2887
2888     if (descriptor.getterPresent())
2889         accessor->setGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
2890     else if (oldDescriptor.getterPresent())
2891         accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
2892     if (descriptor.setterPresent())
2893         accessor->setSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
2894     else if (oldDescriptor.setterPresent())
2895         accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
2896
2897     target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
2898     return true;
2899 }
2900
2901 bool JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
2902 {
2903     if (Optional<uint32_t> index = parseIndex(propertyName))
2904         return putDirectIndex(exec, index.value(), value);
2905     return putDirect(exec->vm(), propertyName, value);
2906 }
2907
2908 // 9.1.6.3 of the spec
2909 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-validateandapplypropertydescriptor
2910 bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, PropertyName propertyName, bool isExtensible,
2911     const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException)
2912 {
2913     VM& vm = exec->vm();
2914     auto scope = DECLARE_THROW_SCOPE(vm);
2915
2916     // If we have a new property we can just put it on normally
2917     // Step 2.
2918     if (!isCurrentDefined) {
2919         // unless extensions are prevented!
2920         // Step 2.a
2921         if (!isExtensible) {
2922             if (throwException)
2923                 throwTypeError(exec, scope, ASCIILiteral("Attempting to define property on object that is not extensible."));
2924             return false;
2925         }
2926         if (!object)
2927             return true;
2928         // Step 2.c/d
2929         PropertyDescriptor oldDescriptor;
2930         oldDescriptor.setValue(jsUndefined());
2931         // FIXME: spec says to always return true here.
2932         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
2933     }
2934     // Step 3.
2935     if (descriptor.isEmpty())
2936         return true;
2937     // Step 4.
2938     if (current.equalTo(exec, descriptor))
2939         return true;
2940
2941     // Step 5.
2942     // Filter out invalid changes
2943     if (!current.configurable()) {
2944         if (descriptor.configurable()) {
2945             if (throwException)
2946                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property."));
2947             return false;
2948         }
2949         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
2950             if (throwException)
2951                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."));
2952             return false;
2953         }
2954     }
2955     
2956     // Step 6.
2957     // A generic descriptor is simply changing the attributes of an existing property
2958     if (descriptor.isGenericDescriptor()) {
2959         if (!current.attributesEqual(descriptor) && object) {
2960             object->methodTable(vm)->deleteProperty(object, exec, propertyName);
2961             return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
2962         }
2963         return true;
2964     }
2965     
2966     // Step 7.
2967     // Changing between a normal property or an accessor property
2968     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
2969         if (!current.configurable()) {
2970             if (throwException)
2971                 throwTypeError(exec, scope, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
2972             return false;
2973         }
2974
2975         if (!object)
2976             return true;
2977
2978         object->methodTable(vm)->deleteProperty(object, exec, propertyName);
2979         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
2980     }
2981
2982     // Step 8.
2983     // Changing the value and attributes of an existing property
2984     if (descriptor.isDataDescriptor()) {
2985         if (!current.configurable()) {
2986             if (!current.writable() && descriptor.writable()) {
2987                 if (throwException)
2988                     throwTypeError(exec, scope, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."));
2989                 return false;
2990             }
2991             if (!current.writable()) {
2992                 if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value())) {
2993                     if (throwException)
2994                         throwTypeError(exec, scope, ASCIILiteral("Attempting to change value of a readonly property."));
2995                     return false;
2996                 }
2997             }
2998         }
2999         if (current.attributesEqual(descriptor) && !descriptor.value())
3000             return true;
3001         if (!object)
3002             return true;
3003         object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3004         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3005     }
3006
3007     // Step 9.
3008     // Changing the accessor functions of an existing accessor property
3009     ASSERT(descriptor.isAccessorDescriptor());
3010     if (!current.configurable()) {
3011         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
3012             if (throwException)
3013                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change the setter of an unconfigurable property."));
3014             return false;
3015         }
3016         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
3017             if (throwException)
3018                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change the getter of an unconfigurable property."));
3019             return false;
3020         }
3021         if (current.attributes() & CustomAccessor) {
3022             if (throwException)
3023                 throwTypeError(exec, scope, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
3024             return false;
3025         }
3026     }
3027
3028     // Step 10/11.
3029     if (!object)
3030         return true;
3031     JSValue accessor = object->getDirect(vm, propertyName);
3032     if (!accessor)
3033         return false;
3034     GetterSetter* getterSetter;
3035     bool getterSetterChanged = false;
3036     if (accessor.isCustomGetterSetter()) {
3037         getterSetter = GetterSetter::create(vm, exec->lexicalGlobalObject());
3038         auto* customGetterSetter = jsCast<CustomGetterSetter*>(accessor);
3039         if (customGetterSetter->setter())
3040             getterSetter->setSetter(vm, exec->lexicalGlobalObject(), getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Setter));
3041         if (customGetterSetter->getter())
3042             getterSetter->setGetter(vm, exec->lexicalGlobalObject(), getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Getter));
3043     } else {
3044         ASSERT(accessor.isGetterSetter());
3045         getterSetter = asGetterSetter(accessor);
3046     }
3047     if (descriptor.setterPresent()) {
3048         getterSetter = getterSetter->withSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
3049         getterSetterChanged = true;
3050     }
3051     if (descriptor.getterPresent()) {
3052         getterSetter = getterSetter->withGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
3053         getterSetterChanged = true;
3054     }
3055     if (current.attributesEqual(descriptor) && !getterSetterChanged)
3056         return true;
3057     object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3058     unsigned attrs = descriptor.attributesOverridingCurrent(current);
3059     object->putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor);
3060     return true;
3061 }
3062
3063 bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3064 {
3065     VM& vm  = exec->vm();
3066     auto throwScope = DECLARE_THROW_SCOPE(vm);
3067
3068     // Track on the globaldata that we're in define property.
3069     // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
3070     // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
3071     // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
3072     VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
3073     PropertyDescriptor current;
3074     bool isCurrentDefined = getOwnPropertyDescriptor(exec, propertyName, current);
3075     bool isExtensible = this->isExtensible(exec);
3076     if (UNLIKELY(throwScope.exception()))
3077         return false;
3078     return validateAndApplyPropertyDescriptor(exec, this, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException);
3079 }
3080
3081 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3082 {
3083     // If it's an array index, then use the indexed property storage.
3084     if (Optional<uint32_t> index = parseIndex(propertyName)) {
3085         // 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.
3086         // d. Reject if succeeded is false.
3087         // e. If index >= oldLen
3088         // e.i. Set oldLenDesc.[[Value]] to index + 1.
3089         // 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.
3090         // f. Return true.
3091         return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException);
3092     }
3093     
3094     return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
3095 }
3096
3097 void JSObject::convertToDictionary(VM& vm)
3098 {
3099     DeferredStructureTransitionWatchpointFire deferredWatchpointFire;
3100     setStructure(
3101         vm, Structure::toCacheableDictionaryTransition(vm, structure(vm), &deferredWatchpointFire));
3102 }
3103
3104 void JSObject::shiftButterflyAfterFlattening(VM& vm, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter)
3105 {
3106     Butterfly* butterfly = this->butterfly();
3107     size_t preCapacity = this->butterflyPreCapacity();
3108     void* currentBase = butterfly->base(preCapacity, outOfLineCapacityAfter);
3109     void* newBase = butterfly->base(preCapacity, outOfLineCapacityBefore);
3110
3111     memmove(newBase, currentBase, this->butterflyTotalSize());
3112     setButterflyWithoutChangingStructure(vm, Butterfly::fromBase(newBase, preCapacity, outOfLineCapacityAfter));
3113 }
3114
3115 uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object)
3116 {
3117     VM& vm = exec->vm();
3118     Structure* structure = object->structure(vm);
3119     if (structure->holesMustForwardToPrototype(vm))
3120         return 0;
3121     switch (object->indexingType()) {
3122     case ALL_BLANK_INDEXING_TYPES:
3123     case ALL_UNDECIDED_INDEXING_TYPES:
3124         return 0;
3125         
3126     case ALL_INT32_INDEXING_TYPES:
3127     case ALL_CONTIGUOUS_INDEXING_TYPES: {
3128         Butterfly* butterfly = object->butterfly();
3129         unsigned usedLength = butterfly->publicLength();
3130         for (unsigned i = 0; i < usedLength; ++i) {
3131             if (!butterfly->contiguous()[i])
3132                 return 0;
3133         }
3134         return usedLength;
3135     }
3136         
3137     case ALL_DOUBLE_INDEXING_TYPES: {
3138         Butterfly* butterfly = object->butterfly();
3139         unsigned usedLength = butterfly->publicLength();
3140         for (unsigned i = 0; i < usedLength; ++i) {
3141             double value = butterfly->contiguousDouble()[i];
3142             if (value != value)
3143                 return 0;
3144         }
3145         return usedLength;
3146     }
3147         
3148     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
3149         ArrayStorage* storage = object->m_butterfly.get()->arrayStorage();
3150         if (storage->m_sparseMap.get())
3151             return 0;
3152         
3153         unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
3154         for (unsigned i = 0; i < usedVectorLength; ++i) {
3155             if (!storage->m_vector[i])
3156                 return 0;
3157         }
3158         return usedVectorLength;
3159     }
3160         
3161     default:
3162         RELEASE_ASSERT_NOT_REACHED();
3163         return 0;
3164     }
3165 }
3166
3167 void JSObject::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
3168 {
3169     VM& vm = exec->vm();
3170     object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
3171 }
3172
3173 void JSObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
3174 {
3175     VM& vm = exec->vm();
3176     auto scope = DECLARE_THROW_SCOPE(vm);
3177     object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, EnumerationMode(mode, JSObjectPropertiesMode::Exclude));
3178     if (UNLIKELY(scope.exception()))
3179         return;
3180
3181     JSValue nextProto = object->getPrototype(vm, exec);
3182     if (UNLIKELY(scope.exception()))
3183         return;
3184     if (nextProto.isNull())
3185         return;
3186
3187     JSObject* prototype = asObject(nextProto);
3188     while (true) {
3189         if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
3190             prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
3191             break;
3192         }
3193         prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
3194         if (UNLIKELY(scope.exception()))
3195             return;
3196         nextProto = prototype->getPrototype(vm, exec);
3197         if (UNLIKELY(scope.exception()))
3198             return;
3199         if (nextProto.isNull())
3200             break;
3201         prototype = asObject(nextProto);
3202     }
3203 }
3204
3205 // Implements GetMethod(O, P) in section 7.3.9 of the spec.
3206 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-getmethod
3207 JSValue JSObject::getMethod(ExecState* exec, CallData& callData, CallType& callType, const Identifier& ident, const String& errorMessage)
3208 {
3209     VM& vm = exec->vm();
3210     auto scope = DECLARE_THROW_SCOPE(vm);
3211
3212     JSValue method = get(exec, ident);
3213     if (UNLIKELY(scope.exception()))
3214         return jsUndefined();
3215
3216     if (!method.isCell()) {
3217         if (method.isUndefinedOrNull())
3218             return jsUndefined();
3219
3220         throwVMTypeError(exec, scope, errorMessage);
3221         return jsUndefined();
3222     }
3223
3224     callType = method.asCell()->methodTable()->getCallData(method.asCell(), callData);
3225     if (callType == CallType::None) {
3226         throwVMTypeError(exec, scope, errorMessage);
3227         return jsUndefined();
3228     }
3229
3230     return method;
3231 }
3232
3233 } // namespace JSC