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