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