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