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