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

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: merging master 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
« no previous file with comments | « src/bootstrapper.cc ('k') | src/elements.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 {
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
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
OLDNEW
« no previous file with comments | « src/bootstrapper.cc ('k') | src/elements.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698