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