| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 7f75611bb43622ce8671c9f4eb60c7a9a04c3759..141dd2ab39ec26f70b7bebc8381e323e1d57350a 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();
|
| }
|
|
|
| @@ -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
|
| + : JSArray::cast(this)->length()->Number();
|
| accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length));
|
| break;
|
| }
|
| @@ -2202,34 +2199,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()) {
|
| @@ -2338,24 +2330,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;
|
| }
|
| @@ -2363,29 +2367,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 has been found 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;
|
| @@ -2437,10 +2447,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 &&
|
| + IsFastElementsKind(to_kind)) {
|
| safe_to_add_transition = false;
|
| }
|
|
|
| @@ -3476,8 +3487,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.
|
| @@ -3512,8 +3522,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);
|
| @@ -4000,9 +4009,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();
|
| @@ -4054,12 +4063,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;
|
| @@ -4075,7 +4087,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;
|
| }
|
| @@ -4309,7 +4322,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;
|
| @@ -4353,9 +4366,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:
|
| @@ -4472,7 +4488,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
|
| @@ -4796,9 +4812,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:
|
| @@ -8587,7 +8606,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());
|
| @@ -8598,34 +8617,40 @@ 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 == kForceSmiElements) ||
|
| + ((smi_mode == kAllowSmiElements) && 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;
|
| { MaybeObject* maybe_obj =
|
| - accessor->CopyElements(this, new_elements, to_kind);
|
| + accessor->CopyElements(this, new_elements, new_elements_kind);
|
| if (maybe_obj->IsFailure()) return maybe_obj;
|
| }
|
| 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);
|
| @@ -8637,11 +8662,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;
|
| }
|
|
|
| @@ -8659,20 +8682,28 @@ 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);
|
| { MaybeObject* maybe_obj =
|
| accessor->CopyElements(this, elems, FAST_DOUBLE_ELEMENTS);
|
| if (maybe_obj->IsFailure()) return maybe_obj;
|
| }
|
| if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) {
|
| + ValidateElements();
|
| set_map_and_elements(new_map, elems);
|
| } else {
|
| FixedArray* parameter_map = FixedArray::cast(old_elements);
|
| @@ -8681,7 +8712,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
|
|
|
| if (FLAG_trace_elements_transitions) {
|
| PrintElementsTransition(stdout, elements_kind, old_elements,
|
| - FAST_DOUBLE_ELEMENTS, elems);
|
| + GetElementsKind(), elems);
|
| }
|
|
|
| if (IsJSArray()) {
|
| @@ -8961,8 +8992,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()) :
|
| @@ -8973,7 +9006,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()) :
|
| @@ -9257,7 +9291,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());
|
| @@ -9283,13 +9317,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) {
|
| @@ -9309,42 +9359,44 @@ 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);
|
| 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) {
|
| - 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()
|
| - ? kAllowSmiOnlyElements
|
| - : kDontAllowSmiOnlyElements;
|
| + SetFastElementsCapacitySmiMode smi_mode =
|
| + value->IsSmi() && HasFastSmiElements()
|
| + ? kAllowSmiElements
|
| + : kDontAllowSmiElements;
|
| { MaybeObject* maybe =
|
| SetFastElementsCapacityAndLength(new_capacity,
|
| array_length,
|
| - set_capacity_mode);
|
| + smi_mode);
|
| if (!maybe->To(&new_elements)) return maybe;
|
| }
|
| new_elements->set(index, value);
|
| + ValidateElements();
|
| return value;
|
| }
|
| +
|
| // Finally, set the new element and length.
|
| ASSERT(elements()->IsFixedArray());
|
| backing_store->set(index, value);
|
| @@ -9468,20 +9520,21 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
|
| } else {
|
| new_length = dictionary->max_number_key() + 1;
|
| }
|
| - SetFastElementsCapacityMode set_capacity_mode = FLAG_smi_only_arrays
|
| - ? kAllowSmiOnlyElements
|
| - : kDontAllowSmiOnlyElements;
|
| + SetFastElementsCapacitySmiMode smi_mode = FLAG_smi_only_arrays
|
| + ? kAllowSmiElements
|
| + : kDontAllowSmiElements;
|
| 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 = kForceSmiElements;
|
| }
|
| MaybeObject* result = should_convert_to_fast_double_elements
|
| ? SetFastDoubleElementsCapacityAndLength(new_length, new_length)
|
| : SetFastElementsCapacityAndLength(new_length,
|
| new_length,
|
| - set_capacity_mode);
|
| + smi_mode);
|
| + ValidateElements();
|
| if (result->IsFailure()) return result;
|
| #ifdef DEBUG
|
| if (FLAG_trace_normalization) {
|
| @@ -9520,27 +9573,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);
|
| + kDontAllowSmiElements);
|
| + 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());
|
| @@ -9562,13 +9628,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;
|
| }
|
| }
|
| @@ -9712,10 +9776,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());
|
| @@ -9806,11 +9873,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 ||
|
| - elements() == isolate->heap()->empty_fixed_array()) &&
|
| - to_kind == FAST_ELEMENTS) {
|
| - ASSERT(from_kind != FAST_ELEMENTS);
|
| + if (elements() == isolate->heap()->empty_fixed_array() ||
|
| + (IsFastSmiOrObjectElementsKind(from_kind) &&
|
| + IsFastSmiOrObjectElementsKind(to_kind)) ||
|
| + (from_kind == FAST_DOUBLE_ELEMENTS &&
|
| + to_kind == FAST_HOLEY_DOUBLE_ELEMENTS)) {
|
| + ASSERT(from_kind != TERMINAL_FAST_ELEMENTS_KIND);
|
| + // 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;
|
| @@ -9837,18 +9912,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);
|
| + capacity, length, kDontAllowSmiElements);
|
| if (maybe_result->IsFailure()) return maybe_result;
|
| + ValidateElements();
|
| return this;
|
| }
|
|
|
| @@ -9862,10 +9940,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);
|
| }
|
|
|
|
|
| @@ -9956,8 +10038,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) {
|
| @@ -9971,7 +10055,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++) {
|
| @@ -10241,16 +10326,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()) :
|
| @@ -10450,7 +10538,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()) :
|
| @@ -10466,8 +10554,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();
|
| @@ -10482,7 +10572,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();
|
| @@ -11415,10 +11506,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;
|
| @@ -11429,9 +11519,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());
|
| @@ -11441,7 +11531,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.
|
|
|