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