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