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