OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 4085 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4096 } else if (FLAG_smi_only_arrays && args.at<Object>(1)->IsSmi()) { | 4096 } else if (FLAG_smi_only_arrays && args.at<Object>(1)->IsSmi()) { |
4097 // JSObject without a string key. If the key is a Smi, check for a | 4097 // JSObject without a string key. If the key is a Smi, check for a |
4098 // definite out-of-bounds access to elements, which is a strong indicator | 4098 // definite out-of-bounds access to elements, which is a strong indicator |
4099 // that subsequent accesses will also call the runtime. Proactively | 4099 // that subsequent accesses will also call the runtime. Proactively |
4100 // transition elements to FAST_*_ELEMENTS to avoid excessive boxing of | 4100 // transition elements to FAST_*_ELEMENTS to avoid excessive boxing of |
4101 // doubles for those future calls in the case that the elements would | 4101 // doubles for those future calls in the case that the elements would |
4102 // become FAST_DOUBLE_ELEMENTS. | 4102 // become FAST_DOUBLE_ELEMENTS. |
4103 Handle<JSObject> js_object(args.at<JSObject>(0)); | 4103 Handle<JSObject> js_object(args.at<JSObject>(0)); |
4104 ElementsKind elements_kind = js_object->GetElementsKind(); | 4104 ElementsKind elements_kind = js_object->GetElementsKind(); |
4105 if (IsFastElementsKind(elements_kind) && | 4105 if (IsFastElementsKind(elements_kind) && |
4106 !IsFastObjectElementsKind(elements_kind)) { | 4106 !IsFastSmiOrObjectElementsKind(elements_kind)) { |
danno
2012/11/13 22:05:12
Can't you reduce the entire multi-part test above
Toon Verwaest
2012/11/14 11:53:13
Done. Added ASSERT to easily find the place back w
| |
4107 FixedArrayBase* elements = js_object->elements(); | 4107 FixedArrayBase* elements = js_object->elements(); |
4108 if (args.at<Smi>(1)->value() >= elements->length()) { | 4108 if (args.at<Smi>(1)->value() >= elements->length()) { |
4109 if (IsFastHoleyElementsKind(elements_kind)) { | 4109 if (IsFastHoleyElementsKind(elements_kind)) { |
4110 elements_kind = FAST_HOLEY_ELEMENTS; | 4110 elements_kind = FAST_HOLEY_ELEMENTS; |
4111 } else { | 4111 } else { |
4112 elements_kind = FAST_ELEMENTS; | 4112 elements_kind = FAST_ELEMENTS; |
4113 } | 4113 } |
4114 MaybeObject* maybe_object = TransitionElements(js_object, | 4114 MaybeObject* maybe_object = TransitionElements(js_object, |
4115 elements_kind, | 4115 elements_kind, |
4116 isolate); | 4116 isolate); |
(...skipping 5179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
9296 ASSERT(!fast_elements_); | 9296 ASSERT(!fast_elements_); |
9297 Handle<SeededNumberDictionary> dict( | 9297 Handle<SeededNumberDictionary> dict( |
9298 SeededNumberDictionary::cast(*storage_)); | 9298 SeededNumberDictionary::cast(*storage_)); |
9299 Handle<SeededNumberDictionary> result = | 9299 Handle<SeededNumberDictionary> result = |
9300 isolate_->factory()->DictionaryAtNumberPut(dict, index, elm); | 9300 isolate_->factory()->DictionaryAtNumberPut(dict, index, elm); |
9301 if (!result.is_identical_to(dict)) { | 9301 if (!result.is_identical_to(dict)) { |
9302 // Dictionary needed to grow. | 9302 // Dictionary needed to grow. |
9303 clear_storage(); | 9303 clear_storage(); |
9304 set_storage(*result); | 9304 set_storage(*result); |
9305 } | 9305 } |
9306 } | 9306 } |
9307 | 9307 |
9308 void increase_index_offset(uint32_t delta) { | 9308 void increase_index_offset(uint32_t delta) { |
9309 if (JSObject::kMaxElementCount - index_offset_ < delta) { | 9309 if (JSObject::kMaxElementCount - index_offset_ < delta) { |
9310 index_offset_ = JSObject::kMaxElementCount; | 9310 index_offset_ = JSObject::kMaxElementCount; |
9311 } else { | 9311 } else { |
9312 index_offset_ += delta; | 9312 index_offset_ += delta; |
9313 } | 9313 } |
9314 } | 9314 } |
9315 | 9315 |
9316 Handle<JSArray> ToArray() { | 9316 Handle<JSArray> ToArray() { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
9387 // a 32-bit signed integer. | 9387 // a 32-bit signed integer. |
9388 ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); | 9388 ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); |
9389 int fast_length = static_cast<int>(length); | 9389 int fast_length = static_cast<int>(length); |
9390 Handle<FixedArray> elements(FixedArray::cast(array->elements())); | 9390 Handle<FixedArray> elements(FixedArray::cast(array->elements())); |
9391 for (int i = 0; i < fast_length; i++) { | 9391 for (int i = 0; i < fast_length; i++) { |
9392 if (!elements->get(i)->IsTheHole()) element_count++; | 9392 if (!elements->get(i)->IsTheHole()) element_count++; |
9393 } | 9393 } |
9394 break; | 9394 break; |
9395 } | 9395 } |
9396 case FAST_DOUBLE_ELEMENTS: | 9396 case FAST_DOUBLE_ELEMENTS: |
9397 case FAST_HOLEY_DOUBLE_ELEMENTS: | 9397 case FAST_HOLEY_DOUBLE_ELEMENTS: { |
9398 // TODO(1810): Decide if it's worthwhile to implement this. | 9398 // Fast elements can't have lengths that are not representable by |
9399 UNREACHABLE(); | 9399 // a 32-bit signed integer. |
9400 ASSERT(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0); | |
9401 int fast_length = static_cast<int>(length); | |
9402 if (array->elements()->IsFixedArray()) { | |
9403 ASSERT(FixedArray::cast(array->elements())->length() == 0); | |
9404 break; | |
9405 } | |
9406 Handle<FixedDoubleArray> elements( | |
9407 FixedDoubleArray::cast(array->elements())); | |
9408 for (int i = 0; i < fast_length; i++) { | |
9409 if (!elements->is_the_hole(i)) element_count++; | |
9410 } | |
9400 break; | 9411 break; |
9412 } | |
9401 case DICTIONARY_ELEMENTS: { | 9413 case DICTIONARY_ELEMENTS: { |
9402 Handle<SeededNumberDictionary> dictionary( | 9414 Handle<SeededNumberDictionary> dictionary( |
9403 SeededNumberDictionary::cast(array->elements())); | 9415 SeededNumberDictionary::cast(array->elements())); |
9404 int capacity = dictionary->Capacity(); | 9416 int capacity = dictionary->Capacity(); |
9405 for (int i = 0; i < capacity; i++) { | 9417 for (int i = 0; i < capacity; i++) { |
9406 Handle<Object> key(dictionary->KeyAt(i)); | 9418 Handle<Object> key(dictionary->KeyAt(i)); |
9407 if (dictionary->IsKey(*key)) { | 9419 if (dictionary->IsKey(*key)) { |
9408 element_count++; | 9420 element_count++; |
9409 } | 9421 } |
9410 } | 9422 } |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
9633 // have the correct receiver. | 9645 // have the correct receiver. |
9634 element_value = Object::GetElement(receiver, j); | 9646 element_value = Object::GetElement(receiver, j); |
9635 RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element_value, false); | 9647 RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element_value, false); |
9636 visitor->visit(j, element_value); | 9648 visitor->visit(j, element_value); |
9637 } | 9649 } |
9638 } | 9650 } |
9639 break; | 9651 break; |
9640 } | 9652 } |
9641 case FAST_HOLEY_DOUBLE_ELEMENTS: | 9653 case FAST_HOLEY_DOUBLE_ELEMENTS: |
9642 case FAST_DOUBLE_ELEMENTS: { | 9654 case FAST_DOUBLE_ELEMENTS: { |
9643 // TODO(1810): Decide if it's worthwhile to implement this. | 9655 // Run through the elements FixedArray and use HasElement and GetElement |
9644 UNREACHABLE(); | 9656 // to check the prototype for missing elements. |
9657 Handle<FixedDoubleArray> elements( | |
9658 FixedDoubleArray::cast(receiver->elements())); | |
9659 int fast_length = static_cast<int>(length); | |
9660 ASSERT(fast_length <= elements->length()); | |
9661 for (int j = 0; j < fast_length; j++) { | |
9662 HandleScope loop_scope(isolate); | |
9663 if (!elements->is_the_hole(j)) { | |
9664 double double_value = elements->get_scalar(j); | |
9665 Handle<Object> element_value = | |
9666 isolate->factory()->NewNumber(double_value); | |
9667 visitor->visit(j, element_value); | |
9668 } else if (receiver->HasElement(j)) { | |
9669 // Call GetElement on receiver, not its prototype, or getters won't | |
9670 // have the correct receiver. | |
9671 Handle<Object> element_value = Object::GetElement(receiver, j); | |
9672 RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element_value, false); | |
9673 visitor->visit(j, element_value); | |
9674 } | |
9675 } | |
9645 break; | 9676 break; |
9646 } | 9677 } |
9647 case DICTIONARY_ELEMENTS: { | 9678 case DICTIONARY_ELEMENTS: { |
9648 Handle<SeededNumberDictionary> dict(receiver->element_dictionary()); | 9679 Handle<SeededNumberDictionary> dict(receiver->element_dictionary()); |
9649 List<uint32_t> indices(dict->Capacity() / 2); | 9680 List<uint32_t> indices(dict->Capacity() / 2); |
9650 // Collect all indices in the object and the prototypes less | 9681 // Collect all indices in the object and the prototypes less |
9651 // than length. This might introduce duplicates in the indices list. | 9682 // than length. This might introduce duplicates in the indices list. |
9652 CollectElementIndices(receiver, length, &indices); | 9683 CollectElementIndices(receiver, length, &indices); |
9653 indices.Sort(&compareUInt32); | 9684 indices.Sort(&compareUInt32); |
9654 int j = 0; | 9685 int j = 0; |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
9737 CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0); | 9768 CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0); |
9738 int argument_count = static_cast<int>(arguments->length()->Number()); | 9769 int argument_count = static_cast<int>(arguments->length()->Number()); |
9739 RUNTIME_ASSERT(arguments->HasFastObjectElements()); | 9770 RUNTIME_ASSERT(arguments->HasFastObjectElements()); |
9740 Handle<FixedArray> elements(FixedArray::cast(arguments->elements())); | 9771 Handle<FixedArray> elements(FixedArray::cast(arguments->elements())); |
9741 | 9772 |
9742 // Pass 1: estimate the length and number of elements of the result. | 9773 // Pass 1: estimate the length and number of elements of the result. |
9743 // The actual length can be larger if any of the arguments have getters | 9774 // The actual length can be larger if any of the arguments have getters |
9744 // that mutate other arguments (but will otherwise be precise). | 9775 // that mutate other arguments (but will otherwise be precise). |
9745 // The number of elements is precise if there are no inherited elements. | 9776 // The number of elements is precise if there are no inherited elements. |
9746 | 9777 |
9778 ElementsKind kind = FAST_SMI_ELEMENTS; | |
9779 | |
9747 uint32_t estimate_result_length = 0; | 9780 uint32_t estimate_result_length = 0; |
9748 uint32_t estimate_nof_elements = 0; | 9781 uint32_t estimate_nof_elements = 0; |
9749 { | 9782 for (int i = 0; i < argument_count; i++) { |
9750 for (int i = 0; i < argument_count; i++) { | 9783 HandleScope loop_scope; |
9751 HandleScope loop_scope; | 9784 Handle<Object> obj(elements->get(i)); |
9752 Handle<Object> obj(elements->get(i)); | 9785 uint32_t length_estimate; |
9753 uint32_t length_estimate; | 9786 uint32_t element_estimate; |
9754 uint32_t element_estimate; | 9787 if (obj->IsJSArray()) { |
9755 if (obj->IsJSArray()) { | 9788 Handle<JSArray> array(Handle<JSArray>::cast(obj)); |
9756 Handle<JSArray> array(Handle<JSArray>::cast(obj)); | 9789 length_estimate = static_cast<uint32_t>(array->length()->Number()); |
9757 // TODO(1810): Find out if it's worthwhile to properly support | 9790 if (length_estimate != 0) { |
9758 // arbitrary ElementsKinds. For now, pessimistically transition to | 9791 ElementsKind array_kind = |
9759 // FAST_*_ELEMENTS. | 9792 GetPackedElementsKind(array->map()->elements_kind()); |
9760 if (array->HasFastDoubleElements()) { | 9793 if (IsMoreGeneralElementsKindTransition(kind, array_kind)) { |
9761 ElementsKind to_kind = FAST_ELEMENTS; | 9794 kind = array_kind; |
9762 if (array->HasFastHoleyElements()) { | 9795 } |
9763 to_kind = FAST_HOLEY_ELEMENTS; | 9796 } |
9797 element_estimate = EstimateElementCount(array); | |
9798 } else { | |
9799 if (obj->IsHeapObject()) { | |
9800 if (obj->IsNumber()) { | |
9801 if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) { | |
9802 kind = FAST_DOUBLE_ELEMENTS; | |
9764 } | 9803 } |
9765 array = Handle<JSArray>::cast( | 9804 } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) { |
9766 JSObject::TransitionElementsKind(array, to_kind)); | 9805 kind = FAST_ELEMENTS; |
9767 } | 9806 } |
9768 length_estimate = | |
9769 static_cast<uint32_t>(array->length()->Number()); | |
9770 element_estimate = | |
9771 EstimateElementCount(array); | |
9772 } else { | |
9773 length_estimate = 1; | |
9774 element_estimate = 1; | |
9775 } | 9807 } |
9776 // Avoid overflows by capping at kMaxElementCount. | 9808 length_estimate = 1; |
9777 if (JSObject::kMaxElementCount - estimate_result_length < | 9809 element_estimate = 1; |
9778 length_estimate) { | 9810 } |
9779 estimate_result_length = JSObject::kMaxElementCount; | 9811 // Avoid overflows by capping at kMaxElementCount. |
9780 } else { | 9812 if (JSObject::kMaxElementCount - estimate_result_length < |
9781 estimate_result_length += length_estimate; | 9813 length_estimate) { |
9782 } | 9814 estimate_result_length = JSObject::kMaxElementCount; |
9783 if (JSObject::kMaxElementCount - estimate_nof_elements < | 9815 } else { |
9784 element_estimate) { | 9816 estimate_result_length += length_estimate; |
9785 estimate_nof_elements = JSObject::kMaxElementCount; | 9817 } |
9786 } else { | 9818 if (JSObject::kMaxElementCount - estimate_nof_elements < |
9787 estimate_nof_elements += element_estimate; | 9819 element_estimate) { |
9788 } | 9820 estimate_nof_elements = JSObject::kMaxElementCount; |
9821 } else { | |
9822 estimate_nof_elements += element_estimate; | |
9789 } | 9823 } |
9790 } | 9824 } |
9791 | 9825 |
9792 // If estimated number of elements is more than half of length, a | 9826 // If estimated number of elements is more than half of length, a |
9793 // fixed array (fast case) is more time and space-efficient than a | 9827 // fixed array (fast case) is more time and space-efficient than a |
9794 // dictionary. | 9828 // dictionary. |
9795 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; | 9829 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; |
9796 | 9830 |
9797 Handle<FixedArray> storage; | 9831 Handle<FixedArray> storage; |
9798 if (fast_case) { | 9832 if (fast_case) { |
9799 // The backing storage array must have non-existing elements to | 9833 if (kind == FAST_DOUBLE_ELEMENTS) { |
9800 // preserve holes across concat operations. | 9834 Handle<FixedDoubleArray> double_storage = |
9835 isolate->factory()->NewFixedDoubleArray(estimate_result_length); | |
9836 int j = 0; | |
9837 bool failure = false; | |
9838 for (int i = 0; i < argument_count; i++) { | |
9839 Handle<Object> obj(elements->get(i)); | |
9840 if (obj->IsSmi()) { | |
9841 double_storage->set(j, Smi::cast(*obj)->value()); | |
9842 j++; | |
9843 } else if (obj->IsNumber()) { | |
9844 double_storage->set(j, obj->Number()); | |
9845 j++; | |
9846 } else { | |
9847 JSArray* array = JSArray::cast(*obj); | |
9848 uint32_t length = static_cast<uint32_t>(array->length()->Number()); | |
9849 switch (array->map()->elements_kind()) { | |
9850 case FAST_HOLEY_DOUBLE_ELEMENTS: | |
9851 case FAST_DOUBLE_ELEMENTS: { | |
9852 // Empty fixed array indicates that there are no elements. | |
9853 if (array->elements()->IsFixedArray()) break; | |
9854 FixedDoubleArray* elements = | |
9855 FixedDoubleArray::cast(array->elements()); | |
9856 for (uint32_t i = 0; i < length; i++) { | |
9857 if (elements->is_the_hole(i)) { | |
9858 failure = true; | |
9859 break; | |
9860 } | |
9861 double double_value = elements->get_scalar(i); | |
9862 double_storage->set(j, double_value); | |
9863 j++; | |
9864 } | |
9865 break; | |
9866 } | |
9867 case FAST_HOLEY_SMI_ELEMENTS: | |
9868 case FAST_SMI_ELEMENTS: { | |
9869 FixedArray* elements( | |
9870 FixedArray::cast(array->elements())); | |
9871 for (uint32_t i = 0; i < length; i++) { | |
9872 Object* element = elements->get(i); | |
9873 if (element->IsTheHole()) { | |
9874 failure = true; | |
9875 break; | |
9876 } | |
9877 int32_t int_value = Smi::cast(element)->value(); | |
9878 double_storage->set(j, int_value); | |
9879 j++; | |
9880 } | |
9881 break; | |
9882 } | |
9883 case FAST_HOLEY_ELEMENTS: | |
9884 ASSERT_EQ(0, length); | |
9885 break; | |
9886 default: | |
9887 UNREACHABLE(); | |
9888 } | |
9889 } | |
9890 if (failure) break; | |
9891 } | |
9892 Handle<JSArray> array = isolate->factory()->NewJSArray(0); | |
9893 Smi* length = Smi::FromInt(j); | |
9894 Handle<Map> map; | |
9895 map = isolate->factory()->GetElementsTransitionMap(array, kind); | |
9896 array->set_map(*map); | |
9897 array->set_length(length); | |
9898 array->set_elements(*double_storage); | |
9899 return *array; | |
9900 } | |
9901 // The backing storage array must have non-existing elements to preserve | |
9902 // holes across concat operations. | |
9801 storage = isolate->factory()->NewFixedArrayWithHoles( | 9903 storage = isolate->factory()->NewFixedArrayWithHoles( |
9802 estimate_result_length); | 9904 estimate_result_length); |
9803 } else { | 9905 } else { |
9804 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate | 9906 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate |
9805 uint32_t at_least_space_for = estimate_nof_elements + | 9907 uint32_t at_least_space_for = estimate_nof_elements + |
9806 (estimate_nof_elements >> 2); | 9908 (estimate_nof_elements >> 2); |
9807 storage = Handle<FixedArray>::cast( | 9909 storage = Handle<FixedArray>::cast( |
9808 isolate->factory()->NewSeededNumberDictionary(at_least_space_for)); | 9910 isolate->factory()->NewSeededNumberDictionary(at_least_space_for)); |
9809 } | 9911 } |
9810 | 9912 |
(...skipping 3506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
13317 // Handle last resort GC and make sure to allow future allocations | 13419 // Handle last resort GC and make sure to allow future allocations |
13318 // to grow the heap without causing GCs (if possible). | 13420 // to grow the heap without causing GCs (if possible). |
13319 isolate->counters()->gc_last_resort_from_js()->Increment(); | 13421 isolate->counters()->gc_last_resort_from_js()->Increment(); |
13320 isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, | 13422 isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, |
13321 "Runtime::PerformGC"); | 13423 "Runtime::PerformGC"); |
13322 } | 13424 } |
13323 } | 13425 } |
13324 | 13426 |
13325 | 13427 |
13326 } } // namespace v8::internal | 13428 } } // namespace v8::internal |
OLD | NEW |