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