2882bc425686f53af256630d53a3379d759fdf57
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003-2006, 2008-2009, 2012-2016 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 "CustomGetterSetter.h"
29 #include "DatePrototype.h"
30 #include "ErrorConstructor.h"
31 #include "Exception.h"
32 #include "Executable.h"
33 #include "GetterSetter.h"
34 #include "HeapSnapshotBuilder.h"
35 #include "IndexingHeaderInlines.h"
36 #include "JSCInlines.h"
37 #include "JSCustomGetterSetterFunction.h"
38 #include "JSFunction.h"
39 #include "JSGlobalObject.h"
40 #include "Lookup.h"
41 #include "NativeErrorConstructor.h"
42 #include "Nodes.h"
43 #include "ObjectPrototype.h"
44 #include "PropertyDescriptor.h"
45 #include "PropertyNameArray.h"
46 #include "PrototypeMapInlines.h"
47 #include "ProxyObject.h"
48 #include "Reject.h"
49 #include "SlotVisitorInlines.h"
50 #include <math.h>
51 #include <wtf/Assertions.h>
52
53 namespace JSC {
54
55 // We keep track of the size of the last array after it was grown. We use this
56 // as a simple heuristic for as the value to grow the next array from size 0.
57 // This value is capped by the constant FIRST_VECTOR_GROW defined in
58 // ArrayConventions.h.
59 static unsigned lastArraySize = 0;
60
61 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
62 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
63
64 const char* ReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
65 const char* UnconfigurablePropertyChangeAccessMechanismError = "Attempting to change access mechanism for an unconfigurable property.";
66
67 const ClassInfo JSObject::s_info = { "Object", 0, 0, CREATE_METHOD_TABLE(JSObject) };
68
69 const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(JSFinalObject) };
70
71 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
72 {
73     VM& vm = exec->vm();
74
75     // Add properties from the static hashtables of properties
76     for (; classInfo; classInfo = classInfo->parentClass) {
77         const HashTable* table = classInfo->staticPropHashTable;
78         if (!table)
79             continue;
80
81         for (auto iter = table->begin(); iter != table->end(); ++iter) {
82             if (!(iter->attributes() & DontEnum) || mode.includeDontEnumProperties())
83                 propertyNames.add(Identifier::fromString(&vm, iter.key()));
84         }
85     }
86 }
87
88 ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, Structure* structure)
89 {
90     ASSERT(butterfly);
91     
92     size_t storageSize = structure->outOfLineSize();
93     size_t propertyCapacity = structure->outOfLineCapacity();
94     size_t preCapacity;
95     bool hasIndexingHeader = this->hasIndexingHeader();
96     if (UNLIKELY(hasIndexingHeader))
97         preCapacity = butterfly->indexingHeader()->preCapacity(structure);
98     else
99         preCapacity = 0;
100     
101     HeapCell* base = bitwise_cast<HeapCell*>(butterfly->base(preCapacity, propertyCapacity));
102     
103     ASSERT(Heap::heap(base) == visitor.heap());
104
105     // Keep the butterfly alive.
106     visitor.markAuxiliary(base);
107     
108     // Mark the properties.
109     visitor.appendValuesHidden(butterfly->propertyStorage() - storageSize, storageSize);
110     
111     // Mark the array if appropriate.
112     switch (this->indexingType()) {
113     case ALL_CONTIGUOUS_INDEXING_TYPES:
114         visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
115         break;
116     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
117         visitor.appendValuesHidden(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
118         if (butterfly->arrayStorage()->m_sparseMap)
119             visitor.append(&butterfly->arrayStorage()->m_sparseMap);
120         break;
121     default:
122         break;
123     }
124 }
125
126 size_t JSObject::estimatedSize(JSCell* cell)
127 {
128     JSObject* thisObject = jsCast<JSObject*>(cell);
129     size_t butterflyOutOfLineSize = thisObject->m_butterfly ? thisObject->structure()->outOfLineSize() : 0;
130     return Base::estimatedSize(cell) + butterflyOutOfLineSize;
131 }
132
133 void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
134 {
135     JSObject* thisObject = jsCast<JSObject*>(cell);
136     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
137 #if !ASSERT_DISABLED
138     bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
139     visitor.m_isCheckingForDefaultMarkViolation = false;
140 #endif
141     
142     JSCell::visitChildren(thisObject, visitor);
143
144     Butterfly* butterfly = thisObject->m_butterfly.get();
145     if (butterfly)
146         thisObject->visitButterfly(visitor, butterfly, thisObject->structure(visitor.vm()));
147
148 #if !ASSERT_DISABLED
149     visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
150 #endif
151 }
152
153 void JSObject::heapSnapshot(JSCell* cell, HeapSnapshotBuilder& builder)
154 {
155     JSObject* thisObject = jsCast<JSObject*>(cell);
156     Base::heapSnapshot(cell, builder);
157
158     Structure* structure = thisObject->structure();
159     for (auto& entry : structure->getPropertiesConcurrently()) {
160         JSValue toValue = thisObject->getDirect(entry.offset);
161         if (toValue && toValue.isCell())
162             builder.appendPropertyNameEdge(thisObject, toValue.asCell(), entry.key);
163     }
164
165     Butterfly* butterfly = thisObject->m_butterfly.get();
166     if (butterfly) {
167         WriteBarrier<Unknown>* data = nullptr;
168         uint32_t count = 0;
169
170         switch (thisObject->indexingType()) {
171         case ALL_CONTIGUOUS_INDEXING_TYPES:
172             data = butterfly->contiguous().data();
173             count = butterfly->publicLength();
174             break;
175         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
176             data = butterfly->arrayStorage()->m_vector;
177             count = butterfly->arrayStorage()->vectorLength();
178             break;
179         default:
180             break;
181         }
182
183         for (uint32_t i = 0; i < count; ++i) {
184             JSValue toValue = data[i].get();
185             if (toValue && toValue.isCell())
186                 builder.appendIndexEdge(thisObject, toValue.asCell(), i);
187         }
188     }
189 }
190
191 void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
192 {
193     JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
194     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
195 #if !ASSERT_DISABLED
196     bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
197     visitor.m_isCheckingForDefaultMarkViolation = false;
198 #endif
199     
200     JSCell::visitChildren(thisObject, visitor);
201
202     Structure* structure = thisObject->structure(visitor.vm());
203     Butterfly* butterfly = thisObject->butterfly();
204     if (butterfly)
205         thisObject->visitButterfly(visitor, butterfly, structure);
206
207     size_t storageSize = structure->inlineSize();
208     if (storageSize)
209         visitor.appendValuesHidden(thisObject->inlineStorage(), storageSize);
210
211 #if !ASSERT_DISABLED
212     visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
213 #endif
214 }
215
216 String JSObject::className(const JSObject* object)
217 {
218     const ClassInfo* info = object->classInfo();
219     ASSERT(info);
220     return info->className;
221 }
222
223 String JSObject::toStringName(const JSObject* object, ExecState*)
224 {
225     const ClassInfo* info = object->classInfo();
226     ASSERT(info);
227     return info->methodTable.className(object);
228 }
229
230 String JSObject::calculatedClassName(JSObject* object)
231 {
232     String prototypeFunctionName;
233     auto globalObject = object->globalObject();
234     VM& vm = globalObject->vm();
235     auto scope = DECLARE_CATCH_SCOPE(vm);
236
237     ExecState* exec = globalObject->globalExec();
238     PropertySlot slot(object->getPrototypeDirect(), PropertySlot::InternalMethodType::VMInquiry);
239     PropertyName constructor(exec->propertyNames().constructor);
240     if (object->getPropertySlot(exec, constructor, slot)) {
241         if (slot.isValue()) {
242             JSValue constructorValue = slot.getValue(exec, constructor);
243             if (constructorValue.isCell()) {
244                 if (JSCell* constructorCell = constructorValue.asCell()) {
245                     if (JSObject* ctorObject = constructorCell->getObject()) {
246                         if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(ctorObject))
247                             prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
248                         else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(ctorObject))
249                             prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
250                     }
251                 }
252             }
253         }
254     }
255     ASSERT(!scope.exception() || prototypeFunctionName.isNull());
256     if (UNLIKELY(scope.exception()))
257         scope.clearException();
258
259     if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") {
260         String tableClassName = object->methodTable()->className(object);
261         if (!tableClassName.isNull() && tableClassName != "Object")
262             return tableClassName;
263
264         String classInfoName = object->classInfo()->className;
265         if (!classInfoName.isNull())
266             return classInfoName;
267
268         if (prototypeFunctionName.isNull())
269             return ASCIILiteral("Object");
270     }
271
272     return prototypeFunctionName;
273 }
274
275 bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot)
276 {
277     // NB. The fact that we're directly consulting our indexed storage implies that it is not
278     // legal for anyone to override getOwnPropertySlot() without also overriding
279     // getOwnPropertySlotByIndex().
280     
281     if (i > MAX_ARRAY_INDEX)
282         return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
283     
284     switch (thisObject->indexingType()) {
285     case ALL_BLANK_INDEXING_TYPES:
286     case ALL_UNDECIDED_INDEXING_TYPES:
287         break;
288         
289     case ALL_INT32_INDEXING_TYPES:
290     case ALL_CONTIGUOUS_INDEXING_TYPES: {
291         Butterfly* butterfly = thisObject->butterfly();
292         if (i >= butterfly->vectorLength())
293             return false;
294         
295         JSValue value = butterfly->contiguous()[i].get();
296         if (value) {
297             slot.setValue(thisObject, None, value);
298             return true;
299         }
300         
301         return false;
302     }
303         
304     case ALL_DOUBLE_INDEXING_TYPES: {
305         Butterfly* butterfly = thisObject->butterfly();
306         if (i >= butterfly->vectorLength())
307             return false;
308         
309         double value = butterfly->contiguousDouble()[i];
310         if (value == value) {
311             slot.setValue(thisObject, None, JSValue(JSValue::EncodeAsDouble, value));
312             return true;
313         }
314         
315         return false;
316     }
317         
318     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
319         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
320         if (i >= storage->length())
321             return false;
322         
323         if (i < storage->vectorLength()) {
324             JSValue value = storage->m_vector[i].get();
325             if (value) {
326                 slot.setValue(thisObject, None, value);
327                 return true;
328             }
329         } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
330             SparseArrayValueMap::iterator it = map->find(i);
331             if (it != map->notFound()) {
332                 it->value.get(thisObject, slot);
333                 return true;
334             }
335         }
336         break;
337     }
338         
339     default:
340         RELEASE_ASSERT_NOT_REACHED();
341         break;
342     }
343     
344     return false;
345 }
346
347 // https://tc39.github.io/ecma262/#sec-ordinaryset
348 bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyName, JSValue value, JSValue receiver, bool shouldThrow)
349 {
350     // If we find the receiver is not the same to the object, we fall to this slow path.
351     // Currently, there are 3 candidates.
352     // 1. Reflect.set can alter the receiver with an arbitrary value.
353     // 2. Window Proxy.
354     // 3. ES6 Proxy.
355
356     VM& vm = exec->vm();
357     auto scope = DECLARE_THROW_SCOPE(vm);
358     JSObject* current = object;
359     PropertyDescriptor ownDescriptor;
360     while (true) {
361         if (current->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
362             ProxyObject* proxy = jsCast<ProxyObject*>(current);
363             PutPropertySlot slot(receiver, shouldThrow);
364             return proxy->ProxyObject::put(proxy, exec, propertyName, value, slot);
365         }
366
367         // 9.1.9.1-2 Let ownDesc be ? O.[[GetOwnProperty]](P).
368         bool ownDescriptorFound = current->getOwnPropertyDescriptor(exec, propertyName, ownDescriptor);
369         RETURN_IF_EXCEPTION(scope, false);
370
371         if (!ownDescriptorFound) {
372             // 9.1.9.1-3-a Let parent be ? O.[[GetPrototypeOf]]().
373             JSValue prototype = current->getPrototype(vm, exec);
374             RETURN_IF_EXCEPTION(scope, false);
375
376             // 9.1.9.1-3-b If parent is not null, then
377             if (!prototype.isNull()) {
378                 // 9.1.9.1-3-b-i Return ? parent.[[Set]](P, V, Receiver).
379                 current = asObject(prototype);
380                 continue;
381             }
382             // 9.1.9.1-3-c-i Let ownDesc be the PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
383             ownDescriptor = PropertyDescriptor(jsUndefined(), None);
384         }
385         break;
386     }
387
388     // 9.1.9.1-4 If IsDataDescriptor(ownDesc) is true, then
389     if (ownDescriptor.isDataDescriptor()) {
390         // 9.1.9.1-4-a If ownDesc.[[Writable]] is false, return false.
391         if (!ownDescriptor.writable())
392             return reject(exec, shouldThrow, ReadonlyPropertyWriteError);
393
394         // 9.1.9.1-4-b If Type(Receiver) is not Object, return false.
395         if (!receiver.isObject())
396             return reject(exec, shouldThrow, ReadonlyPropertyWriteError);
397
398         // In OrdinarySet, the receiver may not be the same to the object.
399         // So, we perform [[GetOwnProperty]] onto the receiver while we already perform [[GetOwnProperty]] onto the object.
400
401         // 9.1.9.1-4-c Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
402         JSObject* receiverObject = asObject(receiver);
403         PropertyDescriptor existingDescriptor;
404         bool existingDescriptorFound = receiverObject->getOwnPropertyDescriptor(exec, propertyName, existingDescriptor);
405         RETURN_IF_EXCEPTION(scope, false);
406
407         // 9.1.9.1-4-d If existingDescriptor is not undefined, then
408         if (existingDescriptorFound) {
409             // 9.1.9.1-4-d-i If IsAccessorDescriptor(existingDescriptor) is true, return false.
410             if (existingDescriptor.isAccessorDescriptor())
411                 return reject(exec, shouldThrow, ReadonlyPropertyWriteError);
412
413             // 9.1.9.1-4-d-ii If existingDescriptor.[[Writable]] is false, return false.
414             if (!existingDescriptor.writable())
415                 return reject(exec, shouldThrow, ReadonlyPropertyWriteError);
416
417             // 9.1.9.1-4-d-iii Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
418             PropertyDescriptor valueDescriptor;
419             valueDescriptor.setValue(value);
420
421             // 9.1.9.1-4-d-iv Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
422             return receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, valueDescriptor, shouldThrow);
423         }
424
425         // 9.1.9.1-4-e Else Receiver does not currently have a property P,
426         // 9.1.9.1-4-e-i Return ? CreateDataProperty(Receiver, P, V).
427         return receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, PropertyDescriptor(value, None), shouldThrow);
428     }
429
430     // 9.1.9.1-5 Assert: IsAccessorDescriptor(ownDesc) is true.
431     ASSERT(ownDescriptor.isAccessorDescriptor());
432
433     // 9.1.9.1-6 Let setter be ownDesc.[[Set]].
434     // 9.1.9.1-7 If setter is undefined, return false.
435     JSValue setter = ownDescriptor.setter();
436     if (!setter.isObject())
437         return reject(exec, shouldThrow, ReadonlyPropertyWriteError);
438
439     // 9.1.9.1-8 Perform ? Call(setter, Receiver, << V >>).
440     JSObject* setterObject = asObject(setter);
441     MarkedArgumentBuffer args;
442     args.append(value);
443
444     CallData callData;
445     CallType callType = setterObject->methodTable(exec->vm())->getCallData(setterObject, callData);
446     call(exec, setterObject, callType, callData, receiver, args);
447
448     // 9.1.9.1-9 Return true.
449     return true;
450 }
451
452 // ECMA 8.6.2.2
453 bool JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
454 {
455     return putInline(cell, exec, propertyName, value, slot);
456 }
457
458 bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
459 {
460     ASSERT(!isThisValueAltered(slot, this));
461
462     VM& vm = exec->vm();
463
464     JSObject* obj = this;
465     for (;;) {
466         unsigned attributes;
467         PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
468         if (isValidOffset(offset)) {
469             if (attributes & ReadOnly) {
470                 ASSERT(structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == this);
471                 return reject(exec, slot.isStrictMode(), ReadonlyPropertyWriteError);
472             }
473
474             JSValue gs = obj->getDirect(offset);
475             if (gs.isGetterSetter()) {
476                 bool result = callSetter(exec, slot.thisValue(), gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
477                 if (!structure()->isDictionary())
478                     slot.setCacheableSetter(obj, offset);
479                 return result;
480             }
481             if (gs.isCustomGetterSetter()) {
482                 bool result = callCustomSetter(exec, gs, attributes & CustomAccessor, obj, slot.thisValue(), value);
483                 if (attributes & CustomAccessor)
484                     slot.setCustomAccessor(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
485                 else
486                     slot.setCustomValue(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter());
487                 return result;
488             }
489             ASSERT(!(attributes & Accessor));
490
491             // If there's an existing property on the object or one of its 
492             // prototypes it should be replaced, so break here.
493             break;
494         }
495         if (!obj->staticPropertiesReified()) {
496             if (obj->classInfo()->hasStaticSetterOrReadonlyProperties()) {
497                 if (auto* entry = obj->findPropertyHashEntry(propertyName))
498                     return putEntry(exec, entry, obj, this, propertyName, value, slot);
499             }
500         }
501         if (obj->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
502             // FIXME: We shouldn't unconditionally perform [[Set]] here.
503             // We need to do more because this is observable behavior.
504             // https://bugs.webkit.org/show_bug.cgi?id=155012
505             ProxyObject* proxy = jsCast<ProxyObject*>(obj);
506             return proxy->ProxyObject::put(proxy, exec, propertyName, value, slot);
507         }
508         JSValue prototype = obj->getPrototypeDirect();
509         if (prototype.isNull())
510             break;
511         obj = asObject(prototype);
512     }
513
514     ASSERT(!structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == this);
515     if (!putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot))
516         return reject(exec, slot.isStrictMode(), ReadonlyPropertyWriteError);
517     return true;
518 }
519
520 bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
521 {
522     JSObject* thisObject = jsCast<JSObject*>(cell);
523     
524     if (propertyName > MAX_ARRAY_INDEX) {
525         PutPropertySlot slot(cell, shouldThrow);
526         return thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
527     }
528     
529     switch (thisObject->indexingType()) {
530     case ALL_BLANK_INDEXING_TYPES:
531         break;
532         
533     case ALL_UNDECIDED_INDEXING_TYPES: {
534         thisObject->convertUndecidedForValue(exec->vm(), value);
535         // Reloop.
536         return putByIndex(cell, exec, propertyName, value, shouldThrow);
537     }
538         
539     case ALL_INT32_INDEXING_TYPES: {
540         if (!value.isInt32()) {
541             thisObject->convertInt32ForValue(exec->vm(), value);
542             return putByIndex(cell, exec, propertyName, value, shouldThrow);
543         }
544         FALLTHROUGH;
545     }
546         
547     case ALL_CONTIGUOUS_INDEXING_TYPES: {
548         Butterfly* butterfly = thisObject->butterfly();
549         if (propertyName >= butterfly->vectorLength())
550             break;
551         butterfly->contiguous()[propertyName].set(exec->vm(), thisObject, value);
552         if (propertyName >= butterfly->publicLength())
553             butterfly->setPublicLength(propertyName + 1);
554         return true;
555     }
556         
557     case ALL_DOUBLE_INDEXING_TYPES: {
558         if (!value.isNumber()) {
559             thisObject->convertDoubleToContiguous(exec->vm());
560             // Reloop.
561             return putByIndex(cell, exec, propertyName, value, shouldThrow);
562         }
563         double valueAsDouble = value.asNumber();
564         if (valueAsDouble != valueAsDouble) {
565             thisObject->convertDoubleToContiguous(exec->vm());
566             // Reloop.
567             return putByIndex(cell, exec, propertyName, value, shouldThrow);
568         }
569         Butterfly* butterfly = thisObject->butterfly();
570         if (propertyName >= butterfly->vectorLength())
571             break;
572         butterfly->contiguousDouble()[propertyName] = valueAsDouble;
573         if (propertyName >= butterfly->publicLength())
574             butterfly->setPublicLength(propertyName + 1);
575         return true;
576     }
577         
578     case NonArrayWithArrayStorage:
579     case ArrayWithArrayStorage: {
580         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
581         
582         if (propertyName >= storage->vectorLength())
583             break;
584         
585         WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
586         unsigned length = storage->length();
587         
588         // Update length & m_numValuesInVector as necessary.
589         if (propertyName >= length) {
590             length = propertyName + 1;
591             storage->setLength(length);
592             ++storage->m_numValuesInVector;
593         } else if (!valueSlot)
594             ++storage->m_numValuesInVector;
595         
596         valueSlot.set(exec->vm(), thisObject, value);
597         return true;
598     }
599         
600     case NonArrayWithSlowPutArrayStorage:
601     case ArrayWithSlowPutArrayStorage: {
602         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
603         
604         if (propertyName >= storage->vectorLength())
605             break;
606         
607         WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
608         unsigned length = storage->length();
609         
610         // Update length & m_numValuesInVector as necessary.
611         if (propertyName >= length) {
612             bool putResult = false;
613             if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
614                 return putResult;
615             length = propertyName + 1;
616             storage->setLength(length);
617             ++storage->m_numValuesInVector;
618         } else if (!valueSlot) {
619             bool putResult = false;
620             if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow, putResult))
621                 return putResult;
622             ++storage->m_numValuesInVector;
623         }
624         
625         valueSlot.set(exec->vm(), thisObject, value);
626         return true;
627     }
628         
629     default:
630         RELEASE_ASSERT_NOT_REACHED();
631     }
632     
633     return thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
634 }
635
636 ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
637 {
638     SparseArrayValueMap* map = storage->m_sparseMap.get();
639
640     if (!map)
641         map = allocateSparseIndexMap(vm);
642
643     if (map->sparseMode())
644         return storage;
645
646     map->setSparseMode();
647
648     unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
649     for (unsigned i = 0; i < usedVectorLength; ++i) {
650         JSValue value = storage->m_vector[i].get();
651         // This will always be a new entry in the map, so no need to check we can write,
652         // and attributes are default so no need to set them.
653         if (value)
654             map->add(this, i).iterator->value.set(vm, map, value);
655     }
656
657     DeferGC deferGC(vm.heap);
658     Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
659     RELEASE_ASSERT(newButterfly);
660     newButterfly->arrayStorage()->m_indexBias = 0;
661     newButterfly->arrayStorage()->setVectorLength(0);
662     newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
663     setButterflyWithoutChangingStructure(vm, newButterfly);
664     
665     return newButterfly->arrayStorage();
666 }
667
668 void JSObject::enterDictionaryIndexingMode(VM& vm)
669 {
670     switch (indexingType()) {
671     case ALL_BLANK_INDEXING_TYPES:
672     case ALL_UNDECIDED_INDEXING_TYPES:
673     case ALL_INT32_INDEXING_TYPES:
674     case ALL_DOUBLE_INDEXING_TYPES:
675     case ALL_CONTIGUOUS_INDEXING_TYPES:
676         // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
677         // this case if we ever cared. Note that ensureArrayStorage() can return null if the object
678         // doesn't support traditional indexed properties. At the time of writing, this just affects
679         // typed arrays.
680         if (ArrayStorage* storage = ensureArrayStorageSlow(vm))
681             enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, storage);
682         break;
683     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
684         enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly.get()->arrayStorage());
685         break;
686         
687     default:
688         break;
689     }
690 }
691
692 void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
693 {
694     if (mayInterceptIndexedAccesses())
695         return;
696     
697     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors));
698     
699     if (!vm.prototypeMap.isPrototype(this))
700         return;
701     
702     globalObject(vm)->haveABadTime(vm);
703 }
704
705 Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length)
706 {
707     ASSERT(length < MAX_ARRAY_INDEX);
708     IndexingType oldType = indexingType();
709     ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
710     ASSERT(!structure()->needsSlowPutIndexing());
711     ASSERT(!indexingShouldBeSparse());
712     Structure* structure = this->structure(vm);
713     unsigned propertyCapacity = structure->outOfLineCapacity();
714     unsigned vectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, length);
715     Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
716         m_butterfly.get(), vm, this, structure, propertyCapacity, false, 0,
717         sizeof(EncodedJSValue) * vectorLength);
718     newButterfly->setPublicLength(length);
719     newButterfly->setVectorLength(vectorLength);
720     return newButterfly;
721 }
722
723 Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
724 {
725     DeferGC deferGC(vm.heap);
726     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
727     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateUndecided);
728     setStructureAndButterfly(vm, newStructure, newButterfly);
729     return newButterfly;
730 }
731
732 ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
733 {
734     DeferGC deferGC(vm.heap);
735     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
736     for (unsigned i = newButterfly->vectorLength(); i--;)
737         newButterfly->contiguousInt32()[i].setWithoutWriteBarrier(JSValue());
738     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32);
739     setStructureAndButterfly(vm, newStructure, newButterfly);
740     return newButterfly->contiguousInt32();
741 }
742
743 ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
744 {
745     DeferGC deferGC(vm.heap);
746     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
747     for (unsigned i = newButterfly->vectorLength(); i--;)
748         newButterfly->contiguousDouble()[i] = PNaN;
749     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble);
750     setStructureAndButterfly(vm, newStructure, newButterfly);
751     return newButterfly->contiguousDouble();
752 }
753
754 ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
755 {
756     DeferGC deferGC(vm.heap);
757     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
758     for (unsigned i = newButterfly->vectorLength(); i--;)
759         newButterfly->contiguous()[i].setWithoutWriteBarrier(JSValue());
760     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous);
761     setStructureAndButterfly(vm, newStructure, newButterfly);
762     return newButterfly->contiguous();
763 }
764
765 ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
766 {
767     DeferGC deferGC(vm.heap);
768     Structure* structure = this->structure(vm);
769     IndexingType oldType = indexingType();
770     ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
771     Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
772         m_butterfly.get(), vm, this, structure, structure->outOfLineCapacity(), false, 0,
773         ArrayStorage::sizeFor(vectorLength));
774     RELEASE_ASSERT(newButterfly);
775
776     ArrayStorage* result = newButterfly->arrayStorage();
777     result->setLength(length);
778     result->setVectorLength(vectorLength);
779     result->m_sparseMap.clear();
780     result->m_numValuesInVector = 0;
781     result->m_indexBias = 0;
782     for (size_t i = vectorLength; i--;)
783         result->m_vector[i].setWithoutWriteBarrier(JSValue());
784     Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition());
785     setStructureAndButterfly(vm, newStructure, newButterfly);
786     return result;
787 }
788
789 ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
790 {
791     return createArrayStorage(
792         vm, 0, ArrayStorage::optimalVectorLength(0, structure(vm)->outOfLineCapacity(), 0));
793 }
794
795 ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
796 {
797     ASSERT(hasUndecided(indexingType()));
798
799     Butterfly* butterfly = m_butterfly.get();
800     for (unsigned i = butterfly->vectorLength(); i--;)
801         butterfly->contiguousInt32()[i].setWithoutWriteBarrier(JSValue());
802
803     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32));
804     return m_butterfly.get()->contiguousInt32();
805 }
806
807 ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
808 {
809     ASSERT(hasUndecided(indexingType()));
810
811     Butterfly* butterfly = m_butterfly.get();
812     for (unsigned i = butterfly->vectorLength(); i--;)
813         butterfly->contiguousDouble()[i] = PNaN;
814     
815     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
816     return m_butterfly.get()->contiguousDouble();
817 }
818
819 ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
820 {
821     ASSERT(hasUndecided(indexingType()));
822
823     Butterfly* butterfly = m_butterfly.get();
824     for (unsigned i = butterfly->vectorLength(); i--;)
825         butterfly->contiguous()[i].setWithoutWriteBarrier(JSValue());
826
827     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
828     return m_butterfly.get()->contiguous();
829 }
830
831 ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
832 {
833     Structure* structure = this->structure(vm);
834     unsigned publicLength = m_butterfly.get()->publicLength();
835     unsigned propertyCapacity = structure->outOfLineCapacity();
836     unsigned propertySize = structure->outOfLineSize();
837     
838     Butterfly* newButterfly = Butterfly::createUninitialized(
839         vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
840     
841     memcpy(
842         newButterfly->propertyStorage() - propertySize,
843         m_butterfly.get()->propertyStorage() - propertySize,
844         propertySize * sizeof(EncodedJSValue));
845     
846     ArrayStorage* newStorage = newButterfly->arrayStorage();
847     newStorage->setVectorLength(neededLength);
848     newStorage->setLength(publicLength);
849     newStorage->m_sparseMap.clear();
850     newStorage->m_indexBias = 0;
851     newStorage->m_numValuesInVector = 0;
852     
853     return newStorage;
854 }
855
856 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
857 {
858     DeferGC deferGC(vm.heap);
859     ASSERT(hasUndecided(indexingType()));
860
861     unsigned vectorLength = m_butterfly.get()->vectorLength();
862     ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
863     
864     for (unsigned i = vectorLength; i--;)
865         storage->m_vector[i].setWithoutWriteBarrier(JSValue());
866     
867     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
868     setStructureAndButterfly(vm, newStructure, storage->butterfly());
869     return storage;
870 }
871
872 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
873 {
874     return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
875 }
876
877 ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
878 {
879     ASSERT(hasInt32(indexingType()));
880
881     Butterfly* butterfly = m_butterfly.get();
882     for (unsigned i = butterfly->vectorLength(); i--;) {
883         WriteBarrier<Unknown>* current = &butterfly->contiguousInt32()[i];
884         double* currentAsDouble = bitwise_cast<double*>(current);
885         JSValue v = current->get();
886         // NOTE: Since this may be used during initialization, v could be garbage. If it's garbage,
887         // that means it will be overwritten later.
888         if (!v.isInt32()) {
889             *currentAsDouble = PNaN;
890             continue;
891         }
892         *currentAsDouble = v.asInt32();
893     }
894     
895     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
896     return m_butterfly.get()->contiguousDouble();
897 }
898
899 ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
900 {
901     ASSERT(hasInt32(indexingType()));
902     
903     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
904     return m_butterfly.get()->contiguous();
905 }
906
907 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
908 {
909     DeferGC deferGC(vm.heap);
910     ASSERT(hasInt32(indexingType()));
911
912     unsigned vectorLength = m_butterfly.get()->vectorLength();
913     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
914     Butterfly* butterfly = m_butterfly.get();
915     for (unsigned i = 0; i < vectorLength; i++) {
916         JSValue v = butterfly->contiguous()[i].get();
917         newStorage->m_vector[i].setWithoutWriteBarrier(v);
918         if (v)
919             newStorage->m_numValuesInVector++;
920     }
921     
922     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
923     setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
924     return newStorage;
925 }
926
927 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
928 {
929     return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
930 }
931
932 ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
933 {
934     ASSERT(hasDouble(indexingType()));
935
936     Butterfly* butterfly = m_butterfly.get();
937     for (unsigned i = butterfly->vectorLength(); i--;) {
938         double* current = &butterfly->contiguousDouble()[i];
939         WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
940         double value = *current;
941         if (value != value) {
942             currentAsValue->clear();
943             continue;
944         }
945         JSValue v = JSValue(JSValue::EncodeAsDouble, value);
946         currentAsValue->setWithoutWriteBarrier(v);
947     }
948     
949     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
950     return m_butterfly.get()->contiguous();
951 }
952
953 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
954 {
955     DeferGC deferGC(vm.heap);
956     ASSERT(hasDouble(indexingType()));
957
958     unsigned vectorLength = m_butterfly.get()->vectorLength();
959     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
960     Butterfly* butterfly = m_butterfly.get();
961     for (unsigned i = 0; i < vectorLength; i++) {
962         double value = butterfly->contiguousDouble()[i];
963         newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
964         if (value == value)
965             newStorage->m_numValuesInVector++;
966     }
967     
968     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
969     setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
970     return newStorage;
971 }
972
973 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
974 {
975     return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
976 }
977
978 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
979 {
980     DeferGC deferGC(vm.heap);
981     ASSERT(hasContiguous(indexingType()));
982
983     unsigned vectorLength = m_butterfly.get()->vectorLength();
984     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
985     Butterfly* butterfly = m_butterfly.get();
986     for (unsigned i = 0; i < vectorLength; i++) {
987         JSValue v = butterfly->contiguous()[i].get();
988         newStorage->m_vector[i].setWithoutWriteBarrier(v);
989         if (v)
990             newStorage->m_numValuesInVector++;
991     }
992     
993     Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
994     setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
995     return newStorage;
996 }
997
998 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
999 {
1000     return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
1001 }
1002
1003 void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
1004 {
1005     IndexingType type = indexingTypeForValue(value);
1006     if (type == Int32Shape) {
1007         convertUndecidedToInt32(vm);
1008         return;
1009     }
1010     
1011     if (type == DoubleShape) {
1012         convertUndecidedToDouble(vm);
1013         return;
1014     }
1015
1016     ASSERT(type == ContiguousShape);
1017     convertUndecidedToContiguous(vm);
1018 }
1019
1020 void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
1021 {
1022     if (value.isInt32()) {
1023         createInitialInt32(vm, index + 1)[index].set(vm, this, value);
1024         return;
1025     }
1026     
1027     if (value.isDouble()) {
1028         double doubleValue = value.asNumber();
1029         if (doubleValue == doubleValue) {
1030             createInitialDouble(vm, index + 1)[index] = doubleValue;
1031             return;
1032         }
1033     }
1034     
1035     createInitialContiguous(vm, index + 1)[index].set(vm, this, value);
1036 }
1037
1038 void JSObject::convertInt32ForValue(VM& vm, JSValue value)
1039 {
1040     ASSERT(!value.isInt32());
1041     
1042     if (value.isDouble() && !std::isnan(value.asDouble())) {
1043         convertInt32ToDouble(vm);
1044         return;
1045     }
1046     
1047     convertInt32ToContiguous(vm);
1048 }
1049
1050 void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
1051 {
1052     ASSERT(index < m_butterfly.get()->publicLength());
1053     ASSERT(index < m_butterfly.get()->vectorLength());
1054     convertUndecidedForValue(vm, value);
1055     setIndexQuickly(vm, index, value);
1056 }
1057
1058 void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1059 {
1060     ASSERT(!value.isInt32());
1061     convertInt32ForValue(vm, value);
1062     setIndexQuickly(vm, index, value);
1063 }
1064
1065 void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
1066 {
1067     ASSERT(!value.isNumber() || value.asNumber() != value.asNumber());
1068     convertDoubleToContiguous(vm);
1069     setIndexQuickly(vm, index, value);
1070 }
1071
1072 ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
1073 {
1074     ASSERT(inherits(info()));
1075     
1076     if (structure(vm)->hijacksIndexingHeader())
1077         return ContiguousJSValues();
1078     
1079     switch (indexingType()) {
1080     case ALL_BLANK_INDEXING_TYPES:
1081         if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1082             return ContiguousJSValues();
1083         return createInitialInt32(vm, 0);
1084         
1085     case ALL_UNDECIDED_INDEXING_TYPES:
1086         return convertUndecidedToInt32(vm);
1087         
1088     case ALL_DOUBLE_INDEXING_TYPES:
1089     case ALL_CONTIGUOUS_INDEXING_TYPES:
1090     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1091         return ContiguousJSValues();
1092         
1093     default:
1094         CRASH();
1095         return ContiguousJSValues();
1096     }
1097 }
1098
1099 ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
1100 {
1101     ASSERT(inherits(info()));
1102     
1103     if (structure(vm)->hijacksIndexingHeader())
1104         return ContiguousDoubles();
1105     
1106     switch (indexingType()) {
1107     case ALL_BLANK_INDEXING_TYPES:
1108         if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1109             return ContiguousDoubles();
1110         return createInitialDouble(vm, 0);
1111         
1112     case ALL_UNDECIDED_INDEXING_TYPES:
1113         return convertUndecidedToDouble(vm);
1114         
1115     case ALL_INT32_INDEXING_TYPES:
1116         return convertInt32ToDouble(vm);
1117         
1118     case ALL_CONTIGUOUS_INDEXING_TYPES:
1119     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1120         return ContiguousDoubles();
1121         
1122     default:
1123         CRASH();
1124         return ContiguousDoubles();
1125     }
1126 }
1127
1128 ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
1129 {
1130     ASSERT(inherits(info()));
1131     
1132     if (structure(vm)->hijacksIndexingHeader())
1133         return ContiguousJSValues();
1134     
1135     switch (indexingType()) {
1136     case ALL_BLANK_INDEXING_TYPES:
1137         if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
1138             return ContiguousJSValues();
1139         return createInitialContiguous(vm, 0);
1140         
1141     case ALL_UNDECIDED_INDEXING_TYPES:
1142         return convertUndecidedToContiguous(vm);
1143         
1144     case ALL_INT32_INDEXING_TYPES:
1145         return convertInt32ToContiguous(vm);
1146         
1147     case ALL_DOUBLE_INDEXING_TYPES:
1148         return convertDoubleToContiguous(vm);
1149         
1150     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1151         return ContiguousJSValues();
1152         
1153     default:
1154         CRASH();
1155         return ContiguousJSValues();
1156     }
1157 }
1158
1159 ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
1160 {
1161     ASSERT(inherits(info()));
1162
1163     if (structure(vm)->hijacksIndexingHeader())
1164         return nullptr;
1165     
1166     switch (indexingType()) {
1167     case ALL_BLANK_INDEXING_TYPES:
1168         if (UNLIKELY(indexingShouldBeSparse()))
1169             return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
1170         return createInitialArrayStorage(vm);
1171         
1172     case ALL_UNDECIDED_INDEXING_TYPES:
1173         ASSERT(!indexingShouldBeSparse());
1174         ASSERT(!structure(vm)->needsSlowPutIndexing());
1175         return convertUndecidedToArrayStorage(vm);
1176         
1177     case ALL_INT32_INDEXING_TYPES:
1178         ASSERT(!indexingShouldBeSparse());
1179         ASSERT(!structure(vm)->needsSlowPutIndexing());
1180         return convertInt32ToArrayStorage(vm);
1181         
1182     case ALL_DOUBLE_INDEXING_TYPES:
1183         ASSERT(!indexingShouldBeSparse());
1184         ASSERT(!structure(vm)->needsSlowPutIndexing());
1185         return convertDoubleToArrayStorage(vm);
1186         
1187     case ALL_CONTIGUOUS_INDEXING_TYPES:
1188         ASSERT(!indexingShouldBeSparse());
1189         ASSERT(!structure(vm)->needsSlowPutIndexing());
1190         return convertContiguousToArrayStorage(vm);
1191         
1192     default:
1193         RELEASE_ASSERT_NOT_REACHED();
1194         return 0;
1195     }
1196 }
1197
1198 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
1199 {
1200     switch (indexingType()) {
1201     case ALL_BLANK_INDEXING_TYPES: {
1202         createArrayStorage(vm, 0, 0);
1203         SparseArrayValueMap* map = allocateSparseIndexMap(vm);
1204         map->setSparseMode();
1205         return arrayStorage();
1206     }
1207         
1208     case ALL_UNDECIDED_INDEXING_TYPES:
1209         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm));
1210         
1211     case ALL_INT32_INDEXING_TYPES:
1212         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm));
1213         
1214     case ALL_DOUBLE_INDEXING_TYPES:
1215         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm));
1216         
1217     case ALL_CONTIGUOUS_INDEXING_TYPES:
1218         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm));
1219         
1220     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
1221         return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly.get()->arrayStorage());
1222         
1223     default:
1224         CRASH();
1225         return 0;
1226     }
1227 }
1228
1229 void JSObject::switchToSlowPutArrayStorage(VM& vm)
1230 {
1231     switch (indexingType()) {
1232     case ALL_UNDECIDED_INDEXING_TYPES:
1233         convertUndecidedToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1234         break;
1235         
1236     case ALL_INT32_INDEXING_TYPES:
1237         convertInt32ToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1238         break;
1239         
1240     case ALL_DOUBLE_INDEXING_TYPES:
1241         convertDoubleToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1242         break;
1243         
1244     case ALL_CONTIGUOUS_INDEXING_TYPES:
1245         convertContiguousToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
1246         break;
1247         
1248     case NonArrayWithArrayStorage:
1249     case ArrayWithArrayStorage: {
1250         Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::SwitchToSlowPutArrayStorage);
1251         setStructure(vm, newStructure);
1252         break;
1253     }
1254         
1255     default:
1256         CRASH();
1257         break;
1258     }
1259 }
1260
1261 void JSObject::setPrototypeDirect(VM& vm, JSValue prototype)
1262 {
1263     ASSERT(prototype);
1264     if (prototype.isObject())
1265         vm.prototypeMap.addPrototype(asObject(prototype));
1266     
1267     Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype);
1268     setStructure(vm, newStructure);
1269     
1270     if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses())
1271         return;
1272     
1273     if (vm.prototypeMap.isPrototype(this)) {
1274         newStructure->globalObject()->haveABadTime(vm);
1275         return;
1276     }
1277     
1278     if (!hasIndexedProperties(indexingType()))
1279         return;
1280     
1281     if (shouldUseSlowPut(indexingType()))
1282         return;
1283     
1284     switchToSlowPutArrayStorage(vm);
1285 }
1286
1287 bool JSObject::setPrototypeWithCycleCheck(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1288 {
1289     auto scope = DECLARE_THROW_SCOPE(vm);
1290     ASSERT(methodTable(vm)->toThis(this, exec, NotStrictMode) == this);
1291
1292     if (this->getPrototypeDirect() == prototype)
1293         return true;
1294
1295     bool isExtensible = this->isExtensible(exec);
1296     RETURN_IF_EXCEPTION(scope, false);
1297
1298     if (!isExtensible) {
1299         if (shouldThrowIfCantSet)
1300             throwTypeError(exec, scope, ReadonlyPropertyWriteError);
1301         return false;
1302     }
1303
1304     JSValue nextPrototype = prototype;
1305     while (nextPrototype && nextPrototype.isObject()) {
1306         if (nextPrototype == this) {
1307             if (shouldThrowIfCantSet)
1308                 throwTypeError(exec, scope, ASCIILiteral("cyclic __proto__ value"));
1309             return false;
1310         }
1311         // FIXME: The specification currently says we should check if the [[GetPrototypeOf]] internal method of nextPrototype
1312         // is not the ordinary object internal method. However, we currently restrict this to Proxy objects as it would allow
1313         // for cycles with certain HTML objects (WindowProxy, Location) otherwise.
1314         // https://bugs.webkit.org/show_bug.cgi?id=161534
1315         if (UNLIKELY(asObject(nextPrototype)->type() == ProxyObjectType))
1316             break; // We're done. Set the prototype.
1317         nextPrototype = asObject(nextPrototype)->getPrototypeDirect();
1318     }
1319     setPrototypeDirect(vm, prototype);
1320     return true;
1321 }
1322
1323 bool JSObject::setPrototype(JSObject* object, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1324 {
1325     return object->setPrototypeWithCycleCheck(exec->vm(), exec, prototype, shouldThrowIfCantSet);
1326 }
1327
1328 JSValue JSObject::getPrototype(JSObject* object, ExecState*)
1329 {
1330     return object->getPrototypeDirect();
1331 }
1332
1333 bool JSObject::setPrototype(VM& vm, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1334 {
1335     return methodTable(vm)->setPrototype(this, exec, prototype, shouldThrowIfCantSet);
1336 }
1337
1338 bool JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter, unsigned attributes)
1339 {
1340     PropertyDescriptor descriptor;
1341     descriptor.setGetter(getter);
1342
1343     ASSERT(attributes & Accessor);
1344     if (!(attributes & ReadOnly))
1345         descriptor.setConfigurable(true);
1346     if (!(attributes & DontEnum))
1347         descriptor.setEnumerable(true);
1348
1349     return defineOwnProperty(this, exec, propertyName, descriptor, false);
1350 }
1351
1352 bool JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter, unsigned attributes)
1353 {
1354     PropertyDescriptor descriptor;
1355     descriptor.setSetter(setter);
1356
1357     ASSERT(attributes & Accessor);
1358     if (!(attributes & ReadOnly))
1359         descriptor.setConfigurable(true);
1360     if (!(attributes & DontEnum))
1361         descriptor.setEnumerable(true);
1362
1363     return defineOwnProperty(this, exec, propertyName, descriptor, false);
1364 }
1365
1366 bool JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
1367 {
1368     ASSERT(value.isGetterSetter() && (attributes & Accessor));
1369
1370     if (Optional<uint32_t> index = parseIndex(propertyName))
1371         return putDirectIndex(exec, index.value(), value, attributes, PutDirectIndexLikePutDirect);
1372
1373     return putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes);
1374 }
1375
1376 bool JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1377 {
1378     ASSERT(!parseIndex(propertyName));
1379
1380     PutPropertySlot slot(this);
1381     bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1382
1383     ASSERT(slot.type() == PutPropertySlot::NewProperty);
1384
1385     Structure* structure = this->structure(vm);
1386     if (attributes & ReadOnly)
1387         structure->setContainsReadOnlyProperties();
1388     structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1389     return result;
1390 }
1391
1392 bool JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1393 {
1394     PutPropertySlot slot(this);
1395     bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1396
1397     Structure* structure = this->structure(vm);
1398     if (attributes & ReadOnly)
1399         structure->setContainsReadOnlyProperties();
1400
1401     structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
1402     return result;
1403 }
1404
1405 // HasProperty(O, P) from Section 7.3.10 of the spec.
1406 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasproperty
1407 bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
1408 {
1409     return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1410 }
1411
1412 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
1413 {
1414     return hasPropertyGeneric(exec, propertyName, PropertySlot::InternalMethodType::HasProperty);
1415 }
1416
1417 bool JSObject::hasPropertyGeneric(ExecState* exec, PropertyName propertyName, PropertySlot::InternalMethodType internalMethodType) const
1418 {
1419     PropertySlot slot(this, internalMethodType);
1420     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1421 }
1422
1423 bool JSObject::hasPropertyGeneric(ExecState* exec, unsigned propertyName, PropertySlot::InternalMethodType internalMethodType) const
1424 {
1425     PropertySlot slot(this, internalMethodType);
1426     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
1427 }
1428
1429 // ECMA 8.6.2.5
1430 bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
1431 {
1432     JSObject* thisObject = jsCast<JSObject*>(cell);
1433     VM& vm = exec->vm();
1434     
1435     if (Optional<uint32_t> index = parseIndex(propertyName))
1436         return thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, index.value());
1437
1438     unsigned attributes;
1439
1440     if (!thisObject->staticPropertiesReified()) {
1441         if (auto* entry = thisObject->findPropertyHashEntry(propertyName)) {
1442             // If the static table contains a non-configurable (DontDelete) property then we can return early;
1443             // if there is a property in the storage array it too must be non-configurable (the language does
1444             // not allow repacement of a non-configurable property with a configurable one).
1445             if (entry->attributes() & DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
1446                 ASSERT(!isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes)) || attributes & DontDelete);
1447                 return false;
1448             }
1449             thisObject->reifyAllStaticProperties(exec);
1450         }
1451     }
1452
1453     Structure* structure = thisObject->structure(vm);
1454
1455     bool propertyIsPresent = isValidOffset(structure->get(vm, propertyName, attributes));
1456     if (propertyIsPresent) {
1457         if (attributes & DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable)
1458             return false;
1459
1460         PropertyOffset offset;
1461         if (structure->isUncacheableDictionary())
1462             offset = structure->removePropertyWithoutTransition(vm, propertyName);
1463         else
1464             thisObject->setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
1465
1466         if (offset != invalidOffset)
1467             thisObject->putDirectUndefined(offset);
1468     }
1469
1470     return true;
1471 }
1472
1473 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
1474 {
1475     JSObject* thisObject = jsCast<JSObject*>(cell);
1476     
1477     if (i > MAX_ARRAY_INDEX)
1478         return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i));
1479     
1480     switch (thisObject->indexingType()) {
1481     case ALL_BLANK_INDEXING_TYPES:
1482     case ALL_UNDECIDED_INDEXING_TYPES:
1483         return true;
1484         
1485     case ALL_INT32_INDEXING_TYPES:
1486     case ALL_CONTIGUOUS_INDEXING_TYPES: {
1487         Butterfly* butterfly = thisObject->butterfly();
1488         if (i >= butterfly->vectorLength())
1489             return true;
1490         butterfly->contiguous()[i].clear();
1491         return true;
1492     }
1493         
1494     case ALL_DOUBLE_INDEXING_TYPES: {
1495         Butterfly* butterfly = thisObject->butterfly();
1496         if (i >= butterfly->vectorLength())
1497             return true;
1498         butterfly->contiguousDouble()[i] = PNaN;
1499         return true;
1500     }
1501         
1502     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
1503         ArrayStorage* storage = thisObject->m_butterfly.get()->arrayStorage();
1504         
1505         if (i < storage->vectorLength()) {
1506             WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
1507             if (valueSlot) {
1508                 valueSlot.clear();
1509                 --storage->m_numValuesInVector;
1510             }
1511         } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
1512             SparseArrayValueMap::iterator it = map->find(i);
1513             if (it != map->notFound()) {
1514                 if (it->value.attributes & DontDelete)
1515                     return false;
1516                 map->remove(it);
1517             }
1518         }
1519         
1520         return true;
1521     }
1522         
1523     default:
1524         RELEASE_ASSERT_NOT_REACHED();
1525         return false;
1526     }
1527 }
1528
1529 enum class TypeHintMode { TakesHint, DoesNotTakeHint };
1530
1531 template<TypeHintMode mode = TypeHintMode::DoesNotTakeHint>
1532 static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSObject* object, PropertyName propertyName, PreferredPrimitiveType hint)
1533 {
1534     VM& vm = exec->vm();
1535     auto scope = DECLARE_THROW_SCOPE(vm);
1536
1537     JSValue function = object->get(exec, propertyName);
1538     RETURN_IF_EXCEPTION(scope, scope.exception());
1539     if (function.isUndefined() && mode == TypeHintMode::TakesHint)
1540         return JSValue();
1541     CallData callData;
1542     CallType callType = getCallData(function, callData);
1543     if (callType == CallType::None)
1544         return scope.exception();
1545
1546     MarkedArgumentBuffer callArgs;
1547     if (mode == TypeHintMode::TakesHint) {
1548         JSString* hintString = nullptr;
1549         switch (hint) {
1550         case NoPreference:
1551             hintString = vm.smallStrings.defaultString();
1552             break;
1553         case PreferNumber:
1554             hintString = vm.smallStrings.numberString();
1555             break;
1556         case PreferString:
1557             hintString = vm.smallStrings.stringString();
1558             break;
1559         }
1560         callArgs.append(hintString);
1561     }
1562
1563     JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), callArgs);
1564     RETURN_IF_EXCEPTION(scope, scope.exception());
1565     ASSERT(!result.isGetterSetter());
1566     if (result.isObject())
1567         return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, scope, ASCIILiteral("Symbol.toPrimitive returned an object"));
1568     return result;
1569 }
1570
1571 // ECMA 7.1.1
1572 JSValue JSObject::ordinaryToPrimitive(ExecState* exec, PreferredPrimitiveType hint) const
1573 {
1574     VM& vm = exec->vm();
1575     auto scope = DECLARE_THROW_SCOPE(vm);
1576
1577     // Make sure that whatever default value methods there are on object's prototype chain are
1578     // being watched.
1579     this->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(vm);
1580
1581     JSValue value;
1582     if (hint == PreferString) {
1583         value = callToPrimitiveFunction(exec, this, exec->propertyNames().toString, hint);
1584         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1585         if (value)
1586             return value;
1587         value = callToPrimitiveFunction(exec, this, exec->propertyNames().valueOf, hint);
1588         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1589         if (value)
1590             return value;
1591     } else {
1592         value = callToPrimitiveFunction(exec, this, exec->propertyNames().valueOf, hint);
1593         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1594         if (value)
1595             return value;
1596         value = callToPrimitiveFunction(exec, this, exec->propertyNames().toString, hint);
1597         ASSERT(!scope.exception() || scope.exception() == value.asCell());
1598         if (value)
1599             return value;
1600     }
1601
1602     ASSERT(!scope.exception());
1603
1604     return throwTypeError(exec, scope, ASCIILiteral("No default value"));
1605 }
1606
1607 JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
1608 {
1609     return object->ordinaryToPrimitive(exec, hint);
1610 }
1611
1612 JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
1613 {
1614     JSValue value = callToPrimitiveFunction<TypeHintMode::TakesHint>(exec, this, exec->propertyNames().toPrimitiveSymbol, preferredType);
1615     if (value)
1616         return value;
1617
1618     return this->methodTable(exec->vm())->defaultValue(this, exec, preferredType);
1619 }
1620
1621 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
1622 {
1623     result = toPrimitive(exec, PreferNumber);
1624     number = result.toNumber(exec);
1625     return !result.isString();
1626 }
1627
1628 bool JSObject::getOwnStaticPropertySlot(VM& vm, PropertyName propertyName, PropertySlot& slot)
1629 {
1630     for (auto* info = classInfo(); info; info = info->parentClass) {
1631         if (auto* table = info->staticPropHashTable) {
1632             if (getStaticPropertySlotFromTable(vm, *table, this, propertyName, slot))
1633                 return true;
1634         }
1635     }
1636     return false;
1637 }
1638
1639 const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName) const
1640 {
1641     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
1642         if (const HashTable* propHashTable = info->staticPropHashTable) {
1643             if (const HashTableValue* entry = propHashTable->entry(propertyName))
1644                 return entry;
1645         }
1646     }
1647     return 0;
1648 }
1649
1650 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue)
1651 {
1652     VM& vm = exec->vm();
1653     auto scope = DECLARE_THROW_SCOPE(vm);
1654     
1655
1656     if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) {
1657         CallData callData;
1658         CallType callType = JSC::getCallData(hasInstanceValue, callData);
1659         if (callType == CallType::None) {
1660             throwException(exec, scope, createInvalidInstanceofParameterErrorhasInstanceValueNotFunction(exec, this));
1661             return false;
1662         }
1663
1664         MarkedArgumentBuffer args;
1665         args.append(value);
1666         JSValue result = call(exec, hasInstanceValue, callType, callData, this, args);
1667         return result.toBoolean(exec);
1668     }
1669
1670     TypeInfo info = structure(vm)->typeInfo();
1671     if (info.implementsDefaultHasInstance())
1672         return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
1673     if (info.implementsHasInstance())
1674         return methodTable(vm)->customHasInstance(this, exec, value);
1675     throwException(exec, scope, createInvalidInstanceofParameterErrorNotFunction(exec, this));
1676     return false;
1677 }
1678
1679 bool JSObject::hasInstance(ExecState* exec, JSValue value)
1680 {
1681     JSValue hasInstanceValue = get(exec, exec->propertyNames().hasInstanceSymbol);
1682
1683     return hasInstance(exec, value, hasInstanceValue);
1684 }
1685
1686 bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
1687 {
1688     VM& vm = exec->vm();
1689     auto scope = DECLARE_THROW_SCOPE(vm);
1690
1691     if (!value.isObject())
1692         return false;
1693
1694     if (!proto.isObject()) {
1695         throwTypeError(exec, scope, ASCIILiteral("instanceof called on an object with an invalid prototype property."));
1696         return false;
1697     }
1698
1699     JSObject* object = asObject(value);
1700     while (true) {
1701         JSValue objectValue = object->getPrototype(vm, exec);
1702         RETURN_IF_EXCEPTION(scope, false);
1703         if (!objectValue.isObject())
1704             return false;
1705         object = asObject(objectValue);
1706         if (proto == object)
1707             return true;
1708     }
1709     ASSERT_NOT_REACHED();
1710 }
1711
1712 EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState* exec)
1713 {
1714     JSValue value = exec->uncheckedArgument(0);
1715     JSValue proto = exec->uncheckedArgument(1);
1716
1717     return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
1718 }
1719
1720 void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1721 {
1722     VM& vm = exec->vm();
1723     auto scope = DECLARE_THROW_SCOPE(vm);
1724     object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, mode);
1725     RETURN_IF_EXCEPTION(scope, void());
1726
1727     JSValue nextProto = object->getPrototype(vm, exec);
1728     RETURN_IF_EXCEPTION(scope, void());
1729     if (nextProto.isNull())
1730         return;
1731
1732     JSObject* prototype = asObject(nextProto);
1733     while(1) {
1734         if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
1735             prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
1736             break;
1737         }
1738         prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
1739         RETURN_IF_EXCEPTION(scope, void());
1740         nextProto = prototype->getPrototype(vm, exec);
1741         RETURN_IF_EXCEPTION(scope, void());
1742         if (nextProto.isNull())
1743             break;
1744         prototype = asObject(nextProto);
1745     }
1746 }
1747
1748 void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1749 {
1750     if (!mode.includeJSObjectProperties()) {
1751         // We still have to get non-indexed properties from any subclasses of JSObject that have them.
1752         object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
1753         return;
1754     }
1755
1756     if (propertyNames.includeStringProperties()) {
1757         // Add numeric properties first. That appears to be the accepted convention.
1758         // FIXME: Filling PropertyNameArray with an identifier for every integer
1759         // is incredibly inefficient for large arrays. We need a different approach,
1760         // which almost certainly means a different structure for PropertyNameArray.
1761         switch (object->indexingType()) {
1762         case ALL_BLANK_INDEXING_TYPES:
1763         case ALL_UNDECIDED_INDEXING_TYPES:
1764             break;
1765             
1766         case ALL_INT32_INDEXING_TYPES:
1767         case ALL_CONTIGUOUS_INDEXING_TYPES: {
1768             Butterfly* butterfly = object->butterfly();
1769             unsigned usedLength = butterfly->publicLength();
1770             for (unsigned i = 0; i < usedLength; ++i) {
1771                 if (!butterfly->contiguous()[i])
1772                     continue;
1773                 propertyNames.add(i);
1774             }
1775             break;
1776         }
1777             
1778         case ALL_DOUBLE_INDEXING_TYPES: {
1779             Butterfly* butterfly = object->butterfly();
1780             unsigned usedLength = butterfly->publicLength();
1781             for (unsigned i = 0; i < usedLength; ++i) {
1782                 double value = butterfly->contiguousDouble()[i];
1783                 if (value != value)
1784                     continue;
1785                 propertyNames.add(i);
1786             }
1787             break;
1788         }
1789             
1790         case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
1791             ArrayStorage* storage = object->m_butterfly.get()->arrayStorage();
1792             
1793             unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
1794             for (unsigned i = 0; i < usedVectorLength; ++i) {
1795                 if (storage->m_vector[i])
1796                     propertyNames.add(i);
1797             }
1798             
1799             if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
1800                 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
1801                 keys.reserveInitialCapacity(map->size());
1802                 
1803                 SparseArrayValueMap::const_iterator end = map->end();
1804                 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
1805                     if (mode.includeDontEnumProperties() || !(it->value.attributes & DontEnum))
1806                         keys.uncheckedAppend(static_cast<unsigned>(it->key));
1807                 }
1808                 
1809                 std::sort(keys.begin(), keys.end());
1810                 for (unsigned i = 0; i < keys.size(); ++i)
1811                     propertyNames.add(keys[i]);
1812             }
1813             break;
1814         }
1815             
1816         default:
1817             RELEASE_ASSERT_NOT_REACHED();
1818         }
1819     }
1820
1821     object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
1822 }
1823
1824 void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
1825 {
1826     if (!object->staticPropertiesReified())
1827         getClassPropertyNames(exec, object->classInfo(), propertyNames, mode);
1828
1829     if (!mode.includeJSObjectProperties())
1830         return;
1831     
1832     VM& vm = exec->vm();
1833     object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
1834 }
1835
1836 double JSObject::toNumber(ExecState* exec) const
1837 {
1838     VM& vm = exec->vm();
1839     auto scope = DECLARE_THROW_SCOPE(vm);
1840     JSValue primitive = toPrimitive(exec, PreferNumber);
1841     RETURN_IF_EXCEPTION(scope, 0.0); // should be picked up soon in Nodes.cpp
1842     return primitive.toNumber(exec);
1843 }
1844
1845 JSString* JSObject::toString(ExecState* exec) const
1846 {
1847     VM& vm = exec->vm();
1848     auto scope = DECLARE_THROW_SCOPE(vm);
1849     JSValue primitive = toPrimitive(exec, PreferString);
1850     RETURN_IF_EXCEPTION(scope, jsEmptyString(exec));
1851     return primitive.toString(exec);
1852 }
1853
1854 JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode)
1855 {
1856     return jsCast<JSObject*>(cell);
1857 }
1858
1859 void JSObject::seal(VM& vm)
1860 {
1861     if (isSealed(vm))
1862         return;
1863     enterDictionaryIndexingMode(vm);
1864     setStructure(vm, Structure::sealTransition(vm, structure(vm)));
1865 }
1866
1867 void JSObject::freeze(VM& vm)
1868 {
1869     if (isFrozen(vm))
1870         return;
1871     enterDictionaryIndexingMode(vm);
1872     setStructure(vm, Structure::freezeTransition(vm, structure(vm)));
1873 }
1874
1875 bool JSObject::preventExtensions(JSObject* object, ExecState* exec)
1876 {
1877     if (!object->isStructureExtensible()) {
1878         // We've already set the internal [[PreventExtensions]] field to false.
1879         // We don't call the methodTable isExtensible here because it's not defined
1880         // that way in the specification. We are just doing an optimization here.
1881         return true;
1882     }
1883
1884     VM& vm = exec->vm();
1885     object->enterDictionaryIndexingMode(vm);
1886     object->setStructure(vm, Structure::preventExtensionsTransition(vm, object->structure(vm)));
1887     return true;
1888 }
1889
1890 bool JSObject::isExtensible(JSObject* obj, ExecState*)
1891 {
1892     return obj->isExtensibleImpl();
1893 }
1894
1895 bool JSObject::isExtensible(ExecState* exec)
1896
1897     VM& vm = exec->vm();
1898     return methodTable(vm)->isExtensible(this, exec);
1899 }
1900
1901 void JSObject::reifyAllStaticProperties(ExecState* exec)
1902 {
1903     ASSERT(!staticPropertiesReified());
1904     VM& vm = exec->vm();
1905
1906     // If this object's ClassInfo has no static properties, then nothing to reify!
1907     // We can safely set the flag to avoid the expensive check again in the future.
1908     if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags())) {
1909         structure(vm)->setStaticPropertiesReified(true);
1910         return;
1911     }
1912
1913     if (!structure(vm)->isDictionary())
1914         setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure(vm)));
1915
1916     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
1917         const HashTable* hashTable = info->staticPropHashTable;
1918         if (!hashTable)
1919             continue;
1920
1921         for (auto& value : *hashTable) {
1922             unsigned attributes;
1923             auto key = Identifier::fromString(&vm, value.m_key);
1924             PropertyOffset offset = getDirectOffset(vm, key, attributes);
1925             if (!isValidOffset(offset))
1926                 reifyStaticProperty(vm, key, value, *this);
1927         }
1928     }
1929
1930     structure(vm)->setStaticPropertiesReified(true);
1931 }
1932
1933 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue getterSetter, unsigned attributes, PropertyOffset offset)
1934 {
1935     if (structure()->isUncacheableDictionary()) {
1936         slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
1937         return;
1938     }
1939
1940     // This access is cacheable because Structure requires an attributeChangedTransition
1941     // if this property stops being an accessor.
1942     slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
1943 }
1944
1945 bool JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
1946 {
1947     VM& vm = exec->vm();
1948     auto map = m_butterfly.get()->arrayStorage()->m_sparseMap.get();
1949
1950     if (descriptor.isDataDescriptor()) {
1951         if (descriptor.value())
1952             entryInMap->set(vm, map, descriptor.value());
1953         else if (oldDescriptor.isAccessorDescriptor())
1954             entryInMap->set(vm, map, jsUndefined());
1955         entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
1956         return true;
1957     }
1958
1959     if (descriptor.isAccessorDescriptor()) {
1960         JSObject* getter = 0;
1961         if (descriptor.getterPresent())
1962             getter = descriptor.getterObject();
1963         else if (oldDescriptor.isAccessorDescriptor())
1964             getter = oldDescriptor.getterObject();
1965         JSObject* setter = 0;
1966         if (descriptor.setterPresent())
1967             setter = descriptor.setterObject();
1968         else if (oldDescriptor.isAccessorDescriptor())
1969             setter = oldDescriptor.setterObject();
1970
1971         GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
1972         if (getter)
1973             accessor->setGetter(vm, exec->lexicalGlobalObject(), getter);
1974         if (setter)
1975             accessor->setSetter(vm, exec->lexicalGlobalObject(), setter);
1976
1977         entryInMap->set(vm, map, accessor);
1978         entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
1979         return true;
1980     }
1981
1982     ASSERT(descriptor.isGenericDescriptor());
1983     entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
1984     return true;
1985 }
1986
1987 // Defined in ES5.1 8.12.9
1988 bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException)
1989 {
1990     ASSERT(index <= MAX_ARRAY_INDEX);
1991
1992     if (!inSparseIndexingMode()) {
1993         // Fast case: we're putting a regular property to a regular array
1994         // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
1995         // however if the property currently exists missing attributes will override from their current 'true'
1996         // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
1997         if (!descriptor.attributes() && descriptor.value()) {
1998             ASSERT(!descriptor.isAccessorDescriptor());
1999             return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
2000         }
2001         
2002         ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->vm());
2003     }
2004
2005     if (descriptor.attributes() & (ReadOnly | Accessor))
2006         notifyPresenceOfIndexedAccessors(exec->vm());
2007
2008     SparseArrayValueMap* map = m_butterfly.get()->arrayStorage()->m_sparseMap.get();
2009     RELEASE_ASSERT(map);
2010
2011     // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
2012     SparseArrayValueMap::AddResult result = map->add(this, index);
2013     SparseArrayEntry* entryInMap = &result.iterator->value;
2014
2015     // 2. Let extensible be the value of the [[Extensible]] internal property of O.
2016     // 3. If current is undefined and extensible is false, then Reject.
2017     // 4. If current is undefined and extensible is true, then
2018     if (result.isNewEntry) {
2019         if (!isStructureExtensible()) {
2020             map->remove(result.iterator);
2021             return reject(exec, throwException, "Attempting to define property on object that is not extensible.");
2022         }
2023
2024         // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
2025         // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
2026         // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
2027         // created property is set to its default value.
2028         // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
2029         // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
2030         // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
2031         // is set to its default value.
2032         // 4.c. Return true.
2033
2034         PropertyDescriptor defaults;
2035         entryInMap->setWithoutWriteBarrier(jsUndefined());
2036         entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
2037         entryInMap->get(defaults);
2038
2039         putIndexedDescriptor(exec, entryInMap, descriptor, defaults);
2040         Butterfly* butterfly = m_butterfly.get();
2041         if (index >= butterfly->arrayStorage()->length())
2042             butterfly->arrayStorage()->setLength(index + 1);
2043         return true;
2044     }
2045
2046     // 5. Return true, if every field in Desc is absent.
2047     // 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).
2048     PropertyDescriptor current;
2049     entryInMap->get(current);
2050     if (descriptor.isEmpty() || descriptor.equalTo(exec, current))
2051         return true;
2052
2053     // 7. If the [[Configurable]] field of current is false then
2054     if (!current.configurable()) {
2055         // 7.a. Reject, if the [[Configurable]] field of Desc is true.
2056         if (descriptor.configurablePresent() && descriptor.configurable())
2057             return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
2058         // 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.
2059         if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
2060             return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
2061     }
2062
2063     // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
2064     if (!descriptor.isGenericDescriptor()) {
2065         // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
2066         if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
2067             // 9.a. Reject, if the [[Configurable]] field of current is false.
2068             if (!current.configurable())
2069                 return reject(exec, throwException, UnconfigurablePropertyChangeAccessMechanismError);
2070             // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
2071             // data property to an accessor property. Preserve the existing values of the converted property's
2072             // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
2073             // their default values.
2074             // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
2075             // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
2076             // attributes and set the rest of the property's attributes to their default values.
2077         } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
2078             // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
2079             // 10.a. If the [[Configurable]] field of current is false, then
2080             if (!current.configurable() && !current.writable()) {
2081                 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
2082                 if (descriptor.writable())
2083                     return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
2084                 // 10.a.ii. If the [[Writable]] field of current is false, then
2085                 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
2086                 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
2087                     return reject(exec, throwException, "Attempting to change value of a readonly property.");
2088             }
2089             // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
2090         } else {
2091             ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
2092             // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
2093             if (!current.configurable()) {
2094                 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
2095                 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
2096                     return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property.");
2097                 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
2098                 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
2099                     return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property.");
2100             }
2101         }
2102     }
2103
2104     // 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.
2105     putIndexedDescriptor(exec, entryInMap, descriptor, current);
2106     // 13. Return true.
2107     return true;
2108 }
2109
2110 SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm)
2111 {
2112     SparseArrayValueMap* result = SparseArrayValueMap::create(vm);
2113     arrayStorage()->m_sparseMap.set(vm, this, result);
2114     return result;
2115 }
2116
2117 void JSObject::deallocateSparseIndexMap()
2118 {
2119     if (ArrayStorage* arrayStorage = arrayStorageOrNull())
2120         arrayStorage->m_sparseMap.clear();
2121 }
2122
2123 bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2124 {
2125     for (JSObject* current = this; ;) {
2126         // This has the same behavior with respect to prototypes as JSObject::put(). It only
2127         // allows a prototype to intercept a put if (a) the prototype declares the property
2128         // we're after rather than intercepting it via an override of JSObject::put(), and
2129         // (b) that property is declared as ReadOnly or Accessor.
2130         
2131         ArrayStorage* storage = current->arrayStorageOrNull();
2132         if (storage && storage->m_sparseMap) {
2133             SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
2134             if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (Accessor | ReadOnly))) {
2135                 putResult = iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
2136                 return true;
2137             }
2138         }
2139
2140         if (current->type() == ProxyObjectType) {
2141             ProxyObject* proxy = jsCast<ProxyObject*>(current);
2142             putResult = proxy->putByIndexCommon(exec, thisValue, i, value, shouldThrow);
2143             return true;
2144         }
2145         
2146         JSValue prototypeValue = current->getPrototypeDirect();
2147         if (prototypeValue.isNull())
2148             return false;
2149         
2150         current = asObject(prototypeValue);
2151     }
2152 }
2153
2154 bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, bool& putResult)
2155 {
2156     JSValue prototypeValue = getPrototypeDirect();
2157     if (prototypeValue.isNull())
2158         return false;
2159     
2160     return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow, putResult);
2161 }
2162
2163 template<IndexingType indexingShape>
2164 bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value)
2165 {
2166     VM& vm = exec->vm();
2167     auto scope = DECLARE_THROW_SCOPE(vm);
2168
2169     ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
2170     ASSERT(!indexingShouldBeSparse());
2171
2172     Butterfly* butterfly = m_butterfly.get();
2173     
2174     // For us to get here, the index is either greater than the public length, or greater than
2175     // or equal to the vector length.
2176     ASSERT(i >= butterfly->vectorLength());
2177     
2178     if (i > MAX_STORAGE_VECTOR_INDEX
2179         || (i >= MIN_SPARSE_ARRAY_INDEX && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly)))
2180         || indexIsSufficientlyBeyondLengthForSparseMap(i, butterfly->vectorLength())) {
2181         ASSERT(i <= MAX_ARRAY_INDEX);
2182         ensureArrayStorageSlow(vm);
2183         SparseArrayValueMap* map = allocateSparseIndexMap(vm);
2184         bool result = map->putEntry(exec, this, i, value, false);
2185         ASSERT(i >= arrayStorage()->length());
2186         arrayStorage()->setLength(i + 1);
2187         return result;
2188     }
2189
2190     if (!ensureLength(vm, i + 1)) {
2191         throwOutOfMemoryError(exec, scope);
2192         return false;
2193     }
2194     butterfly = m_butterfly.get();
2195
2196     RELEASE_ASSERT(i < butterfly->vectorLength());
2197     switch (indexingShape) {
2198     case Int32Shape:
2199         ASSERT(value.isInt32());
2200         butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value);
2201         return true;
2202         
2203     case DoubleShape: {
2204         ASSERT(value.isNumber());
2205         double valueAsDouble = value.asNumber();
2206         ASSERT(valueAsDouble == valueAsDouble);
2207         butterfly->contiguousDouble()[i] = valueAsDouble;
2208         return true;
2209     }
2210         
2211     case ContiguousShape:
2212         butterfly->contiguous()[i].set(vm, this, value);
2213         return true;
2214         
2215     default:
2216         CRASH();
2217         return false;
2218     }
2219 }
2220
2221 // Explicit instantiations needed by JSArray.cpp.
2222 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(ExecState*, unsigned, JSValue);
2223 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(ExecState*, unsigned, JSValue);
2224 template bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(ExecState*, unsigned, JSValue);
2225
2226 bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
2227 {
2228     VM& vm = exec->vm();
2229     auto scope = DECLARE_THROW_SCOPE(vm);
2230
2231     // i should be a valid array index that is outside of the current vector.
2232     ASSERT(i <= MAX_ARRAY_INDEX);
2233     ASSERT(i >= storage->vectorLength());
2234     
2235     SparseArrayValueMap* map = storage->m_sparseMap.get();
2236     
2237     // First, handle cases where we don't currently have a sparse map.
2238     if (LIKELY(!map)) {
2239         // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
2240         ASSERT(isStructureExtensible());
2241     
2242         // Update m_length if necessary.
2243         if (i >= storage->length())
2244             storage->setLength(i + 1);
2245
2246         // Check that it is sensible to still be using a vector, and then try to grow the vector.
2247         if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()) 
2248             && isDenseEnoughForVector(i, storage->m_numValuesInVector)
2249             && increaseVectorLength(vm, i + 1))) {
2250             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2251             storage = arrayStorage();
2252             storage->m_vector[i].set(vm, this, value);
2253             ++storage->m_numValuesInVector;
2254             return true;
2255         }
2256         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2257         map = allocateSparseIndexMap(vm);
2258         return map->putEntry(exec, this, i, value, shouldThrow);
2259     }
2260
2261     // Update m_length if necessary.
2262     unsigned length = storage->length();
2263     if (i >= length) {
2264         // Prohibit growing the array if length is not writable.
2265         if (map->lengthIsReadOnly() || !isStructureExtensible()) {
2266             if (shouldThrow)
2267                 throwTypeError(exec, scope, ReadonlyPropertyWriteError);
2268             return false;
2269         }
2270         length = i + 1;
2271         storage->setLength(length);
2272     }
2273
2274     // We are currently using a map - check whether we still want to be doing so.
2275     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2276     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2277     if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
2278         return map->putEntry(exec, this, i, value, shouldThrow);
2279
2280     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2281     storage = arrayStorage();
2282     storage->m_numValuesInVector = numValuesInArray;
2283
2284     // Copy all values from the map into the vector, and delete the map.
2285     WriteBarrier<Unknown>* vector = storage->m_vector;
2286     SparseArrayValueMap::const_iterator end = map->end();
2287     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2288         vector[it->key].set(vm, this, it->value.getNonSparseMode());
2289     deallocateSparseIndexMap();
2290
2291     // Store the new property into the vector.
2292     WriteBarrier<Unknown>& valueSlot = vector[i];
2293     if (!valueSlot)
2294         ++storage->m_numValuesInVector;
2295     valueSlot.set(vm, this, value);
2296     return true;
2297 }
2298
2299 bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
2300 {
2301     VM& vm = exec->vm();
2302
2303     // i should be a valid array index that is outside of the current vector.
2304     ASSERT(i <= MAX_ARRAY_INDEX);
2305     
2306     switch (indexingType()) {
2307     case ALL_BLANK_INDEXING_TYPES: {
2308         if (indexingShouldBeSparse()) {
2309             return putByIndexBeyondVectorLengthWithArrayStorage(
2310                 exec, i, value, shouldThrow,
2311                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2312         }
2313         if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
2314             return putByIndexBeyondVectorLengthWithArrayStorage(
2315                 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
2316         }
2317         if (structure(vm)->needsSlowPutIndexing()) {
2318             // Convert the indexing type to the SlowPutArrayStorage and retry.
2319             createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, 0, i + 1));
2320             return putByIndex(this, exec, i, value, shouldThrow);
2321         }
2322         
2323         createInitialForValueAndSet(vm, i, value);
2324         return true;
2325     }
2326         
2327     case ALL_UNDECIDED_INDEXING_TYPES: {
2328         CRASH();
2329         break;
2330     }
2331         
2332     case ALL_INT32_INDEXING_TYPES:
2333         return putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2334         
2335     case ALL_DOUBLE_INDEXING_TYPES:
2336         return putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2337         
2338     case ALL_CONTIGUOUS_INDEXING_TYPES:
2339         return putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2340         
2341     case NonArrayWithSlowPutArrayStorage:
2342     case ArrayWithSlowPutArrayStorage: {
2343         // No own property present in the vector, but there might be in the sparse map!
2344         SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
2345         bool putResult = false;
2346         if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow, putResult))
2347             return putResult;
2348         FALLTHROUGH;
2349     }
2350
2351     case NonArrayWithArrayStorage:
2352     case ArrayWithArrayStorage:
2353         return putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
2354         
2355     default:
2356         RELEASE_ASSERT_NOT_REACHED();
2357     }
2358     return false;
2359 }
2360
2361 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
2362 {
2363     VM& vm = exec->vm();
2364     
2365     // i should be a valid array index that is outside of the current vector.
2366     ASSERT(hasAnyArrayStorage(indexingType()));
2367     ASSERT(arrayStorage() == storage);
2368     ASSERT(i >= storage->vectorLength() || attributes);
2369     ASSERT(i <= MAX_ARRAY_INDEX);
2370
2371     SparseArrayValueMap* map = storage->m_sparseMap.get();
2372
2373     // First, handle cases where we don't currently have a sparse map.
2374     if (LIKELY(!map)) {
2375         // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
2376         ASSERT(isStructureExtensible());
2377     
2378         // Update m_length if necessary.
2379         if (i >= storage->length())
2380             storage->setLength(i + 1);
2381
2382         // Check that it is sensible to still be using a vector, and then try to grow the vector.
2383         if (LIKELY(
2384                 !attributes
2385                 && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
2386                 && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
2387                 && increaseVectorLength(vm, i + 1)) {
2388             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
2389             storage = arrayStorage();
2390             storage->m_vector[i].set(vm, this, value);
2391             ++storage->m_numValuesInVector;
2392             return true;
2393         }
2394         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
2395         map = allocateSparseIndexMap(exec->vm());
2396         return map->putDirect(exec, this, i, value, attributes, mode);
2397     }
2398
2399     // Update m_length if necessary.
2400     unsigned length = storage->length();
2401     if (i >= length) {
2402         if (mode != PutDirectIndexLikePutDirect) {
2403             // Prohibit growing the array if length is not writable.
2404             if (map->lengthIsReadOnly())
2405                 return reject(exec, mode == PutDirectIndexShouldThrow, ReadonlyPropertyWriteError);
2406             if (!isStructureExtensible())
2407                 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
2408         }
2409         length = i + 1;
2410         storage->setLength(length);
2411     }
2412
2413     // We are currently using a map - check whether we still want to be doing so.
2414     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
2415     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
2416     if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length))
2417         return map->putDirect(exec, this, i, value, attributes, mode);
2418
2419     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
2420     storage = arrayStorage();
2421     storage->m_numValuesInVector = numValuesInArray;
2422
2423     // Copy all values from the map into the vector, and delete the map.
2424     WriteBarrier<Unknown>* vector = storage->m_vector;
2425     SparseArrayValueMap::const_iterator end = map->end();
2426     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
2427         vector[it->key].set(vm, this, it->value.getNonSparseMode());
2428     deallocateSparseIndexMap();
2429
2430     // Store the new property into the vector.
2431     WriteBarrier<Unknown>& valueSlot = vector[i];
2432     if (!valueSlot)
2433         ++storage->m_numValuesInVector;
2434     valueSlot.set(vm, this, value);
2435     return true;
2436 }
2437
2438 bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
2439 {
2440     VM& vm = exec->vm();
2441
2442     // i should be a valid array index that is outside of the current vector.
2443     ASSERT(i <= MAX_ARRAY_INDEX);
2444     
2445     if (attributes & (ReadOnly | Accessor))
2446         notifyPresenceOfIndexedAccessors(vm);
2447     
2448     switch (indexingType()) {
2449     case ALL_BLANK_INDEXING_TYPES: {
2450         if (indexingShouldBeSparse() || attributes) {
2451             return putDirectIndexBeyondVectorLengthWithArrayStorage(
2452                 exec, i, value, attributes, mode,
2453                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2454         }
2455         if (i >= MIN_SPARSE_ARRAY_INDEX) {
2456             return putDirectIndexBeyondVectorLengthWithArrayStorage(
2457                 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
2458         }
2459         if (structure(vm)->needsSlowPutIndexing()) {
2460             ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, 0, i + 1));
2461             storage->m_vector[i].set(vm, this, value);
2462             storage->m_numValuesInVector++;
2463             return true;
2464         }
2465         
2466         createInitialForValueAndSet(vm, i, value);
2467         return true;
2468     }
2469         
2470     case ALL_UNDECIDED_INDEXING_TYPES: {
2471         convertUndecidedForValue(exec->vm(), value);
2472         // Reloop.
2473         return putDirectIndex(exec, i, value, attributes, mode);
2474     }
2475         
2476     case ALL_INT32_INDEXING_TYPES: {
2477         if (attributes) {
2478             if (i < m_butterfly.get()->vectorLength())
2479                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2480             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm));
2481         }
2482         if (!value.isInt32()) {
2483             convertInt32ForValue(vm, value);
2484             return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2485         }
2486         putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
2487         return true;
2488     }
2489         
2490     case ALL_DOUBLE_INDEXING_TYPES: {
2491         if (attributes) {
2492             if (i < m_butterfly.get()->vectorLength())
2493                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2494             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm));
2495         }
2496         if (!value.isNumber()) {
2497             convertDoubleToContiguous(vm);
2498             return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2499         }
2500         double valueAsDouble = value.asNumber();
2501         if (valueAsDouble != valueAsDouble) {
2502             convertDoubleToContiguous(vm);
2503             return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
2504         }
2505         putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
2506         return true;
2507     }
2508         
2509     case ALL_CONTIGUOUS_INDEXING_TYPES: {
2510         if (attributes) {
2511             if (i < m_butterfly.get()->vectorLength())
2512                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2513             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm));
2514         }
2515         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
2516         return true;
2517     }
2518
2519     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
2520         if (attributes) {
2521             if (i < m_butterfly.get()->vectorLength())
2522                 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
2523         }
2524         return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
2525         
2526     default:
2527         RELEASE_ASSERT_NOT_REACHED();
2528         return false;
2529     }
2530 }
2531
2532 bool JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2533 {
2534     GetterSetter* accessor = GetterSetter::create(vm, globalObject);
2535     JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
2536     accessor->setGetter(vm, globalObject, function);
2537     return putDirectNonIndexAccessor(vm, name, accessor, attributes);
2538 }
2539
2540 bool JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2541 {
2542     StringImpl* name = propertyName.publicName();
2543     if (!name)
2544         name = vm.propertyNames->anonymous.impl();
2545     ASSERT(name);
2546
2547     JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2548     return putDirect(vm, propertyName, function, attributes);
2549 }
2550
2551 JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2552 {
2553     StringImpl* name = propertyName.publicName();
2554     if (!name)
2555         name = vm.propertyNames->anonymous.impl();
2556     ASSERT(name);
2557     JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2558     putDirect(vm, propertyName, function, attributes);
2559     return function;
2560 }
2561
2562 JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
2563 {
2564     JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
2565     putDirectWithoutTransition(vm, propertyName, function, attributes);
2566     return function;
2567 }
2568
2569 void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
2570 {
2571     StringImpl* name = propertyName.publicName();
2572     if (!name)
2573         name = vm.propertyNames->anonymous.impl();
2574     ASSERT(name);
2575     JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
2576     putDirectWithoutTransition(vm, propertyName, function, attributes);
2577 }
2578
2579 // NOTE: This method is for ArrayStorage vectors.
2580 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
2581 {
2582     ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
2583
2584     unsigned increasedLength;
2585     unsigned maxInitLength = std::min(currentLength, 100000U);
2586
2587     if (desiredLength < maxInitLength)
2588         increasedLength = maxInitLength;
2589     else if (!currentVectorLength)
2590         increasedLength = std::max(desiredLength, lastArraySize);
2591     else {
2592         increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
2593     }
2594
2595     ASSERT(increasedLength >= desiredLength);
2596
2597     lastArraySize = std::min(increasedLength, FIRST_ARRAY_STORAGE_VECTOR_GROW);
2598
2599     return ArrayStorage::optimalVectorLength(
2600         indexBias, structure()->outOfLineCapacity(),
2601         std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH));
2602 }
2603
2604 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
2605 {
2606     unsigned indexBias = 0;
2607     unsigned vectorLength = 0;
2608     unsigned length = 0;
2609     
2610     if (hasIndexedProperties(indexingType())) {
2611         if (ArrayStorage* storage = arrayStorageOrNull())
2612             indexBias = storage->m_indexBias;
2613         vectorLength = m_butterfly.get()->vectorLength();
2614         length = m_butterfly.get()->publicLength();
2615     }
2616
2617     return getNewVectorLength(indexBias, vectorLength, length, desiredLength);
2618 }
2619
2620 template<IndexingType indexingShape>
2621 unsigned JSObject::countElements(Butterfly* butterfly)
2622 {
2623     unsigned numValues = 0;
2624     for (unsigned i = butterfly->publicLength(); i--;) {
2625         switch (indexingShape) {
2626         case Int32Shape:
2627         case ContiguousShape:
2628             if (butterfly->contiguous()[i])
2629                 numValues++;
2630             break;
2631             
2632         case DoubleShape: {
2633             double value = butterfly->contiguousDouble()[i];
2634             if (value == value)
2635                 numValues++;
2636             break;
2637         }
2638             
2639         default:
2640             CRASH();
2641         }
2642     }
2643     return numValues;
2644 }
2645
2646 unsigned JSObject::countElements()
2647 {
2648     switch (indexingType()) {
2649     case ALL_BLANK_INDEXING_TYPES:
2650     case ALL_UNDECIDED_INDEXING_TYPES:
2651         return 0;
2652         
2653     case ALL_INT32_INDEXING_TYPES:
2654         return countElements<Int32Shape>(butterfly());
2655         
2656     case ALL_DOUBLE_INDEXING_TYPES:
2657         return countElements<DoubleShape>(butterfly());
2658         
2659     case ALL_CONTIGUOUS_INDEXING_TYPES:
2660         return countElements<ContiguousShape>(butterfly());
2661         
2662     default:
2663         CRASH();
2664         return 0;
2665     }
2666 }
2667
2668 bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
2669 {
2670     ArrayStorage* storage = arrayStorage();
2671     
2672     unsigned vectorLength = storage->vectorLength();
2673     unsigned availableVectorLength = storage->availableVectorLength(structure(vm), vectorLength); 
2674     if (availableVectorLength >= newLength) {
2675         // The cell was already big enough for the desired length!
2676         for (unsigned i = vectorLength; i < availableVectorLength; ++i)
2677             storage->m_vector[i].clear();
2678         storage->setVectorLength(availableVectorLength);
2679         return true;
2680     }
2681     
2682     // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
2683     // to the vector. Callers have to account for that, because they can do it more efficiently.
2684     if (newLength > MAX_STORAGE_VECTOR_LENGTH)
2685         return false;
2686
2687     if (newLength >= MIN_SPARSE_ARRAY_INDEX
2688         && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector))
2689         return false;
2690
2691     unsigned indexBias = storage->m_indexBias;
2692     ASSERT(newLength > vectorLength);
2693     unsigned newVectorLength = getNewVectorLength(newLength);
2694
2695     // Fast case - there is no precapacity. In these cases a realloc makes sense.
2696     Structure* structure = this->structure(vm);
2697     if (LIKELY(!indexBias)) {
2698         DeferGC deferGC(vm.heap);
2699         Butterfly* newButterfly = storage->butterfly()->growArrayRight(
2700             vm, this, structure, structure->outOfLineCapacity(), true,
2701             ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
2702         if (!newButterfly)
2703             return false;
2704         for (unsigned i = vectorLength; i < newVectorLength; ++i)
2705             newButterfly->arrayStorage()->m_vector[i].clear();
2706         newButterfly->arrayStorage()->setVectorLength(newVectorLength);
2707         setButterflyWithoutChangingStructure(vm, newButterfly);
2708         return true;
2709     }
2710     
2711     // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
2712     DeferGC deferGC(vm.heap);
2713     unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
2714     Butterfly* newButterfly = storage->butterfly()->resizeArray(
2715         vm, this,
2716         structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
2717         newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
2718     if (!newButterfly)
2719         return false;
2720     for (unsigned i = vectorLength; i < newVectorLength; ++i)
2721         newButterfly->arrayStorage()->m_vector[i].clear();
2722     newButterfly->arrayStorage()->setVectorLength(newVectorLength);
2723     newButterfly->arrayStorage()->m_indexBias = newIndexBias;
2724     setButterflyWithoutChangingStructure(vm, newButterfly);
2725     return true;
2726 }
2727
2728 bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
2729 {
2730     Butterfly* butterfly = m_butterfly.get();
2731     
2732     ASSERT(length < MAX_ARRAY_INDEX);
2733     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2734     ASSERT(length > butterfly->vectorLength());
2735     
2736     unsigned oldVectorLength = butterfly->vectorLength();
2737     unsigned newVectorLength;
2738     
2739     Structure* structure = this->structure(vm);
2740     unsigned propertyCapacity = structure->outOfLineCapacity();
2741     
2742     unsigned availableOldLength =
2743         Butterfly::availableContiguousVectorLength(propertyCapacity, oldVectorLength);
2744     if (availableOldLength >= length) {
2745         // This is the case where someone else selected a vector length that caused internal
2746         // fragmentation. If we did our jobs right, this would never happen. But I bet we will mess
2747         // this up, so this defense should stay.
2748         newVectorLength = availableOldLength;
2749     } else {
2750         newVectorLength = Butterfly::optimalContiguousVectorLength(
2751             propertyCapacity, std::min(length << 1, MAX_STORAGE_VECTOR_LENGTH));
2752         butterfly = butterfly->growArrayRight(
2753             vm, this, structure, propertyCapacity, true,
2754             oldVectorLength * sizeof(EncodedJSValue),
2755             newVectorLength * sizeof(EncodedJSValue));
2756         if (!butterfly)
2757             return false;
2758         m_butterfly.set(vm, this, butterfly);
2759     }
2760
2761     butterfly->setVectorLength(newVectorLength);
2762
2763     if (hasDouble(indexingType())) {
2764         for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
2765             butterfly->contiguousDouble()[i] = PNaN;
2766     } else {
2767         for (unsigned i = oldVectorLength; i < newVectorLength; ++i)
2768             butterfly->contiguous()[i].clear();
2769     }
2770
2771     return true;
2772 }
2773
2774 void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length)
2775 {
2776     ASSERT(length < MAX_ARRAY_INDEX);
2777     ASSERT(length < MAX_STORAGE_VECTOR_LENGTH);
2778     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
2779     ASSERT(m_butterfly.get()->vectorLength() > length);
2780     ASSERT(!m_butterfly.get()->indexingHeader()->preCapacity(structure()));
2781
2782     DeferGC deferGC(vm.heap);
2783     Butterfly* newButterfly = m_butterfly.get()->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(length));
2784     m_butterfly.set(vm, this, newButterfly);
2785     newButterfly->setVectorLength(length);
2786     newButterfly->setPublicLength(length);
2787 }
2788
2789 Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize)
2790 {
2791     ASSERT(newSize > oldSize);
2792
2793     // It's important that this function not rely on structure(), for the property
2794     // capacity, since we might have already mutated the structure in-place.
2795
2796     return Butterfly::createOrGrowPropertyStorage(m_butterfly.get(), vm, this, structure(vm), oldSize, newSize);
2797 }
2798
2799 static JSCustomGetterSetterFunction* getCustomGetterSetterFunctionForGetterSetter(ExecState* exec, PropertyName propertyName, CustomGetterSetter* getterSetter, JSCustomGetterSetterFunction::Type type)
2800 {
2801     auto key = std::make_pair(getterSetter, (int)type);
2802     JSCustomGetterSetterFunction* customGetterSetterFunction = exec->vm().customGetterSetterFunctionMap.get(key);
2803     if (!customGetterSetterFunction) {
2804         customGetterSetterFunction = JSCustomGetterSetterFunction::create(exec->vm(), exec->lexicalGlobalObject(), getterSetter, type, propertyName.publicName());
2805         exec->vm().customGetterSetterFunctionMap.set(key, customGetterSetterFunction);
2806     }
2807     return customGetterSetterFunction;
2808 }
2809
2810 bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
2811 {
2812     JSC::PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
2813     if (!methodTable(exec->vm())->getOwnPropertySlot(this, exec, propertyName, slot))
2814         return false;
2815
2816     // DebuggerScope::getOwnPropertySlot() (and possibly others) may return attributes from the prototype chain
2817     // but getOwnPropertyDescriptor() should only work for 'own' properties so we exit early if we detect that
2818     // the property is not an own property.
2819     if (slot.slotBase() != this && slot.slotBase()) {
2820         JSProxy* jsProxy = jsDynamicCast<JSProxy*>(this);
2821         if (!jsProxy || jsProxy->target() != slot.slotBase()) {
2822             // Try ProxyObject.
2823             ProxyObject* proxyObject = jsDynamicCast<ProxyObject*>(this);
2824             if (!proxyObject || proxyObject->target() != slot.slotBase())
2825                 return false;
2826         }
2827     }
2828
2829     if (slot.isAccessor())
2830         descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes());
2831     else if (slot.attributes() & CustomAccessor) {
2832         descriptor.setCustomDescriptor(slot.attributes());
2833
2834         JSObject* thisObject = this;
2835         if (auto* proxy = jsDynamicCast<JSProxy*>(this))
2836             thisObject = proxy->target();
2837
2838         CustomGetterSetter* getterSetter;
2839         if (slot.isCustomAccessor())
2840             getterSetter = slot.customGetterSetter();
2841         else {
2842             JSValue maybeGetterSetter = thisObject->getDirect(exec->vm(), propertyName);
2843             if (!maybeGetterSetter) {
2844                 thisObject->reifyAllStaticProperties(exec);
2845                 maybeGetterSetter = thisObject->getDirect(exec->vm(), propertyName);
2846             }
2847
2848             ASSERT(maybeGetterSetter);
2849             getterSetter = jsCast<CustomGetterSetter*>(maybeGetterSetter);
2850         }
2851         if (getterSetter->getter())
2852             descriptor.setGetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Getter));
2853         if (getterSetter->setter())
2854             descriptor.setSetter(getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Setter));
2855     } else
2856         descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes());
2857     return true;
2858 }
2859
2860 static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
2861 {
2862     VM& vm = exec->vm();
2863     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
2864         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
2865             GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
2866             if (oldDescriptor.getterPresent())
2867                 accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
2868             if (oldDescriptor.setterPresent())
2869                 accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
2870             target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
2871             return true;
2872         }
2873         JSValue newValue = jsUndefined();
2874         if (descriptor.value())
2875             newValue = descriptor.value();
2876         else if (oldDescriptor.value())
2877             newValue = oldDescriptor.value();
2878         target->putDirect(vm, propertyName, newValue, attributes & ~Accessor);
2879         if (attributes & ReadOnly)
2880             target->structure(vm)->setContainsReadOnlyProperties();
2881         return true;
2882     }
2883     attributes &= ~ReadOnly;
2884     GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
2885
2886     if (descriptor.getterPresent())
2887         accessor->setGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
2888     else if (oldDescriptor.getterPresent())
2889         accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
2890     if (descriptor.setterPresent())
2891         accessor->setSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
2892     else if (oldDescriptor.setterPresent())
2893         accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
2894
2895     target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
2896     return true;
2897 }
2898
2899 bool JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
2900 {
2901     if (Optional<uint32_t> index = parseIndex(propertyName))
2902         return putDirectIndex(exec, index.value(), value);
2903     return putDirect(exec->vm(), propertyName, value);
2904 }
2905
2906 // 9.1.6.3 of the spec
2907 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-validateandapplypropertydescriptor
2908 bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, PropertyName propertyName, bool isExtensible,
2909     const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException)
2910 {
2911     VM& vm = exec->vm();
2912     auto scope = DECLARE_THROW_SCOPE(vm);
2913
2914     // If we have a new property we can just put it on normally
2915     // Step 2.
2916     if (!isCurrentDefined) {
2917         // unless extensions are prevented!
2918         // Step 2.a
2919         if (!isExtensible) {
2920             if (throwException)
2921                 throwTypeError(exec, scope, ASCIILiteral("Attempting to define property on object that is not extensible."));
2922             return false;
2923         }
2924         if (!object)
2925             return true;
2926         // Step 2.c/d
2927         PropertyDescriptor oldDescriptor;
2928         oldDescriptor.setValue(jsUndefined());
2929         // FIXME: spec says to always return true here.
2930         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
2931     }
2932     // Step 3.
2933     if (descriptor.isEmpty())
2934         return true;
2935     // Step 4.
2936     if (current.equalTo(exec, descriptor))
2937         return true;
2938
2939     // Step 5.
2940     // Filter out invalid changes
2941     if (!current.configurable()) {
2942         if (descriptor.configurable()) {
2943             if (throwException)
2944                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property."));
2945             return false;
2946         }
2947         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
2948             if (throwException)
2949                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."));
2950             return false;
2951         }
2952     }
2953     
2954     // Step 6.
2955     // A generic descriptor is simply changing the attributes of an existing property
2956     if (descriptor.isGenericDescriptor()) {
2957         if (!current.attributesEqual(descriptor) && object) {
2958             object->methodTable(vm)->deleteProperty(object, exec, propertyName);
2959             return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
2960         }
2961         return true;
2962     }
2963     
2964     // Step 7.
2965     // Changing between a normal property or an accessor property
2966     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
2967         if (!current.configurable()) {
2968             if (throwException)
2969                 throwTypeError(exec, scope, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
2970             return false;
2971         }
2972
2973         if (!object)
2974             return true;
2975
2976         object->methodTable(vm)->deleteProperty(object, exec, propertyName);
2977         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
2978     }
2979
2980     // Step 8.
2981     // Changing the value and attributes of an existing property
2982     if (descriptor.isDataDescriptor()) {
2983         if (!current.configurable()) {
2984             if (!current.writable() && descriptor.writable()) {
2985                 if (throwException)
2986                     throwTypeError(exec, scope, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."));
2987                 return false;
2988             }
2989             if (!current.writable()) {
2990                 if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value())) {
2991                     if (throwException)
2992                         throwTypeError(exec, scope, ASCIILiteral("Attempting to change value of a readonly property."));
2993                     return false;
2994                 }
2995             }
2996         }
2997         if (current.attributesEqual(descriptor) && !descriptor.value())
2998             return true;
2999         if (!object)
3000             return true;
3001         object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3002         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
3003     }
3004
3005     // Step 9.
3006     // Changing the accessor functions of an existing accessor property
3007     ASSERT(descriptor.isAccessorDescriptor());
3008     if (!current.configurable()) {
3009         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
3010             if (throwException)
3011                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change the setter of an unconfigurable property."));
3012             return false;
3013         }
3014         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
3015             if (throwException)
3016                 throwTypeError(exec, scope, ASCIILiteral("Attempting to change the getter of an unconfigurable property."));
3017             return false;
3018         }
3019         if (current.attributes() & CustomAccessor) {
3020             if (throwException)
3021                 throwTypeError(exec, scope, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
3022             return false;
3023         }
3024     }
3025
3026     // Step 10/11.
3027     if (!object)
3028         return true;
3029     JSValue accessor = object->getDirect(vm, propertyName);
3030     if (!accessor)
3031         return false;
3032     GetterSetter* getterSetter;
3033     bool getterSetterChanged = false;
3034     if (accessor.isCustomGetterSetter()) {
3035         getterSetter = GetterSetter::create(vm, exec->lexicalGlobalObject());
3036         auto* customGetterSetter = jsCast<CustomGetterSetter*>(accessor);
3037         if (customGetterSetter->setter())
3038             getterSetter->setSetter(vm, exec->lexicalGlobalObject(), getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Setter));
3039         if (customGetterSetter->getter())
3040             getterSetter->setGetter(vm, exec->lexicalGlobalObject(), getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Getter));
3041     } else {
3042         ASSERT(accessor.isGetterSetter());
3043         getterSetter = asGetterSetter(accessor);
3044     }
3045     if (descriptor.setterPresent()) {
3046         getterSetter = getterSetter->withSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
3047         getterSetterChanged = true;
3048     }
3049     if (descriptor.getterPresent()) {
3050         getterSetter = getterSetter->withGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
3051         getterSetterChanged = true;
3052     }
3053     if (current.attributesEqual(descriptor) && !getterSetterChanged)
3054         return true;
3055     object->methodTable(vm)->deleteProperty(object, exec, propertyName);
3056     unsigned attrs = descriptor.attributesOverridingCurrent(current);
3057     object->putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor);
3058     return true;
3059 }
3060
3061 bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3062 {
3063     VM& vm  = exec->vm();
3064     auto throwScope = DECLARE_THROW_SCOPE(vm);
3065
3066     // Track on the globaldata that we're in define property.
3067     // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
3068     // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
3069     // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
3070     VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
3071     PropertyDescriptor current;
3072     bool isCurrentDefined = getOwnPropertyDescriptor(exec, propertyName, current);
3073     bool isExtensible = this->isExtensible(exec);
3074     RETURN_IF_EXCEPTION(throwScope, false);
3075     return validateAndApplyPropertyDescriptor(exec, this, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException);
3076 }
3077
3078 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
3079 {
3080     // If it's an array index, then use the indexed property storage.
3081     if (Optional<uint32_t> index = parseIndex(propertyName)) {
3082         // 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.
3083         // d. Reject if succeeded is false.
3084         // e. If index >= oldLen
3085         // e.i. Set oldLenDesc.[[Value]] to index + 1.
3086         // 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.
3087         // f. Return true.
3088         return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException);
3089     }
3090     
3091     return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
3092 }
3093
3094 void JSObject::convertToDictionary(VM& vm)
3095 {
3096     DeferredStructureTransitionWatchpointFire deferredWatchpointFire;
3097     setStructure(
3098         vm, Structure::toCacheableDictionaryTransition(vm, structure(vm), &deferredWatchpointFire));
3099 }
3100
3101 void JSObject::shiftButterflyAfterFlattening(VM& vm, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter)
3102 {
3103     Butterfly* butterfly = this->butterfly();
3104     size_t preCapacity = this->butterflyPreCapacity();
3105     void* currentBase = butterfly->base(preCapacity, outOfLineCapacityAfter);
3106     void* newBase = butterfly->base(preCapacity, outOfLineCapacityBefore);
3107
3108     memmove(newBase, currentBase, this->butterflyTotalSize());
3109     setButterflyWithoutChangingStructure(vm, Butterfly::fromBase(newBase, preCapacity, outOfLineCapacityAfter));
3110 }
3111
3112 uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object)
3113 {
3114     VM& vm = exec->vm();
3115     Structure* structure = object->structure(vm);
3116     if (structure->holesMustForwardToPrototype(vm))
3117         return 0;
3118     switch (object->indexingType()) {
3119     case ALL_BLANK_INDEXING_TYPES:
3120     case ALL_UNDECIDED_INDEXING_TYPES:
3121         return 0;
3122         
3123     case ALL_INT32_INDEXING_TYPES:
3124     case ALL_CONTIGUOUS_INDEXING_TYPES: {
3125         Butterfly* butterfly = object->butterfly();
3126         unsigned usedLength = butterfly->publicLength();
3127         for (unsigned i = 0; i < usedLength; ++i) {
3128             if (!butterfly->contiguous()[i])
3129                 return 0;
3130         }
3131         return usedLength;
3132     }
3133         
3134     case ALL_DOUBLE_INDEXING_TYPES: {
3135         Butterfly* butterfly = object->butterfly();
3136         unsigned usedLength = butterfly->publicLength();
3137         for (unsigned i = 0; i < usedLength; ++i) {
3138             double value = butterfly->contiguousDouble()[i];
3139             if (value != value)
3140                 return 0;
3141         }
3142         return usedLength;
3143     }
3144         
3145     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
3146         ArrayStorage* storage = object->m_butterfly.get()->arrayStorage();
3147         if (storage->m_sparseMap.get())
3148             return 0;
3149         
3150         unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
3151         for (unsigned i = 0; i < usedVectorLength; ++i) {
3152             if (!storage->m_vector[i])
3153                 return 0;
3154         }
3155         return usedVectorLength;
3156     }
3157         
3158     default:
3159         RELEASE_ASSERT_NOT_REACHED();
3160         return 0;
3161     }
3162 }
3163
3164 void JSObject::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
3165 {
3166     VM& vm = exec->vm();
3167     object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
3168 }
3169
3170 void JSObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
3171 {
3172     VM& vm = exec->vm();
3173     auto scope = DECLARE_THROW_SCOPE(vm);
3174     object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, EnumerationMode(mode, JSObjectPropertiesMode::Exclude));
3175     RETURN_IF_EXCEPTION(scope, void());
3176
3177     JSValue nextProto = object->getPrototype(vm, exec);
3178     RETURN_IF_EXCEPTION(scope, void());
3179     if (nextProto.isNull())
3180         return;
3181
3182     JSObject* prototype = asObject(nextProto);
3183     while (true) {
3184         if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
3185             prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
3186             break;
3187         }
3188         prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
3189         RETURN_IF_EXCEPTION(scope, void());
3190         nextProto = prototype->getPrototype(vm, exec);
3191         RETURN_IF_EXCEPTION(scope, void());
3192         if (nextProto.isNull())
3193             break;
3194         prototype = asObject(nextProto);
3195     }
3196 }
3197
3198 // Implements GetMethod(O, P) in section 7.3.9 of the spec.
3199 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-getmethod
3200 JSValue JSObject::getMethod(ExecState* exec, CallData& callData, CallType& callType, const Identifier& ident, const String& errorMessage)
3201 {
3202     VM& vm = exec->vm();
3203     auto scope = DECLARE_THROW_SCOPE(vm);
3204
3205     JSValue method = get(exec, ident);
3206     RETURN_IF_EXCEPTION(scope, JSValue());
3207
3208     if (!method.isCell()) {
3209         if (method.isUndefinedOrNull())
3210             return jsUndefined();
3211
3212         throwVMTypeError(exec, scope, errorMessage);
3213         return jsUndefined();
3214     }
3215
3216     callType = method.asCell()->methodTable()->getCallData(method.asCell(), callData);
3217     if (callType == CallType::None) {
3218         throwVMTypeError(exec, scope, errorMessage);
3219         return jsUndefined();
3220     }
3221
3222     return method;
3223 }
3224
3225 } // namespace JSC