Index: src/elements.cc |
diff --git a/src/elements.cc b/src/elements.cc |
index 5e7a84e38b8377a6009a93ecc4967832f168dc19..0097b9d810e2575083c7c3bded056e17de38c5fb 100644 |
--- a/src/elements.cc |
+++ b/src/elements.cc |
@@ -31,6 +31,30 @@ |
#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 { |
@@ -38,7 +62,7 @@ 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); |
@@ -52,6 +76,14 @@ bool HasKey(FixedArray* array, Object* key) { |
} |
+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 |
@@ -91,6 +123,17 @@ class ElementsAccessorBase : public ElementsAccessor { |
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; |
@@ -222,8 +265,70 @@ class ElementsAccessorBase : public ElementsAccessor { |
}; |
+// 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) { |
@@ -272,6 +377,22 @@ class FastElementsAccessor |
} |
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) { |
@@ -281,11 +402,21 @@ class FastElementsAccessor |
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, |
@@ -329,6 +460,14 @@ class ExternalElementsAccessor |
} |
} |
+ 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) { |
@@ -396,6 +535,63 @@ class DictionaryElementsAccessor |
: 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* sentinel = heap->null_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, sentinel, sentinel); |
+ 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) { |
@@ -505,9 +701,17 @@ class NonStrictArgumentsElementsAccessor |
} |
} |
+ 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); |
@@ -521,7 +725,7 @@ class NonStrictArgumentsElementsAccessor |
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(); |
@@ -600,8 +804,8 @@ void ElementsAccessor::InitializeOncePerProcess() { |
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; |
@@ -640,4 +844,62 @@ void ElementsAccessor::InitializeOncePerProcess() { |
} |
+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 |