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. |