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 { | |
Camillo Bruni
2015/09/04 13:54:55
Probably ElementsAccessor would be a better place
| |
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<Object> obj) { | |
Camillo Bruni
2015/09/04 13:54:55
At some point we need something faster here, since
| |
1178 if (!obj->IsJSReceiver()) return false; | |
1179 if (!FLAG_harmony_concat_spreadable) return false; | |
1180 | |
1181 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol()); | |
1182 Maybe<bool> maybe = | |
1183 JSReceiver::HasProperty(Handle<JSReceiver>::cast(obj), key); | |
1184 if (!maybe.IsJust()) return false; | |
1185 return maybe.FromJust(); | |
1186 } | |
1187 | |
1188 | |
1189 bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) { | |
1190 HandleScope handle_scope(isolate); | |
1191 if (!obj->IsSpecObject()) return false; | |
1192 if (FLAG_harmony_concat_spreadable) { | |
1193 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol()); | |
1194 Handle<Object> value; | |
1195 MaybeHandle<Object> maybeValue = | |
1196 i::Runtime::GetObjectProperty(isolate, obj, key); | |
1197 if (maybeValue.ToHandle(&value) && !value->IsUndefined()) { | |
1198 return value->BooleanValue(); | |
1199 } | |
1200 } | |
1201 return obj->IsJSArray(); | |
1202 } | |
1203 | |
1204 | |
1205 /** | |
1206 * Array::concat implementation. | |
1207 * See ECMAScript 262, 15.4.4.4. | |
1208 * TODO(581): Fix non-compliance for very large concatenations and update to | |
1209 * following the ECMAScript 5 specification. | |
1210 */ | |
1211 Object* Slow_ArrayConcat(Arguments* args, Isolate* isolate) { | |
1212 int argument_count = args->length(); | |
1213 | |
1214 // Pass 1: estimate the length and number of elements of the result. | |
1215 // The actual length can be larger if any of the arguments have getters | |
1216 // that mutate other arguments (but will otherwise be precise). | |
1217 // The number of elements is precise if there are no inherited elements. | |
1218 | |
1219 ElementsKind kind = FAST_SMI_ELEMENTS; | |
1220 | |
1221 uint32_t estimate_result_length = 0; | |
1222 uint32_t estimate_nof_elements = 0; | |
1223 for (int i = 0; i < argument_count; i++) { | |
1224 HandleScope loop_scope(isolate); | |
1225 Handle<Object> obj((*args)[i], isolate); | |
1226 uint32_t length_estimate; | |
1227 uint32_t element_estimate; | |
1228 if (obj->IsJSArray()) { | |
1229 Handle<JSArray> array(Handle<JSArray>::cast(obj)); | |
1230 length_estimate = static_cast<uint32_t>(array->length()->Number()); | |
1231 if (length_estimate != 0) { | |
1232 ElementsKind array_kind = | |
1233 GetPackedElementsKind(array->map()->elements_kind()); | |
1234 if (IsMoreGeneralElementsKindTransition(kind, array_kind)) { | |
1235 kind = array_kind; | |
1236 } | |
1237 } | |
1238 element_estimate = EstimateElementCount(array); | |
1239 } else { | |
1240 if (obj->IsHeapObject()) { | |
1241 if (obj->IsNumber()) { | |
1242 if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) { | |
1243 kind = FAST_DOUBLE_ELEMENTS; | |
1244 } | |
1245 } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) { | |
1246 kind = FAST_ELEMENTS; | |
1247 } | |
1248 } | |
1249 length_estimate = 1; | |
1250 element_estimate = 1; | |
1251 } | |
1252 // Avoid overflows by capping at kMaxElementCount. | |
1253 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) { | |
1254 estimate_result_length = JSObject::kMaxElementCount; | |
1255 } else { | |
1256 estimate_result_length += length_estimate; | |
1257 } | |
1258 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) { | |
1259 estimate_nof_elements = JSObject::kMaxElementCount; | |
1260 } else { | |
1261 estimate_nof_elements += element_estimate; | |
1262 } | |
1263 } | |
1264 | |
1265 // If estimated number of elements is more than half of length, a | |
1266 // fixed array (fast case) is more time and space-efficient than a | |
1267 // dictionary. | |
1268 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; | |
1269 | |
1270 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) { | |
1271 Handle<FixedArrayBase> storage = | |
1272 isolate->factory()->NewFixedDoubleArray(estimate_result_length); | |
1273 int j = 0; | |
1274 bool failure = false; | |
1275 if (estimate_result_length > 0) { | |
1276 Handle<FixedDoubleArray> double_storage = | |
1277 Handle<FixedDoubleArray>::cast(storage); | |
1278 for (int i = 0; i < argument_count; i++) { | |
1279 Handle<Object> obj((*args)[i], isolate); | |
1280 if (obj->IsSmi()) { | |
1281 double_storage->set(j, Smi::cast(*obj)->value()); | |
1282 j++; | |
1283 } else if (obj->IsNumber()) { | |
1284 double_storage->set(j, obj->Number()); | |
1285 j++; | |
1286 } else { | |
1287 JSArray* array = JSArray::cast(*obj); | |
1288 uint32_t length = static_cast<uint32_t>(array->length()->Number()); | |
1289 switch (array->map()->elements_kind()) { | |
1290 case FAST_HOLEY_DOUBLE_ELEMENTS: | |
1291 case FAST_DOUBLE_ELEMENTS: { | |
1292 // Empty array is FixedArray but not FixedDoubleArray. | |
1293 if (length == 0) break; | |
1294 FixedDoubleArray* elements = | |
1295 FixedDoubleArray::cast(array->elements()); | |
1296 for (uint32_t i = 0; i < length; i++) { | |
1297 if (elements->is_the_hole(i)) { | |
1298 // TODO(jkummerow/verwaest): We could be a bit more clever | |
1299 // here: Check if there are no elements/getters on the | |
1300 // prototype chain, and if so, allow creation of a holey | |
1301 // result array. | |
1302 // Same thing below (holey smi case). | |
1303 failure = true; | |
1304 break; | |
1305 } | |
1306 double double_value = elements->get_scalar(i); | |
1307 double_storage->set(j, double_value); | |
1308 j++; | |
1309 } | |
1310 break; | |
1311 } | |
1312 case FAST_HOLEY_SMI_ELEMENTS: | |
1313 case FAST_SMI_ELEMENTS: { | |
1314 FixedArray* elements(FixedArray::cast(array->elements())); | |
1315 for (uint32_t i = 0; i < length; i++) { | |
1316 Object* element = elements->get(i); | |
1317 if (element->IsTheHole()) { | |
1318 failure = true; | |
1319 break; | |
1320 } | |
1321 int32_t int_value = Smi::cast(element)->value(); | |
1322 double_storage->set(j, int_value); | |
1323 j++; | |
1324 } | |
1325 break; | |
1326 } | |
1327 case FAST_HOLEY_ELEMENTS: | |
1328 case FAST_ELEMENTS: | |
1329 case DICTIONARY_ELEMENTS: | |
1330 DCHECK_EQ(0u, length); | |
1331 break; | |
1332 default: | |
1333 UNREACHABLE(); | |
1334 } | |
1335 } | |
1336 if (failure) break; | |
1337 } | |
1338 } | |
1339 if (!failure) { | |
1340 Handle<JSArray> array = isolate->factory()->NewJSArray(0); | |
1341 Smi* length = Smi::FromInt(j); | |
1342 Handle<Map> map; | |
1343 map = JSObject::GetElementsTransitionMap(array, kind); | |
1344 array->set_map(*map); | |
1345 array->set_length(length); | |
1346 array->set_elements(*storage); | |
1347 return *array; | |
1348 } | |
1349 // In case of failure, fall through. | |
1350 } | |
1351 | |
1352 Handle<FixedArray> storage; | |
1353 if (fast_case) { | |
1354 // The backing storage array must have non-existing elements to preserve | |
1355 // holes across concat operations. | |
1356 storage = | |
1357 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length); | |
1358 } else { | |
1359 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate | |
1360 uint32_t at_least_space_for = | |
1361 estimate_nof_elements + (estimate_nof_elements >> 2); | |
1362 storage = Handle<FixedArray>::cast( | |
1363 SeededNumberDictionary::New(isolate, at_least_space_for)); | |
1364 } | |
1365 | |
1366 ArrayConcatVisitor visitor(isolate, storage, fast_case); | |
1367 | |
1368 for (int i = 0; i < argument_count; i++) { | |
1369 Handle<Object> obj((*args)[i], isolate); | |
1370 bool spreadable = IsConcatSpreadable(isolate, obj); | |
1371 if (isolate->has_pending_exception()) return isolate->heap()->exception(); | |
1372 if (spreadable) { | |
1373 Handle<JSObject> object = Handle<JSObject>::cast(obj); | |
1374 if (!IterateElements(isolate, object, &visitor)) { | |
1375 return isolate->heap()->exception(); | |
1376 } | |
1377 } else { | |
1378 visitor.visit(0, obj); | |
1379 visitor.increase_index_offset(1); | |
1380 } | |
1381 } | |
1382 | |
1383 if (visitor.exceeds_array_limit()) { | |
1384 THROW_NEW_ERROR_RETURN_FAILURE( | |
1385 isolate, NewRangeError(MessageTemplate::kInvalidArrayLength)); | |
1386 } | |
1387 return *visitor.ToArray(); | |
1388 } | |
1389 | |
1390 | |
1391 MaybeHandle<JSArray> Fast_ArrayConcat(Isolate* isolate, Arguments* args) { | |
1392 if (!isolate->IsFastArrayConstructorPrototypeChainIntact()) { | |
1393 return MaybeHandle<JSArray>(); | |
1394 } | |
1395 int n_arguments = args->length(); | |
611 int result_len = 0; | 1396 int result_len = 0; |
612 ElementsKind elements_kind = GetInitialFastElementsKind(); | |
613 bool has_double = false; | |
614 { | 1397 { |
615 DisallowHeapAllocation no_gc; | 1398 DisallowHeapAllocation no_gc; |
616 Context* native_context = isolate->context()->native_context(); | 1399 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 | 1400 // Iterate through all the arguments performing checks |
626 // and calculating total length. | 1401 // and calculating total length. |
627 bool is_holey = false; | |
628 for (int i = 0; i < n_arguments; i++) { | 1402 for (int i = 0; i < n_arguments; i++) { |
629 Object* arg = args[i]; | 1403 Object* arg = (*args)[i]; |
1404 if (!arg->IsJSArray()) return MaybeHandle<JSArray>(); | |
1405 Handle<JSArray> array(JSArray::cast(arg), isolate); | |
1406 if (!array->HasFastElements()) return MaybeHandle<JSArray>(); | |
630 PrototypeIterator iter(isolate, arg); | 1407 PrototypeIterator iter(isolate, arg); |
631 if (!arg->IsJSArray() || !JSArray::cast(arg)->HasFastElements() || | 1408 if (iter.GetCurrent() != array_proto) return MaybeHandle<JSArray>(); |
632 iter.GetCurrent() != array_proto) { | 1409 if (HasConcatSpreadableModifier(isolate, array)) { |
Camillo Bruni
2015/09/04 13:54:55
Needed for certain cases (see https://codereview.c
| |
633 AllowHeapAllocation allow_allocation; | 1410 return MaybeHandle<JSArray>(); |
634 return CallJsIntrinsic(isolate, isolate->array_concat(), args); | 1411 } |
635 } | 1412 int len = Smi::cast(array->length())->value(); |
636 int len = Smi::cast(JSArray::cast(arg)->length())->value(); | |
637 | 1413 |
638 // We shouldn't overflow when adding another len. | 1414 // We shouldn't overflow when adding another len. |
639 const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2); | 1415 const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2); |
640 STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt); | 1416 STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt); |
641 USE(kHalfOfMaxInt); | 1417 USE(kHalfOfMaxInt); |
642 result_len += len; | 1418 result_len += len; |
643 DCHECK(result_len >= 0); | 1419 DCHECK(result_len >= 0); |
644 | 1420 |
645 if (result_len > FixedDoubleArray::kMaxLength) { | 1421 if (result_len > FixedDoubleArray::kMaxLength) { |
646 AllowHeapAllocation allow_allocation; | 1422 isolate->Throw(*isolate->factory()->NewRangeError( |
647 return CallJsIntrinsic(isolate, isolate->array_concat(), args); | 1423 MessageTemplate::kInvalidArrayLength)); |
648 } | 1424 return MaybeHandle<JSArray>(); |
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 if (IsMoreGeneralElementsKindTransition(elements_kind, arg_kind)) { | |
654 elements_kind = arg_kind; | |
655 } | 1425 } |
656 } | 1426 } |
657 if (is_holey) elements_kind = GetHoleyElementsKind(elements_kind); | |
658 } | 1427 } |
1428 return ElementsAccessor::Concat(isolate, args, n_arguments); | |
1429 } | |
659 | 1430 |
660 // If a double array is concatted into a fast elements array, the fast | 1431 } // namespace |
661 // elements array needs to be initialized to contain proper holes, since | |
662 // boxing doubles may cause incremental marking. | |
663 ArrayStorageAllocationMode mode = | |
664 has_double && IsFastObjectElementsKind(elements_kind) | |
665 ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE : DONT_INITIALIZE_ARRAY_ELEMENTS; | |
666 Handle<JSArray> result_array = isolate->factory()->NewJSArray( | |
667 elements_kind, result_len, result_len, Strength::WEAK, mode); | |
668 if (result_len == 0) return *result_array; | |
669 | 1432 |
670 int j = 0; | 1433 BUILTIN(ArrayConcat) { |
671 Handle<FixedArrayBase> storage(result_array->elements(), isolate); | 1434 HandleScope scope(isolate); |
672 ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); | 1435 |
673 for (int i = 0; i < n_arguments; i++) { | 1436 Handle<Object> receiver; |
674 // It is crucial to keep |array| in a raw pointer form to avoid performance | 1437 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
675 // degradation. | 1438 isolate, receiver, |
676 JSArray* array = JSArray::cast(args[i]); | 1439 Execution::ToObject(isolate, handle(args[0], isolate))); |
677 int len = Smi::cast(array->length())->value(); | 1440 args[0] = *receiver; |
678 if (len > 0) { | 1441 |
679 ElementsKind from_kind = array->GetElementsKind(); | 1442 Handle<JSArray> result_array; |
680 accessor->CopyElements(array, 0, from_kind, storage, j, len); | 1443 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) { |
681 j += len; | 1444 return *result_array; |
682 } | |
683 } | 1445 } |
684 | 1446 if (isolate->has_pending_exception()) return isolate->heap()->exception(); |
685 DCHECK(j == result_len); | 1447 return Slow_ArrayConcat(&args, isolate); |
686 | |
687 return *result_array; | |
688 } | 1448 } |
689 | 1449 |
690 | 1450 |
691 // ----------------------------------------------------------------------------- | 1451 // ----------------------------------------------------------------------------- |
692 // | 1452 // |
693 | 1453 |
694 | 1454 |
695 // 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint ) | 1455 // 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint ) |
696 BUILTIN(DateToPrimitive) { | 1456 BUILTIN(DateToPrimitive) { |
697 HandleScope scope(isolate); | 1457 HandleScope scope(isolate); |
(...skipping 615 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1313 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) | 2073 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) |
1314 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) | 2074 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) |
1315 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) | 2075 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) |
1316 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) | 2076 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) |
1317 #undef DEFINE_BUILTIN_ACCESSOR_C | 2077 #undef DEFINE_BUILTIN_ACCESSOR_C |
1318 #undef DEFINE_BUILTIN_ACCESSOR_A | 2078 #undef DEFINE_BUILTIN_ACCESSOR_A |
1319 | 2079 |
1320 | 2080 |
1321 } // namespace internal | 2081 } // namespace internal |
1322 } // namespace v8 | 2082 } // namespace v8 |
OLD | NEW |