OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/builtins.h" | 5 #include "src/builtins.h" |
6 | 6 |
7 #include "src/api.h" | 7 #include "src/api.h" |
8 #include "src/api-natives.h" | 8 #include "src/api-natives.h" |
9 #include "src/arguments.h" | 9 #include "src/arguments.h" |
10 #include "src/base/once.h" | 10 #include "src/base/once.h" |
(...skipping 586 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
597 AllowHeapAllocation allow_allocation; | 597 AllowHeapAllocation allow_allocation; |
598 return CallJsIntrinsic(isolate, isolate->array_splice(), args); | 598 return CallJsIntrinsic(isolate, isolate->array_splice(), args); |
599 } | 599 } |
600 ElementsAccessor* accessor = array->GetElementsAccessor(); | 600 ElementsAccessor* accessor = array->GetElementsAccessor(); |
601 Handle<JSArray> result_array = accessor->Splice( | 601 Handle<JSArray> result_array = accessor->Splice( |
602 array, elms_obj, actual_start, actual_delete_count, &args, add_count); | 602 array, elms_obj, actual_start, actual_delete_count, &args, add_count); |
603 return *result_array; | 603 return *result_array; |
604 } | 604 } |
605 | 605 |
606 | 606 |
607 BUILTIN(ArrayConcat) { | 607 // Array Concat ------------------------------------------------------------- |
608 HandleScope scope(isolate); | 608 |
609 | 609 namespace { |
610 int n_arguments = args.length(); | 610 |
| 611 /** |
| 612 * A simple visitor visits every element of Array's. |
| 613 * The backend storage can be a fixed array for fast elements case, |
| 614 * or a dictionary for sparse array. Since Dictionary is a subtype |
| 615 * of FixedArray, the class can be used by both fast and slow cases. |
| 616 * The second parameter of the constructor, fast_elements, specifies |
| 617 * whether the storage is a FixedArray or Dictionary. |
| 618 * |
| 619 * An index limit is used to deal with the situation that a result array |
| 620 * length overflows 32-bit non-negative integer. |
| 621 */ |
| 622 class ArrayConcatVisitor { |
| 623 public: |
| 624 ArrayConcatVisitor(Isolate* isolate, Handle<FixedArray> storage, |
| 625 bool fast_elements) |
| 626 : isolate_(isolate), |
| 627 storage_(Handle<FixedArray>::cast( |
| 628 isolate->global_handles()->Create(*storage))), |
| 629 index_offset_(0u), |
| 630 bit_field_(FastElementsField::encode(fast_elements) | |
| 631 ExceedsLimitField::encode(false)) {} |
| 632 |
| 633 ~ArrayConcatVisitor() { clear_storage(); } |
| 634 |
| 635 void visit(uint32_t i, Handle<Object> elm) { |
| 636 if (i >= JSObject::kMaxElementCount - index_offset_) { |
| 637 set_exceeds_array_limit(true); |
| 638 return; |
| 639 } |
| 640 uint32_t index = index_offset_ + i; |
| 641 |
| 642 if (fast_elements()) { |
| 643 if (index < static_cast<uint32_t>(storage_->length())) { |
| 644 storage_->set(index, *elm); |
| 645 return; |
| 646 } |
| 647 // Our initial estimate of length was foiled, possibly by |
| 648 // getters on the arrays increasing the length of later arrays |
| 649 // during iteration. |
| 650 // This shouldn't happen in anything but pathological cases. |
| 651 SetDictionaryMode(); |
| 652 // Fall-through to dictionary mode. |
| 653 } |
| 654 DCHECK(!fast_elements()); |
| 655 Handle<SeededNumberDictionary> dict( |
| 656 SeededNumberDictionary::cast(*storage_)); |
| 657 // The object holding this backing store has just been allocated, so |
| 658 // it cannot yet be used as a prototype. |
| 659 Handle<SeededNumberDictionary> result = |
| 660 SeededNumberDictionary::AtNumberPut(dict, index, elm, false); |
| 661 if (!result.is_identical_to(dict)) { |
| 662 // Dictionary needed to grow. |
| 663 clear_storage(); |
| 664 set_storage(*result); |
| 665 } |
| 666 } |
| 667 |
| 668 void increase_index_offset(uint32_t delta) { |
| 669 if (JSObject::kMaxElementCount - index_offset_ < delta) { |
| 670 index_offset_ = JSObject::kMaxElementCount; |
| 671 } else { |
| 672 index_offset_ += delta; |
| 673 } |
| 674 // If the initial length estimate was off (see special case in visit()), |
| 675 // but the array blowing the limit didn't contain elements beyond the |
| 676 // provided-for index range, go to dictionary mode now. |
| 677 if (fast_elements() && |
| 678 index_offset_ > |
| 679 static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) { |
| 680 SetDictionaryMode(); |
| 681 } |
| 682 } |
| 683 |
| 684 bool exceeds_array_limit() const { |
| 685 return ExceedsLimitField::decode(bit_field_); |
| 686 } |
| 687 |
| 688 Handle<JSArray> ToArray() { |
| 689 Handle<JSArray> array = isolate_->factory()->NewJSArray(0); |
| 690 Handle<Object> length = |
| 691 isolate_->factory()->NewNumber(static_cast<double>(index_offset_)); |
| 692 Handle<Map> map = JSObject::GetElementsTransitionMap( |
| 693 array, fast_elements() ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS); |
| 694 array->set_map(*map); |
| 695 array->set_length(*length); |
| 696 array->set_elements(*storage_); |
| 697 return array; |
| 698 } |
| 699 |
| 700 private: |
| 701 // Convert storage to dictionary mode. |
| 702 void SetDictionaryMode() { |
| 703 DCHECK(fast_elements()); |
| 704 Handle<FixedArray> current_storage(*storage_); |
| 705 Handle<SeededNumberDictionary> slow_storage( |
| 706 SeededNumberDictionary::New(isolate_, current_storage->length())); |
| 707 uint32_t current_length = static_cast<uint32_t>(current_storage->length()); |
| 708 for (uint32_t i = 0; i < current_length; i++) { |
| 709 HandleScope loop_scope(isolate_); |
| 710 Handle<Object> element(current_storage->get(i), isolate_); |
| 711 if (!element->IsTheHole()) { |
| 712 // The object holding this backing store has just been allocated, so |
| 713 // it cannot yet be used as a prototype. |
| 714 Handle<SeededNumberDictionary> new_storage = |
| 715 SeededNumberDictionary::AtNumberPut(slow_storage, i, element, |
| 716 false); |
| 717 if (!new_storage.is_identical_to(slow_storage)) { |
| 718 slow_storage = loop_scope.CloseAndEscape(new_storage); |
| 719 } |
| 720 } |
| 721 } |
| 722 clear_storage(); |
| 723 set_storage(*slow_storage); |
| 724 set_fast_elements(false); |
| 725 } |
| 726 |
| 727 inline void clear_storage() { |
| 728 GlobalHandles::Destroy(Handle<Object>::cast(storage_).location()); |
| 729 } |
| 730 |
| 731 inline void set_storage(FixedArray* storage) { |
| 732 storage_ = |
| 733 Handle<FixedArray>::cast(isolate_->global_handles()->Create(storage)); |
| 734 } |
| 735 |
| 736 class FastElementsField : public BitField<bool, 0, 1> {}; |
| 737 class ExceedsLimitField : public BitField<bool, 1, 1> {}; |
| 738 |
| 739 bool fast_elements() const { return FastElementsField::decode(bit_field_); } |
| 740 void set_fast_elements(bool fast) { |
| 741 bit_field_ = FastElementsField::update(bit_field_, fast); |
| 742 } |
| 743 void set_exceeds_array_limit(bool exceeds) { |
| 744 bit_field_ = ExceedsLimitField::update(bit_field_, exceeds); |
| 745 } |
| 746 |
| 747 Isolate* isolate_; |
| 748 Handle<FixedArray> storage_; // Always a global handle. |
| 749 // Index after last seen index. Always less than or equal to |
| 750 // JSObject::kMaxElementCount. |
| 751 uint32_t index_offset_; |
| 752 uint32_t bit_field_; |
| 753 }; |
| 754 |
| 755 |
| 756 uint32_t EstimateElementCount(Handle<JSArray> array) { |
| 757 uint32_t length = static_cast<uint32_t>(array->length()->Number()); |
| 758 int element_count = 0; |
| 759 switch (array->GetElementsKind()) { |
| 760 case FAST_SMI_ELEMENTS: |
| 761 case FAST_HOLEY_SMI_ELEMENTS: |
| 762 case FAST_ELEMENTS: |
| 763 case FAST_HOLEY_ELEMENTS: { |
| 764 // Fast elements can't have lengths that are not representable by |
| 765 // a 32-bit signed integer. |
| 766 DCHECK(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); |
| 767 int fast_length = static_cast<int>(length); |
| 768 Handle<FixedArray> elements(FixedArray::cast(array->elements())); |
| 769 for (int i = 0; i < fast_length; i++) { |
| 770 if (!elements->get(i)->IsTheHole()) element_count++; |
| 771 } |
| 772 break; |
| 773 } |
| 774 case FAST_DOUBLE_ELEMENTS: |
| 775 case FAST_HOLEY_DOUBLE_ELEMENTS: { |
| 776 // Fast elements can't have lengths that are not representable by |
| 777 // a 32-bit signed integer. |
| 778 DCHECK(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0); |
| 779 int fast_length = static_cast<int>(length); |
| 780 if (array->elements()->IsFixedArray()) { |
| 781 DCHECK(FixedArray::cast(array->elements())->length() == 0); |
| 782 break; |
| 783 } |
| 784 Handle<FixedDoubleArray> elements( |
| 785 FixedDoubleArray::cast(array->elements())); |
| 786 for (int i = 0; i < fast_length; i++) { |
| 787 if (!elements->is_the_hole(i)) element_count++; |
| 788 } |
| 789 break; |
| 790 } |
| 791 case DICTIONARY_ELEMENTS: { |
| 792 Handle<SeededNumberDictionary> dictionary( |
| 793 SeededNumberDictionary::cast(array->elements())); |
| 794 int capacity = dictionary->Capacity(); |
| 795 for (int i = 0; i < capacity; i++) { |
| 796 Handle<Object> key(dictionary->KeyAt(i), array->GetIsolate()); |
| 797 if (dictionary->IsKey(*key)) { |
| 798 element_count++; |
| 799 } |
| 800 } |
| 801 break; |
| 802 } |
| 803 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: |
| 804 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: |
| 805 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS: |
| 806 |
| 807 TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 808 #undef TYPED_ARRAY_CASE |
| 809 // External arrays are always dense. |
| 810 return length; |
| 811 } |
| 812 // As an estimate, we assume that the prototype doesn't contain any |
| 813 // inherited elements. |
| 814 return element_count; |
| 815 } |
| 816 |
| 817 |
| 818 template <class ExternalArrayClass, class ElementType> |
| 819 void IterateTypedArrayElements(Isolate* isolate, Handle<JSObject> receiver, |
| 820 bool elements_are_ints, |
| 821 bool elements_are_guaranteed_smis, |
| 822 ArrayConcatVisitor* visitor) { |
| 823 Handle<ExternalArrayClass> array( |
| 824 ExternalArrayClass::cast(receiver->elements())); |
| 825 uint32_t len = static_cast<uint32_t>(array->length()); |
| 826 |
| 827 DCHECK(visitor != NULL); |
| 828 if (elements_are_ints) { |
| 829 if (elements_are_guaranteed_smis) { |
| 830 for (uint32_t j = 0; j < len; j++) { |
| 831 HandleScope loop_scope(isolate); |
| 832 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get_scalar(j))), |
| 833 isolate); |
| 834 visitor->visit(j, e); |
| 835 } |
| 836 } else { |
| 837 for (uint32_t j = 0; j < len; j++) { |
| 838 HandleScope loop_scope(isolate); |
| 839 int64_t val = static_cast<int64_t>(array->get_scalar(j)); |
| 840 if (Smi::IsValid(static_cast<intptr_t>(val))) { |
| 841 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)), isolate); |
| 842 visitor->visit(j, e); |
| 843 } else { |
| 844 Handle<Object> e = |
| 845 isolate->factory()->NewNumber(static_cast<ElementType>(val)); |
| 846 visitor->visit(j, e); |
| 847 } |
| 848 } |
| 849 } |
| 850 } else { |
| 851 for (uint32_t j = 0; j < len; j++) { |
| 852 HandleScope loop_scope(isolate); |
| 853 Handle<Object> e = isolate->factory()->NewNumber(array->get_scalar(j)); |
| 854 visitor->visit(j, e); |
| 855 } |
| 856 } |
| 857 } |
| 858 |
| 859 |
| 860 // Used for sorting indices in a List<uint32_t>. |
| 861 int compareUInt32(const uint32_t* ap, const uint32_t* bp) { |
| 862 uint32_t a = *ap; |
| 863 uint32_t b = *bp; |
| 864 return (a == b) ? 0 : (a < b) ? -1 : 1; |
| 865 } |
| 866 |
| 867 |
| 868 void CollectElementIndices(Handle<JSObject> object, uint32_t range, |
| 869 List<uint32_t>* indices) { |
| 870 Isolate* isolate = object->GetIsolate(); |
| 871 ElementsKind kind = object->GetElementsKind(); |
| 872 switch (kind) { |
| 873 case FAST_SMI_ELEMENTS: |
| 874 case FAST_ELEMENTS: |
| 875 case FAST_HOLEY_SMI_ELEMENTS: |
| 876 case FAST_HOLEY_ELEMENTS: { |
| 877 Handle<FixedArray> elements(FixedArray::cast(object->elements())); |
| 878 uint32_t length = static_cast<uint32_t>(elements->length()); |
| 879 if (range < length) length = range; |
| 880 for (uint32_t i = 0; i < length; i++) { |
| 881 if (!elements->get(i)->IsTheHole()) { |
| 882 indices->Add(i); |
| 883 } |
| 884 } |
| 885 break; |
| 886 } |
| 887 case FAST_HOLEY_DOUBLE_ELEMENTS: |
| 888 case FAST_DOUBLE_ELEMENTS: { |
| 889 if (object->elements()->IsFixedArray()) { |
| 890 DCHECK(object->elements()->length() == 0); |
| 891 break; |
| 892 } |
| 893 Handle<FixedDoubleArray> elements( |
| 894 FixedDoubleArray::cast(object->elements())); |
| 895 uint32_t length = static_cast<uint32_t>(elements->length()); |
| 896 if (range < length) length = range; |
| 897 for (uint32_t i = 0; i < length; i++) { |
| 898 if (!elements->is_the_hole(i)) { |
| 899 indices->Add(i); |
| 900 } |
| 901 } |
| 902 break; |
| 903 } |
| 904 case DICTIONARY_ELEMENTS: { |
| 905 Handle<SeededNumberDictionary> dict( |
| 906 SeededNumberDictionary::cast(object->elements())); |
| 907 uint32_t capacity = dict->Capacity(); |
| 908 for (uint32_t j = 0; j < capacity; j++) { |
| 909 HandleScope loop_scope(isolate); |
| 910 Handle<Object> k(dict->KeyAt(j), isolate); |
| 911 if (dict->IsKey(*k)) { |
| 912 DCHECK(k->IsNumber()); |
| 913 uint32_t index = static_cast<uint32_t>(k->Number()); |
| 914 if (index < range) { |
| 915 indices->Add(index); |
| 916 } |
| 917 } |
| 918 } |
| 919 break; |
| 920 } |
| 921 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS: |
| 922 |
| 923 TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| 924 #undef TYPED_ARRAY_CASE |
| 925 { |
| 926 uint32_t length = static_cast<uint32_t>( |
| 927 FixedArrayBase::cast(object->elements())->length()); |
| 928 if (range <= length) { |
| 929 length = range; |
| 930 // We will add all indices, so we might as well clear it first |
| 931 // and avoid duplicates. |
| 932 indices->Clear(); |
| 933 } |
| 934 for (uint32_t i = 0; i < length; i++) { |
| 935 indices->Add(i); |
| 936 } |
| 937 if (length == range) return; // All indices accounted for already. |
| 938 break; |
| 939 } |
| 940 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: |
| 941 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: { |
| 942 ElementsAccessor* accessor = object->GetElementsAccessor(); |
| 943 for (uint32_t i = 0; i < range; i++) { |
| 944 if (accessor->HasElement(object, i)) { |
| 945 indices->Add(i); |
| 946 } |
| 947 } |
| 948 break; |
| 949 } |
| 950 } |
| 951 |
| 952 PrototypeIterator iter(isolate, object); |
| 953 if (!iter.IsAtEnd()) { |
| 954 // The prototype will usually have no inherited element indices, |
| 955 // but we have to check. |
| 956 CollectElementIndices( |
| 957 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), range, |
| 958 indices); |
| 959 } |
| 960 } |
| 961 |
| 962 |
| 963 bool IterateElementsSlow(Isolate* isolate, Handle<JSObject> receiver, |
| 964 uint32_t length, ArrayConcatVisitor* visitor) { |
| 965 for (uint32_t i = 0; i < length; ++i) { |
| 966 HandleScope loop_scope(isolate); |
| 967 Maybe<bool> maybe = JSReceiver::HasElement(receiver, i); |
| 968 if (!maybe.IsJust()) return false; |
| 969 if (maybe.FromJust()) { |
| 970 Handle<Object> element_value; |
| 971 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_value, |
| 972 Object::GetElement(isolate, receiver, i), |
| 973 false); |
| 974 visitor->visit(i, element_value); |
| 975 } |
| 976 } |
| 977 visitor->increase_index_offset(length); |
| 978 return true; |
| 979 } |
| 980 |
| 981 |
| 982 /** |
| 983 * A helper function that visits elements of a JSObject in numerical |
| 984 * order. |
| 985 * |
| 986 * The visitor argument called for each existing element in the array |
| 987 * with the element index and the element's value. |
| 988 * Afterwards it increments the base-index of the visitor by the array |
| 989 * length. |
| 990 * Returns false if any access threw an exception, otherwise true. |
| 991 */ |
| 992 bool IterateElements(Isolate* isolate, Handle<JSObject> receiver, |
| 993 ArrayConcatVisitor* visitor) { |
| 994 uint32_t length = 0; |
| 995 |
| 996 if (receiver->IsJSArray()) { |
| 997 Handle<JSArray> array(Handle<JSArray>::cast(receiver)); |
| 998 length = static_cast<uint32_t>(array->length()->Number()); |
| 999 } else { |
| 1000 Handle<Object> val; |
| 1001 Handle<Object> key(isolate->heap()->length_string(), isolate); |
| 1002 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 1003 isolate, val, Runtime::GetObjectProperty(isolate, receiver, key), |
| 1004 false); |
| 1005 // TODO(caitp): Support larger element indexes (up to 2^53-1). |
| 1006 if (!val->ToUint32(&length)) { |
| 1007 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 1008 isolate, val, Execution::ToLength(isolate, val), false); |
| 1009 val->ToUint32(&length); |
| 1010 } |
| 1011 } |
| 1012 |
| 1013 if (!(receiver->IsJSArray() || receiver->IsJSTypedArray())) { |
| 1014 // For classes which are not known to be safe to access via elements alone, |
| 1015 // use the slow case. |
| 1016 return IterateElementsSlow(isolate, receiver, length, visitor); |
| 1017 } |
| 1018 |
| 1019 switch (receiver->GetElementsKind()) { |
| 1020 case FAST_SMI_ELEMENTS: |
| 1021 case FAST_ELEMENTS: |
| 1022 case FAST_HOLEY_SMI_ELEMENTS: |
| 1023 case FAST_HOLEY_ELEMENTS: { |
| 1024 // Run through the elements FixedArray and use HasElement and GetElement |
| 1025 // to check the prototype for missing elements. |
| 1026 Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); |
| 1027 int fast_length = static_cast<int>(length); |
| 1028 DCHECK(fast_length <= elements->length()); |
| 1029 for (int j = 0; j < fast_length; j++) { |
| 1030 HandleScope loop_scope(isolate); |
| 1031 Handle<Object> element_value(elements->get(j), isolate); |
| 1032 if (!element_value->IsTheHole()) { |
| 1033 visitor->visit(j, element_value); |
| 1034 } else { |
| 1035 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j); |
| 1036 if (!maybe.IsJust()) return false; |
| 1037 if (maybe.FromJust()) { |
| 1038 // Call GetElement on receiver, not its prototype, or getters won't |
| 1039 // have the correct receiver. |
| 1040 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 1041 isolate, element_value, |
| 1042 Object::GetElement(isolate, receiver, j), false); |
| 1043 visitor->visit(j, element_value); |
| 1044 } |
| 1045 } |
| 1046 } |
| 1047 break; |
| 1048 } |
| 1049 case FAST_HOLEY_DOUBLE_ELEMENTS: |
| 1050 case FAST_DOUBLE_ELEMENTS: { |
| 1051 // Empty array is FixedArray but not FixedDoubleArray. |
| 1052 if (length == 0) break; |
| 1053 // Run through the elements FixedArray and use HasElement and GetElement |
| 1054 // to check the prototype for missing elements. |
| 1055 if (receiver->elements()->IsFixedArray()) { |
| 1056 DCHECK(receiver->elements()->length() == 0); |
| 1057 break; |
| 1058 } |
| 1059 Handle<FixedDoubleArray> elements( |
| 1060 FixedDoubleArray::cast(receiver->elements())); |
| 1061 int fast_length = static_cast<int>(length); |
| 1062 DCHECK(fast_length <= elements->length()); |
| 1063 for (int j = 0; j < fast_length; j++) { |
| 1064 HandleScope loop_scope(isolate); |
| 1065 if (!elements->is_the_hole(j)) { |
| 1066 double double_value = elements->get_scalar(j); |
| 1067 Handle<Object> element_value = |
| 1068 isolate->factory()->NewNumber(double_value); |
| 1069 visitor->visit(j, element_value); |
| 1070 } else { |
| 1071 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j); |
| 1072 if (!maybe.IsJust()) return false; |
| 1073 if (maybe.FromJust()) { |
| 1074 // Call GetElement on receiver, not its prototype, or getters won't |
| 1075 // have the correct receiver. |
| 1076 Handle<Object> element_value; |
| 1077 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 1078 isolate, element_value, |
| 1079 Object::GetElement(isolate, receiver, j), false); |
| 1080 visitor->visit(j, element_value); |
| 1081 } |
| 1082 } |
| 1083 } |
| 1084 break; |
| 1085 } |
| 1086 case DICTIONARY_ELEMENTS: { |
| 1087 Handle<SeededNumberDictionary> dict(receiver->element_dictionary()); |
| 1088 List<uint32_t> indices(dict->Capacity() / 2); |
| 1089 // Collect all indices in the object and the prototypes less |
| 1090 // than length. This might introduce duplicates in the indices list. |
| 1091 CollectElementIndices(receiver, length, &indices); |
| 1092 indices.Sort(&compareUInt32); |
| 1093 int j = 0; |
| 1094 int n = indices.length(); |
| 1095 while (j < n) { |
| 1096 HandleScope loop_scope(isolate); |
| 1097 uint32_t index = indices[j]; |
| 1098 Handle<Object> element; |
| 1099 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 1100 isolate, element, Object::GetElement(isolate, receiver, index), |
| 1101 false); |
| 1102 visitor->visit(index, element); |
| 1103 // Skip to next different index (i.e., omit duplicates). |
| 1104 do { |
| 1105 j++; |
| 1106 } while (j < n && indices[j] == index); |
| 1107 } |
| 1108 break; |
| 1109 } |
| 1110 case UINT8_CLAMPED_ELEMENTS: { |
| 1111 Handle<FixedUint8ClampedArray> pixels( |
| 1112 FixedUint8ClampedArray::cast(receiver->elements())); |
| 1113 for (uint32_t j = 0; j < length; j++) { |
| 1114 Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate); |
| 1115 visitor->visit(j, e); |
| 1116 } |
| 1117 break; |
| 1118 } |
| 1119 case INT8_ELEMENTS: { |
| 1120 IterateTypedArrayElements<FixedInt8Array, int8_t>(isolate, receiver, true, |
| 1121 true, visitor); |
| 1122 break; |
| 1123 } |
| 1124 case UINT8_ELEMENTS: { |
| 1125 IterateTypedArrayElements<FixedUint8Array, uint8_t>(isolate, receiver, |
| 1126 true, true, visitor); |
| 1127 break; |
| 1128 } |
| 1129 case INT16_ELEMENTS: { |
| 1130 IterateTypedArrayElements<FixedInt16Array, int16_t>(isolate, receiver, |
| 1131 true, true, visitor); |
| 1132 break; |
| 1133 } |
| 1134 case UINT16_ELEMENTS: { |
| 1135 IterateTypedArrayElements<FixedUint16Array, uint16_t>( |
| 1136 isolate, receiver, true, true, visitor); |
| 1137 break; |
| 1138 } |
| 1139 case INT32_ELEMENTS: { |
| 1140 IterateTypedArrayElements<FixedInt32Array, int32_t>(isolate, receiver, |
| 1141 true, false, visitor); |
| 1142 break; |
| 1143 } |
| 1144 case UINT32_ELEMENTS: { |
| 1145 IterateTypedArrayElements<FixedUint32Array, uint32_t>( |
| 1146 isolate, receiver, true, false, visitor); |
| 1147 break; |
| 1148 } |
| 1149 case FLOAT32_ELEMENTS: { |
| 1150 IterateTypedArrayElements<FixedFloat32Array, float>( |
| 1151 isolate, receiver, false, false, visitor); |
| 1152 break; |
| 1153 } |
| 1154 case FLOAT64_ELEMENTS: { |
| 1155 IterateTypedArrayElements<FixedFloat64Array, double>( |
| 1156 isolate, receiver, false, false, visitor); |
| 1157 break; |
| 1158 } |
| 1159 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: |
| 1160 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: { |
| 1161 for (uint32_t index = 0; index < length; index++) { |
| 1162 HandleScope loop_scope(isolate); |
| 1163 Handle<Object> element; |
| 1164 ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 1165 isolate, element, Object::GetElement(isolate, receiver, index), |
| 1166 false); |
| 1167 visitor->visit(index, element); |
| 1168 } |
| 1169 break; |
| 1170 } |
| 1171 } |
| 1172 visitor->increase_index_offset(length); |
| 1173 return true; |
| 1174 } |
| 1175 |
| 1176 |
| 1177 bool HasConcatSpreadableModifier(Isolate* isolate, Handle<JSArray> obj) { |
| 1178 if (!FLAG_harmony_concat_spreadable) return false; |
| 1179 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol()); |
| 1180 Maybe<bool> maybe = |
| 1181 JSReceiver::HasProperty(Handle<JSReceiver>::cast(obj), key); |
| 1182 if (!maybe.IsJust()) return false; |
| 1183 return maybe.FromJust(); |
| 1184 } |
| 1185 |
| 1186 |
| 1187 bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) { |
| 1188 HandleScope handle_scope(isolate); |
| 1189 if (!obj->IsSpecObject()) return false; |
| 1190 if (FLAG_harmony_concat_spreadable) { |
| 1191 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol()); |
| 1192 Handle<Object> value; |
| 1193 MaybeHandle<Object> maybeValue = |
| 1194 i::Runtime::GetObjectProperty(isolate, obj, key); |
| 1195 if (maybeValue.ToHandle(&value) && !value->IsUndefined()) { |
| 1196 return value->BooleanValue(); |
| 1197 } |
| 1198 } |
| 1199 return obj->IsJSArray(); |
| 1200 } |
| 1201 |
| 1202 |
| 1203 /** |
| 1204 * Array::concat implementation. |
| 1205 * See ECMAScript 262, 15.4.4.4. |
| 1206 * TODO(581): Fix non-compliance for very large concatenations and update to |
| 1207 * following the ECMAScript 5 specification. |
| 1208 */ |
| 1209 Object* Slow_ArrayConcat(Arguments* args, Isolate* isolate) { |
| 1210 int argument_count = args->length(); |
| 1211 |
| 1212 // Pass 1: estimate the length and number of elements of the result. |
| 1213 // The actual length can be larger if any of the arguments have getters |
| 1214 // that mutate other arguments (but will otherwise be precise). |
| 1215 // The number of elements is precise if there are no inherited elements. |
| 1216 |
| 1217 ElementsKind kind = FAST_SMI_ELEMENTS; |
| 1218 |
| 1219 uint32_t estimate_result_length = 0; |
| 1220 uint32_t estimate_nof_elements = 0; |
| 1221 for (int i = 0; i < argument_count; i++) { |
| 1222 HandleScope loop_scope(isolate); |
| 1223 Handle<Object> obj((*args)[i], isolate); |
| 1224 uint32_t length_estimate; |
| 1225 uint32_t element_estimate; |
| 1226 if (obj->IsJSArray()) { |
| 1227 Handle<JSArray> array(Handle<JSArray>::cast(obj)); |
| 1228 length_estimate = static_cast<uint32_t>(array->length()->Number()); |
| 1229 if (length_estimate != 0) { |
| 1230 ElementsKind array_kind = |
| 1231 GetPackedElementsKind(array->map()->elements_kind()); |
| 1232 if (IsMoreGeneralElementsKindTransition(kind, array_kind)) { |
| 1233 kind = array_kind; |
| 1234 } |
| 1235 } |
| 1236 element_estimate = EstimateElementCount(array); |
| 1237 } else { |
| 1238 if (obj->IsHeapObject()) { |
| 1239 if (obj->IsNumber()) { |
| 1240 if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) { |
| 1241 kind = FAST_DOUBLE_ELEMENTS; |
| 1242 } |
| 1243 } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) { |
| 1244 kind = FAST_ELEMENTS; |
| 1245 } |
| 1246 } |
| 1247 length_estimate = 1; |
| 1248 element_estimate = 1; |
| 1249 } |
| 1250 // Avoid overflows by capping at kMaxElementCount. |
| 1251 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) { |
| 1252 estimate_result_length = JSObject::kMaxElementCount; |
| 1253 } else { |
| 1254 estimate_result_length += length_estimate; |
| 1255 } |
| 1256 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) { |
| 1257 estimate_nof_elements = JSObject::kMaxElementCount; |
| 1258 } else { |
| 1259 estimate_nof_elements += element_estimate; |
| 1260 } |
| 1261 } |
| 1262 |
| 1263 // If estimated number of elements is more than half of length, a |
| 1264 // fixed array (fast case) is more time and space-efficient than a |
| 1265 // dictionary. |
| 1266 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; |
| 1267 |
| 1268 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) { |
| 1269 Handle<FixedArrayBase> storage = |
| 1270 isolate->factory()->NewFixedDoubleArray(estimate_result_length); |
| 1271 int j = 0; |
| 1272 bool failure = false; |
| 1273 if (estimate_result_length > 0) { |
| 1274 Handle<FixedDoubleArray> double_storage = |
| 1275 Handle<FixedDoubleArray>::cast(storage); |
| 1276 for (int i = 0; i < argument_count; i++) { |
| 1277 Handle<Object> obj((*args)[i], isolate); |
| 1278 if (obj->IsSmi()) { |
| 1279 double_storage->set(j, Smi::cast(*obj)->value()); |
| 1280 j++; |
| 1281 } else if (obj->IsNumber()) { |
| 1282 double_storage->set(j, obj->Number()); |
| 1283 j++; |
| 1284 } else { |
| 1285 JSArray* array = JSArray::cast(*obj); |
| 1286 uint32_t length = static_cast<uint32_t>(array->length()->Number()); |
| 1287 switch (array->map()->elements_kind()) { |
| 1288 case FAST_HOLEY_DOUBLE_ELEMENTS: |
| 1289 case FAST_DOUBLE_ELEMENTS: { |
| 1290 // Empty array is FixedArray but not FixedDoubleArray. |
| 1291 if (length == 0) break; |
| 1292 FixedDoubleArray* elements = |
| 1293 FixedDoubleArray::cast(array->elements()); |
| 1294 for (uint32_t i = 0; i < length; i++) { |
| 1295 if (elements->is_the_hole(i)) { |
| 1296 // TODO(jkummerow/verwaest): We could be a bit more clever |
| 1297 // here: Check if there are no elements/getters on the |
| 1298 // prototype chain, and if so, allow creation of a holey |
| 1299 // result array. |
| 1300 // Same thing below (holey smi case). |
| 1301 failure = true; |
| 1302 break; |
| 1303 } |
| 1304 double double_value = elements->get_scalar(i); |
| 1305 double_storage->set(j, double_value); |
| 1306 j++; |
| 1307 } |
| 1308 break; |
| 1309 } |
| 1310 case FAST_HOLEY_SMI_ELEMENTS: |
| 1311 case FAST_SMI_ELEMENTS: { |
| 1312 FixedArray* elements(FixedArray::cast(array->elements())); |
| 1313 for (uint32_t i = 0; i < length; i++) { |
| 1314 Object* element = elements->get(i); |
| 1315 if (element->IsTheHole()) { |
| 1316 failure = true; |
| 1317 break; |
| 1318 } |
| 1319 int32_t int_value = Smi::cast(element)->value(); |
| 1320 double_storage->set(j, int_value); |
| 1321 j++; |
| 1322 } |
| 1323 break; |
| 1324 } |
| 1325 case FAST_HOLEY_ELEMENTS: |
| 1326 case FAST_ELEMENTS: |
| 1327 case DICTIONARY_ELEMENTS: |
| 1328 DCHECK_EQ(0u, length); |
| 1329 break; |
| 1330 default: |
| 1331 UNREACHABLE(); |
| 1332 } |
| 1333 } |
| 1334 if (failure) break; |
| 1335 } |
| 1336 } |
| 1337 if (!failure) { |
| 1338 Handle<JSArray> array = isolate->factory()->NewJSArray(0); |
| 1339 Smi* length = Smi::FromInt(j); |
| 1340 Handle<Map> map; |
| 1341 map = JSObject::GetElementsTransitionMap(array, kind); |
| 1342 array->set_map(*map); |
| 1343 array->set_length(length); |
| 1344 array->set_elements(*storage); |
| 1345 return *array; |
| 1346 } |
| 1347 // In case of failure, fall through. |
| 1348 } |
| 1349 |
| 1350 Handle<FixedArray> storage; |
| 1351 if (fast_case) { |
| 1352 // The backing storage array must have non-existing elements to preserve |
| 1353 // holes across concat operations. |
| 1354 storage = |
| 1355 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length); |
| 1356 } else { |
| 1357 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate |
| 1358 uint32_t at_least_space_for = |
| 1359 estimate_nof_elements + (estimate_nof_elements >> 2); |
| 1360 storage = Handle<FixedArray>::cast( |
| 1361 SeededNumberDictionary::New(isolate, at_least_space_for)); |
| 1362 } |
| 1363 |
| 1364 ArrayConcatVisitor visitor(isolate, storage, fast_case); |
| 1365 |
| 1366 for (int i = 0; i < argument_count; i++) { |
| 1367 Handle<Object> obj((*args)[i], isolate); |
| 1368 bool spreadable = IsConcatSpreadable(isolate, obj); |
| 1369 if (isolate->has_pending_exception()) return isolate->heap()->exception(); |
| 1370 if (spreadable) { |
| 1371 Handle<JSObject> object = Handle<JSObject>::cast(obj); |
| 1372 if (!IterateElements(isolate, object, &visitor)) { |
| 1373 return isolate->heap()->exception(); |
| 1374 } |
| 1375 } else { |
| 1376 visitor.visit(0, obj); |
| 1377 visitor.increase_index_offset(1); |
| 1378 } |
| 1379 } |
| 1380 |
| 1381 if (visitor.exceeds_array_limit()) { |
| 1382 THROW_NEW_ERROR_RETURN_FAILURE( |
| 1383 isolate, NewRangeError(MessageTemplate::kInvalidArrayLength)); |
| 1384 } |
| 1385 return *visitor.ToArray(); |
| 1386 } |
| 1387 |
| 1388 |
| 1389 MaybeHandle<JSArray> Fast_ArrayConcat(Isolate* isolate, Arguments* args) { |
| 1390 if (!isolate->IsFastArrayConstructorPrototypeChainIntact()) { |
| 1391 return MaybeHandle<JSArray>(); |
| 1392 } |
| 1393 int n_arguments = args->length(); |
611 int result_len = 0; | 1394 int result_len = 0; |
612 ElementsKind elements_kind = GetInitialFastElementsKind(); | |
613 bool has_double = false; | |
614 { | 1395 { |
615 DisallowHeapAllocation no_gc; | 1396 DisallowHeapAllocation no_gc; |
616 Context* native_context = isolate->context()->native_context(); | 1397 Object* array_proto = isolate->array_function()->prototype(); |
617 Object* array_proto = native_context->array_function()->prototype(); | |
618 PrototypeIterator iter(isolate, array_proto, | |
619 PrototypeIterator::START_AT_RECEIVER); | |
620 if (!PrototypeHasNoElements(&iter)) { | |
621 AllowHeapAllocation allow_allocation; | |
622 return CallJsIntrinsic(isolate, isolate->array_concat(), args); | |
623 } | |
624 | |
625 // Iterate through all the arguments performing checks | 1398 // Iterate through all the arguments performing checks |
626 // and calculating total length. | 1399 // and calculating total length. |
627 bool is_holey = false; | |
628 for (int i = 0; i < n_arguments; i++) { | 1400 for (int i = 0; i < n_arguments; i++) { |
629 Object* arg = args[i]; | 1401 Object* arg = (*args)[i]; |
| 1402 if (!arg->IsJSArray()) return MaybeHandle<JSArray>(); |
| 1403 Handle<JSArray> array(JSArray::cast(arg), isolate); |
| 1404 if (!array->HasFastElements()) return MaybeHandle<JSArray>(); |
630 PrototypeIterator iter(isolate, arg); | 1405 PrototypeIterator iter(isolate, arg); |
631 if (!arg->IsJSArray() || !JSArray::cast(arg)->HasFastElements() || | 1406 if (iter.GetCurrent() != array_proto) return MaybeHandle<JSArray>(); |
632 iter.GetCurrent() != array_proto) { | 1407 if (HasConcatSpreadableModifier(isolate, array)) { |
633 AllowHeapAllocation allow_allocation; | 1408 return MaybeHandle<JSArray>(); |
634 return CallJsIntrinsic(isolate, isolate->array_concat(), args); | 1409 } |
635 } | 1410 int len = Smi::cast(array->length())->value(); |
636 int len = Smi::cast(JSArray::cast(arg)->length())->value(); | |
637 | 1411 |
638 // We shouldn't overflow when adding another len. | 1412 // We shouldn't overflow when adding another len. |
639 const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2); | 1413 const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2); |
640 STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt); | 1414 STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt); |
641 USE(kHalfOfMaxInt); | 1415 USE(kHalfOfMaxInt); |
642 result_len += len; | 1416 result_len += len; |
643 DCHECK(result_len >= 0); | 1417 DCHECK(result_len >= 0); |
644 | 1418 // Throw an Error if we overflow the FixedArray limits |
645 if (result_len > FixedDoubleArray::kMaxLength) { | 1419 if (FixedArray::kMaxLength < result_len) { |
646 AllowHeapAllocation allow_allocation; | 1420 THROW_NEW_ERROR(isolate, |
647 return CallJsIntrinsic(isolate, isolate->array_concat(), args); | 1421 NewRangeError(MessageTemplate::kInvalidArrayLength), |
| 1422 JSArray); |
648 } | 1423 } |
649 | |
650 ElementsKind arg_kind = JSArray::cast(arg)->map()->elements_kind(); | |
651 has_double = has_double || IsFastDoubleElementsKind(arg_kind); | |
652 is_holey = is_holey || IsFastHoleyElementsKind(arg_kind); | |
653 elements_kind = GetMoreGeneralElementsKind(elements_kind, arg_kind); | |
654 } | |
655 if (is_holey) elements_kind = GetHoleyElementsKind(elements_kind); | |
656 } | |
657 | |
658 // If a double array is concatted into a fast elements array, the fast | |
659 // elements array needs to be initialized to contain proper holes, since | |
660 // boxing doubles may cause incremental marking. | |
661 ArrayStorageAllocationMode mode = | |
662 has_double && IsFastObjectElementsKind(elements_kind) | |
663 ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE : DONT_INITIALIZE_ARRAY_ELEMENTS; | |
664 Handle<JSArray> result_array = isolate->factory()->NewJSArray( | |
665 elements_kind, result_len, result_len, Strength::WEAK, mode); | |
666 if (result_len == 0) return *result_array; | |
667 | |
668 int j = 0; | |
669 Handle<FixedArrayBase> storage(result_array->elements(), isolate); | |
670 ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); | |
671 for (int i = 0; i < n_arguments; i++) { | |
672 // It is crucial to keep |array| in a raw pointer form to avoid performance | |
673 // degradation. | |
674 JSArray* array = JSArray::cast(args[i]); | |
675 int len = Smi::cast(array->length())->value(); | |
676 if (len > 0) { | |
677 ElementsKind from_kind = array->GetElementsKind(); | |
678 accessor->CopyElements(array, 0, from_kind, storage, j, len); | |
679 j += len; | |
680 } | 1424 } |
681 } | 1425 } |
| 1426 return ElementsAccessor::Concat(isolate, args, n_arguments); |
| 1427 } |
682 | 1428 |
683 DCHECK(j == result_len); | 1429 } // namespace |
684 | 1430 |
685 return *result_array; | 1431 BUILTIN(ArrayConcat) { |
| 1432 HandleScope scope(isolate); |
| 1433 |
| 1434 Handle<Object> receiver; |
| 1435 if (!Object::ToObject(isolate, handle(args[0], isolate)) |
| 1436 .ToHandle(&receiver)) { |
| 1437 THROW_NEW_ERROR_RETURN_FAILURE( |
| 1438 isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined, |
| 1439 isolate->factory()->NewStringFromAsciiChecked( |
| 1440 "Array.prototype.concat"))); |
| 1441 } |
| 1442 args[0] = *receiver; |
| 1443 |
| 1444 Handle<JSArray> result_array; |
| 1445 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) { |
| 1446 return *result_array; |
| 1447 } |
| 1448 if (isolate->has_pending_exception()) return isolate->heap()->exception(); |
| 1449 return Slow_ArrayConcat(&args, isolate); |
686 } | 1450 } |
687 | 1451 |
688 | 1452 |
689 // ----------------------------------------------------------------------------- | 1453 // ----------------------------------------------------------------------------- |
690 // | 1454 // |
691 | 1455 |
692 | 1456 |
693 // 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint ) | 1457 // 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint ) |
694 BUILTIN(DateToPrimitive) { | 1458 BUILTIN(DateToPrimitive) { |
695 HandleScope scope(isolate); | 1459 HandleScope scope(isolate); |
(...skipping 615 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1311 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) | 2075 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) |
1312 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) | 2076 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) |
1313 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) | 2077 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) |
1314 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) | 2078 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) |
1315 #undef DEFINE_BUILTIN_ACCESSOR_C | 2079 #undef DEFINE_BUILTIN_ACCESSOR_C |
1316 #undef DEFINE_BUILTIN_ACCESSOR_A | 2080 #undef DEFINE_BUILTIN_ACCESSOR_A |
1317 | 2081 |
1318 | 2082 |
1319 } // namespace internal | 2083 } // namespace internal |
1320 } // namespace v8 | 2084 } // namespace v8 |
OLD | NEW |