Chromium Code Reviews| Index: src/objects.cc | 
| diff --git a/src/objects.cc b/src/objects.cc | 
| index 970fc24550878072e45297bd6c0601a576165ba3..dd9437221c2c6b33d9e17086ab39931fa07c2016 100644 | 
| --- a/src/objects.cc | 
| +++ b/src/objects.cc | 
| @@ -56,11 +56,6 @@ | 
| namespace v8 { | 
| namespace internal { | 
| -void PrintElementsKind(FILE* out, ElementsKind kind) { | 
| - ElementsAccessor* accessor = ElementsAccessor::ForKind(kind); | 
| - PrintF(out, "%s", accessor->name()); | 
| -} | 
| - | 
| MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor, | 
| Object* value) { | 
| @@ -543,7 +538,7 @@ bool JSObject::IsDirty() { | 
| // If the object is fully fast case and has the same map it was | 
| // created with then no changes can have been made to it. | 
| return map() != fun->initial_map() | 
| - || !HasFastElements() | 
| + || !HasFastObjectElements() | 
| || !HasFastProperties(); | 
| } | 
| @@ -902,8 +897,8 @@ MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) { | 
| bool String::MakeExternal(v8::String::ExternalStringResource* resource) { | 
| - // Externalizing twice leaks the external resource, so it's | 
| - // prohibited by the API. | 
| + // Externalizing twice leaks the external resource, so its prohibited by the | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
Why this change? Original version was correct.
 
danno
2012/05/22 11:05:21
Done.
 
 | 
| + // API. | 
| ASSERT(!this->IsExternalString()); | 
| #ifdef DEBUG | 
| if (FLAG_enable_slow_asserts) { | 
| @@ -1067,7 +1062,9 @@ void String::StringShortPrint(StringStream* accumulator) { | 
| void JSObject::JSObjectShortPrint(StringStream* accumulator) { | 
| switch (map()->instance_type()) { | 
| case JS_ARRAY_TYPE: { | 
| - double length = JSArray::cast(this)->length()->Number(); | 
| + double length = JSArray::cast(this)->length()->IsUndefined() | 
| + ? 0 : | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
nit: I'd put the colon on the next line, directly
 
danno
2012/05/22 11:05:21
Done.
 
 | 
| + JSArray::cast(this)->length()->Number(); | 
| accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length)); | 
| break; | 
| } | 
| @@ -2199,34 +2196,29 @@ static Handle<T> MaybeNull(T* p) { | 
| Handle<Map> Map::FindTransitionedMap(MapHandleList* candidates) { | 
| - ElementsKind elms_kind = elements_kind(); | 
| - if (elms_kind == FAST_DOUBLE_ELEMENTS) { | 
| - bool dummy = true; | 
| - Handle<Map> fast_map = | 
| - MaybeNull(LookupElementsTransitionMap(FAST_ELEMENTS, &dummy)); | 
| - if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { | 
| - return fast_map; | 
| - } | 
| - return Handle<Map>::null(); | 
| - } | 
| - if (elms_kind == FAST_SMI_ONLY_ELEMENTS) { | 
| - bool dummy = true; | 
| - Handle<Map> double_map = | 
| - MaybeNull(LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, &dummy)); | 
| - // In the current implementation, if the DOUBLE map doesn't exist, the | 
| - // FAST map can't exist either. | 
| - if (double_map.is_null()) return Handle<Map>::null(); | 
| - Handle<Map> fast_map = | 
| - MaybeNull(double_map->LookupElementsTransitionMap(FAST_ELEMENTS, | 
| - &dummy)); | 
| - if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { | 
| - return fast_map; | 
| - } | 
| - if (ContainsMap(candidates, double_map)) return double_map; | 
| - } | 
| - return Handle<Map>::null(); | 
| + ElementsKind kind = elements_kind(); | 
| + Handle<Map> transitioned_map = Handle<Map>::null(); | 
| + Handle<Map> current_map(this); | 
| + bool packed = IsFastPackedElementsKind(kind); | 
| + if (IsTransitionableFastElementsKind(kind)) { | 
| + while (CanTransitionToMoreGeneralFastElementsKind(kind, false)) { | 
| + kind = GetNextMoreGeneralFastElementsKind(kind, false); | 
| + bool dummy = true; | 
| + Handle<Map> maybe_transitioned_map = | 
| + MaybeNull(current_map->LookupElementsTransitionMap(kind, &dummy)); | 
| + if (maybe_transitioned_map.is_null()) break; | 
| + if (ContainsMap(candidates, maybe_transitioned_map) && | 
| + (packed || !IsFastPackedElementsKind(kind))) { | 
| + transitioned_map = maybe_transitioned_map; | 
| + if (!IsFastPackedElementsKind(kind)) packed = false; | 
| + } | 
| + current_map = maybe_transitioned_map; | 
| + } | 
| + } | 
| + return transitioned_map; | 
| } | 
| + | 
| static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents, | 
| ElementsKind elements_kind) { | 
| if (descriptor_contents->IsMap()) { | 
| @@ -2335,24 +2327,36 @@ Object* Map::GetDescriptorContents(String* sentinel_name, | 
| } | 
| -Map* Map::LookupElementsTransitionMap(ElementsKind elements_kind, | 
| +Map* Map::LookupElementsTransitionMap(ElementsKind to_kind, | 
| bool* safe_to_add_transition) { | 
| - // Special case: indirect SMI->FAST transition (cf. comment in | 
| - // AddElementsTransition()). | 
| - if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && | 
| - elements_kind == FAST_ELEMENTS) { | 
| - Map* double_map = this->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, | 
| - safe_to_add_transition); | 
| - if (double_map == NULL) return double_map; | 
| - return double_map->LookupElementsTransitionMap(FAST_ELEMENTS, | 
| + ElementsKind from_kind = elements_kind(); | 
| + if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) { | 
| + if (!IsMoreGeneralElementsKindTransition(from_kind, to_kind)) { | 
| + if (safe_to_add_transition) *safe_to_add_transition = false; | 
| + return NULL; | 
| + } | 
| + ElementsKind transitioned_from_kind = | 
| + GetNextMoreGeneralFastElementsKind(from_kind, false); | 
| + | 
| + | 
| + // If the transition is a single step in the transition sequence, fall | 
| + // through to looking it up and returning it. If it requires several steps, | 
| + // divide and conquer. | 
| + if (transitioned_from_kind != to_kind) { | 
| + // If the transition is several steps in the lattice, divide and conquer. | 
| + Map* from_map = LookupElementsTransitionMap(transitioned_from_kind, | 
| + safe_to_add_transition); | 
| + if (from_map == NULL) return NULL; | 
| + return from_map->LookupElementsTransitionMap(to_kind, | 
| safe_to_add_transition); | 
| + } | 
| } | 
| Object* descriptor_contents = GetDescriptorContents( | 
| elements_transition_sentinel_name(), safe_to_add_transition); | 
| if (descriptor_contents != NULL) { | 
| Map* maybe_transition_map = | 
| GetElementsTransitionMapFromDescriptor(descriptor_contents, | 
| - elements_kind); | 
| + to_kind); | 
| ASSERT(maybe_transition_map == NULL || maybe_transition_map->IsMap()); | 
| return maybe_transition_map; | 
| } | 
| @@ -2360,29 +2364,35 @@ Map* Map::LookupElementsTransitionMap(ElementsKind elements_kind, | 
| } | 
| -MaybeObject* Map::AddElementsTransition(ElementsKind elements_kind, | 
| +MaybeObject* Map::AddElementsTransition(ElementsKind to_kind, | 
| Map* transitioned_map) { | 
| - // The map transition graph should be a tree, therefore the transition | 
| - // from SMI to FAST elements is not done directly, but by going through | 
| - // DOUBLE elements first. | 
| - if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && | 
| - elements_kind == FAST_ELEMENTS) { | 
| - bool safe_to_add = true; | 
| - Map* double_map = this->LookupElementsTransitionMap( | 
| - FAST_DOUBLE_ELEMENTS, &safe_to_add); | 
| - // This method is only called when safe_to_add_transition has been found | 
| - // to be true earlier. | 
| - ASSERT(safe_to_add); | 
| - | 
| - if (double_map == NULL) { | 
| - MaybeObject* maybe_map = this->CopyDropTransitions(); | 
| - if (!maybe_map->To(&double_map)) return maybe_map; | 
| - double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); | 
| - MaybeObject* maybe_double_transition = this->AddElementsTransition( | 
| - FAST_DOUBLE_ELEMENTS, double_map); | 
| - if (maybe_double_transition->IsFailure()) return maybe_double_transition; | 
| - } | 
| - return double_map->AddElementsTransition(FAST_ELEMENTS, transitioned_map); | 
| + ElementsKind from_kind = elements_kind(); | 
| + if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) { | 
| + ASSERT(IsMoreGeneralElementsKindTransition(from_kind, to_kind)); | 
| + ElementsKind transitioned_from_kind = | 
| + GetNextMoreGeneralFastElementsKind(from_kind, false); | 
| + // The map transitions graph should be a tree, therefore transitions to | 
| + // ElementsKind that are not adjacent in the ElementsKind sequence are not | 
| + // done directly, but instead by going through intermediate ElementsKinds | 
| + // first. | 
| + if (to_kind != transitioned_from_kind) { | 
| + bool safe_to_add = true; | 
| + Map* intermediate_map = LookupElementsTransitionMap( | 
| + transitioned_from_kind, &safe_to_add); | 
| + // This method is only called when safe_to_add_transition has been found | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
nit: s/safe_to_add_transition/safe_to_add/
 
danno
2012/05/22 11:05:21
Done.
 
 | 
| + // to be true earlier. | 
| + ASSERT(safe_to_add); | 
| + | 
| + if (intermediate_map == NULL) { | 
| + MaybeObject* maybe_map = CopyDropTransitions(); | 
| + if (!maybe_map->To(&intermediate_map)) return maybe_map; | 
| + intermediate_map->set_elements_kind(transitioned_from_kind); | 
| + MaybeObject* maybe_transition = AddElementsTransition( | 
| + transitioned_from_kind, intermediate_map); | 
| + if (maybe_transition->IsFailure()) return maybe_transition; | 
| + } | 
| + return intermediate_map->AddElementsTransition(to_kind, transitioned_map); | 
| + } | 
| } | 
| bool safe_to_add_transition = true; | 
| @@ -2433,10 +2443,11 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { | 
| !current_map->IsUndefined() && | 
| !current_map->is_shared(); | 
| - // Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps caused by objects | 
| + // Prevent long chains of DICTIONARY -> FAST_*_ELEMENTS maps caused by objects | 
| // with elements that switch back and forth between dictionary and fast | 
| - // element mode. | 
| - if (from_kind == DICTIONARY_ELEMENTS && to_kind == FAST_ELEMENTS) { | 
| + // element modes. | 
| + if (from_kind == DICTIONARY_ELEMENTS && | 
| + IsFastObjectElementsKind(to_kind)) { | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
Wouldn't it make sense to check for any FAST kind
 
danno
2012/05/22 11:05:21
Done.
 
 | 
| safe_to_add_transition = false; | 
| } | 
| @@ -3472,8 +3483,7 @@ MaybeObject* JSObject::NormalizeElements() { | 
| } | 
| if (array->IsDictionary()) return array; | 
| - ASSERT(HasFastElements() || | 
| - HasFastSmiOnlyElements() || | 
| + ASSERT(HasFastSmiOrObjectElements() || | 
| HasFastDoubleElements() || | 
| HasFastArgumentsElements()); | 
| // Compute the effective length and allocate a new backing store. | 
| @@ -3508,8 +3518,7 @@ MaybeObject* JSObject::NormalizeElements() { | 
| if (!maybe_value_object->ToObject(&value)) return maybe_value_object; | 
| } | 
| } else { | 
| - ASSERT(old_map->has_fast_elements() || | 
| - old_map->has_fast_smi_only_elements()); | 
| + ASSERT(old_map->has_fast_smi_or_object_elements()); | 
| value = FixedArray::cast(array)->get(i); | 
| } | 
| PropertyDetails details = PropertyDetails(NONE, NORMAL); | 
| @@ -3996,9 +4005,9 @@ MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) { | 
| bool JSObject::ReferencesObjectFromElements(FixedArray* elements, | 
| ElementsKind kind, | 
| Object* object) { | 
| - ASSERT(kind == FAST_ELEMENTS || | 
| + ASSERT(IsFastObjectElementsKind(kind) || | 
| kind == DICTIONARY_ELEMENTS); | 
| - if (kind == FAST_ELEMENTS) { | 
| + if (IsFastObjectElementsKind(kind)) { | 
| int length = IsJSArray() | 
| ? Smi::cast(JSArray::cast(this)->length())->value() | 
| : elements->length(); | 
| @@ -4050,12 +4059,15 @@ bool JSObject::ReferencesObject(Object* obj) { | 
| case EXTERNAL_FLOAT_ELEMENTS: | 
| case EXTERNAL_DOUBLE_ELEMENTS: | 
| case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: | 
| // Raw pixels and external arrays do not reference other | 
| // objects. | 
| break; | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| + case FAST_SMI_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| break; | 
| case FAST_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: | 
| case DICTIONARY_ELEMENTS: { | 
| FixedArray* elements = FixedArray::cast(this->elements()); | 
| if (ReferencesObjectFromElements(elements, kind, obj)) return true; | 
| @@ -4071,7 +4083,8 @@ bool JSObject::ReferencesObject(Object* obj) { | 
| } | 
| // Check the arguments. | 
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); | 
| - kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : FAST_ELEMENTS; | 
| + kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : | 
| + FAST_HOLEY_ELEMENTS; | 
| if (ReferencesObjectFromElements(arguments, kind, obj)) return true; | 
| break; | 
| } | 
| @@ -4305,7 +4318,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) { | 
| } | 
| -// Search object and it's prototype chain for callback properties. | 
| +// Search object and its prototype chain for callback properties. | 
| void JSObject::LookupCallback(String* name, LookupResult* result) { | 
| Heap* heap = GetHeap(); | 
| for (Object* current = this; | 
| @@ -4349,9 +4362,12 @@ MaybeObject* JSObject::DefineElementAccessor(uint32_t index, | 
| Object* setter, | 
| PropertyAttributes attributes) { | 
| switch (GetElementsKind()) { | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| + case FAST_SMI_ELEMENTS: | 
| case FAST_ELEMENTS: | 
| case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: | 
| break; | 
| case EXTERNAL_PIXEL_ELEMENTS: | 
| case EXTERNAL_BYTE_ELEMENTS: | 
| @@ -4468,7 +4484,7 @@ bool JSObject::CanSetCallback(String* name) { | 
| GetIsolate()->MayNamedAccess(this, name, v8::ACCESS_SET)); | 
| // Check if there is an API defined callback object which prohibits | 
| - // callback overwriting in this object or it's prototype chain. | 
| + // callback overwriting in this object or its prototype chain. | 
| // This mechanism is needed for instance in a browser setting, where | 
| // certain accessors such as window.location should not be allowed | 
| // to be overwritten because allowing overwriting could potentially | 
| @@ -4790,9 +4806,12 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { | 
| // Accessors overwrite previous callbacks (cf. with getters/setters). | 
| switch (GetElementsKind()) { | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| + case FAST_SMI_ELEMENTS: | 
| case FAST_ELEMENTS: | 
| case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: | 
| break; | 
| case EXTERNAL_PIXEL_ELEMENTS: | 
| case EXTERNAL_BYTE_ELEMENTS: | 
| @@ -8645,7 +8664,7 @@ void Code::Disassemble(const char* name, FILE* out) { | 
| MaybeObject* JSObject::SetFastElementsCapacityAndLength( | 
| int capacity, | 
| int length, | 
| - SetFastElementsCapacityMode set_capacity_mode) { | 
| + SetFastElementsCapacitySmiMode smi_mode) { | 
| Heap* heap = GetHeap(); | 
| // We should never end in here with a pixel or external array. | 
| ASSERT(!HasExternalArrayElements()); | 
| @@ -8656,32 +8675,37 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( | 
| if (!maybe->To(&new_elements)) return maybe; | 
| } | 
| - // Find the new map to use for this object if there is a map change. | 
| - Map* new_map = NULL; | 
| - if (elements()->map() != heap->non_strict_arguments_elements_map()) { | 
| - // The resized array has FAST_SMI_ONLY_ELEMENTS if the capacity mode forces | 
| - // it, or if it's allowed and the old elements array contained only SMIs. | 
| - bool has_fast_smi_only_elements = | 
| - (set_capacity_mode == kForceSmiOnlyElements) || | 
| - ((set_capacity_mode == kAllowSmiOnlyElements) && | 
| - (elements()->map()->has_fast_smi_only_elements() || | 
| - elements() == heap->empty_fixed_array())); | 
| - ElementsKind elements_kind = has_fast_smi_only_elements | 
| - ? FAST_SMI_ONLY_ELEMENTS | 
| - : FAST_ELEMENTS; | 
| - MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), elements_kind); | 
| - if (!maybe->To(&new_map)) return maybe; | 
| + ElementsKind elements_kind = GetElementsKind(); | 
| + ElementsKind new_elements_kind; | 
| + // The resized array has FAST_*_SMI_ELEMENTS if the capacity mode forces it, | 
| + // or if it's allowed and the old elements array contained only SMIs. | 
| + bool has_fast_smi_elements = | 
| + (smi_mode == kForceSmiOnlyElements) || | 
| + ((smi_mode == kAllowSmiOnlyElements) && HasFastSmiElements()); | 
| + if (has_fast_smi_elements) { | 
| + if (IsHoleyElementsKind(elements_kind)) { | 
| + new_elements_kind = FAST_HOLEY_SMI_ELEMENTS; | 
| + } else { | 
| + new_elements_kind = FAST_SMI_ELEMENTS; | 
| + } | 
| + } else { | 
| + if (IsHoleyElementsKind(elements_kind)) { | 
| + new_elements_kind = FAST_HOLEY_ELEMENTS; | 
| + } else { | 
| + new_elements_kind = FAST_ELEMENTS; | 
| + } | 
| } | 
| - | 
| FixedArrayBase* old_elements = elements(); | 
| - ElementsKind elements_kind = GetElementsKind(); | 
| ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); | 
| - ElementsKind to_kind = (elements_kind == FAST_SMI_ONLY_ELEMENTS) | 
| - ? FAST_SMI_ONLY_ELEMENTS | 
| - : FAST_ELEMENTS; | 
| - // int copy_size = Min(old_elements_raw->length(), new_elements->length()); | 
| - accessor->CopyElements(this, new_elements, to_kind); | 
| + accessor->CopyElements(this, new_elements, new_elements_kind); | 
| if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { | 
| + Map* new_map = map(); | 
| + if (new_elements_kind != elements_kind) { | 
| + MaybeObject* maybe = | 
| + GetElementsTransitionMap(GetIsolate(), new_elements_kind); | 
| + if (!maybe->To(&new_map)) return maybe; | 
| + } | 
| + ValidateElements(); | 
| set_map_and_elements(new_map, new_elements); | 
| } else { | 
| FixedArray* parameter_map = FixedArray::cast(old_elements); | 
| @@ -8693,11 +8717,9 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( | 
| GetElementsKind(), new_elements); | 
| } | 
| - // Update the length if necessary. | 
| if (IsJSArray()) { | 
| JSArray::cast(this)->set_length(Smi::FromInt(length)); | 
| } | 
| - | 
| return new_elements; | 
| } | 
| @@ -8715,17 +8737,25 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( | 
| if (!maybe_obj->To(&elems)) return maybe_obj; | 
| } | 
| + ElementsKind elements_kind = GetElementsKind(); | 
| + ElementsKind new_elements_kind = elements_kind; | 
| + if (IsHoleyElementsKind(elements_kind)) { | 
| + new_elements_kind = FAST_HOLEY_DOUBLE_ELEMENTS; | 
| + } else { | 
| + new_elements_kind = FAST_DOUBLE_ELEMENTS; | 
| + } | 
| + | 
| Map* new_map; | 
| { MaybeObject* maybe_obj = | 
| - GetElementsTransitionMap(heap->isolate(), FAST_DOUBLE_ELEMENTS); | 
| + GetElementsTransitionMap(heap->isolate(), new_elements_kind); | 
| if (!maybe_obj->To(&new_map)) return maybe_obj; | 
| } | 
| FixedArrayBase* old_elements = elements(); | 
| - ElementsKind elements_kind = GetElementsKind(); | 
| ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); | 
| accessor->CopyElements(this, elems, FAST_DOUBLE_ELEMENTS); | 
| if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { | 
| + ValidateElements(); | 
| set_map_and_elements(new_map, elems); | 
| } else { | 
| FixedArray* parameter_map = FixedArray::cast(old_elements); | 
| @@ -8734,7 +8764,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( | 
| if (FLAG_trace_elements_transitions) { | 
| PrintElementsTransition(stdout, elements_kind, old_elements, | 
| - FAST_DOUBLE_ELEMENTS, elems); | 
| + GetElementsKind(), elems); | 
| } | 
| if (IsJSArray()) { | 
| @@ -9014,8 +9044,10 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { | 
| } | 
| switch (GetElementsKind()) { | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| - case FAST_ELEMENTS: { | 
| + case FAST_SMI_ELEMENTS: | 
| + case FAST_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: { | 
| uint32_t length = IsJSArray() ? | 
| static_cast<uint32_t> | 
| (Smi::cast(JSArray::cast(this)->length())->value()) : | 
| @@ -9026,7 +9058,8 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { | 
| } | 
| break; | 
| } | 
| - case FAST_DOUBLE_ELEMENTS: { | 
| + case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: { | 
| uint32_t length = IsJSArray() ? | 
| static_cast<uint32_t> | 
| (Smi::cast(JSArray::cast(this)->length())->value()) : | 
| @@ -9310,7 +9343,7 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, | 
| Object* value, | 
| StrictModeFlag strict_mode, | 
| bool check_prototype) { | 
| - ASSERT(HasFastTypeElements() || | 
| + ASSERT(HasFastSmiOrObjectElements() || | 
| HasFastArgumentsElements()); | 
| FixedArray* backing_store = FixedArray::cast(elements()); | 
| @@ -9336,13 +9369,29 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, | 
| // Check if the length property of this object needs to be updated. | 
| uint32_t array_length = 0; | 
| bool must_update_array_length = false; | 
| + bool introduces_holes = true; | 
| if (IsJSArray()) { | 
| CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); | 
| + introduces_holes = index > array_length; | 
| if (index >= array_length) { | 
| must_update_array_length = true; | 
| array_length = index + 1; | 
| } | 
| + } else { | 
| + introduces_holes = index >= capacity; | 
| + } | 
| + | 
| + // If the array is growing, and it's not growth by a single element at the | 
| + // end, make sure that the ElementsKind is HOLEY. | 
| + ElementsKind elements_kind = GetElementsKind(); | 
| + if (introduces_holes && | 
| + IsFastElementsKind(elements_kind) && | 
| + !IsFastHoleyElementsKind(elements_kind)) { | 
| + ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); | 
| + MaybeObject* maybe = TransitionElementsKind(transitioned_kind); | 
| + if (maybe->IsFailure()) return maybe; | 
| } | 
| + | 
| // Check if the capacity of the backing store needs to be increased, or if | 
| // a transition to slow elements is necessary. | 
| if (index >= capacity) { | 
| @@ -9362,42 +9411,47 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, | 
| } | 
| } | 
| // Convert to fast double elements if appropriate. | 
| - if (HasFastSmiOnlyElements() && !value->IsSmi() && value->IsNumber()) { | 
| + if (HasFastSmiElements() && !value->IsSmi() && value->IsNumber()) { | 
| MaybeObject* maybe = | 
| - SetFastDoubleElementsCapacityAndLength(new_capacity, array_length); | 
| + SetFastDoubleElementsCapacityAndLength(new_capacity, | 
| + array_length); | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
nit: why the line break?
 
danno
2012/05/22 11:05:21
Done.
 
 | 
| if (maybe->IsFailure()) return maybe; | 
| FixedDoubleArray::cast(elements())->set(index, value->Number()); | 
| + ValidateElements(); | 
| return value; | 
| } | 
| - // Change elements kind from SMI_ONLY to generic FAST if necessary. | 
| - if (HasFastSmiOnlyElements() && !value->IsSmi()) { | 
| + // Change elements kind from Smi-only to generic FAST if necessary. | 
| + if (HasFastSmiElements() && !value->IsSmi()) { | 
| Map* new_map; | 
| - { MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), | 
| - FAST_ELEMENTS); | 
| - if (!maybe_new_map->To(&new_map)) return maybe_new_map; | 
| - } | 
| + ElementsKind kind = HasFastHoleyElements() | 
| + ? FAST_HOLEY_ELEMENTS | 
| + : FAST_ELEMENTS; | 
| + MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), | 
| + kind); | 
| + if (!maybe_new_map->To(&new_map)) return maybe_new_map; | 
| + | 
| set_map(new_map); | 
| - if (FLAG_trace_elements_transitions) { | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
I don't see why you would remove this.
 
danno
2012/05/22 11:05:21
Because it's a dupe of the transition printing don
 
 | 
| - PrintElementsTransition(stdout, FAST_SMI_ONLY_ELEMENTS, elements(), | 
| - FAST_ELEMENTS, elements()); | 
| - } | 
| } | 
| // Increase backing store capacity if that's been decided previously. | 
| if (new_capacity != capacity) { | 
| FixedArray* new_elements; | 
| - SetFastElementsCapacityMode set_capacity_mode = | 
| - value->IsSmi() && HasFastSmiOnlyElements() | 
| + SetFastElementsCapacitySmiMode smi_mode = | 
| + value->IsSmi() && HasFastSmiElements() | 
| ? kAllowSmiOnlyElements | 
| : kDontAllowSmiOnlyElements; | 
| { MaybeObject* maybe = | 
| SetFastElementsCapacityAndLength(new_capacity, | 
| array_length, | 
| - set_capacity_mode); | 
| + smi_mode); | 
| if (!maybe->To(&new_elements)) return maybe; | 
| } | 
| new_elements->set(index, value); | 
| +#if DEBUG | 
| + ValidateElements(); | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
Calls to ValidateElements() aren't wrapped in an #
 
danno
2012/05/22 11:05:21
Done.
 
 | 
| +#endif | 
| return value; | 
| } | 
| + | 
| // Finally, set the new element and length. | 
| ASSERT(elements()->IsFixedArray()); | 
| backing_store->set(index, value); | 
| @@ -9521,20 +9575,22 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, | 
| } else { | 
| new_length = dictionary->max_number_key() + 1; | 
| } | 
| - SetFastElementsCapacityMode set_capacity_mode = FLAG_smi_only_arrays | 
| + SetFastElementsCapacitySmiMode smi_mode = FLAG_smi_only_arrays | 
| ? kAllowSmiOnlyElements | 
| : kDontAllowSmiOnlyElements; | 
| bool has_smi_only_elements = false; | 
| bool should_convert_to_fast_double_elements = | 
| ShouldConvertToFastDoubleElements(&has_smi_only_elements); | 
| if (has_smi_only_elements) { | 
| - set_capacity_mode = kForceSmiOnlyElements; | 
| + smi_mode = kForceSmiOnlyElements; | 
| } | 
| MaybeObject* result = should_convert_to_fast_double_elements | 
| - ? SetFastDoubleElementsCapacityAndLength(new_length, new_length) | 
| + ? SetFastDoubleElementsCapacityAndLength(new_length, | 
| 
 
Jakob Kummerow
2012/05/13 21:55:27
nit: why the line break?
 
danno
2012/05/22 11:05:21
Done.
 
 | 
| + new_length) | 
| : SetFastElementsCapacityAndLength(new_length, | 
| new_length, | 
| - set_capacity_mode); | 
| + smi_mode); | 
| + ValidateElements(); | 
| if (result->IsFailure()) return result; | 
| #ifdef DEBUG | 
| if (FLAG_trace_normalization) { | 
| @@ -9573,27 +9629,40 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( | 
| // If the value object is not a heap number, switch to fast elements and try | 
| // again. | 
| bool value_is_smi = value->IsSmi(); | 
| + bool introduces_holes = true; | 
| + uint32_t length = elms_length; | 
| + if (IsJSArray()) { | 
| + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); | 
| + introduces_holes = index > length; | 
| + } else { | 
| + introduces_holes = index >= elms_length; | 
| + } | 
| + | 
| 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, | 
| kDontAllowSmiOnlyElements); | 
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj; | 
| - return SetFastElement(index, | 
| - value, | 
| - strict_mode, | 
| - check_prototype); | 
| + if (maybe_obj->IsFailure()) return maybe_obj; | 
| + maybe_obj = SetFastElement(index, value, strict_mode, check_prototype); | 
| + if (maybe_obj->IsFailure()) return maybe_obj; | 
| + ValidateElements(); | 
| + return maybe_obj; | 
| } | 
| double double_value = value_is_smi | 
| ? static_cast<double>(Smi::cast(value)->value()) | 
| : HeapNumber::cast(value)->value(); | 
| + // If the array is growing, and it's not growth by a single element at the | 
| + // end, make sure that the ElementsKind is HOLEY. | 
| + ElementsKind elements_kind = GetElementsKind(); | 
| + if (introduces_holes && !IsFastHoleyElementsKind(elements_kind)) { | 
| + ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); | 
| + MaybeObject* maybe = TransitionElementsKind(transitioned_kind); | 
| + if (maybe->IsFailure()) return maybe; | 
| + } | 
| + | 
| // Check whether there is extra space in the fixed array. | 
| if (index < elms_length) { | 
| FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); | 
| @@ -9615,13 +9684,11 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( | 
| int new_capacity = NewElementsCapacity(index+1); | 
| if (!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; | 
| - } | 
| + MaybeObject* maybe_obj = | 
| + SetFastDoubleElementsCapacityAndLength(new_capacity, index + 1); | 
| + if (maybe_obj->IsFailure()) return maybe_obj; | 
| FixedDoubleArray::cast(elements())->set(index, double_value); | 
| + ValidateElements(); | 
| return value; | 
| } | 
| } | 
| @@ -9765,10 +9832,13 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, | 
| (attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0); | 
| Isolate* isolate = GetIsolate(); | 
| switch (GetElementsKind()) { | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| + case FAST_SMI_ELEMENTS: | 
| case FAST_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: | 
| return SetFastElement(index, value, strict_mode, check_prototype); | 
| case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: | 
| return SetFastDoubleElement(index, value, strict_mode, check_prototype); | 
| case EXTERNAL_PIXEL_ELEMENTS: { | 
| ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); | 
| @@ -9859,10 +9929,19 @@ Handle<Object> JSObject::TransitionElementsKind(Handle<JSObject> object, | 
| MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { | 
| ElementsKind from_kind = map()->elements_kind(); | 
| + if (IsFastHoleyElementsKind(from_kind)) { | 
| + to_kind = GetHoleyElementsKind(to_kind); | 
| + } | 
| + | 
| Isolate* isolate = GetIsolate(); | 
| - if (from_kind == FAST_SMI_ONLY_ELEMENTS && | 
| - (to_kind == FAST_ELEMENTS || | 
| - elements() == isolate->heap()->empty_fixed_array())) { | 
| + | 
| + if (elements() == isolate->heap()->empty_fixed_array() || | 
| + (IsFastSmiOrObjectElementsKind(from_kind) && | 
| + IsFastSmiOrObjectElementsKind(to_kind)) || | 
| + (from_kind == FAST_DOUBLE_ELEMENTS && | 
| + to_kind == FAST_HOLEY_DOUBLE_ELEMENTS)) { | 
| + // No change is needed to the elements() buffer, the transition | 
| + // only requires a map change. | 
| MaybeObject* maybe_new_map = GetElementsTransitionMap(isolate, to_kind); | 
| Map* new_map; | 
| if (!maybe_new_map->To(&new_map)) return maybe_new_map; | 
| @@ -9889,18 +9968,21 @@ MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { | 
| } | 
| } | 
| - if (from_kind == FAST_SMI_ONLY_ELEMENTS && | 
| - to_kind == FAST_DOUBLE_ELEMENTS) { | 
| + if (IsFastSmiElementsKind(from_kind) && | 
| + IsFastDoubleElementsKind(to_kind)) { | 
| MaybeObject* maybe_result = | 
| SetFastDoubleElementsCapacityAndLength(capacity, length); | 
| if (maybe_result->IsFailure()) return maybe_result; | 
| + ValidateElements(); | 
| return this; | 
| } | 
| - if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { | 
| + if (IsFastDoubleElementsKind(from_kind) && | 
| + IsFastObjectElementsKind(to_kind)) { | 
| MaybeObject* maybe_result = SetFastElementsCapacityAndLength( | 
| capacity, length, kDontAllowSmiOnlyElements); | 
| if (maybe_result->IsFailure()) return maybe_result; | 
| + ValidateElements(); | 
| return this; | 
| } | 
| @@ -9914,10 +9996,14 @@ MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { | 
| // static | 
| bool Map::IsValidElementsTransition(ElementsKind from_kind, | 
| ElementsKind to_kind) { | 
| - return | 
| - (from_kind == FAST_SMI_ONLY_ELEMENTS && | 
| - (to_kind == FAST_DOUBLE_ELEMENTS || to_kind == FAST_ELEMENTS)) || | 
| - (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS); | 
| + // Transitions can't go backwards. | 
| + if (!IsMoreGeneralElementsKindTransition(from_kind, to_kind)) { | 
| + return false; | 
| + } | 
| + | 
| + // Transitions from HOLEY -> PACKED are not allowed. | 
| + return !IsFastHoleyElementsKind(from_kind) || | 
| + IsFastHoleyElementsKind(to_kind); | 
| } | 
| @@ -10008,8 +10094,10 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { | 
| break; | 
| } | 
| // Fall through. | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| + case FAST_SMI_ELEMENTS: | 
| case FAST_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: | 
| backing_store = FixedArray::cast(backing_store_base); | 
| *capacity = backing_store->length(); | 
| for (int i = 0; i < *capacity; ++i) { | 
| @@ -10023,7 +10111,8 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { | 
| *used = dictionary->NumberOfElements(); | 
| break; | 
| } | 
| - case FAST_DOUBLE_ELEMENTS: { | 
| + case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: { | 
| FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); | 
| *capacity = elms->length(); | 
| for (int i = 0; i < *capacity; i++) { | 
| @@ -10293,16 +10382,19 @@ bool JSObject::HasRealElementProperty(uint32_t index) { | 
| if (this->IsStringObjectWithCharacterAt(index)) return true; | 
| switch (GetElementsKind()) { | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| - case FAST_ELEMENTS: { | 
| - uint32_t length = IsJSArray() ? | 
| + case FAST_SMI_ELEMENTS: | 
| + case FAST_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: { | 
| + uint32_t length = IsJSArray() ? | 
| static_cast<uint32_t>( | 
| Smi::cast(JSArray::cast(this)->length())->value()) : | 
| static_cast<uint32_t>(FixedArray::cast(elements())->length()); | 
| return (index < length) && | 
| !FixedArray::cast(elements())->get(index)->IsTheHole(); | 
| } | 
| - case FAST_DOUBLE_ELEMENTS: { | 
| + case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: { | 
| uint32_t length = IsJSArray() ? | 
| static_cast<uint32_t>( | 
| Smi::cast(JSArray::cast(this)->length())->value()) : | 
| @@ -10502,7 +10594,7 @@ int JSObject::NumberOfLocalElements(PropertyAttributes filter) { | 
| int JSObject::NumberOfEnumElements() { | 
| // Fast case for objects with no elements. | 
| - if (!IsJSValue() && HasFastElements()) { | 
| + if (!IsJSValue() && HasFastObjectElements()) { | 
| uint32_t length = IsJSArray() ? | 
| static_cast<uint32_t>( | 
| Smi::cast(JSArray::cast(this)->length())->value()) : | 
| @@ -10518,8 +10610,10 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, | 
| PropertyAttributes filter) { | 
| int counter = 0; | 
| switch (GetElementsKind()) { | 
| - case FAST_SMI_ONLY_ELEMENTS: | 
| - case FAST_ELEMENTS: { | 
| + case FAST_SMI_ELEMENTS: | 
| + case FAST_ELEMENTS: | 
| + case FAST_HOLEY_SMI_ELEMENTS: | 
| + case FAST_HOLEY_ELEMENTS: { | 
| int length = IsJSArray() ? | 
| Smi::cast(JSArray::cast(this)->length())->value() : | 
| FixedArray::cast(elements())->length(); | 
| @@ -10534,7 +10628,8 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, | 
| ASSERT(!storage || storage->length() >= counter); | 
| break; | 
| } | 
| - case FAST_DOUBLE_ELEMENTS: { | 
| + case FAST_DOUBLE_ELEMENTS: | 
| + case FAST_HOLEY_DOUBLE_ELEMENTS: { | 
| int length = IsJSArray() ? | 
| Smi::cast(JSArray::cast(this)->length())->value() : | 
| FixedDoubleArray::cast(elements())->length(); | 
| @@ -11467,10 +11562,9 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { | 
| // Convert to fast elements. | 
| Object* obj; | 
| - { MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), | 
| - FAST_ELEMENTS); | 
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj; | 
| - } | 
| + MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), | 
| + FAST_HOLEY_ELEMENTS); | 
| + if (!maybe_obj->ToObject(&obj)) return maybe_obj; | 
| Map* new_map = Map::cast(obj); | 
| PretenureFlag tenure = heap->InNewSpace(this) ? NOT_TENURED: TENURED; | 
| @@ -11481,9 +11575,9 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { | 
| } | 
| FixedArray* fast_elements = FixedArray::cast(new_array); | 
| dict->CopyValuesTo(fast_elements); | 
| + ValidateElements(); | 
| - set_map(new_map); | 
| - set_elements(fast_elements); | 
| + set_map_and_elements(new_map, fast_elements); | 
| } else if (HasExternalArrayElements()) { | 
| // External arrays cannot have holes or undefined elements. | 
| return Smi::FromInt(ExternalArray::cast(elements())->length()); | 
| @@ -11493,7 +11587,7 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { | 
| if (!maybe_obj->ToObject(&obj)) return maybe_obj; | 
| } | 
| } | 
| - ASSERT(HasFastTypeElements() || HasFastDoubleElements()); | 
| + ASSERT(HasFastSmiOrObjectElements() || HasFastDoubleElements()); | 
| // Collect holes at the end, undefined before that and the rest at the | 
| // start, and return the number of non-hole, non-undefined values. |