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