| // Copyright 2012 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "v8.h" |
| |
| #include "objects.h" |
| #include "elements.h" |
| #include "utils.h" |
| |
| |
| // Each concrete ElementsAccessor can handle exactly one ElementsKind, |
| // several abstract ElementsAccessor classes are used to allow sharing |
| // common code. |
| // |
| // Inheritance hierarchy: |
| // - ElementsAccessorBase (abstract) |
| // - FastElementsAccessor (abstract) |
| // - FastSmiOrObjectElementsAccessor |
| // - FastPackedSmiElementsAccessor |
| // - FastHoleySmiElementsAccessor |
| // - FastPackedObjectElementsAccessor |
| // - FastHoleyObjectElementsAccessor |
| // - FastDoubleElementsAccessor |
| // - FastPackedDoubleElementsAccessor |
| // - FastHoleyDoubleElementsAccessor |
| // - ExternalElementsAccessor (abstract) |
| // - ExternalByteElementsAccessor |
| // - ExternalUnsignedByteElementsAccessor |
| // - ExternalShortElementsAccessor |
| // - ExternalUnsignedShortElementsAccessor |
| // - ExternalIntElementsAccessor |
| // - ExternalUnsignedIntElementsAccessor |
| // - ExternalFloatElementsAccessor |
| // - ExternalDoubleElementsAccessor |
| // - PixelElementsAccessor |
| // - DictionaryElementsAccessor |
| // - NonStrictArgumentsElementsAccessor |
| |
| |
| namespace v8 { |
| namespace internal { |
| |
| |
| static const int kPackedSizeNotKnown = -1; |
| |
| |
| // First argument in list is the accessor class, the second argument is the |
| // accessor ElementsKind, and the third is the backing store class. Use the |
| // fast element handler for smi-only arrays. The implementation is currently |
| // identical. Note that the order must match that of the ElementsKind enum for |
| // the |accessor_array[]| below to work. |
| #define ELEMENTS_LIST(V) \ |
| V(FastPackedSmiElementsAccessor, FAST_SMI_ELEMENTS, FixedArray) \ |
| V(FastHoleySmiElementsAccessor, FAST_HOLEY_SMI_ELEMENTS, \ |
| FixedArray) \ |
| V(FastPackedObjectElementsAccessor, FAST_ELEMENTS, FixedArray) \ |
| V(FastHoleyObjectElementsAccessor, FAST_HOLEY_ELEMENTS, FixedArray) \ |
| V(FastPackedDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, \ |
| FixedDoubleArray) \ |
| V(FastHoleyDoubleElementsAccessor, FAST_HOLEY_DOUBLE_ELEMENTS, \ |
| FixedDoubleArray) \ |
| V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, \ |
| SeededNumberDictionary) \ |
| V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS, \ |
| FixedArray) \ |
| V(ExternalByteElementsAccessor, EXTERNAL_BYTE_ELEMENTS, \ |
| ExternalByteArray) \ |
| V(ExternalUnsignedByteElementsAccessor, \ |
| EXTERNAL_UNSIGNED_BYTE_ELEMENTS, ExternalUnsignedByteArray) \ |
| V(ExternalShortElementsAccessor, EXTERNAL_SHORT_ELEMENTS, \ |
| ExternalShortArray) \ |
| V(ExternalUnsignedShortElementsAccessor, \ |
| EXTERNAL_UNSIGNED_SHORT_ELEMENTS, ExternalUnsignedShortArray) \ |
| V(ExternalIntElementsAccessor, EXTERNAL_INT_ELEMENTS, \ |
| ExternalIntArray) \ |
| V(ExternalUnsignedIntElementsAccessor, \ |
| EXTERNAL_UNSIGNED_INT_ELEMENTS, ExternalUnsignedIntArray) \ |
| V(ExternalFloatElementsAccessor, \ |
| EXTERNAL_FLOAT_ELEMENTS, ExternalFloatArray) \ |
| V(ExternalDoubleElementsAccessor, \ |
| EXTERNAL_DOUBLE_ELEMENTS, ExternalDoubleArray) \ |
| V(PixelElementsAccessor, EXTERNAL_PIXEL_ELEMENTS, ExternalPixelArray) |
| |
| |
| template<ElementsKind Kind> class ElementsKindTraits { |
| public: |
| typedef FixedArrayBase BackingStore; |
| }; |
| |
| #define ELEMENTS_TRAITS(Class, KindParam, Store) \ |
| template<> class ElementsKindTraits<KindParam> { \ |
| public: \ |
| static const ElementsKind Kind = KindParam; \ |
| typedef Store BackingStore; \ |
| }; |
| ELEMENTS_LIST(ELEMENTS_TRAITS) |
| #undef ELEMENTS_TRAITS |
| |
| |
| ElementsAccessor** ElementsAccessor::elements_accessors_; |
| |
| |
| static bool HasKey(FixedArray* array, Object* key) { |
| int len0 = array->length(); |
| for (int i = 0; i < len0; i++) { |
| Object* element = array->get(i); |
| if (element->IsSmi() && element == key) return true; |
| if (element->IsString() && |
| key->IsString() && String::cast(element)->Equals(String::cast(key))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| static Failure* ThrowArrayLengthRangeError(Heap* heap) { |
| HandleScope scope(heap->isolate()); |
| return heap->isolate()->Throw( |
| *heap->isolate()->factory()->NewRangeError("invalid_array_length", |
| HandleVector<Object>(NULL, 0))); |
| } |
| |
| |
| static void CopyObjectToObjectElements(FixedArray* from, |
| ElementsKind from_kind, |
| uint32_t from_start, |
| FixedArray* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int raw_copy_size) { |
| ASSERT(to->map() != HEAP->fixed_cow_array_map()); |
| int copy_size = raw_copy_size; |
| if (raw_copy_size < 0) { |
| ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || |
| raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = Min(from->length() - from_start, |
| to->length() - to_start); |
| #ifdef DEBUG |
| // FAST_*_ELEMENTS arrays cannot be uninitialized. Ensure they are already |
| // marked with the hole. |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| for (int i = to_start + copy_size; i < to->length(); ++i) { |
| ASSERT(to->get(i)->IsTheHole()); |
| } |
| } |
| #endif |
| } |
| ASSERT((copy_size + static_cast<int>(to_start)) <= to->length() && |
| (copy_size + static_cast<int>(from_start)) <= from->length()); |
| if (copy_size == 0) return; |
| ASSERT(IsFastSmiOrObjectElementsKind(from_kind)); |
| ASSERT(IsFastSmiOrObjectElementsKind(to_kind)); |
| Address to_address = to->address() + FixedArray::kHeaderSize; |
| Address from_address = from->address() + FixedArray::kHeaderSize; |
| CopyWords(reinterpret_cast<Object**>(to_address) + to_start, |
| reinterpret_cast<Object**>(from_address) + from_start, |
| copy_size); |
| if (IsFastObjectElementsKind(from_kind) && |
| IsFastObjectElementsKind(to_kind)) { |
| Heap* heap = from->GetHeap(); |
| if (!heap->InNewSpace(to)) { |
| heap->RecordWrites(to->address(), |
| to->OffsetOfElementAt(to_start), |
| copy_size); |
| } |
| heap->incremental_marking()->RecordWrites(to); |
| } |
| } |
| |
| |
| static void CopyDictionaryToObjectElements(SeededNumberDictionary* from, |
| uint32_t from_start, |
| FixedArray* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int raw_copy_size) { |
| int copy_size = raw_copy_size; |
| Heap* heap = from->GetHeap(); |
| if (raw_copy_size < 0) { |
| ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || |
| raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = from->max_number_key() + 1 - from_start; |
| #ifdef DEBUG |
| // Fast object arrays cannot be uninitialized. Ensure they are already |
| // marked with the hole. |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| for (int i = to_start + copy_size; i < to->length(); ++i) { |
| ASSERT(to->get(i)->IsTheHole()); |
| } |
| } |
| #endif |
| } |
| ASSERT(to != from); |
| ASSERT(IsFastSmiOrObjectElementsKind(to_kind)); |
| if (copy_size == 0) return; |
| uint32_t to_length = to->length(); |
| if (to_start + copy_size > to_length) { |
| copy_size = to_length - to_start; |
| } |
| for (int i = 0; i < copy_size; i++) { |
| int entry = from->FindEntry(i + from_start); |
| if (entry != SeededNumberDictionary::kNotFound) { |
| Object* value = from->ValueAt(entry); |
| ASSERT(!value->IsTheHole()); |
| to->set(i + to_start, value, SKIP_WRITE_BARRIER); |
| } else { |
| to->set_the_hole(i + to_start); |
| } |
| } |
| if (IsFastObjectElementsKind(to_kind)) { |
| if (!heap->InNewSpace(to)) { |
| heap->RecordWrites(to->address(), |
| to->OffsetOfElementAt(to_start), |
| copy_size); |
| } |
| heap->incremental_marking()->RecordWrites(to); |
| } |
| } |
| |
| |
| MUST_USE_RESULT static MaybeObject* CopyDoubleToObjectElements( |
| FixedDoubleArray* from, |
| uint32_t from_start, |
| FixedArray* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int raw_copy_size) { |
| ASSERT(IsFastSmiOrObjectElementsKind(to_kind)); |
| int copy_size = raw_copy_size; |
| if (raw_copy_size < 0) { |
| ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || |
| raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = Min(from->length() - from_start, |
| to->length() - to_start); |
| #ifdef DEBUG |
| // FAST_*_ELEMENTS arrays cannot be uninitialized. Ensure they are already |
| // marked with the hole. |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| for (int i = to_start + copy_size; i < to->length(); ++i) { |
| ASSERT(to->get(i)->IsTheHole()); |
| } |
| } |
| #endif |
| } |
| ASSERT((copy_size + static_cast<int>(to_start)) <= to->length() && |
| (copy_size + static_cast<int>(from_start)) <= from->length()); |
| if (copy_size == 0) return from; |
| for (int i = 0; i < copy_size; ++i) { |
| if (IsFastSmiElementsKind(to_kind)) { |
| UNIMPLEMENTED(); |
| return Failure::Exception(); |
| } else { |
| MaybeObject* maybe_value = from->get(i + from_start); |
| Object* value; |
| ASSERT(IsFastObjectElementsKind(to_kind)); |
| // Because Double -> Object elements transitions allocate HeapObjects |
| // iteratively, the allocate must succeed within a single GC cycle, |
| // otherwise the retry after the GC will also fail. In order to ensure |
| // that no GC is triggered, allocate HeapNumbers from old space if they |
| // can't be taken from new space. |
| if (!maybe_value->ToObject(&value)) { |
| ASSERT(maybe_value->IsRetryAfterGC() || maybe_value->IsOutOfMemory()); |
| Heap* heap = from->GetHeap(); |
| MaybeObject* maybe_value_object = |
| heap->AllocateHeapNumber(from->get_scalar(i + from_start), |
| TENURED); |
| if (!maybe_value_object->ToObject(&value)) return maybe_value_object; |
| } |
| to->set(i + to_start, value, UPDATE_WRITE_BARRIER); |
| } |
| } |
| return to; |
| } |
| |
| |
| static void CopyDoubleToDoubleElements(FixedDoubleArray* from, |
| uint32_t from_start, |
| FixedDoubleArray* to, |
| uint32_t to_start, |
| int raw_copy_size) { |
| int copy_size = raw_copy_size; |
| if (raw_copy_size < 0) { |
| ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || |
| raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = Min(from->length() - from_start, |
| to->length() - to_start); |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| for (int i = to_start + copy_size; i < to->length(); ++i) { |
| to->set_the_hole(i); |
| } |
| } |
| } |
| ASSERT((copy_size + static_cast<int>(to_start)) <= to->length() && |
| (copy_size + static_cast<int>(from_start)) <= from->length()); |
| if (copy_size == 0) return; |
| Address to_address = to->address() + FixedDoubleArray::kHeaderSize; |
| Address from_address = from->address() + FixedDoubleArray::kHeaderSize; |
| to_address += kDoubleSize * to_start; |
| from_address += kDoubleSize * from_start; |
| int words_per_double = (kDoubleSize / kPointerSize); |
| CopyWords(reinterpret_cast<Object**>(to_address), |
| reinterpret_cast<Object**>(from_address), |
| words_per_double * copy_size); |
| } |
| |
| |
| static void CopySmiToDoubleElements(FixedArray* from, |
| uint32_t from_start, |
| FixedDoubleArray* to, |
| uint32_t to_start, |
| int raw_copy_size) { |
| int copy_size = raw_copy_size; |
| if (raw_copy_size < 0) { |
| ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || |
| raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = from->length() - from_start; |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| for (int i = to_start + copy_size; i < to->length(); ++i) { |
| to->set_the_hole(i); |
| } |
| } |
| } |
| ASSERT((copy_size + static_cast<int>(to_start)) <= to->length() && |
| (copy_size + static_cast<int>(from_start)) <= from->length()); |
| if (copy_size == 0) return; |
| Object* the_hole = from->GetHeap()->the_hole_value(); |
| for (uint32_t from_end = from_start + static_cast<uint32_t>(copy_size); |
| from_start < from_end; from_start++, to_start++) { |
| Object* hole_or_smi = from->get(from_start); |
| if (hole_or_smi == the_hole) { |
| to->set_the_hole(to_start); |
| } else { |
| to->set(to_start, Smi::cast(hole_or_smi)->value()); |
| } |
| } |
| } |
| |
| |
| static void CopyPackedSmiToDoubleElements(FixedArray* from, |
| uint32_t from_start, |
| FixedDoubleArray* to, |
| uint32_t to_start, |
| int packed_size, |
| int raw_copy_size) { |
| int copy_size = raw_copy_size; |
| uint32_t to_end; |
| if (raw_copy_size < 0) { |
| ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || |
| raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = from->length() - from_start; |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| to_end = to->length(); |
| for (uint32_t i = to_start + copy_size; i < to_end; ++i) { |
| to->set_the_hole(i); |
| } |
| } else { |
| to_end = to_start + static_cast<uint32_t>(copy_size); |
| } |
| } else { |
| to_end = to_start + static_cast<uint32_t>(copy_size); |
| } |
| ASSERT(static_cast<int>(to_end) <= to->length()); |
| ASSERT(packed_size >= 0 && packed_size <= copy_size); |
| ASSERT((copy_size + static_cast<int>(to_start)) <= to->length() && |
| (copy_size + static_cast<int>(from_start)) <= from->length()); |
| if (copy_size == 0) return; |
| for (uint32_t from_end = from_start + static_cast<uint32_t>(packed_size); |
| from_start < from_end; from_start++, to_start++) { |
| Object* smi = from->get(from_start); |
| ASSERT(!smi->IsTheHole()); |
| to->set(to_start, Smi::cast(smi)->value()); |
| } |
| } |
| |
| |
| static void CopyObjectToDoubleElements(FixedArray* from, |
| uint32_t from_start, |
| FixedDoubleArray* to, |
| uint32_t to_start, |
| int raw_copy_size) { |
| int copy_size = raw_copy_size; |
| if (raw_copy_size < 0) { |
| ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd || |
| raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = from->length() - from_start; |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| for (int i = to_start + copy_size; i < to->length(); ++i) { |
| to->set_the_hole(i); |
| } |
| } |
| } |
| ASSERT((copy_size + static_cast<int>(to_start)) <= to->length() && |
| (copy_size + static_cast<int>(from_start)) <= from->length()); |
| if (copy_size == 0) return; |
| Object* the_hole = from->GetHeap()->the_hole_value(); |
| for (uint32_t from_end = from_start + copy_size; |
| from_start < from_end; from_start++, to_start++) { |
| Object* hole_or_object = from->get(from_start); |
| if (hole_or_object == the_hole) { |
| to->set_the_hole(to_start); |
| } else { |
| to->set(to_start, hole_or_object->Number()); |
| } |
| } |
| } |
| |
| |
| static void CopyDictionaryToDoubleElements(SeededNumberDictionary* from, |
| uint32_t from_start, |
| FixedDoubleArray* to, |
| uint32_t to_start, |
| int raw_copy_size) { |
| int copy_size = raw_copy_size; |
| if (copy_size < 0) { |
| ASSERT(copy_size == ElementsAccessor::kCopyToEnd || |
| copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole); |
| copy_size = from->max_number_key() + 1 - from_start; |
| if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) { |
| for (int i = to_start + copy_size; i < to->length(); ++i) { |
| to->set_the_hole(i); |
| } |
| } |
| } |
| if (copy_size == 0) return; |
| uint32_t to_length = to->length(); |
| if (to_start + copy_size > to_length) { |
| copy_size = to_length - to_start; |
| } |
| for (int i = 0; i < copy_size; i++) { |
| int entry = from->FindEntry(i + from_start); |
| if (entry != SeededNumberDictionary::kNotFound) { |
| to->set(i + to_start, from->ValueAt(entry)->Number()); |
| } else { |
| to->set_the_hole(i + to_start); |
| } |
| } |
| } |
| |
| |
| // Base class for element handler implementations. Contains the |
| // the common logic for objects with different ElementsKinds. |
| // Subclasses must specialize method for which the element |
| // implementation differs from the base class implementation. |
| // |
| // This class is intended to be used in the following way: |
| // |
| // class SomeElementsAccessor : |
| // public ElementsAccessorBase<SomeElementsAccessor, |
| // BackingStoreClass> { |
| // ... |
| // } |
| // |
| // This is an example of the Curiously Recurring Template Pattern (see |
| // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use |
| // CRTP to guarantee aggressive compile time optimizations (i.e. inlining and |
| // specialization of SomeElementsAccessor methods). |
| template <typename ElementsAccessorSubclass, |
| typename ElementsTraitsParam> |
| class ElementsAccessorBase : public ElementsAccessor { |
| protected: |
| explicit ElementsAccessorBase(const char* name) |
| : ElementsAccessor(name) { } |
| |
| typedef ElementsTraitsParam ElementsTraits; |
| typedef typename ElementsTraitsParam::BackingStore BackingStore; |
| |
| virtual ElementsKind kind() const { return ElementsTraits::Kind; } |
| |
| static void ValidateContents(JSObject* holder, int length) { |
| } |
| |
| static void ValidateImpl(JSObject* holder) { |
| FixedArrayBase* fixed_array_base = holder->elements(); |
| // When objects are first allocated, its elements are Failures. |
| if (fixed_array_base->IsFailure()) return; |
| if (!fixed_array_base->IsHeapObject()) return; |
| Map* map = fixed_array_base->map(); |
| // Arrays that have been shifted in place can't be verified. |
| Heap* heap = holder->GetHeap(); |
| if (map == heap->one_pointer_filler_map() || |
| map == heap->two_pointer_filler_map() || |
| map == heap->free_space_map()) { |
| return; |
| } |
| int length = 0; |
| if (holder->IsJSArray()) { |
| Object* length_obj = JSArray::cast(holder)->length(); |
| if (length_obj->IsSmi()) { |
| length = Smi::cast(length_obj)->value(); |
| } |
| } else { |
| length = fixed_array_base->length(); |
| } |
| ElementsAccessorSubclass::ValidateContents(holder, length); |
| } |
| |
| virtual void Validate(JSObject* holder) { |
| ElementsAccessorSubclass::ValidateImpl(holder); |
| } |
| |
| static bool HasElementImpl(Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| BackingStore* backing_store) { |
| return ElementsAccessorSubclass::GetAttributesImpl( |
| receiver, holder, key, backing_store) != ABSENT; |
| } |
| |
| virtual bool HasElement(Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| FixedArrayBase* backing_store) { |
| if (backing_store == NULL) { |
| backing_store = holder->elements(); |
| } |
| return ElementsAccessorSubclass::HasElementImpl( |
| receiver, holder, key, BackingStore::cast(backing_store)); |
| } |
| |
| MUST_USE_RESULT virtual MaybeObject* Get(Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| FixedArrayBase* backing_store) { |
| if (backing_store == NULL) { |
| backing_store = holder->elements(); |
| } |
| return ElementsAccessorSubclass::GetImpl( |
| receiver, holder, key, BackingStore::cast(backing_store)); |
| } |
| |
| MUST_USE_RESULT static MaybeObject* GetImpl(Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| return (key < ElementsAccessorSubclass::GetCapacityImpl(backing_store)) |
| ? backing_store->get(key) |
| : backing_store->GetHeap()->the_hole_value(); |
| } |
| |
| MUST_USE_RESULT virtual PropertyAttributes GetAttributes( |
| Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| FixedArrayBase* backing_store) { |
| if (backing_store == NULL) { |
| backing_store = holder->elements(); |
| } |
| return ElementsAccessorSubclass::GetAttributesImpl( |
| receiver, holder, key, BackingStore::cast(backing_store)); |
| } |
| |
| MUST_USE_RESULT static PropertyAttributes GetAttributesImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| if (key >= ElementsAccessorSubclass::GetCapacityImpl(backing_store)) { |
| return ABSENT; |
| } |
| return backing_store->is_the_hole(key) ? ABSENT : NONE; |
| } |
| |
| MUST_USE_RESULT virtual PropertyType GetType( |
| Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| FixedArrayBase* backing_store) { |
| if (backing_store == NULL) { |
| backing_store = holder->elements(); |
| } |
| return ElementsAccessorSubclass::GetTypeImpl( |
| receiver, holder, key, BackingStore::cast(backing_store)); |
| } |
| |
| MUST_USE_RESULT static PropertyType GetTypeImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| if (key >= ElementsAccessorSubclass::GetCapacityImpl(backing_store)) { |
| return NONEXISTENT; |
| } |
| return backing_store->is_the_hole(key) ? NONEXISTENT : FIELD; |
| } |
| |
| MUST_USE_RESULT virtual AccessorPair* GetAccessorPair( |
| Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| FixedArrayBase* backing_store) { |
| if (backing_store == NULL) { |
| backing_store = holder->elements(); |
| } |
| return ElementsAccessorSubclass::GetAccessorPairImpl( |
| receiver, holder, key, BackingStore::cast(backing_store)); |
| } |
| |
| MUST_USE_RESULT static AccessorPair* GetAccessorPairImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| return NULL; |
| } |
| |
| MUST_USE_RESULT virtual MaybeObject* SetLength(JSArray* array, |
| Object* length) { |
| return ElementsAccessorSubclass::SetLengthImpl( |
| array, length, BackingStore::cast(array->elements())); |
| } |
| |
| MUST_USE_RESULT static MaybeObject* SetLengthImpl( |
| JSObject* obj, |
| Object* length, |
| BackingStore* backing_store); |
| |
| MUST_USE_RESULT virtual MaybeObject* SetCapacityAndLength( |
| JSArray* array, |
| int capacity, |
| int length) { |
| return ElementsAccessorSubclass::SetFastElementsCapacityAndLength( |
| array, |
| capacity, |
| length); |
| } |
| |
| MUST_USE_RESULT static MaybeObject* SetFastElementsCapacityAndLength( |
| JSObject* obj, |
| int capacity, |
| int length) { |
| UNIMPLEMENTED(); |
| return obj; |
| } |
| |
| MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) = 0; |
| |
| MUST_USE_RESULT static MaybeObject* CopyElementsImpl(FixedArrayBase* from, |
| uint32_t from_start, |
| FixedArrayBase* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int packed_size, |
| int copy_size) { |
| UNREACHABLE(); |
| return NULL; |
| } |
| |
| MUST_USE_RESULT virtual MaybeObject* CopyElements(JSObject* from_holder, |
| uint32_t from_start, |
| FixedArrayBase* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int copy_size, |
| FixedArrayBase* from) { |
| int packed_size = kPackedSizeNotKnown; |
| if (from == NULL) { |
| from = from_holder->elements(); |
| } |
| |
| if (from_holder) { |
| ElementsKind elements_kind = from_holder->GetElementsKind(); |
| bool is_packed = IsFastPackedElementsKind(elements_kind) && |
| from_holder->IsJSArray(); |
| if (is_packed) { |
| packed_size = Smi::cast(JSArray::cast(from_holder)->length())->value(); |
| if (copy_size >= 0 && packed_size > copy_size) { |
| packed_size = copy_size; |
| } |
| } |
| } |
| return ElementsAccessorSubclass::CopyElementsImpl( |
| from, from_start, to, to_kind, to_start, packed_size, copy_size); |
| } |
| |
| MUST_USE_RESULT virtual MaybeObject* AddElementsToFixedArray( |
| Object* receiver, |
| JSObject* holder, |
| FixedArray* to, |
| FixedArrayBase* from) { |
| int len0 = to->length(); |
| #ifdef DEBUG |
| if (FLAG_enable_slow_asserts) { |
| for (int i = 0; i < len0; i++) { |
| ASSERT(!to->get(i)->IsTheHole()); |
| } |
| } |
| #endif |
| if (from == NULL) { |
| from = holder->elements(); |
| } |
| BackingStore* backing_store = BackingStore::cast(from); |
| uint32_t len1 = ElementsAccessorSubclass::GetCapacityImpl(backing_store); |
| |
| // Optimize if 'other' is empty. |
| // We cannot optimize if 'this' is empty, as other may have holes. |
| if (len1 == 0) return to; |
| |
| // Compute how many elements are not in other. |
| uint32_t extra = 0; |
| for (uint32_t y = 0; y < len1; y++) { |
| uint32_t key = |
| ElementsAccessorSubclass::GetKeyForIndexImpl(backing_store, y); |
| if (ElementsAccessorSubclass::HasElementImpl( |
| receiver, holder, key, backing_store)) { |
| MaybeObject* maybe_value = |
| ElementsAccessorSubclass::GetImpl(receiver, holder, |
| key, backing_store); |
| Object* value; |
| if (!maybe_value->ToObject(&value)) return maybe_value; |
| ASSERT(!value->IsTheHole()); |
| if (!HasKey(to, value)) { |
| extra++; |
| } |
| } |
| } |
| |
| if (extra == 0) return to; |
| |
| // Allocate the result |
| FixedArray* result; |
| MaybeObject* maybe_obj = |
| backing_store->GetHeap()->AllocateFixedArray(len0 + extra); |
| if (!maybe_obj->To<FixedArray>(&result)) return maybe_obj; |
| |
| // Fill in the content |
| { |
| AssertNoAllocation no_gc; |
| WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); |
| for (int i = 0; i < len0; i++) { |
| Object* e = to->get(i); |
| ASSERT(e->IsString() || e->IsNumber()); |
| result->set(i, e, mode); |
| } |
| } |
| // Fill in the extra values. |
| uint32_t index = 0; |
| for (uint32_t y = 0; y < len1; y++) { |
| uint32_t key = |
| ElementsAccessorSubclass::GetKeyForIndexImpl(backing_store, y); |
| if (ElementsAccessorSubclass::HasElementImpl( |
| receiver, holder, key, backing_store)) { |
| MaybeObject* maybe_value = |
| ElementsAccessorSubclass::GetImpl(receiver, holder, |
| key, backing_store); |
| Object* value; |
| if (!maybe_value->ToObject(&value)) return maybe_value; |
| if (!value->IsTheHole() && !HasKey(to, value)) { |
| result->set(len0 + index, value); |
| index++; |
| } |
| } |
| } |
| ASSERT(extra == index); |
| return result; |
| } |
| |
| protected: |
| static uint32_t GetCapacityImpl(BackingStore* backing_store) { |
| return backing_store->length(); |
| } |
| |
| virtual uint32_t GetCapacity(FixedArrayBase* backing_store) { |
| return ElementsAccessorSubclass::GetCapacityImpl( |
| BackingStore::cast(backing_store)); |
| } |
| |
| static uint32_t GetKeyForIndexImpl(BackingStore* backing_store, |
| uint32_t index) { |
| return index; |
| } |
| |
| virtual uint32_t GetKeyForIndex(FixedArrayBase* backing_store, |
| uint32_t index) { |
| return ElementsAccessorSubclass::GetKeyForIndexImpl( |
| BackingStore::cast(backing_store), index); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase); |
| }; |
| |
| |
| // Super class for all fast element arrays. |
| template<typename FastElementsAccessorSubclass, |
| typename KindTraits, |
| int ElementSize> |
| class FastElementsAccessor |
| : public ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits> { |
| public: |
| explicit FastElementsAccessor(const char* name) |
| : ElementsAccessorBase<FastElementsAccessorSubclass, |
| KindTraits>(name) {} |
| protected: |
| friend class ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits>; |
| friend class NonStrictArgumentsElementsAccessor; |
| |
| typedef typename KindTraits::BackingStore BackingStore; |
| |
| // Adjusts the length of the fast backing store or returns the new length or |
| // undefined in case conversion to a slow backing store should be performed. |
| static MaybeObject* SetLengthWithoutNormalize(BackingStore* backing_store, |
| JSArray* array, |
| Object* length_object, |
| uint32_t length) { |
| uint32_t old_capacity = backing_store->length(); |
| Object* old_length = array->length(); |
| bool same_or_smaller_size = old_length->IsSmi() && |
| static_cast<uint32_t>(Smi::cast(old_length)->value()) >= length; |
| ElementsKind kind = array->GetElementsKind(); |
| |
| if (!same_or_smaller_size && IsFastElementsKind(kind) && |
| !IsFastHoleyElementsKind(kind)) { |
| kind = GetHoleyElementsKind(kind); |
| MaybeObject* maybe_obj = array->TransitionElementsKind(kind); |
| if (maybe_obj->IsFailure()) return maybe_obj; |
| } |
| |
| // Check whether the backing store should be shrunk. |
| if (length <= old_capacity) { |
| if (array->HasFastSmiOrObjectElements()) { |
| MaybeObject* maybe_obj = array->EnsureWritableFastElements(); |
| if (!maybe_obj->To(&backing_store)) return maybe_obj; |
| } |
| if (2 * length <= old_capacity) { |
| // If more than half the elements won't be used, trim the array. |
| if (length == 0) { |
| array->initialize_elements(); |
| } else { |
| backing_store->set_length(length); |
| Address filler_start = backing_store->address() + |
| BackingStore::OffsetOfElementAt(length); |
| int filler_size = (old_capacity - length) * ElementSize; |
| array->GetHeap()->CreateFillerObjectAt(filler_start, filler_size); |
| } |
| } else { |
| // Otherwise, fill the unused tail with holes. |
| int old_length = FastD2IChecked(array->length()->Number()); |
| for (int i = length; i < old_length; i++) { |
| backing_store->set_the_hole(i); |
| } |
| } |
| return length_object; |
| } |
| |
| // Check whether the backing store should be expanded. |
| uint32_t min = JSObject::NewElementsCapacity(old_capacity); |
| uint32_t new_capacity = length > min ? length : min; |
| if (!array->ShouldConvertToSlowElements(new_capacity)) { |
| MaybeObject* result = FastElementsAccessorSubclass:: |
| SetFastElementsCapacityAndLength(array, new_capacity, length); |
| if (result->IsFailure()) return result; |
| array->ValidateElements(); |
| return length_object; |
| } |
| |
| // Request conversion to slow elements. |
| return array->GetHeap()->undefined_value(); |
| } |
| |
| static MaybeObject* DeleteCommon(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| ASSERT(obj->HasFastSmiOrObjectElements() || |
| obj->HasFastDoubleElements() || |
| obj->HasFastArgumentsElements()); |
| Heap* heap = obj->GetHeap(); |
| Object* elements = obj->elements(); |
| if (elements == heap->empty_fixed_array()) { |
| return heap->true_value(); |
| } |
| typename KindTraits::BackingStore* backing_store = |
| KindTraits::BackingStore::cast(elements); |
| bool is_non_strict_arguments_elements_map = |
| backing_store->map() == heap->non_strict_arguments_elements_map(); |
| if (is_non_strict_arguments_elements_map) { |
| backing_store = |
| KindTraits::BackingStore::cast( |
| FixedArray::cast(backing_store)->get(1)); |
| } |
| uint32_t length = static_cast<uint32_t>( |
| obj->IsJSArray() |
| ? Smi::cast(JSArray::cast(obj)->length())->value() |
| : backing_store->length()); |
| if (key < length) { |
| if (!is_non_strict_arguments_elements_map) { |
| ElementsKind kind = KindTraits::Kind; |
| if (IsFastPackedElementsKind(kind)) { |
| MaybeObject* transitioned = |
| obj->TransitionElementsKind(GetHoleyElementsKind(kind)); |
| if (transitioned->IsFailure()) return transitioned; |
| } |
| if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) { |
| Object* writable; |
| MaybeObject* maybe = obj->EnsureWritableFastElements(); |
| if (!maybe->ToObject(&writable)) return maybe; |
| backing_store = KindTraits::BackingStore::cast(writable); |
| } |
| } |
| backing_store->set_the_hole(key); |
| // If an old space backing store is larger than a certain size and |
| // has too few used values, normalize it. |
| // To avoid doing the check on every delete we require at least |
| // one adjacent hole to the value being deleted. |
| const int kMinLengthForSparsenessCheck = 64; |
| if (backing_store->length() >= kMinLengthForSparsenessCheck && |
| !heap->InNewSpace(backing_store) && |
| ((key > 0 && backing_store->is_the_hole(key - 1)) || |
| (key + 1 < length && backing_store->is_the_hole(key + 1)))) { |
| int num_used = 0; |
| for (int i = 0; i < backing_store->length(); ++i) { |
| if (!backing_store->is_the_hole(i)) ++num_used; |
| // Bail out early if more than 1/4 is used. |
| if (4 * num_used > backing_store->length()) break; |
| } |
| if (4 * num_used <= backing_store->length()) { |
| MaybeObject* result = obj->NormalizeElements(); |
| if (result->IsFailure()) return result; |
| } |
| } |
| } |
| return heap->true_value(); |
| } |
| |
| virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| return DeleteCommon(obj, key, mode); |
| } |
| |
| static bool HasElementImpl( |
| Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| typename KindTraits::BackingStore* backing_store) { |
| if (key >= static_cast<uint32_t>(backing_store->length())) { |
| return false; |
| } |
| return !backing_store->is_the_hole(key); |
| } |
| |
| static void ValidateContents(JSObject* holder, int length) { |
| #if DEBUG |
| FixedArrayBase* elements = holder->elements(); |
| Heap* heap = elements->GetHeap(); |
| Map* map = elements->map(); |
| ASSERT((IsFastSmiOrObjectElementsKind(KindTraits::Kind) && |
| (map == heap->fixed_array_map() || |
| map == heap->fixed_cow_array_map())) || |
| (IsFastDoubleElementsKind(KindTraits::Kind) == |
| ((map == heap->fixed_array_map() && length == 0) || |
| map == heap->fixed_double_array_map()))); |
| for (int i = 0; i < length; i++) { |
| typename KindTraits::BackingStore* backing_store = |
| KindTraits::BackingStore::cast(elements); |
| ASSERT((!IsFastSmiElementsKind(KindTraits::Kind) || |
| static_cast<Object*>(backing_store->get(i))->IsSmi()) || |
| (IsFastHoleyElementsKind(KindTraits::Kind) == |
| backing_store->is_the_hole(i))); |
| } |
| #endif |
| } |
| }; |
| |
| |
| template<typename FastElementsAccessorSubclass, |
| typename KindTraits> |
| class FastSmiOrObjectElementsAccessor |
| : public FastElementsAccessor<FastElementsAccessorSubclass, |
| KindTraits, |
| kPointerSize> { |
| public: |
| explicit FastSmiOrObjectElementsAccessor(const char* name) |
| : FastElementsAccessor<FastElementsAccessorSubclass, |
| KindTraits, |
| kPointerSize>(name) {} |
| |
| static MaybeObject* CopyElementsImpl(FixedArrayBase* from, |
| uint32_t from_start, |
| FixedArrayBase* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int packed_size, |
| int copy_size) { |
| if (IsFastSmiOrObjectElementsKind(to_kind)) { |
| CopyObjectToObjectElements( |
| FixedArray::cast(from), KindTraits::Kind, from_start, |
| FixedArray::cast(to), to_kind, to_start, copy_size); |
| } else if (IsFastDoubleElementsKind(to_kind)) { |
| if (IsFastSmiElementsKind(KindTraits::Kind)) { |
| if (IsFastPackedElementsKind(KindTraits::Kind) && |
| packed_size != kPackedSizeNotKnown) { |
| CopyPackedSmiToDoubleElements( |
| FixedArray::cast(from), from_start, |
| FixedDoubleArray::castOrEmptyFixedArray(to), to_start, |
| packed_size, copy_size); |
| } else { |
| CopySmiToDoubleElements( |
| FixedArray::cast(from), from_start, |
| FixedDoubleArray::castOrEmptyFixedArray(to), to_start, copy_size); |
| } |
| } else { |
| CopyObjectToDoubleElements( |
| FixedArray::cast(from), from_start, |
| FixedDoubleArray::castOrEmptyFixedArray(to), to_start, copy_size); |
| } |
| } else { |
| UNREACHABLE(); |
| } |
| return to->GetHeap()->undefined_value(); |
| } |
| |
| |
| static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj, |
| uint32_t capacity, |
| uint32_t length) { |
| JSObject::SetFastElementsCapacitySmiMode set_capacity_mode = |
| obj->HasFastSmiElements() |
| ? JSObject::kAllowSmiElements |
| : JSObject::kDontAllowSmiElements; |
| return obj->SetFastElementsCapacityAndLength(capacity, |
| length, |
| set_capacity_mode); |
| } |
| }; |
| |
| |
| class FastPackedSmiElementsAccessor |
| : public FastSmiOrObjectElementsAccessor< |
| FastPackedSmiElementsAccessor, |
| ElementsKindTraits<FAST_SMI_ELEMENTS> > { |
| public: |
| explicit FastPackedSmiElementsAccessor(const char* name) |
| : FastSmiOrObjectElementsAccessor< |
| FastPackedSmiElementsAccessor, |
| ElementsKindTraits<FAST_SMI_ELEMENTS> >(name) {} |
| }; |
| |
| |
| class FastHoleySmiElementsAccessor |
| : public FastSmiOrObjectElementsAccessor< |
| FastHoleySmiElementsAccessor, |
| ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> > { |
| public: |
| explicit FastHoleySmiElementsAccessor(const char* name) |
| : FastSmiOrObjectElementsAccessor< |
| FastHoleySmiElementsAccessor, |
| ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> >(name) {} |
| }; |
| |
| |
| class FastPackedObjectElementsAccessor |
| : public FastSmiOrObjectElementsAccessor< |
| FastPackedObjectElementsAccessor, |
| ElementsKindTraits<FAST_ELEMENTS> > { |
| public: |
| explicit FastPackedObjectElementsAccessor(const char* name) |
| : FastSmiOrObjectElementsAccessor< |
| FastPackedObjectElementsAccessor, |
| ElementsKindTraits<FAST_ELEMENTS> >(name) {} |
| }; |
| |
| |
| class FastHoleyObjectElementsAccessor |
| : public FastSmiOrObjectElementsAccessor< |
| FastHoleyObjectElementsAccessor, |
| ElementsKindTraits<FAST_HOLEY_ELEMENTS> > { |
| public: |
| explicit FastHoleyObjectElementsAccessor(const char* name) |
| : FastSmiOrObjectElementsAccessor< |
| FastHoleyObjectElementsAccessor, |
| ElementsKindTraits<FAST_HOLEY_ELEMENTS> >(name) {} |
| }; |
| |
| |
| template<typename FastElementsAccessorSubclass, |
| typename KindTraits> |
| class FastDoubleElementsAccessor |
| : public FastElementsAccessor<FastElementsAccessorSubclass, |
| KindTraits, |
| kDoubleSize> { |
| public: |
| explicit FastDoubleElementsAccessor(const char* name) |
| : FastElementsAccessor<FastElementsAccessorSubclass, |
| KindTraits, |
| kDoubleSize>(name) {} |
| |
| static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj, |
| uint32_t capacity, |
| uint32_t length) { |
| return obj->SetFastDoubleElementsCapacityAndLength(capacity, |
| length); |
| } |
| |
| protected: |
| static MaybeObject* CopyElementsImpl(FixedArrayBase* from, |
| uint32_t from_start, |
| FixedArrayBase* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int packed_size, |
| int copy_size) { |
| switch (to_kind) { |
| case FAST_SMI_ELEMENTS: |
| case FAST_ELEMENTS: |
| case FAST_HOLEY_SMI_ELEMENTS: |
| case FAST_HOLEY_ELEMENTS: |
| return CopyDoubleToObjectElements( |
| FixedDoubleArray::castOrEmptyFixedArray(from), from_start, |
| FixedArray::cast(to), to_kind, to_start, copy_size); |
| case FAST_DOUBLE_ELEMENTS: |
| case FAST_HOLEY_DOUBLE_ELEMENTS: |
| CopyDoubleToDoubleElements( |
| FixedDoubleArray::castOrEmptyFixedArray(from), from_start, |
| FixedDoubleArray::castOrEmptyFixedArray(to), to_start, copy_size); |
| return from; |
| default: |
| UNREACHABLE(); |
| } |
| return to->GetHeap()->undefined_value(); |
| } |
| }; |
| |
| |
| class FastPackedDoubleElementsAccessor |
| : public FastDoubleElementsAccessor< |
| FastPackedDoubleElementsAccessor, |
| ElementsKindTraits<FAST_DOUBLE_ELEMENTS> > { |
| public: |
| friend class ElementsAccessorBase<FastPackedDoubleElementsAccessor, |
| ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >; |
| explicit FastPackedDoubleElementsAccessor(const char* name) |
| : FastDoubleElementsAccessor< |
| FastPackedDoubleElementsAccessor, |
| ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >(name) {} |
| }; |
| |
| |
| class FastHoleyDoubleElementsAccessor |
| : public FastDoubleElementsAccessor< |
| FastHoleyDoubleElementsAccessor, |
| ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> > { |
| public: |
| friend class ElementsAccessorBase< |
| FastHoleyDoubleElementsAccessor, |
| ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> >; |
| explicit FastHoleyDoubleElementsAccessor(const char* name) |
| : FastDoubleElementsAccessor< |
| FastHoleyDoubleElementsAccessor, |
| ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> >(name) {} |
| }; |
| |
| |
| // Super class for all external element arrays. |
| template<typename ExternalElementsAccessorSubclass, |
| ElementsKind Kind> |
| class ExternalElementsAccessor |
| : public ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| ElementsKindTraits<Kind> > { |
| public: |
| explicit ExternalElementsAccessor(const char* name) |
| : ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| ElementsKindTraits<Kind> >(name) {} |
| |
| protected: |
| typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore; |
| |
| friend class ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| ElementsKindTraits<Kind> >; |
| |
| MUST_USE_RESULT static MaybeObject* GetImpl(Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| return |
| key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store) |
| ? backing_store->get(key) |
| : backing_store->GetHeap()->undefined_value(); |
| } |
| |
| MUST_USE_RESULT static PropertyAttributes GetAttributesImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| return |
| key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store) |
| ? NONE : ABSENT; |
| } |
| |
| MUST_USE_RESULT static PropertyType GetTypeImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| return |
| key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store) |
| ? FIELD : NONEXISTENT; |
| } |
| |
| MUST_USE_RESULT static MaybeObject* SetLengthImpl( |
| JSObject* obj, |
| Object* length, |
| BackingStore* backing_store) { |
| // External arrays do not support changing their length. |
| UNREACHABLE(); |
| return obj; |
| } |
| |
| MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| // External arrays always ignore deletes. |
| return obj->GetHeap()->true_value(); |
| } |
| |
| static bool HasElementImpl(Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| BackingStore* backing_store) { |
| uint32_t capacity = |
| ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store); |
| return key < capacity; |
| } |
| }; |
| |
| |
| class ExternalByteElementsAccessor |
| : public ExternalElementsAccessor<ExternalByteElementsAccessor, |
| EXTERNAL_BYTE_ELEMENTS> { |
| public: |
| explicit ExternalByteElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalByteElementsAccessor, |
| EXTERNAL_BYTE_ELEMENTS>(name) {} |
| }; |
| |
| |
| class ExternalUnsignedByteElementsAccessor |
| : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor, |
| EXTERNAL_UNSIGNED_BYTE_ELEMENTS> { |
| public: |
| explicit ExternalUnsignedByteElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor, |
| EXTERNAL_UNSIGNED_BYTE_ELEMENTS>(name) {} |
| }; |
| |
| |
| class ExternalShortElementsAccessor |
| : public ExternalElementsAccessor<ExternalShortElementsAccessor, |
| EXTERNAL_SHORT_ELEMENTS> { |
| public: |
| explicit ExternalShortElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalShortElementsAccessor, |
| EXTERNAL_SHORT_ELEMENTS>(name) {} |
| }; |
| |
| |
| class ExternalUnsignedShortElementsAccessor |
| : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor, |
| EXTERNAL_UNSIGNED_SHORT_ELEMENTS> { |
| public: |
| explicit ExternalUnsignedShortElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor, |
| EXTERNAL_UNSIGNED_SHORT_ELEMENTS>(name) {} |
| }; |
| |
| |
| class ExternalIntElementsAccessor |
| : public ExternalElementsAccessor<ExternalIntElementsAccessor, |
| EXTERNAL_INT_ELEMENTS> { |
| public: |
| explicit ExternalIntElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalIntElementsAccessor, |
| EXTERNAL_INT_ELEMENTS>(name) {} |
| }; |
| |
| |
| class ExternalUnsignedIntElementsAccessor |
| : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor, |
| EXTERNAL_UNSIGNED_INT_ELEMENTS> { |
| public: |
| explicit ExternalUnsignedIntElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor, |
| EXTERNAL_UNSIGNED_INT_ELEMENTS>(name) {} |
| }; |
| |
| |
| class ExternalFloatElementsAccessor |
| : public ExternalElementsAccessor<ExternalFloatElementsAccessor, |
| EXTERNAL_FLOAT_ELEMENTS> { |
| public: |
| explicit ExternalFloatElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalFloatElementsAccessor, |
| EXTERNAL_FLOAT_ELEMENTS>(name) {} |
| }; |
| |
| |
| class ExternalDoubleElementsAccessor |
| : public ExternalElementsAccessor<ExternalDoubleElementsAccessor, |
| EXTERNAL_DOUBLE_ELEMENTS> { |
| public: |
| explicit ExternalDoubleElementsAccessor(const char* name) |
| : ExternalElementsAccessor<ExternalDoubleElementsAccessor, |
| EXTERNAL_DOUBLE_ELEMENTS>(name) {} |
| }; |
| |
| |
| class PixelElementsAccessor |
| : public ExternalElementsAccessor<PixelElementsAccessor, |
| EXTERNAL_PIXEL_ELEMENTS> { |
| public: |
| explicit PixelElementsAccessor(const char* name) |
| : ExternalElementsAccessor<PixelElementsAccessor, |
| EXTERNAL_PIXEL_ELEMENTS>(name) {} |
| }; |
| |
| |
| class DictionaryElementsAccessor |
| : public ElementsAccessorBase<DictionaryElementsAccessor, |
| ElementsKindTraits<DICTIONARY_ELEMENTS> > { |
| public: |
| explicit DictionaryElementsAccessor(const char* name) |
| : ElementsAccessorBase<DictionaryElementsAccessor, |
| ElementsKindTraits<DICTIONARY_ELEMENTS> >(name) {} |
| |
| // Adjusts the length of the dictionary backing store and returns the new |
| // length according to ES5 section 15.4.5.2 behavior. |
| MUST_USE_RESULT static MaybeObject* SetLengthWithoutNormalize( |
| SeededNumberDictionary* dict, |
| JSArray* array, |
| Object* length_object, |
| uint32_t length) { |
| Heap* heap = array->GetHeap(); |
| int capacity = dict->Capacity(); |
| uint32_t new_length = length; |
| uint32_t old_length = static_cast<uint32_t>(array->length()->Number()); |
| if (new_length < old_length) { |
| // Find last non-deletable element in range of elements to be |
| // deleted and adjust range accordingly. |
| for (int i = 0; i < capacity; i++) { |
| Object* key = dict->KeyAt(i); |
| if (key->IsNumber()) { |
| uint32_t number = static_cast<uint32_t>(key->Number()); |
| if (new_length <= number && number < old_length) { |
| PropertyDetails details = dict->DetailsAt(i); |
| if (details.IsDontDelete()) new_length = number + 1; |
| } |
| } |
| } |
| if (new_length != length) { |
| MaybeObject* maybe_object = heap->NumberFromUint32(new_length); |
| if (!maybe_object->To(&length_object)) return maybe_object; |
| } |
| } |
| |
| if (new_length == 0) { |
| // If the length of a slow array is reset to zero, we clear |
| // the array and flush backing storage. This has the added |
| // benefit that the array returns to fast mode. |
| Object* obj; |
| MaybeObject* maybe_obj = array->ResetElements(); |
| if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
| } else { |
| // Remove elements that should be deleted. |
| int removed_entries = 0; |
| Object* the_hole_value = heap->the_hole_value(); |
| for (int i = 0; i < capacity; i++) { |
| Object* key = dict->KeyAt(i); |
| if (key->IsNumber()) { |
| uint32_t number = static_cast<uint32_t>(key->Number()); |
| if (new_length <= number && number < old_length) { |
| dict->SetEntry(i, the_hole_value, the_hole_value); |
| removed_entries++; |
| } |
| } |
| } |
| |
| // Update the number of elements. |
| dict->ElementsRemoved(removed_entries); |
| } |
| return length_object; |
| } |
| |
| MUST_USE_RESULT static MaybeObject* DeleteCommon( |
| JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| Isolate* isolate = obj->GetIsolate(); |
| Heap* heap = isolate->heap(); |
| FixedArray* backing_store = FixedArray::cast(obj->elements()); |
| bool is_arguments = |
| (obj->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS); |
| if (is_arguments) { |
| backing_store = FixedArray::cast(backing_store->get(1)); |
| } |
| SeededNumberDictionary* dictionary = |
| SeededNumberDictionary::cast(backing_store); |
| int entry = dictionary->FindEntry(key); |
| if (entry != SeededNumberDictionary::kNotFound) { |
| Object* result = dictionary->DeleteProperty(entry, mode); |
| if (result == heap->false_value()) { |
| if (mode == JSObject::STRICT_DELETION) { |
| // Deleting a non-configurable property in strict mode. |
| HandleScope scope(isolate); |
| Handle<Object> holder(obj); |
| Handle<Object> name = isolate->factory()->NewNumberFromUint(key); |
| Handle<Object> args[2] = { name, holder }; |
| Handle<Object> error = |
| isolate->factory()->NewTypeError("strict_delete_property", |
| HandleVector(args, 2)); |
| return isolate->Throw(*error); |
| } |
| return heap->false_value(); |
| } |
| MaybeObject* maybe_elements = dictionary->Shrink(key); |
| FixedArray* new_elements = NULL; |
| if (!maybe_elements->To(&new_elements)) { |
| return maybe_elements; |
| } |
| if (is_arguments) { |
| FixedArray::cast(obj->elements())->set(1, new_elements); |
| } else { |
| obj->set_elements(new_elements); |
| } |
| } |
| return heap->true_value(); |
| } |
| |
| MUST_USE_RESULT static MaybeObject* CopyElementsImpl(FixedArrayBase* from, |
| uint32_t from_start, |
| FixedArrayBase* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int packed_size, |
| int copy_size) { |
| switch (to_kind) { |
| case FAST_SMI_ELEMENTS: |
| case FAST_ELEMENTS: |
| case FAST_HOLEY_SMI_ELEMENTS: |
| case FAST_HOLEY_ELEMENTS: |
| CopyDictionaryToObjectElements( |
| SeededNumberDictionary::cast(from), from_start, |
| FixedArray::cast(to), to_kind, to_start, copy_size); |
| return from; |
| case FAST_DOUBLE_ELEMENTS: |
| case FAST_HOLEY_DOUBLE_ELEMENTS: |
| CopyDictionaryToDoubleElements( |
| SeededNumberDictionary::cast(from), from_start, |
| FixedDoubleArray::castOrEmptyFixedArray(to), to_start, |
| copy_size); |
| return from; |
| default: |
| UNREACHABLE(); |
| } |
| return to->GetHeap()->undefined_value(); |
| } |
| |
| |
| protected: |
| friend class ElementsAccessorBase<DictionaryElementsAccessor, |
| ElementsKindTraits<DICTIONARY_ELEMENTS> >; |
| |
| MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| return DeleteCommon(obj, key, mode); |
| } |
| |
| MUST_USE_RESULT static MaybeObject* GetImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| SeededNumberDictionary* backing_store) { |
| int entry = backing_store->FindEntry(key); |
| if (entry != SeededNumberDictionary::kNotFound) { |
| Object* element = backing_store->ValueAt(entry); |
| PropertyDetails details = backing_store->DetailsAt(entry); |
| if (details.type() == CALLBACKS) { |
| return obj->GetElementWithCallback(receiver, |
| element, |
| key, |
| obj); |
| } else { |
| return element; |
| } |
| } |
| return obj->GetHeap()->the_hole_value(); |
| } |
| |
| MUST_USE_RESULT static PropertyAttributes GetAttributesImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| SeededNumberDictionary* backing_store) { |
| int entry = backing_store->FindEntry(key); |
| if (entry != SeededNumberDictionary::kNotFound) { |
| return backing_store->DetailsAt(entry).attributes(); |
| } |
| return ABSENT; |
| } |
| |
| MUST_USE_RESULT static PropertyType GetTypeImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| SeededNumberDictionary* backing_store) { |
| int entry = backing_store->FindEntry(key); |
| if (entry != SeededNumberDictionary::kNotFound) { |
| return backing_store->DetailsAt(entry).type(); |
| } |
| return NONEXISTENT; |
| } |
| |
| MUST_USE_RESULT static AccessorPair* GetAccessorPairImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| BackingStore* backing_store) { |
| int entry = backing_store->FindEntry(key); |
| if (entry != SeededNumberDictionary::kNotFound && |
| backing_store->DetailsAt(entry).type() == CALLBACKS && |
| backing_store->ValueAt(entry)->IsAccessorPair()) { |
| return AccessorPair::cast(backing_store->ValueAt(entry)); |
| } |
| return NULL; |
| } |
| |
| static bool HasElementImpl(Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| SeededNumberDictionary* backing_store) { |
| return backing_store->FindEntry(key) != |
| SeededNumberDictionary::kNotFound; |
| } |
| |
| static uint32_t GetKeyForIndexImpl(SeededNumberDictionary* dict, |
| uint32_t index) { |
| Object* key = dict->KeyAt(index); |
| return Smi::cast(key)->value(); |
| } |
| }; |
| |
| |
| class NonStrictArgumentsElementsAccessor : public ElementsAccessorBase< |
| NonStrictArgumentsElementsAccessor, |
| ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> > { |
| public: |
| explicit NonStrictArgumentsElementsAccessor(const char* name) |
| : ElementsAccessorBase< |
| NonStrictArgumentsElementsAccessor, |
| ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >(name) {} |
| protected: |
| friend class ElementsAccessorBase< |
| NonStrictArgumentsElementsAccessor, |
| ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >; |
| |
| MUST_USE_RESULT static MaybeObject* GetImpl(Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| FixedArray* parameter_map) { |
| Object* probe = GetParameterMapArg(obj, parameter_map, key); |
| if (!probe->IsTheHole()) { |
| Context* context = Context::cast(parameter_map->get(0)); |
| int context_index = Smi::cast(probe)->value(); |
| ASSERT(!context->get(context_index)->IsTheHole()); |
| return context->get(context_index); |
| } else { |
| // Object is not mapped, defer to the arguments. |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| MaybeObject* maybe_result = ElementsAccessor::ForArray(arguments)->Get( |
| receiver, obj, key, arguments); |
| Object* result; |
| if (!maybe_result->ToObject(&result)) return maybe_result; |
| // Elements of the arguments object in slow mode might be slow aliases. |
| if (result->IsAliasedArgumentsEntry()) { |
| AliasedArgumentsEntry* entry = AliasedArgumentsEntry::cast(result); |
| Context* context = Context::cast(parameter_map->get(0)); |
| int context_index = entry->aliased_context_slot(); |
| ASSERT(!context->get(context_index)->IsTheHole()); |
| return context->get(context_index); |
| } else { |
| return result; |
| } |
| } |
| } |
| |
| MUST_USE_RESULT static PropertyAttributes GetAttributesImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| FixedArray* parameter_map) { |
| Object* probe = GetParameterMapArg(obj, parameter_map, key); |
| if (!probe->IsTheHole()) { |
| return NONE; |
| } else { |
| // If not aliased, check the arguments. |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| return ElementsAccessor::ForArray(arguments)->GetAttributes( |
| receiver, obj, key, arguments); |
| } |
| } |
| |
| MUST_USE_RESULT static PropertyType GetTypeImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| FixedArray* parameter_map) { |
| Object* probe = GetParameterMapArg(obj, parameter_map, key); |
| if (!probe->IsTheHole()) { |
| return FIELD; |
| } else { |
| // If not aliased, check the arguments. |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| return ElementsAccessor::ForArray(arguments)->GetType( |
| receiver, obj, key, arguments); |
| } |
| } |
| |
| MUST_USE_RESULT static AccessorPair* GetAccessorPairImpl( |
| Object* receiver, |
| JSObject* obj, |
| uint32_t key, |
| FixedArray* parameter_map) { |
| Object* probe = GetParameterMapArg(obj, parameter_map, key); |
| if (!probe->IsTheHole()) { |
| return NULL; |
| } else { |
| // If not aliased, check the arguments. |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| return ElementsAccessor::ForArray(arguments)->GetAccessorPair( |
| receiver, obj, key, arguments); |
| } |
| } |
| |
| MUST_USE_RESULT static MaybeObject* SetLengthImpl( |
| JSObject* obj, |
| Object* length, |
| FixedArray* parameter_map) { |
| // TODO(mstarzinger): This was never implemented but will be used once we |
| // correctly implement [[DefineOwnProperty]] on arrays. |
| UNIMPLEMENTED(); |
| return obj; |
| } |
| |
| MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| FixedArray* parameter_map = FixedArray::cast(obj->elements()); |
| Object* probe = GetParameterMapArg(obj, parameter_map, key); |
| if (!probe->IsTheHole()) { |
| // TODO(kmillikin): We could check if this was the last aliased |
| // parameter, and revert to normal elements in that case. That |
| // would enable GC of the context. |
| parameter_map->set_the_hole(key + 2); |
| } else { |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| if (arguments->IsDictionary()) { |
| return DictionaryElementsAccessor::DeleteCommon(obj, key, mode); |
| } else { |
| // It's difficult to access the version of DeleteCommon that is declared |
| // in the templatized super class, call the concrete implementation in |
| // the class for the most generalized ElementsKind subclass. |
| return FastHoleyObjectElementsAccessor::DeleteCommon(obj, key, mode); |
| } |
| } |
| return obj->GetHeap()->true_value(); |
| } |
| |
| MUST_USE_RESULT static MaybeObject* CopyElementsImpl(FixedArrayBase* from, |
| uint32_t from_start, |
| FixedArrayBase* to, |
| ElementsKind to_kind, |
| uint32_t to_start, |
| int packed_size, |
| int copy_size) { |
| FixedArray* parameter_map = FixedArray::cast(from); |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| ElementsAccessor* accessor = ElementsAccessor::ForArray(arguments); |
| return accessor->CopyElements(NULL, from_start, to, to_kind, |
| to_start, copy_size, arguments); |
| } |
| |
| static uint32_t GetCapacityImpl(FixedArray* parameter_map) { |
| FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); |
| return Max(static_cast<uint32_t>(parameter_map->length() - 2), |
| ForArray(arguments)->GetCapacity(arguments)); |
| } |
| |
| static uint32_t GetKeyForIndexImpl(FixedArray* dict, |
| uint32_t index) { |
| return index; |
| } |
| |
| static bool HasElementImpl(Object* receiver, |
| JSObject* holder, |
| uint32_t key, |
| FixedArray* parameter_map) { |
| Object* probe = GetParameterMapArg(holder, parameter_map, key); |
| if (!probe->IsTheHole()) { |
| return true; |
| } else { |
| FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); |
| ElementsAccessor* accessor = ElementsAccessor::ForArray(arguments); |
| return !accessor->Get(receiver, holder, key, arguments)->IsTheHole(); |
| } |
| } |
| |
| private: |
| static Object* GetParameterMapArg(JSObject* holder, |
| FixedArray* parameter_map, |
| uint32_t key) { |
| uint32_t length = holder->IsJSArray() |
| ? Smi::cast(JSArray::cast(holder)->length())->value() |
| : parameter_map->length(); |
| return key < (length - 2 ) |
| ? parameter_map->get(key + 2) |
| : parameter_map->GetHeap()->the_hole_value(); |
| } |
| }; |
| |
| |
| ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) { |
| switch (array->map()->instance_type()) { |
| case FIXED_ARRAY_TYPE: |
| if (array->IsDictionary()) { |
| return elements_accessors_[DICTIONARY_ELEMENTS]; |
| } else { |
| return elements_accessors_[FAST_HOLEY_ELEMENTS]; |
| } |
| case EXTERNAL_BYTE_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_BYTE_ELEMENTS]; |
| case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_UNSIGNED_BYTE_ELEMENTS]; |
| case EXTERNAL_SHORT_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_SHORT_ELEMENTS]; |
| case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_UNSIGNED_SHORT_ELEMENTS]; |
| case EXTERNAL_INT_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_INT_ELEMENTS]; |
| case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_UNSIGNED_INT_ELEMENTS]; |
| case EXTERNAL_FLOAT_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_FLOAT_ELEMENTS]; |
| case EXTERNAL_DOUBLE_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_DOUBLE_ELEMENTS]; |
| case EXTERNAL_PIXEL_ARRAY_TYPE: |
| return elements_accessors_[EXTERNAL_PIXEL_ELEMENTS]; |
| default: |
| UNREACHABLE(); |
| return NULL; |
| } |
| } |
| |
| |
| void ElementsAccessor::InitializeOncePerProcess() { |
| static ElementsAccessor* accessor_array[] = { |
| #define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind), |
| ELEMENTS_LIST(ACCESSOR_ARRAY) |
| #undef ACCESSOR_ARRAY |
| }; |
| |
| STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) == |
| kElementsKindCount); |
| |
| elements_accessors_ = accessor_array; |
| } |
| |
| |
| void ElementsAccessor::TearDown() { |
| #define ACCESSOR_DELETE(Class, Kind, Store) delete elements_accessors_[Kind]; |
| ELEMENTS_LIST(ACCESSOR_DELETE) |
| #undef ACCESSOR_DELETE |
| elements_accessors_ = NULL; |
| } |
| |
| |
| template <typename ElementsAccessorSubclass, typename ElementsKindTraits> |
| MUST_USE_RESULT MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass, |
| ElementsKindTraits>:: |
| SetLengthImpl(JSObject* obj, |
| Object* length, |
| typename ElementsKindTraits::BackingStore* backing_store) { |
| JSArray* array = JSArray::cast(obj); |
| |
| // Fast case: The new length fits into a Smi. |
| MaybeObject* maybe_smi_length = length->ToSmi(); |
| Object* smi_length = Smi::FromInt(0); |
| if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) { |
| const int value = Smi::cast(smi_length)->value(); |
| if (value >= 0) { |
| Object* new_length; |
| MaybeObject* result = ElementsAccessorSubclass:: |
| SetLengthWithoutNormalize(backing_store, array, smi_length, value); |
| if (!result->ToObject(&new_length)) return result; |
| ASSERT(new_length->IsSmi() || new_length->IsUndefined()); |
| if (new_length->IsSmi()) { |
| array->set_length(Smi::cast(new_length)); |
| return array; |
| } |
| } else { |
| return ThrowArrayLengthRangeError(array->GetHeap()); |
| } |
| } |
| |
| // Slow case: The new length does not fit into a Smi or conversion |
| // to slow elements is needed for other reasons. |
| if (length->IsNumber()) { |
| uint32_t value; |
| if (length->ToArrayIndex(&value)) { |
| SeededNumberDictionary* dictionary; |
| MaybeObject* maybe_object = array->NormalizeElements(); |
| if (!maybe_object->To(&dictionary)) return maybe_object; |
| Object* new_length; |
| MaybeObject* result = DictionaryElementsAccessor:: |
| SetLengthWithoutNormalize(dictionary, array, length, value); |
| if (!result->ToObject(&new_length)) return result; |
| ASSERT(new_length->IsNumber()); |
| array->set_length(new_length); |
| return array; |
| } else { |
| return ThrowArrayLengthRangeError(array->GetHeap()); |
| } |
| } |
| |
| // Fall-back case: The new length is not a number so make the array |
| // size one and set only element to length. |
| FixedArray* new_backing_store; |
| MaybeObject* maybe_obj = array->GetHeap()->AllocateFixedArray(1); |
| if (!maybe_obj->To(&new_backing_store)) return maybe_obj; |
| new_backing_store->set(0, length); |
| { MaybeObject* result = array->SetContent(new_backing_store); |
| if (result->IsFailure()) return result; |
| } |
| return array; |
| } |
| |
| |
| } } // namespace v8::internal |