- ASSERT(!attributes);
- return Base::get();
-}
-
-inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor)
-{
- iterator end = m_map.end();
- for (iterator it = m_map.begin(); it != end; ++it)
- visitor.append(&it->second);
-}
-
-void JSArray::allocateSparseMap(JSGlobalData& globalData)
-{
- m_sparseValueMap = new SparseArrayValueMap;
- globalData.heap.addFinalizer(this, finalize);
-}
-
-void JSArray::deallocateSparseMap()
-{
- delete m_sparseValueMap;
- m_sparseValueMap = 0;
-}
-
-void JSArray::enterDictionaryMode(JSGlobalData& globalData)
-{
- ArrayStorage* storage = m_storage;
- SparseArrayValueMap* map = m_sparseValueMap;
-
- if (!map) {
- allocateSparseMap(globalData);
- map = m_sparseValueMap;
- }
-
- if (map->sparseMode())
- return;
-
- map->setSparseMode();
-
- unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
- for (unsigned i = 0; i < usedVectorLength; ++i) {
- JSValue value = storage->m_vector[i].get();
- // This will always be a new entry in the map, so no need to check we can write,
- // and attributes are default so no need to set them.
- if (value)
- map->add(this, i).iterator->second.set(globalData, this, value);
- }
-
- void* newRawStorage = 0;
- if (!globalData.heap.tryAllocateStorage(storageSize(0), &newRawStorage))
- CRASH();
-
- ArrayStorage* newStorage = static_cast<ArrayStorage*>(newRawStorage);
- memcpy(newStorage, m_storage, storageSize(0));
- newStorage->m_allocBase = newStorage;
- m_storage = newStorage;
- m_indexBias = 0;
- m_vectorLength = 0;
-}
-
-void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
-{
- if (descriptor.isDataDescriptor()) {
- if (descriptor.value())
- entryInMap->set(exec->globalData(), this, descriptor.value());
- else if (oldDescriptor.isAccessorDescriptor())
- entryInMap->set(exec->globalData(), 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->globalData(), getter);
- if (setter)
- accessor->setSetter(exec->globalData(), setter);
-
- entryInMap->set(exec->globalData(), 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 JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException)
-{
- ASSERT(index != 0xFFFFFFFF);
-
- if (!inSparseMode()) {
- // 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(), throwException);
- }
-
- enterDictionaryMode(exec->globalData());
- }
-
- SparseArrayValueMap* map = m_sparseValueMap;
- 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->second;
-
- // 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);
-
- putDescriptor(exec, entryInMap, descriptor, defaults);
- if (index >= m_storage->m_length)
- m_storage->m_length = 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.
- putDescriptor(exec, entryInMap, descriptor, current);
- // 13. Return true.
- return true;