+ ASSERT(hasDouble(structure()->indexingType()));
+
+ for (unsigned i = m_butterfly->vectorLength(); i--;) {
+ double* current = &m_butterfly->contiguousDouble()[i];
+ WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
+ double value = *current;
+ if (value != value) {
+ currentAsValue->clear();
+ continue;
+ }
+ JSValue v;
+ switch (mode) {
+ case EncodeValueAsDouble:
+ v = JSValue(JSValue::EncodeAsDouble, value);
+ break;
+ case RageConvertDoubleToValue:
+ v = jsNumber(value);
+ break;
+ }
+ ASSERT(v.isNumber());
+ currentAsValue->setWithoutWriteBarrier(v);
+ }
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly);
+ return m_butterfly->contiguous();
+}
+
+ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
+{
+ return genericConvertDoubleToContiguous<EncodeValueAsDouble>(vm);
+}
+
+ContiguousJSValues JSObject::rageConvertDoubleToContiguous(VM& vm)
+{
+ return genericConvertDoubleToContiguous<RageConvertDoubleToValue>(vm);
+}
+
+ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
+{
+ ASSERT(hasDouble(structure()->indexingType()));
+
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
+ for (unsigned i = m_butterfly->publicLength(); i--;) {
+ double value = m_butterfly->contiguousDouble()[i];
+ if (value != value)
+ continue;
+ newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
+ newStorage->m_numValuesInVector++;
+ }
+
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition);
+ setButterfly(vm, newStorage->butterfly(), newStructure);
+ return newStorage;
+}
+
+ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
+{
+ return convertDoubleToArrayStorage(vm, transition, m_butterfly->vectorLength());
+}
+
+ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
+{
+ return convertDoubleToArrayStorage(vm, structure()->suggestedArrayStorageTransition());
+}
+
+ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
+{
+ ASSERT(hasContiguous(structure()->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(), transition);
+ setButterfly(vm, newStorage->butterfly(), newStructure);
+ 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()->suggestedArrayStorageTransition());
+}
+
+void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
+{
+ if (value.isInt32()) {
+ convertUndecidedToInt32(vm);
+ return;
+ }
+
+ if (value.isDouble()) {
+ convertUndecidedToDouble(vm);
+ return;
+ }
+
+ convertUndecidedToContiguous(vm);
+}
+
+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(&s_info));
+
+ switch (structure()->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse() || structure()->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(&s_info));
+
+ switch (structure()->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse() || structure()->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(&s_info));
+
+ switch (structure()->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse() || structure()->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(&s_info));
+
+ switch (structure()->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ if (UNLIKELY(indexingShouldBeSparse()))
+ return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
+ return createInitialArrayStorage(vm);
+
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure()->needsSlowPutIndexing());
+ return convertUndecidedToArrayStorage(vm);
+
+ case ALL_INT32_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure()->needsSlowPutIndexing());
+ return convertInt32ToArrayStorage(vm);
+
+ case ALL_DOUBLE_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure()->needsSlowPutIndexing());
+ return convertDoubleToArrayStorage(vm);
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ ASSERT(!indexingShouldBeSparse());
+ ASSERT(!structure()->needsSlowPutIndexing());
+ return convertContiguousToArrayStorage(vm);
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return 0;
+ }
+}
+
+ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
+{
+ switch (structure()->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 (structure()->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(), SwitchToSlowPutArrayStorage);
+ setStructure(vm, newStructure, m_butterfly);
+ break;
+ }
+
+ default:
+ CRASH();
+ break;
+ }
+}
+
+void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
+{
+ ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
+ PutPropertySlot slot;
+ object->putDirectInternal<PutModeDefineOwnProperty>(exec->vm(), propertyName, value, attributes, slot, getCallableObject(value));
+}
+
+void JSObject::setPrototype(VM& vm, JSValue prototype)
+{
+ ASSERT(prototype);
+ if (prototype.isObject())
+ vm.prototypeMap.addPrototype(asObject(prototype));
+
+ Structure* newStructure = Structure::changePrototypeTransition(vm, structure(), prototype);
+ setStructure(vm, newStructure, m_butterfly);
+
+ if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses())
+ return;
+
+ if (vm.prototypeMap.isPrototype(this)) {
+ newStructure->globalObject()->haveABadTime(vm);
+ return;
+ }
+
+ if (!hasIndexingHeader(structure()->indexingType()))
+ return;
+
+ if (shouldUseSlowPut(structure()->indexingType()))
+ return;
+
+ switchToSlowPutArrayStorage(vm);
+}
+
+bool JSObject::setPrototypeWithCycleCheck(VM& vm, JSValue prototype)
+{
+ JSValue checkFor = this;
+ if (this->isGlobalObject())
+ checkFor = jsCast<JSGlobalObject*>(this)->globalExec()->thisValue();
+
+ JSValue nextPrototype = prototype;
+ while (nextPrototype && nextPrototype.isObject()) {
+ if (nextPrototype == checkFor)
+ return false;
+ nextPrototype = asObject(nextPrototype)->prototype();
+ }
+ setPrototype(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;
+ }
+
+ VM& vm = exec->vm();
+
+ PutPropertySlot slot;
+ 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(), propertyName, attributes), m_butterfly);
+
+ if (attributes & ReadOnly)
+ structure()->setContainsReadOnlyProperties();
+
+ structure()->setHasGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto);
+}
+
+bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
+{
+ PropertySlot slot;
+ return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
+}
+
+bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
+{
+ PropertySlot slot;
+ 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()->deletePropertyByIndex(thisObject, exec, i);
+
+ if (!thisObject->staticFunctionsReified())
+ thisObject->reifyStaticFunctionsForDelete(exec);
+
+ unsigned attributes;
+ JSCell* specificValue;
+ if (isValidOffset(thisObject->structure()->get(exec->vm(), propertyName, attributes, specificValue))) {
+ if (attributes & DontDelete && !exec->vm().isInDefineOwnProperty())
+ return false;
+ thisObject->removeDirect(exec->vm(), propertyName);
+ return true;
+ }
+
+ // Look in the static hashtable of properties
+ const HashEntry* entry = thisObject->findPropertyHashEntry(exec, propertyName);
+ if (entry) {
+ if (entry->attributes() & DontDelete && !exec->vm().isInDefineOwnProperty())
+ return false; // this builtin property can't be deleted
+
+ putEntry(exec, entry, propertyName, jsUndefined(), thisObject);
+ }
+
+ return true;
+}
+
+bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
+{
+ PropertySlot slot;
+ return const_cast<JSObject*>(this)->methodTable()->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()->deleteProperty(thisObject, exec, Identifier::from(exec, i));
+
+ switch (thisObject->structure()->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->m_butterfly;
+ if (i >= butterfly->vectorLength())
+ return true;
+ butterfly->contiguous()[i].clear();
+ return true;
+ }
+
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ Butterfly* butterfly = thisObject->m_butterfly;
+ if (i >= butterfly->vectorLength())
+ return true;
+ butterfly->contiguousDouble()[i] = QNaN;
+ 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()->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 throwError(exec, createTypeError(exec, ASCIILiteral("No default value")));
+}
+
+const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, PropertyName propertyName) const
+{
+ for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
+ if (const HashTable* propHashTable = info->propHashTable(exec)) {
+ if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
+ return entry;
+ }
+ }
+ return 0;
+}
+
+bool JSObject::hasInstance(ExecState* exec, JSValue value)
+{
+ TypeInfo info = structure()->typeInfo();
+ if (info.implementsDefaultHasInstance())
+ return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
+ if (info.implementsHasInstance())
+ return methodTable()->customHasInstance(this, exec, value);
+ throwError(exec, createInvalidParamError(exec, "instanceof" , this));
+ return false;
+}
+
+bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
+{
+ if (!value.isObject())
+ return false;
+
+ if (!proto.isObject()) {
+ throwError(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::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
+{
+ PropertyDescriptor descriptor;
+ if (!const_cast<JSObject*>(this)->methodTable()->getOwnPropertyDescriptor(const_cast<JSObject*>(this), exec, propertyName, descriptor))
+ return false;
+ return descriptor.enumerable();
+}
+
+bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyName, JSCell*& specificValue) const
+{
+ unsigned attributes;
+ if (isValidOffset(structure()->get(exec->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()->getOwnPropertyNames(object, exec, propertyNames, mode);
+
+ if (object->prototype().isNull())
+ return;
+
+ JSObject* prototype = asObject(object->prototype());
+ while(1) {
+ if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
+ prototype->methodTable()->getPropertyNames(prototype, exec, propertyNames, mode);
+ break;
+ }
+ prototype->methodTable()->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->structure()->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->m_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->m_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()->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
+}
+
+void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified());
+
+ bool canCachePropertiesFromStructure = !propertyNames.size();
+ object->structure()->getPropertyNamesFromStructure(exec->vm(), propertyNames, mode);
+
+ if (canCachePropertiesFromStructure)
+ propertyNames.setNumCacheableSlotsForObject(object, propertyNames.size());
+}
+
+double JSObject::toNumber(ExecState* exec) const
+{
+ JSValue primitive = toPrimitive(exec, PreferNumber);
+ if (exec->hadException()) // should be picked up soon in Nodes.cpp
+ return 0.0;
+ return primitive.toNumber(exec);
+}
+
+JSString* JSObject::toString(ExecState* exec) const
+{
+ JSValue primitive = toPrimitive(exec, PreferString);
+ if (exec->hadException())
+ return jsEmptyString(exec);
+ return primitive.toString(exec);
+}
+
+JSObject* JSObject::toThisObject(JSCell* cell, ExecState*)
+{
+ return jsCast<JSObject*>(cell);
+}
+
+void JSObject::seal(VM& vm)
+{
+ if (isSealed(vm))
+ return;
+ preventExtensions(vm);
+ setStructure(vm, Structure::sealTransition(vm, structure()), m_butterfly);
+}
+
+void JSObject::freeze(VM& vm)
+{
+ if (isFrozen(vm))
+ return;
+ preventExtensions(vm);
+ setStructure(vm, Structure::freezeTransition(vm, structure()), m_butterfly);
+}
+
+void JSObject::preventExtensions(VM& vm)
+{
+ enterDictionaryIndexingMode(vm);
+ if (isExtensible())
+ setStructure(vm, Structure::preventExtensionsTransition(vm, structure()), m_butterfly);
+}
+
+// This presently will flatten to an uncachable dictionary; this is suitable
+// for use in delete, we may want to do something different elsewhere.
+void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
+{
+ ASSERT(!staticFunctionsReified());
+ VM& vm = exec->vm();
+
+ // If this object's ClassInfo has no static properties, then nothing to reify!
+ // We can safely set the flag to avoid the expensive check again in the future.
+ if (!classInfo()->hasStaticProperties()) {
+ structure()->setStaticFunctionsReified();
+ return;
+ }
+
+ if (!structure()->isUncacheableDictionary())
+ setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure()), m_butterfly);
+
+ for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
+ const HashTable* hashTable = info->propHashTable(globalObject()->globalExec());
+ if (!hashTable)
+ continue;
+ PropertySlot slot;
+ for (HashTable::ConstIterator iter = hashTable->begin(vm); iter != hashTable->end(vm); ++iter) {
+ if (iter->attributes() & Function)
+ setUpStaticFunctionSlot(globalObject()->globalExec(), *iter, this, Identifier(&vm, iter->key()), slot);
+ }
+ }
+
+ structure()->setStaticFunctionsReified();
+}
+
+bool JSObject::removeDirect(VM& vm, PropertyName propertyName)
+{
+ if (!isValidOffset(structure()->get(vm, propertyName)))
+ return false;
+
+ PropertyOffset offset;
+ if (structure()->isUncacheableDictionary()) {
+ offset = structure()->removePropertyWithoutTransition(vm, propertyName);
+ if (offset == invalidOffset)
+ return false;
+ putDirectUndefined(offset);
+ return true;
+ }
+
+ setStructure(vm, Structure::removePropertyTransition(vm, structure(), propertyName, offset), m_butterfly);
+ if (offset == invalidOffset)
+ return false;
+ putDirectUndefined(offset);
+ return true;
+}
+
+NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, PropertyOffset offset)
+{
+ if (JSObject* getterFunction = asGetterSetter(getDirect(offset))->getter()) {
+ if (!structure()->isDictionary())
+ slot.setCacheableGetterSlot(this, getterFunction, offset);
+ else
+ slot.setGetterSlot(getterFunction);
+ } else
+ slot.setUndefined();
+}
+
+void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
+{
+ if (descriptor.isDataDescriptor()) {
+ if (descriptor.value())
+ entryInMap->set(exec->vm(), this, descriptor.value());
+ else if (oldDescriptor.isAccessorDescriptor())
+ entryInMap->set(exec->vm(), this, jsUndefined());
+ entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
+ return;
+ }
+
+ if (descriptor.isAccessorDescriptor()) {
+ JSObject* getter = 0;
+ if (descriptor.getterPresent())
+ getter = descriptor.getterObject();
+ else if (oldDescriptor.isAccessorDescriptor())
+ getter = oldDescriptor.getterObject();
+ JSObject* setter = 0;
+ if (descriptor.setterPresent())
+ setter = descriptor.setterObject();
+ else if (oldDescriptor.isAccessorDescriptor())
+ setter = oldDescriptor.setterObject();
+
+ GetterSetter* accessor = GetterSetter::create(exec);
+ if (getter)
+ accessor->setGetter(exec->vm(), getter);
+ if (setter)
+ accessor->setSetter(exec->vm(), setter);
+
+ entryInMap->set(exec->vm(), this, accessor);
+ entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
+ return;
+ }
+
+ ASSERT(descriptor.isGenericDescriptor());
+ entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
+}
+
+// Defined in ES5.1 8.12.9
+bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException)
+{
+ ASSERT(index <= MAX_ARRAY_INDEX);
+
+ if (!inSparseIndexingMode()) {
+ // Fast case: we're putting a regular property to a regular array
+ // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
+ // however if the property currently exists missing attributes will override from their current 'true'
+ // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
+ if (!descriptor.attributes()) {
+ ASSERT(!descriptor.isAccessorDescriptor());
+ return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
+ }
+
+ ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->vm());
+ }
+
+ if (descriptor.attributes() & (ReadOnly | Accessor))
+ notifyPresenceOfIndexedAccessors(exec->vm());
+
+ SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
+ RELEASE_ASSERT(map);
+
+ // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
+ SparseArrayValueMap::AddResult result = map->add(this, index);
+ SparseArrayEntry* entryInMap = &result.iterator->value;
+
+ // 2. Let extensible be the value of the [[Extensible]] internal property of O.
+ // 3. If current is undefined and extensible is false, then Reject.
+ // 4. If current is undefined and extensible is true, then
+ if (result.isNewEntry) {
+ if (!isExtensible()) {
+ map->remove(result.iterator);
+ return reject(exec, throwException, "Attempting to define property on object that is not extensible.");
+ }
+
+ // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
+ // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
+ // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
+ // created property is set to its default value.
+ // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
+ // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
+ // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
+ // is set to its default value.
+ // 4.c. Return true.
+
+ PropertyDescriptor defaults;
+ entryInMap->setWithoutWriteBarrier(jsUndefined());
+ entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
+ entryInMap->get(defaults);
+
+ putIndexedDescriptor(exec, entryInMap, descriptor, defaults);
+ if (index >= m_butterfly->arrayStorage()->length())
+ m_butterfly->arrayStorage()->setLength(index + 1);
+ return true;
+ }
+
+ // 5. Return true, if every field in Desc is absent.
+ // 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).
+ PropertyDescriptor current;
+ entryInMap->get(current);
+ if (descriptor.isEmpty() || descriptor.equalTo(exec, current))
+ return true;
+
+ // 7. If the [[Configurable]] field of current is false then
+ if (!current.configurable()) {
+ // 7.a. Reject, if the [[Configurable]] field of Desc is true.
+ if (descriptor.configurablePresent() && descriptor.configurable())
+ return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
+ // 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.
+ if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
+ return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
+ }
+
+ // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
+ if (!descriptor.isGenericDescriptor()) {
+ // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
+ if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
+ // 9.a. Reject, if the [[Configurable]] field of current is false.
+ if (!current.configurable())
+ return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
+ // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
+ // data property to an accessor property. Preserve the existing values of the converted property's
+ // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
+ // their default values.
+ // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
+ // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
+ // attributes and set the rest of the property's attributes to their default values.
+ } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
+ // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
+ // 10.a. If the [[Configurable]] field of current is false, then
+ if (!current.configurable() && !current.writable()) {
+ // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
+ if (descriptor.writable())
+ return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
+ // 10.a.ii. If the [[Writable]] field of current is false, then
+ // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
+ if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
+ return reject(exec, throwException, "Attempting to change value of a readonly property.");
+ }
+ // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
+ } else {
+ ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
+ // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
+ if (!current.configurable()) {
+ // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
+ if (descriptor.setterPresent() && descriptor.setter() != current.setter())
+ return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property.");
+ // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
+ if (descriptor.getterPresent() && descriptor.getter() != current.getter())
+ return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property.");
+ }
+ }
+ }
+
+ // 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.
+ putIndexedDescriptor(exec, entryInMap, descriptor, current);
+ // 13. Return true.
+ return true;