+ DeferGC deferGC(vm.heap);
+ ASSERT(hasContiguous(indexingType()));
+
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
+ for (unsigned i = m_butterfly->publicLength(); i--;) {
+ JSValue v = m_butterfly->contiguous()[i].get();
+ if (!v)
+ continue;
+ newStorage->m_vector[i].setWithoutWriteBarrier(v);
+ newStorage->m_numValuesInVector++;
+ }
+
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
+ setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
+ return newStorage;
+}
+
+ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
+{
+ return convertContiguousToArrayStorage(vm, transition, m_butterfly->vectorLength());
+}
+
+ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
+{
+ return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
+}
+
+void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
+{
+ if (value.isInt32()) {
+ convertUndecidedToInt32(vm);
+ return;
+ }
+
+ if (value.isDouble() && value.asNumber() == value.asNumber()) {
+ convertUndecidedToDouble(vm);
+ return;
+ }
+
+ convertUndecidedToContiguous(vm);
+}
+
+void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
+{
+ if (value.isInt32()) {
+ createInitialInt32(vm, index + 1)[index].set(vm, this, value);
+ return;
+ }
+
+ if (value.isDouble()) {
+ double doubleValue = value.asNumber();
+ if (doubleValue == doubleValue) {
+ createInitialDouble(vm, index + 1)[index] = doubleValue;
+ return;
+ }
+ }
+
+ createInitialContiguous(vm, index + 1)[index].set(vm, this, value);
+}
+
+void JSObject::convertInt32ForValue(VM& vm, JSValue value)
+{
+ ASSERT(!value.isInt32());
+
+ if (value.isDouble()) {
+ convertInt32ToDouble(vm);
+ return;
+ }
+
+ convertInt32ToContiguous(vm);
+}
+
+void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
+{
+ ASSERT(index < m_butterfly->publicLength());
+ ASSERT(index < m_butterfly->vectorLength());
+ convertUndecidedForValue(vm, value);
+ setIndexQuickly(vm, index, value);
+}
+
+void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
+{
+ ASSERT(!value.isInt32());
+ convertInt32ForValue(vm, value);
+ setIndexQuickly(vm, index, value);
+}
+
+void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value)
+{
+ ASSERT(!value.isNumber() || value.asNumber() != value.asNumber());
+ convertDoubleToContiguous(vm);
+ setIndexQuickly(vm, index, value);
+}
+
+ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
+{
+ ASSERT(inherits(info()));
+
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
+ return ContiguousJSValues();
+ return createInitialInt32(vm, 0);
+
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ return convertUndecidedToInt32(vm);
+
+ case ALL_DOUBLE_INDEXING_TYPES:
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES:
+ return ContiguousJSValues();
+
+ default:
+ CRASH();
+ return ContiguousJSValues();
+ }
+}
+
+ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
+{
+ ASSERT(inherits(info()));
+
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
+ return ContiguousDoubles();
+ return createInitialDouble(vm, 0);
+
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ return convertUndecidedToDouble(vm);
+
+ case ALL_INT32_INDEXING_TYPES:
+ return convertInt32ToDouble(vm);
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES:
+ return ContiguousDoubles();
+
+ default:
+ CRASH();
+ return ContiguousDoubles();
+ }
+}
+
+ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm, DoubleToContiguousMode mode)
+{
+ ASSERT(inherits(info()));
+
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing()))
+ return ContiguousJSValues();
+ return createInitialContiguous(vm, 0);
+
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ return convertUndecidedToContiguous(vm);
+
+ case ALL_INT32_INDEXING_TYPES:
+ return convertInt32ToContiguous(vm);
+
+ case ALL_DOUBLE_INDEXING_TYPES:
+ if (mode == RageConvertDoubleToValue)
+ return rageConvertDoubleToContiguous(vm);
+ return convertDoubleToContiguous(vm);
+
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES:
+ return ContiguousJSValues();
+
+ default:
+ CRASH();
+ return ContiguousJSValues();
+ }
+}
+
+ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
+{
+ return ensureContiguousSlow(vm, EncodeValueAsDouble);
+}
+
+ContiguousJSValues JSObject::rageEnsureContiguousSlow(VM& vm)
+{
+ return ensureContiguousSlow(vm, RageConvertDoubleToValue);
+}
+
+ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
+{
+ ASSERT(inherits(info()));
+
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse()))
+ return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
+ return createInitialArrayStorage(vm);
+
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure(vm)->needsSlowPutIndexing());
+ return convertUndecidedToArrayStorage(vm);
+
+ case ALL_INT32_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure(vm)->needsSlowPutIndexing());
+ return convertInt32ToArrayStorage(vm);
+
+ case ALL_DOUBLE_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure(vm)->needsSlowPutIndexing());
+ return convertDoubleToArrayStorage(vm);
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure(vm)->needsSlowPutIndexing());
+ return convertContiguousToArrayStorage(vm);
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return 0;
+ }
+}
+
+ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
+{
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES: {
+ createArrayStorage(vm, 0, 0);
+ SparseArrayValueMap* map = allocateSparseIndexMap(vm);
+ map->setSparseMode();
+ return arrayStorage();
+ }
+
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm));
+
+ case ALL_INT32_INDEXING_TYPES:
+ return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm));
+
+ case ALL_DOUBLE_INDEXING_TYPES:
+ return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm));
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm));
+
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES:
+ return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
+
+ default:
+ CRASH();
+ return 0;
+ }
+}
+
+void JSObject::switchToSlowPutArrayStorage(VM& vm)
+{
+ switch (indexingType()) {
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ convertUndecidedToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ break;
+
+ case ALL_INT32_INDEXING_TYPES:
+ convertInt32ToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ break;
+
+ case ALL_DOUBLE_INDEXING_TYPES:
+ convertDoubleToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ break;
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ convertContiguousToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ break;
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage);
+ setStructure(vm, newStructure);
+ break;
+ }
+
+ default:
+ CRASH();
+ break;
+ }
+}
+
+void JSObject::setPrototype(VM& vm, JSValue prototype)
+{
+ ASSERT(prototype);
+ if (prototype.isObject())
+ vm.prototypeMap.addPrototype(asObject(prototype));
+
+ Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype);
+ setStructure(vm, newStructure);
+
+ if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses())
+ return;
+
+ if (vm.prototypeMap.isPrototype(this)) {
+ newStructure->globalObject()->haveABadTime(vm);
+ return;
+ }
+
+ if (!hasIndexedProperties(indexingType()))
+ return;
+
+ if (shouldUseSlowPut(indexingType()))
+ return;
+
+ switchToSlowPutArrayStorage(vm);
+}
+
+bool JSObject::setPrototypeWithCycleCheck(ExecState* exec, JSValue prototype)
+{
+ ASSERT(methodTable(exec->vm())->toThis(this, exec, NotStrictMode) == this);
+ JSValue nextPrototype = prototype;
+ while (nextPrototype && nextPrototype.isObject()) {
+ if (nextPrototype == this)
+ return false;
+ nextPrototype = asObject(nextPrototype)->prototype();
+ }
+ setPrototype(exec->vm(), prototype);
+ return true;
+}
+
+bool JSObject::allowsAccessFrom(ExecState* exec)
+{
+ JSGlobalObject* globalObject = this->globalObject();
+ return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
+}
+
+void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
+{
+ ASSERT(value.isGetterSetter() && (attributes & Accessor));
+
+ unsigned index = propertyName.asIndex();
+ if (index != PropertyName::NotAnIndex) {
+ putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect);
+ return;
+ }
+
+ putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes);
+}
+
+void JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
+{
+ ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex);
+
+ PutPropertySlot slot(this);
+ putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot, getCallableObject(value));
+
+ ASSERT(slot.type() == PutPropertySlot::NewProperty);
+
+ Structure* structure = this->structure(vm);
+ if (attributes & ReadOnly)
+ structure->setContainsReadOnlyProperties();
+ structure->setHasCustomGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto);
+}
+
+void JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
+{
+ PutPropertySlot slot(this);
+ putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot, getCallableObject(value));
+
+ // putDirect will change our Structure if we add a new property. For
+ // getters and setters, though, we also need to change our Structure
+ // if we override an existing non-getter or non-setter.
+ if (slot.type() != PutPropertySlot::NewProperty)
+ setStructure(vm, Structure::attributeChangeTransition(vm, structure(vm), propertyName, attributes));
+
+ Structure* structure = this->structure(vm);
+ if (attributes & ReadOnly)
+ structure->setContainsReadOnlyProperties();
+
+ structure->setHasGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto);
+}
+
+bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
+{
+ PropertySlot slot(this);
+ return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
+}
+
+bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
+{
+ PropertySlot slot(this);
+ return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
+}
+
+// ECMA 8.6.2.5
+bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
+{
+ JSObject* thisObject = jsCast<JSObject*>(cell);
+
+ unsigned i = propertyName.asIndex();
+ if (i != PropertyName::NotAnIndex)
+ return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, i);
+
+ if (!thisObject->staticFunctionsReified())
+ thisObject->reifyStaticFunctionsForDelete(exec);
+
+ unsigned attributes;
+ JSCell* specificValue;
+ VM& vm = exec->vm();
+ if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes, specificValue))) {
+ if (attributes & DontDelete && !vm.isInDefineOwnProperty())
+ return false;
+ thisObject->removeDirect(vm, propertyName);
+ return true;
+ }
+
+ // Look in the static hashtable of properties
+ const HashTableValue* entry = thisObject->findPropertyHashEntry(vm, propertyName);
+ if (entry) {
+ if (entry->attributes() & DontDelete && !vm.isInDefineOwnProperty())
+ return false; // this builtin property can't be deleted
+
+ PutPropertySlot slot(thisObject);
+ putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot);
+ }
+
+ return true;
+}
+
+bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
+{
+ PropertySlot slot(this);
+ return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
+}
+
+bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
+{
+ JSObject* thisObject = jsCast<JSObject*>(cell);
+
+ if (i > MAX_ARRAY_INDEX)
+ return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i));
+
+ switch (thisObject->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ return true;
+
+ case ALL_INT32_INDEXING_TYPES:
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ Butterfly* butterfly = thisObject->butterfly();
+ if (i >= butterfly->vectorLength())
+ return true;
+ butterfly->contiguous()[i].clear();
+ return true;
+ }
+
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ Butterfly* butterfly = thisObject->butterfly();
+ if (i >= butterfly->vectorLength())
+ return true;
+ butterfly->contiguousDouble()[i] = PNaN;
+ return true;
+ }
+
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
+ ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
+
+ if (i < storage->vectorLength()) {
+ WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
+ if (valueSlot) {
+ valueSlot.clear();
+ --storage->m_numValuesInVector;
+ }
+ } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
+ SparseArrayValueMap::iterator it = map->find(i);
+ if (it != map->notFound()) {
+ if (it->value.attributes & DontDelete)
+ return false;
+ map->remove(it);
+ }
+ }
+
+ return true;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
+static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, PropertyName propertyName)
+{
+ JSValue function = object->get(exec, propertyName);
+ CallData callData;
+ CallType callType = getCallData(function, callData);
+ if (callType == CallTypeNone)
+ return exec->exception();
+
+ // Prevent "toString" and "valueOf" from observing execution if an exception
+ // is pending.
+ if (exec->hadException())
+ return exec->exception();
+
+ JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
+ ASSERT(!result.isGetterSetter());
+ if (exec->hadException())
+ return exec->exception();
+ if (result.isObject())
+ return JSValue();
+ return result;
+}
+
+bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
+{
+ result = methodTable(exec->vm())->defaultValue(this, exec, PreferNumber);
+ number = result.toNumber(exec);
+ return !result.isString();
+}
+
+// ECMA 8.6.2.6
+JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
+{
+ // Must call toString first for Date objects.
+ if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) {
+ JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
+ if (value)
+ return value;
+ value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
+ if (value)
+ return value;
+ } else {
+ JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
+ if (value)
+ return value;
+ value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
+ if (value)
+ return value;
+ }
+
+ ASSERT(!exec->hadException());
+
+ return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value")));
+}
+
+const HashTableValue* JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const
+{
+ for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
+ if (const HashTable* propHashTable = info->propHashTable(vm)) {
+ if (const HashTableValue* entry = propHashTable->entry(vm, propertyName))
+ return entry;
+ }
+ }
+ return 0;
+}
+
+bool JSObject::hasInstance(ExecState* exec, JSValue value)
+{
+ VM& vm = exec->vm();
+ TypeInfo info = structure(vm)->typeInfo();
+ if (info.implementsDefaultHasInstance())
+ return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
+ if (info.implementsHasInstance())
+ return methodTable(vm)->customHasInstance(this, exec, value);
+ vm.throwException(exec, createInvalidParameterError(exec, "instanceof" , this));
+ return false;
+}
+
+bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
+{
+ if (!value.isObject())
+ return false;
+
+ if (!proto.isObject()) {
+ exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("instanceof called on an object with an invalid prototype property.")));
+ return false;
+ }
+
+ JSObject* object = asObject(value);
+ while ((object = object->prototype().getObject())) {
+ if (proto == object)
+ return true;
+ }
+ return false;
+}
+
+bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyName, JSCell*& specificValue) const
+{
+ VM& vm = exec->vm();
+ unsigned attributes;
+ if (isValidOffset(structure(vm)->get(vm, propertyName, attributes, specificValue)))
+ return true;
+
+ // This could be a function within the static table? - should probably
+ // also look in the hash? This currently should not be a problem, since
+ // we've currently always call 'get' first, which should have populated
+ // the normal storage.
+ return false;
+}
+
+void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ propertyNames.setBaseObject(object);
+ object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode);
+
+ if (object->prototype().isNull())
+ return;
+
+ VM& vm = exec->vm();
+ JSObject* prototype = asObject(object->prototype());
+ while(1) {
+ if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) {
+ prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode);
+ break;
+ }
+ prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode);
+ JSValue nextProto = prototype->prototype();
+ if (nextProto.isNull())
+ break;
+ prototype = asObject(nextProto);
+ }
+}
+
+void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ // Add numeric properties first. That appears to be the accepted convention.
+ // FIXME: Filling PropertyNameArray with an identifier for every integer
+ // is incredibly inefficient for large arrays. We need a different approach,
+ // which almost certainly means a different structure for PropertyNameArray.
+ switch (object->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ break;
+
+ case ALL_INT32_INDEXING_TYPES:
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ Butterfly* butterfly = object->butterfly();
+ unsigned usedLength = butterfly->publicLength();
+ for (unsigned i = 0; i < usedLength; ++i) {
+ if (!butterfly->contiguous()[i])
+ continue;
+ propertyNames.add(Identifier::from(exec, i));
+ }
+ break;
+ }
+
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ Butterfly* butterfly = object->butterfly();
+ unsigned usedLength = butterfly->publicLength();
+ for (unsigned i = 0; i < usedLength; ++i) {
+ double value = butterfly->contiguousDouble()[i];
+ if (value != value)
+ continue;
+ propertyNames.add(Identifier::from(exec, i));
+ }
+ break;
+ }
+
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
+ ArrayStorage* storage = object->m_butterfly->arrayStorage();
+
+ unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
+ for (unsigned i = 0; i < usedVectorLength; ++i) {
+ if (storage->m_vector[i])
+ propertyNames.add(Identifier::from(exec, i));
+ }
+
+ if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
+ Vector<unsigned, 0, UnsafeVectorOverflow> keys;
+ keys.reserveInitialCapacity(map->size());
+
+ SparseArrayValueMap::const_iterator end = map->end();
+ for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
+ if (mode == IncludeDontEnumProperties || !(it->value.attributes & DontEnum))
+ keys.uncheckedAppend(static_cast<unsigned>(it->key));
+ }
+
+ std::sort(keys.begin(), keys.end());
+ for (unsigned i = 0; i < keys.size(); ++i)
+ propertyNames.add(Identifier::from(exec, keys[i]));
+ }
+ break;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
+}
+
+void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified());
+
+ VM& vm = exec->vm();
+ bool canCachePropertiesFromStructure = !propertyNames.size();
+ object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
+
+ if (canCachePropertiesFromStructure)
+ propertyNames.setNumCacheableSlotsForObject(object, propertyNames.size());
+}
+
+double JSObject::toNumber(ExecState* exec) const
+{
+ JSValue primitive = toPrimitive(exec, PreferNumber);