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