Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1340)

Side by Side Diff: src/builtins.cc

Issue 1330483003: Adding ElementsAccessor::Concat (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@2015-09-01_array_builtins_shift
Patch Set: fixing concat spreadable issues Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698