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