| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index fef784c7bff90e64a0cf356f4f5ac0b70a88db03..610301f119887c9a86c3ec5e945c5da6510d7e09 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -58,6 +58,10 @@ namespace internal {
|
| const int kGetterIndex = 0;
|
| const int kSetterIndex = 1;
|
|
|
| +uint64_t FixedDoubleArray::kHoleNanInt64 = -1;
|
| +uint64_t FixedDoubleArray::kCanonicalNonHoleNanLower32 = 0x7FF00000;
|
| +uint64_t FixedDoubleArray::kCanonicalNonHoleNanInt64 =
|
| + kCanonicalNonHoleNanLower32 << 32;
|
|
|
| MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor,
|
| Object* value) {
|
| @@ -1178,6 +1182,8 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
|
| case FIXED_ARRAY_TYPE:
|
| FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
|
| break;
|
| + case FIXED_DOUBLE_ARRAY_TYPE:
|
| + break;
|
| case JS_OBJECT_TYPE:
|
| case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
| case JS_VALUE_TYPE:
|
| @@ -2800,7 +2806,7 @@ MaybeObject* JSObject::NormalizeElements() {
|
| ASSERT(!HasExternalArrayElements());
|
| if (HasDictionaryElements()) return this;
|
| Map* old_map = map();
|
| - ASSERT(old_map->has_fast_elements());
|
| + ASSERT(old_map->has_fast_elements() || old_map->has_fast_double_elements());
|
|
|
| Object* obj;
|
| { MaybeObject* maybe_obj = old_map->GetSlowElementsMap();
|
| @@ -2809,7 +2815,7 @@ MaybeObject* JSObject::NormalizeElements() {
|
| Map* new_map = Map::cast(obj);
|
|
|
| // Get number of entries.
|
| - FixedArray* array = FixedArray::cast(elements());
|
| + FixedArrayBase* array = FixedArrayBase::cast(elements());
|
|
|
| // Compute the effective length.
|
| int length = IsJSArray() ?
|
| @@ -2818,17 +2824,35 @@ MaybeObject* JSObject::NormalizeElements() {
|
| { MaybeObject* maybe_obj = NumberDictionary::Allocate(length);
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| }
|
| + bool has_double_elements = old_map->has_fast_double_elements();
|
| NumberDictionary* dictionary = NumberDictionary::cast(obj);
|
| // Copy entries.
|
| for (int i = 0; i < length; i++) {
|
| - Object* value = array->get(i);
|
| + Object* value = NULL;
|
| + if (has_double_elements) {
|
| + FixedDoubleArray* double_array = FixedDoubleArray::cast(array);
|
| + if (double_array->is_the_hole(i)) {
|
| + value = GetIsolate()->heap()->the_hole_value();
|
| + } else {
|
| + // Objects must be allocated in the old object space, since the
|
| + // overall number of HeapNumbers needed for the conversion might
|
| + // exceed the capacity of new space, and we would fail repeatedly
|
| + // trying to convert the FixedDoubleArray.
|
| + MaybeObject* maybe_value_object =
|
| + GetHeap()->AllocateHeapNumber(double_array->get(i), TENURED);
|
| + if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
|
| + }
|
| + } else {
|
| + ASSERT(old_map->has_fast_elements());
|
| + FixedArray* fixed_array = FixedArray::cast(array);
|
| + value = fixed_array->get(i);
|
| + }
|
| + PropertyDetails details = PropertyDetails(NONE, NORMAL);
|
| if (!value->IsTheHole()) {
|
| - PropertyDetails details = PropertyDetails(NONE, NORMAL);
|
| Object* result;
|
| - { MaybeObject* maybe_result =
|
| - dictionary->AddNumberEntry(i, array->get(i), details);
|
| - if (!maybe_result->ToObject(&result)) return maybe_result;
|
| - }
|
| + MaybeObject* maybe_result =
|
| + dictionary->AddNumberEntry(i, value, details);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| dictionary = NumberDictionary::cast(result);
|
| }
|
| }
|
| @@ -2998,14 +3022,23 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
|
| { MaybeObject* maybe_obj = EnsureWritableFastElements();
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| }
|
| - uint32_t length = IsJSArray() ?
|
| - static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
|
| - static_cast<uint32_t>(FixedArray::cast(elements())->length());
|
| - if (index < length) {
|
| + int length = IsJSArray()
|
| + ? Smi::cast(JSArray::cast(this)->length())->value()
|
| + : FixedArray::cast(elements())->length();
|
| + if (index < static_cast<uint32_t>(length)) {
|
| FixedArray::cast(elements())->set_the_hole(index);
|
| }
|
| break;
|
| }
|
| + case FAST_DOUBLE_ELEMENTS: {
|
| + int length = IsJSArray()
|
| + ? Smi::cast(JSArray::cast(this)->length())->value()
|
| + : FixedArray::cast(elements())->length();
|
| + if (index < static_cast<uint32_t>(length)) {
|
| + FixedDoubleArray::cast(elements())->set_the_hole(index);
|
| + }
|
| + break;
|
| + }
|
| case EXTERNAL_PIXEL_ELEMENTS:
|
| case EXTERNAL_BYTE_ELEMENTS:
|
| case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| @@ -7098,10 +7131,10 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
|
| }
|
| Map* new_map = Map::cast(obj);
|
|
|
| - AssertNoAllocation no_gc;
|
| - WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
|
| switch (GetElementsKind()) {
|
| case FAST_ELEMENTS: {
|
| + AssertNoAllocation no_gc;
|
| + WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
|
| FixedArray* old_elements = FixedArray::cast(elements());
|
| uint32_t old_length = static_cast<uint32_t>(old_elements->length());
|
| // Fill out the new array with this content and array holes.
|
| @@ -7110,7 +7143,32 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
|
| }
|
| break;
|
| }
|
| + case FAST_DOUBLE_ELEMENTS: {
|
| + FixedDoubleArray* old_elements = FixedDoubleArray::cast(elements());
|
| + uint32_t old_length = static_cast<uint32_t>(old_elements->length());
|
| + // Fill out the new array with this content and array holes.
|
| + for (uint32_t i = 0; i < old_length; i++) {
|
| + if (!old_elements->is_the_hole(i)) {
|
| + Object* obj;
|
| + // Objects must be allocated in the old object space, since the
|
| + // overall number of HeapNumbers needed for the conversion might
|
| + // exceed the capacity of new space, and we would fail repeatedly
|
| + // trying to convert the FixedDoubleArray.
|
| + MaybeObject* maybe_value_object =
|
| + GetHeap()->AllocateHeapNumber(old_elements->get(i), TENURED);
|
| + if (!maybe_value_object->ToObject(&obj)) return maybe_value_object;
|
| + // Force write barrier. It's not worth trying to exploit
|
| + // elems->GetWriteBarrierMode(), since it requires an
|
| + // AssertNoAllocation stack object that would have to be positioned
|
| + // after the HeapNumber allocation anyway.
|
| + elems->set(i, obj, UPDATE_WRITE_BARRIER);
|
| + }
|
| + }
|
| + break;
|
| + }
|
| case DICTIONARY_ELEMENTS: {
|
| + AssertNoAllocation no_gc;
|
| + WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
|
| NumberDictionary* dictionary = NumberDictionary::cast(elements());
|
| for (int i = 0; i < dictionary->Capacity(); i++) {
|
| Object* key = dictionary->KeyAt(i);
|
| @@ -7137,6 +7195,55 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
|
| }
|
|
|
|
|
| +MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
|
| + int capacity,
|
| + int length) {
|
| + Heap* heap = GetHeap();
|
| + // We should never end in here with a pixel or external array.
|
| + ASSERT(!HasExternalArrayElements());
|
| +
|
| + Object* obj;
|
| + { MaybeObject* maybe_obj =
|
| + heap->AllocateUninitializedFixedDoubleArray(capacity);
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + }
|
| + FixedDoubleArray* elems = FixedDoubleArray::cast(obj);
|
| +
|
| + { MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap();
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + }
|
| + Map* new_map = Map::cast(obj);
|
| +
|
| + AssertNoAllocation no_gc;
|
| + switch (GetElementsKind()) {
|
| + case FAST_ELEMENTS: {
|
| + elems->Initialize(FixedArray::cast(elements()));
|
| + break;
|
| + }
|
| + case FAST_DOUBLE_ELEMENTS: {
|
| + elems->Initialize(FixedDoubleArray::cast(elements()));
|
| + break;
|
| + }
|
| + case DICTIONARY_ELEMENTS: {
|
| + elems->Initialize(NumberDictionary::cast(elements()));
|
| + break;
|
| + }
|
| + default:
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| +
|
| + set_map(new_map);
|
| + set_elements(elems);
|
| +
|
| + if (IsJSArray()) {
|
| + JSArray::cast(this)->set_length(Smi::FromInt(length));
|
| + }
|
| +
|
| + return this;
|
| +}
|
| +
|
| +
|
| MaybeObject* JSObject::SetSlowElements(Object* len) {
|
| // We should never end in here with a pixel or external array.
|
| ASSERT(!HasExternalArrayElements());
|
| @@ -7858,7 +7965,6 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
|
| if (found) return result;
|
| }
|
|
|
| -
|
| // Check whether there is extra space in fixed array..
|
| if (index < elms_length) {
|
| elms->set(index, value);
|
| @@ -7900,6 +8006,89 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
|
| }
|
|
|
|
|
| +MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
|
| + uint32_t index,
|
| + Object* value,
|
| + StrictModeFlag strict_mode,
|
| + bool check_prototype) {
|
| + ASSERT(HasFastDoubleElements());
|
| +
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
|
| + uint32_t elms_length = static_cast<uint32_t>(elms->length());
|
| +
|
| + // If storing to an element that isn't in the array, pass the store request
|
| + // up the prototype chain before storing in the receiver's elements.
|
| + if (check_prototype &&
|
| + (index >= elms_length || elms->is_the_hole(index))) {
|
| + bool found;
|
| + MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
|
| + value,
|
| + &found,
|
| + strict_mode);
|
| + if (found) return result;
|
| + }
|
| +
|
| + // If the value object is not a heap number, switch to fast elements and try
|
| + // again.
|
| + bool value_is_smi = value->IsSmi();
|
| + if (!value->IsNumber()) {
|
| + Object* obj;
|
| + uint32_t length = elms_length;
|
| + if (IsJSArray()) {
|
| + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
|
| + }
|
| + MaybeObject* maybe_obj =
|
| + SetFastElementsCapacityAndLength(elms_length, length);
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + return SetFastElement(index, value, strict_mode, check_prototype);
|
| + }
|
| +
|
| + double double_value = value_is_smi
|
| + ? static_cast<double>(Smi::cast(value)->value())
|
| + : HeapNumber::cast(value)->value();
|
| +
|
| + // Check whether there is extra space in the fixed array.
|
| + if (index < elms_length) {
|
| + elms->set(index, double_value);
|
| + if (IsJSArray()) {
|
| + // Update the length of the array if needed.
|
| + uint32_t array_length = 0;
|
| + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
|
| + if (index >= array_length) {
|
| + JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
|
| + }
|
| + }
|
| + return value;
|
| + }
|
| +
|
| + // Allow gap in fast case.
|
| + if ((index - elms_length) < kMaxGap) {
|
| + // Try allocating extra space.
|
| + int new_capacity = NewElementsCapacity(index+1);
|
| + if (new_capacity <= kMaxFastElementsLength ||
|
| + !ShouldConvertToSlowElements(new_capacity)) {
|
| + ASSERT(static_cast<uint32_t>(new_capacity) > index);
|
| + Object* obj;
|
| + { MaybeObject* maybe_obj =
|
| + SetFastDoubleElementsCapacityAndLength(new_capacity,
|
| + index + 1);
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + }
|
| + FixedDoubleArray::cast(elements())->set(index, double_value);
|
| + return value;
|
| + }
|
| + }
|
| +
|
| + // Otherwise default to slow case.
|
| + Object* obj;
|
| + { MaybeObject* maybe_obj = NormalizeElements();
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + }
|
| + ASSERT(HasDictionaryElements());
|
| + return SetElement(index, value, strict_mode, check_prototype);
|
| +}
|
| +
|
| +
|
| MaybeObject* JSObject::SetElement(uint32_t index,
|
| Object* value,
|
| StrictModeFlag strict_mode,
|
| @@ -7949,6 +8138,8 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
|
| case FAST_ELEMENTS:
|
| // Fast case.
|
| return SetFastElement(index, value, strict_mode, check_prototype);
|
| + case FAST_DOUBLE_ELEMENTS:
|
| + return SetFastDoubleElement(index, value, strict_mode, check_prototype);
|
| case EXTERNAL_PIXEL_ELEMENTS: {
|
| ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
|
| return pixels->SetValue(index, value);
|
| @@ -8072,24 +8263,35 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
|
| } else {
|
| new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
|
| }
|
| - Object* obj;
|
| - { MaybeObject* maybe_obj =
|
| - SetFastElementsCapacityAndLength(new_length, new_length);
|
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| - }
|
| + if (ShouldConvertToFastDoubleElements()) {
|
| + Object* obj;
|
| + { MaybeObject* maybe_obj =
|
| + SetFastDoubleElementsCapacityAndLength(new_length, new_length);
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + }
|
| #ifdef DEBUG
|
| - if (FLAG_trace_normalization) {
|
| - PrintF("Object elements are fast case again:\n");
|
| - Print();
|
| - }
|
| + if (FLAG_trace_normalization) {
|
| + PrintF("Object elements are fast double case again:\n");
|
| + Print();
|
| + }
|
| #endif
|
| + } else {
|
| + Object* obj;
|
| + { MaybeObject* maybe_obj =
|
| + SetFastElementsCapacityAndLength(new_length, new_length);
|
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + }
|
| +#ifdef DEBUG
|
| + if (FLAG_trace_normalization) {
|
| + PrintF("Object elements are fast case again:\n");
|
| + Print();
|
| + }
|
| +#endif
|
| + }
|
| }
|
|
|
| return value;
|
| }
|
| - default:
|
| - UNREACHABLE();
|
| - break;
|
| }
|
| // All possible cases have been handled above. Add a return to avoid the
|
| // complaints from the compiler.
|
| @@ -8129,6 +8331,15 @@ MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
|
| }
|
| break;
|
| }
|
| + case FAST_DOUBLE_ELEMENTS: {
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
|
| + if (index < static_cast<uint32_t>(elms->length())) {
|
| + if (!elms->is_the_hole(index)) {
|
| + return GetHeap()->NumberFromDouble(elms->get(index));
|
| + }
|
| + }
|
| + break;
|
| + }
|
| case EXTERNAL_PIXEL_ELEMENTS:
|
| case EXTERNAL_BYTE_ELEMENTS:
|
| case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| @@ -8232,6 +8443,16 @@ MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
|
| }
|
| break;
|
| }
|
| + case FAST_DOUBLE_ELEMENTS: {
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
|
| + if (index < static_cast<uint32_t>(elms->length())) {
|
| + if (!elms->is_the_hole(index)) {
|
| + double double_value = elms->get(index);
|
| + return GetHeap()->NumberFromDouble(double_value);
|
| + }
|
| + }
|
| + break;
|
| + }
|
| case EXTERNAL_PIXEL_ELEMENTS:
|
| case EXTERNAL_BYTE_ELEMENTS:
|
| case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| @@ -8351,6 +8572,7 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) {
|
| }
|
| break;
|
| }
|
| + case FAST_DOUBLE_ELEMENTS:
|
| case FAST_ELEMENTS:
|
| case DICTIONARY_ELEMENTS:
|
| UNREACHABLE();
|
| @@ -8373,6 +8595,14 @@ bool JSObject::HasDenseElements() {
|
| }
|
| break;
|
| }
|
| + case FAST_DOUBLE_ELEMENTS: {
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
|
| + capacity = elms->length();
|
| + for (int i = 0; i < capacity; i++) {
|
| + if (!elms->is_the_hole(i)) number_of_elements++;
|
| + }
|
| + break;
|
| + }
|
| case EXTERNAL_PIXEL_ELEMENTS:
|
| case EXTERNAL_BYTE_ELEMENTS:
|
| case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| @@ -8401,11 +8631,17 @@ bool JSObject::HasDenseElements() {
|
|
|
|
|
| bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
|
| - ASSERT(HasFastElements());
|
| // Keep the array in fast case if the current backing storage is
|
| // almost filled and if the new capacity is no more than twice the
|
| // old capacity.
|
| - int elements_length = FixedArray::cast(elements())->length();
|
| + int elements_length = 0;
|
| + if (HasFastElements()) {
|
| + elements_length = FixedArray::cast(elements())->length();
|
| + } else if (HasFastDoubleElements()) {
|
| + elements_length = FixedDoubleArray::cast(elements())->length();
|
| + } else {
|
| + UNREACHABLE();
|
| + }
|
| return !HasDenseElements() || ((new_capacity / 2) > elements_length);
|
| }
|
|
|
| @@ -8435,6 +8671,23 @@ bool JSObject::ShouldConvertToFastElements() {
|
| }
|
|
|
|
|
| +bool JSObject::ShouldConvertToFastDoubleElements() {
|
| + if (FLAG_unbox_double_arrays) {
|
| + ASSERT(HasDictionaryElements());
|
| + NumberDictionary* dictionary = NumberDictionary::cast(elements());
|
| + for (int i = 0; i < dictionary->Capacity(); i++) {
|
| + Object* key = dictionary->KeyAt(i);
|
| + if (key->IsNumber()) {
|
| + if (!dictionary->ValueAt(i)->IsNumber()) return false;
|
| + }
|
| + }
|
| + return true;
|
| + } else {
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +
|
| // Certain compilers request function template instantiation when they
|
| // see the definition of the other template functions in the
|
| // class. This requires us to have the template functions put
|
|
|