#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)
+// - FastObjectElementsAccessor
+// - FastDoubleElementsAccessor
+// - ExternalElementsAccessor (abstract)
+// - ExternalByteElementsAccessor
+// - ExternalUnsignedByteElementsAccessor
+// - ExternalShortElementsAccessor
+// - ExternalUnsignedShortElementsAccessor
+// - ExternalIntElementsAccessor
+// - ExternalUnsignedIntElementsAccessor
+// - ExternalFloatElementsAccessor
+// - ExternalDoubleElementsAccessor
+// - PixelElementsAccessor
+// - DictionaryElementsAccessor
+// - NonStrictArgumentsElementsAccessor
+
+
namespace v8 {
namespace internal {
ElementsAccessor** ElementsAccessor::elements_accessors_;
-bool HasKey(FixedArray* array, Object* key) {
+static bool HasKey(FixedArray* array, Object* key) {
int len0 = array->length();
for (int i = 0; i < len0; i++) {
Object* element = array->get(i);
}
+static Failure* ThrowArrayLengthRangeError(Heap* heap) {
+ HandleScope scope(heap->isolate());
+ return heap->isolate()->Throw(
+ *heap->isolate()->factory()->NewRangeError("invalid_array_length",
+ HandleVector<Object>(NULL, 0)));
+}
+
+
// Base class for element handler implementations. Contains the
// the common logic for objects with different ElementsKinds.
// Subclasses must specialize method for which the element
return backing_store->GetHeap()->the_hole_value();
}
+ virtual MaybeObject* SetLength(JSObject* obj,
+ Object* length) {
+ ASSERT(obj->IsJSArray());
+ return ElementsAccessorSubclass::SetLength(
+ BackingStoreClass::cast(obj->elements()), obj, length);
+ }
+
+ static MaybeObject* SetLength(BackingStoreClass* backing_store,
+ JSObject* obj,
+ Object* length);
+
virtual MaybeObject* Delete(JSObject* obj,
uint32_t key,
JSReceiver::DeleteMode mode) = 0;
};
+// Super class for all fast element arrays.
+template<typename FastElementsAccessorSubclass,
+ typename BackingStore,
+ int ElementSize>
class FastElementsAccessor
- : public ElementsAccessorBase<FastElementsAccessor, FixedArray> {
+ : public ElementsAccessorBase<FastElementsAccessorSubclass, BackingStore> {
+ protected:
+ friend class ElementsAccessorBase<FastElementsAccessorSubclass, 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();
+
+ // Check whether the backing store should be shrunk.
+ if (length <= old_capacity) {
+ if (array->HasFastTypeElements()) {
+ 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 = FastD2I(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;
+ return length_object;
+ }
+
+ // Request conversion to slow elements.
+ return array->GetHeap()->undefined_value();
+ }
+};
+
+
+class FastObjectElementsAccessor
+ : public FastElementsAccessor<FastObjectElementsAccessor,
+ FixedArray,
+ kPointerSize> {
public:
static MaybeObject* DeleteCommon(JSObject* obj,
uint32_t key) {
}
protected:
+ friend class FastElementsAccessor<FastObjectElementsAccessor,
+ FixedArray,
+ kPointerSize>;
+
+ static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
+ uint32_t capacity,
+ uint32_t length) {
+ JSObject::SetFastElementsCapacityMode set_capacity_mode =
+ obj->HasFastSmiOnlyElements()
+ ? JSObject::kAllowSmiOnlyElements
+ : JSObject::kDontAllowSmiOnlyElements;
+ return obj->SetFastElementsCapacityAndLength(capacity,
+ length,
+ set_capacity_mode);
+ }
+
virtual MaybeObject* Delete(JSObject* obj,
uint32_t key,
JSReceiver::DeleteMode mode) {
class FastDoubleElementsAccessor
- : public ElementsAccessorBase<FastDoubleElementsAccessor,
- FixedDoubleArray> {
+ : public FastElementsAccessor<FastDoubleElementsAccessor,
+ FixedDoubleArray,
+ kDoubleSize> {
protected:
friend class ElementsAccessorBase<FastDoubleElementsAccessor,
FixedDoubleArray>;
+ friend class FastElementsAccessor<FastDoubleElementsAccessor,
+ FixedDoubleArray,
+ kDoubleSize>;
+
+ static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
+ uint32_t capacity,
+ uint32_t length) {
+ return obj->SetFastDoubleElementsCapacityAndLength(capacity, length);
+ }
virtual MaybeObject* Delete(JSObject* obj,
uint32_t key,
}
}
+ static MaybeObject* SetLength(ExternalArray* backing_store,
+ JSObject* obj,
+ Object* length) {
+ // External arrays do not support changing their length.
+ UNREACHABLE();
+ return obj;
+ }
+
virtual MaybeObject* Delete(JSObject* obj,
uint32_t key,
JSReceiver::DeleteMode mode) {
: public ElementsAccessorBase<DictionaryElementsAccessor,
NumberDictionary> {
public:
+ // Adjusts the length of the dictionary backing store and returns the new
+ // length according to ES5 section 15.4.5.2 behavior.
+ static MaybeObject* SetLengthWithoutNormalize(NumberDictionary* dict,
+ JSArray* array,
+ Object* length_object,
+ uint32_t length) {
+ if (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 {
+ 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.
+ Heap* heap = array->GetHeap();
+ int capacity = dict->Capacity();
+ 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;
+ }
+
+ // 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;
+ }
+
static MaybeObject* DeleteCommon(JSObject* obj,
uint32_t key,
JSReceiver::DeleteMode mode) {
}
}
+ static MaybeObject* SetLength(FixedArray* parameter_map,
+ JSObject* obj,
+ Object* length) {
+ // TODO(mstarzinger): This was never implemented but will be used once we
+ // correctly implement [[DefineOwnProperty]] on arrays.
+ UNIMPLEMENTED();
+ return obj;
+ }
+
virtual MaybeObject* Delete(JSObject* obj,
- uint32_t key
- ,
+ uint32_t key,
JSReceiver::DeleteMode mode) {
FixedArray* parameter_map = FixedArray::cast(obj->elements());
Object* probe = GetParameterMapArg(parameter_map, key);
if (arguments->IsDictionary()) {
return DictionaryElementsAccessor::DeleteCommon(obj, key, mode);
} else {
- return FastElementsAccessor::DeleteCommon(obj, key);
+ return FastObjectElementsAccessor::DeleteCommon(obj, key);
}
}
return obj->GetHeap()->true_value();
static struct ConcreteElementsAccessors {
// Use the fast element handler for smi-only arrays. The implementation is
// currently identical.
- FastElementsAccessor fast_smi_elements_handler;
- FastElementsAccessor fast_elements_handler;
+ FastObjectElementsAccessor fast_smi_elements_handler;
+ FastObjectElementsAccessor fast_elements_handler;
FastDoubleElementsAccessor fast_double_elements_handler;
DictionaryElementsAccessor dictionary_elements_handler;
NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler;
}
+template <typename ElementsAccessorSubclass, typename BackingStoreClass>
+MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass, BackingStoreClass>::
+ SetLength(BackingStoreClass* backing_store,
+ JSObject* obj,
+ Object* length) {
+ 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)) {
+ NumberDictionary* 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);
+ array->SetContent(new_backing_store);
+ return array;
+}
+
+
} } // namespace v8::internal
}
-MaybeObject* JSObject::SetSlowElements(Object* len) {
- // We should never end in here with a pixel or external array.
- ASSERT(!HasExternalArrayElements());
-
- uint32_t new_length = static_cast<uint32_t>(len->Number());
-
- FixedArrayBase* old_elements = elements();
- ElementsKind elements_kind = GetElementsKind();
- switch (elements_kind) {
- case FAST_SMI_ONLY_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS: {
- // Make sure we never try to shrink dense arrays into sparse arrays.
- ASSERT(static_cast<uint32_t>(old_elements->length()) <= new_length);
- MaybeObject* result = NormalizeElements();
- if (result->IsFailure()) return result;
-
- // Update length for JSArrays.
- if (IsJSArray()) JSArray::cast(this)->set_length(len);
- break;
- }
- case DICTIONARY_ELEMENTS: {
- if (IsJSArray()) {
- uint32_t old_length =
- static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
- element_dictionary()->RemoveNumberEntries(new_length, old_length),
- JSArray::cast(this)->set_length(len);
- }
- break;
- }
- case NON_STRICT_ARGUMENTS_ELEMENTS:
- UNIMPLEMENTED();
- break;
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS:
- case EXTERNAL_PIXEL_ELEMENTS:
- UNREACHABLE();
- break;
- }
-
- if (FLAG_trace_elements_transitions) {
- PrintElementsTransition(stdout, elements_kind, old_elements,
- DICTIONARY_ELEMENTS, elements());
- }
-
- return this;
-}
-
-
MaybeObject* JSArray::Initialize(int capacity) {
Heap* heap = GetHeap();
ASSERT(capacity >= 0);
}
-static Failure* ArrayLengthRangeError(Heap* heap) {
- HandleScope scope(heap->isolate());
- return heap->isolate()->Throw(
- *FACTORY->NewRangeError("invalid_array_length",
- HandleVector<Object>(NULL, 0)));
-}
-
-
MaybeObject* JSObject::SetElementsLength(Object* len) {
// We should never end in here with a pixel or external array.
ASSERT(AllowsSetElementsLength());
-
- MaybeObject* maybe_smi_length = len->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) return ArrayLengthRangeError(GetHeap());
- ElementsKind elements_kind = GetElementsKind();
- switch (elements_kind) {
- case FAST_SMI_ONLY_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS: {
- int old_capacity = FixedArrayBase::cast(elements())->length();
- if (value <= old_capacity) {
- if (IsJSArray()) {
- Object* obj;
- if (elements_kind == FAST_ELEMENTS ||
- elements_kind == FAST_SMI_ONLY_ELEMENTS) {
- MaybeObject* maybe_obj = EnsureWritableFastElements();
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
- if (2 * value <= old_capacity) {
- // If more than half the elements won't be used, trim the array.
- if (value == 0) {
- initialize_elements();
- } else {
- Address filler_start;
- int filler_size;
- if (elements_kind == FAST_ELEMENTS ||
- elements_kind == FAST_SMI_ONLY_ELEMENTS) {
- FixedArray* fast_elements = FixedArray::cast(elements());
- fast_elements->set_length(value);
- filler_start = fast_elements->address() +
- FixedArray::OffsetOfElementAt(value);
- filler_size = (old_capacity - value) * kPointerSize;
- } else {
- ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
- FixedDoubleArray* fast_double_elements =
- FixedDoubleArray::cast(elements());
- fast_double_elements->set_length(value);
- filler_start = fast_double_elements->address() +
- FixedDoubleArray::OffsetOfElementAt(value);
- filler_size = (old_capacity - value) * kDoubleSize;
- }
- GetHeap()->CreateFillerObjectAt(filler_start, filler_size);
- }
- } else {
- // Otherwise, fill the unused tail with holes.
- int old_length = FastD2I(JSArray::cast(this)->length()->Number());
- if (elements_kind == FAST_ELEMENTS ||
- elements_kind == FAST_SMI_ONLY_ELEMENTS) {
- FixedArray* fast_elements = FixedArray::cast(elements());
- for (int i = value; i < old_length; i++) {
- fast_elements->set_the_hole(i);
- }
- } else {
- ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS);
- FixedDoubleArray* fast_double_elements =
- FixedDoubleArray::cast(elements());
- for (int i = value; i < old_length; i++) {
- fast_double_elements->set_the_hole(i);
- }
- }
- }
- JSArray::cast(this)->set_length(Smi::cast(smi_length));
- }
- return this;
- }
- int min = NewElementsCapacity(old_capacity);
- int new_capacity = value > min ? value : min;
- if (!ShouldConvertToSlowElements(new_capacity)) {
- MaybeObject* result;
- if (elements_kind == FAST_ELEMENTS ||
- elements_kind == FAST_SMI_ONLY_ELEMENTS) {
- SetFastElementsCapacityMode set_capacity_mode =
- elements_kind == FAST_SMI_ONLY_ELEMENTS
- ? kAllowSmiOnlyElements
- : kDontAllowSmiOnlyElements;
- result = SetFastElementsCapacityAndLength(new_capacity,
- value,
- set_capacity_mode);
- } else {
- ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS);
- result = SetFastDoubleElementsCapacityAndLength(new_capacity,
- value);
- }
- if (result->IsFailure()) return result;
- return this;
- }
- break;
- }
- case DICTIONARY_ELEMENTS: {
- if (IsJSArray()) {
- if (value == 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 = ResetElements();
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
- } else {
- // Remove deleted elements.
- uint32_t old_length =
- static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
- element_dictionary()->RemoveNumberEntries(value, old_length);
- }
- JSArray::cast(this)->set_length(Smi::cast(smi_length));
- }
- return this;
- }
- case NON_STRICT_ARGUMENTS_ELEMENTS:
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS:
- case EXTERNAL_PIXEL_ELEMENTS:
- UNREACHABLE();
- break;
- }
- }
-
- // General slow case.
- if (len->IsNumber()) {
- uint32_t length;
- if (len->ToArrayIndex(&length)) {
- return SetSlowElements(len);
- } else {
- return ArrayLengthRangeError(GetHeap());
- }
- }
-
- // len is not a number so make the array size one and
- // set only element to len.
- Object* obj;
- MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- FixedArray::cast(obj)->set(0, len);
-
- maybe_obj = EnsureCanContainElements(&len, 1);
- if (maybe_obj->IsFailure()) return maybe_obj;
-
- if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
- set_elements(FixedArray::cast(obj));
- return this;
+ return GetElementsAccessor()->SetLength(this, len);
}
}
-void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
- // Do nothing if the interval [from, to) is empty.
- if (from >= to) return;
-
- Heap* heap = GetHeap();
- int removed_entries = 0;
- Object* the_hole_value = heap->the_hole_value();
- int capacity = Capacity();
- for (int i = 0; i < capacity; i++) {
- Object* key = KeyAt(i);
- if (key->IsNumber()) {
- uint32_t number = static_cast<uint32_t>(key->Number());
- if (from <= number && number < to) {
- SetEntry(i, the_hole_value, the_hole_value);
- removed_entries++;
- }
- }
- }
-
- // Update the number of elements.
- ElementsRemoved(removed_entries);
-}
-
-
template<typename Shape, typename Key>
Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
JSReceiver::DeleteMode mode) {