Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 7873 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 7884 * A simple visitor visits every element of Array's. | 7884 * A simple visitor visits every element of Array's. |
| 7885 * The backend storage can be a fixed array for fast elements case, | 7885 * The backend storage can be a fixed array for fast elements case, |
| 7886 * or a dictionary for sparse array. Since Dictionary is a subtype | 7886 * or a dictionary for sparse array. Since Dictionary is a subtype |
| 7887 * of FixedArray, the class can be used by both fast and slow cases. | 7887 * of FixedArray, the class can be used by both fast and slow cases. |
| 7888 * The second parameter of the constructor, fast_elements, specifies | 7888 * The second parameter of the constructor, fast_elements, specifies |
| 7889 * whether the storage is a FixedArray or Dictionary. | 7889 * whether the storage is a FixedArray or Dictionary. |
| 7890 * | 7890 * |
| 7891 * An index limit is used to deal with the situation that a result array | 7891 * An index limit is used to deal with the situation that a result array |
| 7892 * length overflows 32-bit non-negative integer. | 7892 * length overflows 32-bit non-negative integer. |
| 7893 */ | 7893 */ |
| 7894 class ArrayConcatVisitor { | 7894 class ArrayConcatVisitor { |
|
Mads Ager (chromium)
2011/02/28 08:11:20
Should this BASE_EMBEDDED. This should not be allo
| |
| 7895 public: | 7895 public: |
| 7896 ArrayConcatVisitor(Handle<FixedArray> storage, | 7896 ArrayConcatVisitor(Handle<FixedArray> storage, |
| 7897 uint32_t index_limit, | |
| 7898 bool fast_elements) : | 7897 bool fast_elements) : |
| 7899 storage_(storage), index_limit_(index_limit), | 7898 storage_(storage), |
| 7900 index_offset_(0), fast_elements_(fast_elements) { } | 7899 index_offset_(0u), |
| 7900 fast_elements_(fast_elements) { } | |
| 7901 | 7901 |
| 7902 void visit(uint32_t i, Handle<Object> elm) { | 7902 void visit(uint32_t i, Handle<Object> elm) { |
| 7903 if (i >= index_limit_ - index_offset_) return; | 7903 if (i >= JSObject::kMaxElementCount - index_offset_) return; |
| 7904 uint32_t index = index_offset_ + i; | 7904 uint32_t index = index_offset_ + i; |
| 7905 | 7905 |
| 7906 if (fast_elements_) { | 7906 if (fast_elements_) { |
| 7907 ASSERT(index < static_cast<uint32_t>(storage_->length())); | 7907 if (index < static_cast<uint32_t>(storage_->length())) { |
| 7908 storage_->set(index, *elm); | 7908 storage_->set(index, *elm); |
| 7909 | 7909 return; |
| 7910 } else { | 7910 } |
| 7911 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_); | 7911 // Our initial estimate of length was foiled, possibly by |
| 7912 Handle<NumberDictionary> result = | 7912 // getters on the arrays increasing the length of later arrays |
| 7913 Factory::DictionaryAtNumberPut(dict, index, elm); | 7913 // during iteration. |
| 7914 if (!result.is_identical_to(dict)) | 7914 // This shouldn't happen in anything but pathological cases. |
| 7915 storage_ = result; | 7915 SetDictionaryMode(index); |
| 7916 } | 7916 // Fall-through to dictionary mode. |
| 7917 } | 7917 } |
| 7918 ASSERT(!fast_elements_); | |
| 7919 Handle<NumberDictionary> dict(storage_.cast<NumberDictionary>()); | |
| 7920 Handle<NumberDictionary> result = | |
| 7921 Factory::DictionaryAtNumberPut(dict, index, elm); | |
| 7922 if (!result.is_identical_to(dict)) { | |
| 7923 storage_ = Handle<FixedArray>::cast(result); | |
| 7924 } | |
| 7925 } | |
| 7918 | 7926 |
| 7919 void increase_index_offset(uint32_t delta) { | 7927 void increase_index_offset(uint32_t delta) { |
| 7920 if (index_limit_ - index_offset_ < delta) { | 7928 if (JSObject::kMaxElementCount - index_offset_ < delta) { |
| 7921 index_offset_ = index_limit_; | 7929 index_offset_ = JSObject::kMaxElementCount; |
| 7922 } else { | 7930 } else { |
| 7923 index_offset_ += delta; | 7931 index_offset_ += delta; |
| 7924 } | 7932 } |
| 7925 } | 7933 } |
| 7926 | 7934 |
| 7927 Handle<FixedArray> storage() { return storage_; } | 7935 Handle<JSArray> ToArray() { |
| 7936 Handle<JSArray> array = Factory::NewJSArray(0); | |
| 7937 Handle<Object> length = | |
| 7938 Factory::NewNumber(static_cast<double>(index_offset_)); | |
| 7939 Handle<Map> map; | |
| 7940 if (fast_elements_) { | |
| 7941 map = Factory::GetFastElementsMap(Handle<Map>(array->map())); | |
| 7942 } else { | |
| 7943 map = Factory::GetSlowElementsMap(Handle<Map>(array->map())); | |
| 7944 } | |
| 7945 array->set_map(*map); | |
| 7946 array->set_length(*length); | |
| 7947 array->set_elements(*storage_); | |
| 7948 return array; | |
| 7949 } | |
| 7928 | 7950 |
| 7929 private: | 7951 private: |
| 7930 Handle<FixedArray> storage_; | 7952 // Convert storage to dictionary mode. |
| 7931 // Limit on the accepted indices. Elements with indices larger than the | 7953 void SetDictionaryMode(uint32_t index) { |
| 7932 // limit are ignored by the visitor. | 7954 ASSERT(fast_elements_); |
| 7933 uint32_t index_limit_; | 7955 Handle<FixedArray> current_storage(storage_.ToHandle()); |
| 7934 // Index after last seen index. Always less than or equal to index_limit_. | 7956 HandleCell<NumberDictionary> slow_storage( |
|
Mads Ager (chromium)
2011/02/28 08:11:20
Just use a normal handle and escape the handle fro
| |
| 7957 Factory::NewNumberDictionary(current_storage->length())); | |
| 7958 uint32_t current_length = static_cast<uint32_t>(current_storage->length()); | |
| 7959 for (uint32_t i = 0; i < current_length; i++) { | |
| 7960 HandleScope loop_scope; | |
| 7961 Handle<Object> element(current_storage->get(i)); | |
| 7962 if (!element->IsTheHole()) { | |
| 7963 slow_storage = | |
|
Mads Ager (chromium)
2011/02/28 08:11:20
slow_storage = GetNewHandle().EscapeFrom(&loop_sco
| |
| 7964 Factory::DictionaryAtNumberPut(slow_storage.ToHandle(), i, element); | |
| 7965 } | |
| 7966 } | |
| 7967 storage_ = slow_storage.cast<FixedArray>(); | |
| 7968 fast_elements_ = false; | |
| 7969 } | |
| 7970 | |
| 7971 HandleCell<FixedArray> storage_; | |
| 7972 // Index after last seen index. Always less than or equal to | |
| 7973 // JSObject::kMaxElementCount. | |
| 7935 uint32_t index_offset_; | 7974 uint32_t index_offset_; |
| 7936 const bool fast_elements_; | 7975 bool fast_elements_; |
| 7937 }; | 7976 }; |
| 7938 | 7977 |
| 7939 | 7978 |
| 7979 static uint32_t EstimateElementCount(Handle<JSArray> array) { | |
| 7980 uint32_t length = static_cast<uint32_t>(array->length()->Number()); | |
| 7981 int element_count = 0; | |
| 7982 switch (array->GetElementsKind()) { | |
| 7983 case JSObject::FAST_ELEMENTS: { | |
| 7984 // Fast elements can't have lengths that are not representable by | |
| 7985 // a 32-bit signed integer. | |
| 7986 ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); | |
| 7987 int fast_length = static_cast<int>(length); | |
| 7988 Handle<FixedArray> elements(FixedArray::cast(array->elements())); | |
| 7989 for (int i = 0; i < fast_length; i++) { | |
| 7990 if (!elements->get(i)->IsTheHole()) element_count++; | |
| 7991 } | |
| 7992 break; | |
| 7993 } | |
| 7994 case JSObject::DICTIONARY_ELEMENTS: { | |
| 7995 Handle<NumberDictionary> dictionary( | |
| 7996 NumberDictionary::cast(array->elements())); | |
| 7997 int capacity = dictionary->Capacity(); | |
| 7998 for (int i = 0; i < capacity; i++) { | |
| 7999 Handle<Object> key(dictionary->KeyAt(i)); | |
| 8000 if (dictionary->IsKey(*key)) { | |
| 8001 element_count++; | |
| 8002 } | |
| 8003 } | |
| 8004 break; | |
| 8005 } | |
| 8006 default: | |
| 8007 // External arrays are always dense. | |
| 8008 return length; | |
| 8009 } | |
| 8010 // As an estimate, we assume that the prototype doesn't contain any | |
| 8011 // inherited elements. | |
| 8012 return element_count; | |
| 8013 } | |
| 8014 | |
| 8015 | |
| 8016 | |
| 7940 template<class ExternalArrayClass, class ElementType> | 8017 template<class ExternalArrayClass, class ElementType> |
| 7941 static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver, | 8018 static void IterateExternalArrayElements(Handle<JSObject> receiver, |
| 7942 bool elements_are_ints, | 8019 bool elements_are_ints, |
| 7943 bool elements_are_guaranteed_smis, | 8020 bool elements_are_guaranteed_smis, |
| 7944 uint32_t range, | 8021 ArrayConcatVisitor* visitor) { |
| 7945 ArrayConcatVisitor* visitor) { | |
| 7946 Handle<ExternalArrayClass> array( | 8022 Handle<ExternalArrayClass> array( |
| 7947 ExternalArrayClass::cast(receiver->elements())); | 8023 ExternalArrayClass::cast(receiver->elements())); |
| 7948 uint32_t len = Min(static_cast<uint32_t>(array->length()), range); | 8024 uint32_t len = static_cast<uint32_t>(array->length()); |
| 7949 | 8025 |
| 7950 if (visitor != NULL) { | 8026 ASSERT(visitor != NULL); |
| 7951 if (elements_are_ints) { | 8027 if (elements_are_ints) { |
| 7952 if (elements_are_guaranteed_smis) { | 8028 if (elements_are_guaranteed_smis) { |
| 7953 for (uint32_t j = 0; j < len; j++) { | 8029 for (uint32_t j = 0; j < len; j++) { |
| 7954 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j)))); | 8030 HandleScope loop_scope; |
| 7955 visitor->visit(j, e); | 8031 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j)))); |
| 7956 } | 8032 visitor->visit(j, e); |
| 7957 } else { | |
| 7958 for (uint32_t j = 0; j < len; j++) { | |
| 7959 int64_t val = static_cast<int64_t>(array->get(j)); | |
| 7960 if (Smi::IsValid(static_cast<intptr_t>(val))) { | |
| 7961 Handle<Smi> e(Smi::FromInt(static_cast<int>(val))); | |
| 7962 visitor->visit(j, e); | |
| 7963 } else { | |
| 7964 Handle<Object> e = | |
| 7965 Factory::NewNumber(static_cast<ElementType>(val)); | |
| 7966 visitor->visit(j, e); | |
| 7967 } | |
| 7968 } | |
| 7969 } | 8033 } |
| 7970 } else { | 8034 } else { |
| 7971 for (uint32_t j = 0; j < len; j++) { | 8035 for (uint32_t j = 0; j < len; j++) { |
| 7972 Handle<Object> e = Factory::NewNumber(array->get(j)); | 8036 HandleScope loop_scope; |
| 7973 visitor->visit(j, e); | 8037 int64_t val = static_cast<int64_t>(array->get(j)); |
| 7974 } | 8038 if (Smi::IsValid(static_cast<intptr_t>(val))) { |
| 7975 } | 8039 Handle<Smi> e(Smi::FromInt(static_cast<int>(val))); |
| 7976 } | 8040 visitor->visit(j, e); |
| 7977 | 8041 } else { |
| 7978 return len; | 8042 Handle<Object> e = |
| 7979 } | 8043 Factory::NewNumber(static_cast<ElementType>(val)); |
| 7980 | 8044 visitor->visit(j, e); |
| 7981 /** | 8045 } |
| 7982 * A helper function that visits elements of a JSObject. Only elements | 8046 } |
| 7983 * whose index between 0 and range (exclusive) are visited. | 8047 } |
| 7984 * | 8048 } else { |
| 7985 * If the third parameter, visitor, is not NULL, the visitor is called | 8049 for (uint32_t j = 0; j < len; j++) { |
| 7986 * with parameters, 'visitor_index_offset + element index' and the element. | 8050 HandleScope loop_scope; |
| 7987 * | 8051 Handle<Object> e = Factory::NewNumber(array->get(j)); |
| 7988 * It returns the number of visisted elements. | 8052 visitor->visit(j, e); |
| 7989 */ | 8053 } |
| 7990 static uint32_t IterateElements(Handle<JSObject> receiver, | 8054 } |
| 7991 uint32_t range, | 8055 } |
| 7992 ArrayConcatVisitor* visitor) { | 8056 |
| 7993 uint32_t num_of_elements = 0; | 8057 |
| 7994 | 8058 // Used for sorting indices in a List<uint32_t>. |
| 7995 switch (receiver->GetElementsKind()) { | 8059 static int compareUInt32(const uint32_t* ap, const uint32_t* bp) { |
| 8060 uint32_t a = *ap; | |
| 8061 uint32_t b = *bp; | |
| 8062 return (a == b) ? 0 : (a < b) ? -1 : 1; | |
| 8063 } | |
| 8064 | |
| 8065 | |
| 8066 static void CollectElementIndices(Handle<JSObject> object, | |
| 8067 uint32_t range, | |
| 8068 List<uint32_t>* indices) { | |
| 8069 JSObject::ElementsKind kind = object->GetElementsKind(); | |
| 8070 switch (kind) { | |
| 7996 case JSObject::FAST_ELEMENTS: { | 8071 case JSObject::FAST_ELEMENTS: { |
| 7997 Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); | 8072 Handle<FixedArray> elements(FixedArray::cast(object->elements())); |
| 7998 uint32_t len = elements->length(); | 8073 uint32_t length = static_cast<uint32_t>(elements->length()); |
| 7999 if (range < len) { | 8074 if (range < length) length = range; |
| 8000 len = range; | 8075 for (uint32_t i = 0; i < length; i++) { |
| 8001 } | 8076 if (!elements->get(i)->IsTheHole()) { |
| 8002 | 8077 indices->Add(i); |
| 8003 for (uint32_t j = 0; j < len; j++) { | 8078 } |
| 8004 Handle<Object> e(elements->get(j)); | 8079 } |
| 8005 if (!e->IsTheHole()) { | |
| 8006 num_of_elements++; | |
| 8007 if (visitor) { | |
| 8008 visitor->visit(j, e); | |
| 8009 } | |
| 8010 } | |
| 8011 } | |
| 8012 break; | |
| 8013 } | |
| 8014 case JSObject::PIXEL_ELEMENTS: { | |
| 8015 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements())); | |
| 8016 uint32_t len = pixels->length(); | |
| 8017 if (range < len) { | |
| 8018 len = range; | |
| 8019 } | |
| 8020 | |
| 8021 for (uint32_t j = 0; j < len; j++) { | |
| 8022 num_of_elements++; | |
| 8023 if (visitor != NULL) { | |
| 8024 Handle<Smi> e(Smi::FromInt(pixels->get(j))); | |
| 8025 visitor->visit(j, e); | |
| 8026 } | |
| 8027 } | |
| 8028 break; | |
| 8029 } | |
| 8030 case JSObject::EXTERNAL_BYTE_ELEMENTS: { | |
| 8031 num_of_elements = | |
| 8032 IterateExternalArrayElements<ExternalByteArray, int8_t>( | |
| 8033 receiver, true, true, range, visitor); | |
| 8034 break; | |
| 8035 } | |
| 8036 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { | |
| 8037 num_of_elements = | |
| 8038 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>( | |
| 8039 receiver, true, true, range, visitor); | |
| 8040 break; | |
| 8041 } | |
| 8042 case JSObject::EXTERNAL_SHORT_ELEMENTS: { | |
| 8043 num_of_elements = | |
| 8044 IterateExternalArrayElements<ExternalShortArray, int16_t>( | |
| 8045 receiver, true, true, range, visitor); | |
| 8046 break; | |
| 8047 } | |
| 8048 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { | |
| 8049 num_of_elements = | |
| 8050 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>( | |
| 8051 receiver, true, true, range, visitor); | |
| 8052 break; | |
| 8053 } | |
| 8054 case JSObject::EXTERNAL_INT_ELEMENTS: { | |
| 8055 num_of_elements = | |
| 8056 IterateExternalArrayElements<ExternalIntArray, int32_t>( | |
| 8057 receiver, true, false, range, visitor); | |
| 8058 break; | |
| 8059 } | |
| 8060 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { | |
| 8061 num_of_elements = | |
| 8062 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>( | |
| 8063 receiver, true, false, range, visitor); | |
| 8064 break; | |
| 8065 } | |
| 8066 case JSObject::EXTERNAL_FLOAT_ELEMENTS: { | |
| 8067 num_of_elements = | |
| 8068 IterateExternalArrayElements<ExternalFloatArray, float>( | |
| 8069 receiver, false, false, range, visitor); | |
| 8070 break; | 8080 break; |
| 8071 } | 8081 } |
| 8072 case JSObject::DICTIONARY_ELEMENTS: { | 8082 case JSObject::DICTIONARY_ELEMENTS: { |
| 8073 Handle<NumberDictionary> dict(receiver->element_dictionary()); | 8083 Handle<NumberDictionary> dict(NumberDictionary::cast(object->elements())); |
| 8074 uint32_t capacity = dict->Capacity(); | 8084 uint32_t capacity = dict->Capacity(); |
| 8075 for (uint32_t j = 0; j < capacity; j++) { | 8085 for (uint32_t j = 0; j < capacity; j++) { |
| 8086 HandleScope loop_scope; | |
| 8076 Handle<Object> k(dict->KeyAt(j)); | 8087 Handle<Object> k(dict->KeyAt(j)); |
| 8077 if (dict->IsKey(*k)) { | 8088 if (dict->IsKey(*k)) { |
| 8078 ASSERT(k->IsNumber()); | 8089 ASSERT(k->IsNumber()); |
| 8079 uint32_t index = static_cast<uint32_t>(k->Number()); | 8090 uint32_t index = static_cast<uint32_t>(k->Number()); |
| 8080 if (index < range) { | 8091 if (index < range) { |
| 8081 num_of_elements++; | 8092 indices->Add(index); |
| 8082 if (visitor) { | |
| 8083 visitor->visit(index, Handle<Object>(dict->ValueAt(j))); | |
| 8084 } | |
| 8085 } | 8093 } |
| 8086 } | 8094 } |
| 8087 } | 8095 } |
| 8088 break; | 8096 break; |
| 8089 } | 8097 } |
| 8098 default: { | |
| 8099 int dense_elements_length; | |
| 8100 switch (kind) { | |
| 8101 case JSObject::PIXEL_ELEMENTS: { | |
| 8102 dense_elements_length = | |
| 8103 PixelArray::cast(object->elements())->length(); | |
| 8104 break; | |
| 8105 } | |
| 8106 case JSObject::EXTERNAL_BYTE_ELEMENTS: { | |
| 8107 dense_elements_length = | |
| 8108 ExternalByteArray::cast(object->elements())->length(); | |
| 8109 break; | |
| 8110 } | |
| 8111 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { | |
| 8112 dense_elements_length = | |
| 8113 ExternalUnsignedByteArray::cast(object->elements())->length(); | |
| 8114 break; | |
| 8115 } | |
| 8116 case JSObject::EXTERNAL_SHORT_ELEMENTS: { | |
| 8117 dense_elements_length = | |
| 8118 ExternalShortArray::cast(object->elements())->length(); | |
| 8119 break; | |
| 8120 } | |
| 8121 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { | |
| 8122 dense_elements_length = | |
| 8123 ExternalUnsignedShortArray::cast(object->elements())->length(); | |
| 8124 break; | |
| 8125 } | |
| 8126 case JSObject::EXTERNAL_INT_ELEMENTS: { | |
| 8127 dense_elements_length = | |
| 8128 ExternalIntArray::cast(object->elements())->length(); | |
| 8129 break; | |
| 8130 } | |
| 8131 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { | |
| 8132 dense_elements_length = | |
| 8133 ExternalUnsignedIntArray::cast(object->elements())->length(); | |
| 8134 break; | |
| 8135 } | |
| 8136 case JSObject::EXTERNAL_FLOAT_ELEMENTS: { | |
| 8137 dense_elements_length = | |
| 8138 ExternalFloatArray::cast(object->elements())->length(); | |
| 8139 break; | |
| 8140 } | |
| 8141 default: | |
| 8142 UNREACHABLE(); | |
| 8143 dense_elements_length = 0; | |
| 8144 break; | |
| 8145 } | |
| 8146 uint32_t length = static_cast<uint32_t>(dense_elements_length); | |
| 8147 if (range <= length) { | |
| 8148 length = range; | |
| 8149 // We will add all indices, so we might as well clear it first | |
| 8150 // and avoid duplicates. | |
| 8151 indices->Clear(); | |
| 8152 } | |
| 8153 for (uint32_t i = 0; i < length; i++) { | |
| 8154 indices->Add(i); | |
| 8155 } | |
| 8156 if (length == range) return; // All indices accounted for already. | |
| 8157 break; | |
| 8158 } | |
| 8159 } | |
| 8160 | |
| 8161 Handle<Object> prototype(object->GetPrototype()); | |
| 8162 if (prototype->IsJSObject()) { | |
| 8163 // The prototype will usually have no inherited element indices, | |
| 8164 // but we have to check. | |
| 8165 CollectElementIndices(Handle<JSObject>::cast(prototype), range, indices); | |
| 8166 } | |
| 8167 } | |
| 8168 | |
| 8169 | |
| 8170 /** | |
| 8171 * A helper function that visits elements of a JSArray in numerical | |
| 8172 * order. | |
| 8173 * | |
| 8174 * The visitor argument called for each existing element in the array | |
| 8175 * with the element index and the element's value. | |
| 8176 * Afterwards it increments the base-index of the visitor by the array | |
| 8177 * length. | |
| 8178 */ | |
| 8179 static void IterateElements(Handle<JSArray> receiver, | |
| 8180 ArrayConcatVisitor* visitor) { | |
| 8181 uint32_t length = static_cast<uint32_t>(receiver->length()->Number()); | |
| 8182 switch (receiver->GetElementsKind()) { | |
| 8183 case JSObject::FAST_ELEMENTS: { | |
| 8184 // Run through the elements FixedArray and use HasElement and GetElement | |
| 8185 // to check the prototype for missing elements. | |
| 8186 Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); | |
| 8187 int fast_length = static_cast<int>(length); | |
| 8188 ASSERT(fast_length <= elements->length()); | |
| 8189 for (int j = 0; j < fast_length; j++) { | |
| 8190 HandleScope loop_scope; | |
| 8191 Handle<Object> element_value(elements->get(j)); | |
| 8192 if (!element_value->IsTheHole()) { | |
| 8193 visitor->visit(j, element_value); | |
| 8194 } else if (receiver->HasElement(j)) { | |
| 8195 // Call GetElement on receiver, not its prototype, or getters won't | |
| 8196 // have the correct receiver. | |
| 8197 element_value = GetElement(receiver, j); | |
| 8198 visitor->visit(j, element_value); | |
| 8199 } | |
| 8200 } | |
| 8201 break; | |
| 8202 } | |
| 8203 case JSObject::DICTIONARY_ELEMENTS: { | |
| 8204 Handle<NumberDictionary> dict(receiver->element_dictionary()); | |
| 8205 List<uint32_t> indices(dict->Capacity() / 2); | |
| 8206 // Collect all indices in the object and the prototypes less | |
| 8207 // than length. This might introduce duplicates in the indices list. | |
| 8208 CollectElementIndices(receiver, length, &indices); | |
| 8209 indices.Sort(&compareUInt32); | |
| 8210 int j = 0; | |
| 8211 int n = indices.length(); | |
| 8212 while (j < n) { | |
| 8213 HandleScope loop_scope; | |
| 8214 uint32_t index = indices[j]; | |
| 8215 Handle<Object> element = GetElement(receiver, index); | |
| 8216 visitor->visit(index, element); | |
| 8217 // Skip to next different index (i.e., omit duplicates). | |
| 8218 do { | |
| 8219 j++; | |
| 8220 } while (j < n && indices[j] == index); | |
| 8221 } | |
| 8222 break; | |
| 8223 } | |
| 8224 case JSObject::PIXEL_ELEMENTS: { | |
| 8225 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements())); | |
| 8226 for (uint32_t j = 0; j < length; j++) { | |
| 8227 Handle<Smi> e(Smi::FromInt(pixels->get(j))); | |
| 8228 visitor->visit(j, e); | |
| 8229 } | |
| 8230 break; | |
| 8231 } | |
| 8232 case JSObject::EXTERNAL_BYTE_ELEMENTS: { | |
| 8233 IterateExternalArrayElements<ExternalByteArray, int8_t>( | |
| 8234 receiver, true, true, visitor); | |
| 8235 break; | |
| 8236 } | |
| 8237 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { | |
| 8238 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>( | |
| 8239 receiver, true, true, visitor); | |
| 8240 break; | |
| 8241 } | |
| 8242 case JSObject::EXTERNAL_SHORT_ELEMENTS: { | |
| 8243 IterateExternalArrayElements<ExternalShortArray, int16_t>( | |
| 8244 receiver, true, true, visitor); | |
| 8245 break; | |
| 8246 } | |
| 8247 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { | |
| 8248 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>( | |
| 8249 receiver, true, true, visitor); | |
| 8250 break; | |
| 8251 } | |
| 8252 case JSObject::EXTERNAL_INT_ELEMENTS: { | |
| 8253 IterateExternalArrayElements<ExternalIntArray, int32_t>( | |
| 8254 receiver, true, false, visitor); | |
| 8255 break; | |
| 8256 } | |
| 8257 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { | |
| 8258 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>( | |
| 8259 receiver, true, false, visitor); | |
| 8260 break; | |
| 8261 } | |
| 8262 case JSObject::EXTERNAL_FLOAT_ELEMENTS: { | |
| 8263 IterateExternalArrayElements<ExternalFloatArray, float>( | |
| 8264 receiver, false, false, visitor); | |
| 8265 break; | |
| 8266 } | |
| 8090 default: | 8267 default: |
| 8091 UNREACHABLE(); | 8268 UNREACHABLE(); |
| 8092 break; | 8269 break; |
| 8093 } | 8270 } |
| 8094 | 8271 visitor->increase_index_offset(length); |
| 8095 return num_of_elements; | |
| 8096 } | |
| 8097 | |
| 8098 | |
| 8099 /** | |
| 8100 * A helper function that visits elements of an Array object, and elements | |
| 8101 * on its prototypes. | |
| 8102 * | |
| 8103 * Elements on prototypes are visited first, and only elements whose indices | |
| 8104 * less than Array length are visited. | |
| 8105 * | |
| 8106 * If a ArrayConcatVisitor object is given, the visitor is called with | |
| 8107 * parameters, element's index + visitor_index_offset and the element. | |
| 8108 * | |
| 8109 * The returned number of elements is an upper bound on the actual number | |
| 8110 * of elements added. If the same element occurs in more than one object | |
| 8111 * in the array's prototype chain, it will be counted more than once, but | |
| 8112 * will only occur once in the result. | |
| 8113 */ | |
| 8114 static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array, | |
| 8115 ArrayConcatVisitor* visitor) { | |
| 8116 uint32_t range = static_cast<uint32_t>(array->length()->Number()); | |
| 8117 Handle<Object> obj = array; | |
| 8118 | |
| 8119 static const int kEstimatedPrototypes = 3; | |
| 8120 List< Handle<JSObject> > objects(kEstimatedPrototypes); | |
| 8121 | |
| 8122 // Visit prototype first. If an element on the prototype is shadowed by | |
| 8123 // the inheritor using the same index, the ArrayConcatVisitor visits | |
| 8124 // the prototype element before the shadowing element. | |
| 8125 // The visitor can simply overwrite the old value by new value using | |
| 8126 // the same index. This follows Array::concat semantics. | |
| 8127 while (!obj->IsNull()) { | |
| 8128 objects.Add(Handle<JSObject>::cast(obj)); | |
| 8129 obj = Handle<Object>(obj->GetPrototype()); | |
| 8130 } | |
| 8131 | |
| 8132 uint32_t nof_elements = 0; | |
| 8133 for (int i = objects.length() - 1; i >= 0; i--) { | |
| 8134 Handle<JSObject> obj = objects[i]; | |
| 8135 uint32_t encountered_elements = | |
| 8136 IterateElements(Handle<JSObject>::cast(obj), range, visitor); | |
| 8137 | |
| 8138 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) { | |
| 8139 nof_elements = JSObject::kMaxElementCount; | |
| 8140 } else { | |
| 8141 nof_elements += encountered_elements; | |
| 8142 } | |
| 8143 } | |
| 8144 | |
| 8145 return nof_elements; | |
| 8146 } | |
| 8147 | |
| 8148 | |
| 8149 /** | |
| 8150 * A helper function of Runtime_ArrayConcat. | |
| 8151 * | |
| 8152 * The first argument is an Array of arrays and objects. It is the | |
| 8153 * same as the arguments array of Array::concat JS function. | |
| 8154 * | |
| 8155 * If an argument is an Array object, the function visits array | |
| 8156 * elements. If an argument is not an Array object, the function | |
| 8157 * visits the object as if it is an one-element array. | |
| 8158 * | |
| 8159 * If the result array index overflows 32-bit unsigned integer, the rounded | |
| 8160 * non-negative number is used as new length. For example, if one | |
| 8161 * array length is 2^32 - 1, second array length is 1, the | |
| 8162 * concatenated array length is 0. | |
| 8163 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length | |
| 8164 * is one more than the last array index to get a value assigned). | |
| 8165 */ | |
| 8166 static uint32_t IterateArguments(Handle<JSArray> arguments, | |
| 8167 ArrayConcatVisitor* visitor) { | |
| 8168 uint32_t visited_elements = 0; | |
| 8169 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number()); | |
| 8170 | |
| 8171 for (uint32_t i = 0; i < num_of_args; i++) { | |
| 8172 Object *element; | |
| 8173 MaybeObject* maybe_element = arguments->GetElement(i); | |
| 8174 // This if() is not expected to fail, but we have the check in the | |
| 8175 // interest of hardening the runtime calls. | |
| 8176 if (maybe_element->ToObject(&element)) { | |
| 8177 Handle<Object> obj(element); | |
| 8178 if (obj->IsJSArray()) { | |
| 8179 Handle<JSArray> array = Handle<JSArray>::cast(obj); | |
| 8180 uint32_t len = static_cast<uint32_t>(array->length()->Number()); | |
| 8181 uint32_t nof_elements = | |
| 8182 IterateArrayAndPrototypeElements(array, visitor); | |
| 8183 // Total elements of array and its prototype chain can be more than | |
| 8184 // the array length, but ArrayConcat can only concatenate at most | |
| 8185 // the array length number of elements. We use the length as an estimate | |
| 8186 // for the actual number of elements added. | |
| 8187 uint32_t added_elements = (nof_elements > len) ? len : nof_elements; | |
| 8188 if (JSArray::kMaxElementCount - visited_elements < added_elements) { | |
| 8189 visited_elements = JSArray::kMaxElementCount; | |
| 8190 } else { | |
| 8191 visited_elements += added_elements; | |
| 8192 } | |
| 8193 if (visitor) visitor->increase_index_offset(len); | |
| 8194 } else { | |
| 8195 if (visitor) { | |
| 8196 visitor->visit(0, obj); | |
| 8197 visitor->increase_index_offset(1); | |
| 8198 } | |
| 8199 if (visited_elements < JSArray::kMaxElementCount) { | |
| 8200 visited_elements++; | |
| 8201 } | |
| 8202 } | |
| 8203 } | |
| 8204 } | |
| 8205 return visited_elements; | |
| 8206 } | 8272 } |
| 8207 | 8273 |
| 8208 | 8274 |
| 8209 /** | 8275 /** |
| 8210 * Array::concat implementation. | 8276 * Array::concat implementation. |
| 8211 * See ECMAScript 262, 15.4.4.4. | 8277 * See ECMAScript 262, 15.4.4.4. |
| 8212 * TODO(lrn): Fix non-compliance for very large concatenations and update to | 8278 * TODO(581): Fix non-compliance for very large concatenations and update to |
| 8213 * following the ECMAScript 5 specification. | 8279 * following the ECMAScript 5 specification. |
| 8214 */ | 8280 */ |
| 8215 static MaybeObject* Runtime_ArrayConcat(Arguments args) { | 8281 static MaybeObject* Runtime_ArrayConcat(Arguments args) { |
| 8216 ASSERT(args.length() == 1); | 8282 ASSERT(args.length() == 1); |
| 8217 HandleScope handle_scope; | 8283 HandleScope handle_scope; |
| 8218 | 8284 |
| 8219 CONVERT_CHECKED(JSArray, arg_arrays, args[0]); | 8285 CONVERT_ARG_CHECKED(JSArray, arguments, 0); |
| 8220 Handle<JSArray> arguments(arg_arrays); | 8286 int argument_count = static_cast<int>(arguments->length()->Number()); |
| 8221 | 8287 RUNTIME_ASSERT(arguments->HasFastElements()); |
| 8222 // Pass 1: estimate the number of elements of the result | 8288 Handle<FixedArray> elements(FixedArray::cast(arguments->elements())); |
| 8223 // (it could be more than real numbers if prototype has elements). | 8289 |
| 8224 uint32_t result_length = 0; | 8290 // Pass 1: estimate the length and number of elements of the result. |
| 8225 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number()); | 8291 // The actual length can be larger if any of the arguments have getters |
| 8226 | 8292 // that mutate other arguments (but will otherwise be precise). |
| 8227 { AssertNoAllocation nogc; | 8293 // The number of elements is precise if there are no inherited elements. |
| 8228 for (uint32_t i = 0; i < num_of_args; i++) { | 8294 |
| 8229 Object* obj; | 8295 uint32_t estimate_result_length = 0; |
| 8230 MaybeObject* maybe_object = arguments->GetElement(i); | 8296 uint32_t estimate_nof_elements = 0; |
| 8231 // This if() is not expected to fail, but we have the check in the | 8297 { |
| 8232 // interest of hardening the runtime calls. | 8298 for (int i = 0; i < argument_count; i++) { |
| 8233 if (maybe_object->ToObject(&obj)) { | 8299 HandleScope loop_scope; |
| 8234 uint32_t length_estimate; | 8300 Handle<Object> obj(elements->get(i)); |
| 8235 if (obj->IsJSArray()) { | 8301 uint32_t length_estimate; |
| 8236 length_estimate = | 8302 uint32_t element_estimate; |
| 8237 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number()); | 8303 if (obj->IsJSArray()) { |
| 8238 } else { | 8304 Handle<JSArray> array(Handle<JSArray>::cast(obj)); |
| 8239 length_estimate = 1; | 8305 length_estimate = |
| 8240 } | 8306 static_cast<uint32_t>(array->length()->Number()); |
| 8241 if (JSObject::kMaxElementCount - result_length < length_estimate) { | 8307 element_estimate = |
| 8242 result_length = JSObject::kMaxElementCount; | 8308 EstimateElementCount(array); |
| 8243 break; | 8309 } else { |
| 8244 } | 8310 length_estimate = 1; |
| 8245 result_length += length_estimate; | 8311 element_estimate = 1; |
| 8246 } | 8312 } |
| 8247 } | 8313 // Avoid overflows by capping at kMaxElementCount. |
| 8248 } | 8314 if (JSObject::kMaxElementCount - estimate_result_length < |
| 8249 | 8315 length_estimate) { |
| 8250 // Allocate an empty array, will set length and content later. | 8316 estimate_result_length = JSObject::kMaxElementCount; |
| 8251 Handle<JSArray> result = Factory::NewJSArray(0); | 8317 } else { |
| 8252 | 8318 estimate_result_length += length_estimate; |
| 8253 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL); | 8319 } |
| 8320 if (JSObject::kMaxElementCount - estimate_nof_elements < | |
| 8321 element_estimate) { | |
| 8322 estimate_nof_elements = JSObject::kMaxElementCount; | |
| 8323 } else { | |
| 8324 estimate_nof_elements += element_estimate; | |
| 8325 } | |
| 8326 } | |
| 8327 } | |
| 8328 | |
| 8254 // If estimated number of elements is more than half of length, a | 8329 // If estimated number of elements is more than half of length, a |
| 8255 // fixed array (fast case) is more time and space-efficient than a | 8330 // fixed array (fast case) is more time and space-efficient than a |
| 8256 // dictionary. | 8331 // dictionary. |
| 8257 bool fast_case = (estimate_nof_elements * 2) >= result_length; | 8332 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; |
| 8258 | 8333 |
| 8259 Handle<FixedArray> storage; | 8334 Handle<FixedArray> storage; |
| 8260 if (fast_case) { | 8335 if (fast_case) { |
| 8261 // The backing storage array must have non-existing elements to | 8336 // The backing storage array must have non-existing elements to |
| 8262 // preserve holes across concat operations. | 8337 // preserve holes across concat operations. |
| 8263 storage = Factory::NewFixedArrayWithHoles(result_length); | 8338 storage = Factory::NewFixedArrayWithHoles(estimate_result_length); |
| 8264 Handle<Map> fast_map = | |
| 8265 Factory::GetFastElementsMap(Handle<Map>(result->map())); | |
| 8266 result->set_map(*fast_map); | |
| 8267 } else { | 8339 } else { |
| 8268 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate | 8340 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate |
| 8269 uint32_t at_least_space_for = estimate_nof_elements + | 8341 uint32_t at_least_space_for = estimate_nof_elements + |
| 8270 (estimate_nof_elements >> 2); | 8342 (estimate_nof_elements >> 2); |
| 8271 storage = Handle<FixedArray>::cast( | 8343 storage = Handle<FixedArray>::cast( |
| 8272 Factory::NewNumberDictionary(at_least_space_for)); | 8344 Factory::NewNumberDictionary(at_least_space_for)); |
| 8273 Handle<Map> slow_map = | 8345 Handle<Map> slow_map = |
| 8274 Factory::GetSlowElementsMap(Handle<Map>(result->map())); | 8346 Factory::GetSlowElementsMap(Handle<Map>(result->map())); |
| 8275 result->set_map(*slow_map); | 8347 result->set_map(*slow_map); |
| 8276 } | 8348 } |
| 8277 | 8349 |
| 8278 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length)); | 8350 ArrayConcatVisitor visitor(storage, fast_case); |
| 8279 | 8351 |
| 8280 ArrayConcatVisitor visitor(storage, result_length, fast_case); | 8352 for (int i = 0; i < argument_count; i++) { |
| 8353 Handle<Object> obj(elements->get(i)); | |
| 8354 if (obj->IsJSArray()) { | |
| 8355 Handle<JSArray> array = Handle<JSArray>::cast(obj); | |
| 8356 IterateElements(array, &visitor); | |
| 8357 } else { | |
| 8358 visitor.visit(0, obj); | |
| 8359 visitor.increase_index_offset(1); | |
| 8360 } | |
| 8361 } | |
| 8281 | 8362 |
| 8282 IterateArguments(arguments, &visitor); | 8363 return *visitor.ToArray(); |
| 8283 | |
| 8284 result->set_length(*len); | |
| 8285 // Please note the storage might have changed in the visitor. | |
| 8286 result->set_elements(*visitor.storage()); | |
| 8287 | |
| 8288 return *result; | |
| 8289 } | 8364 } |
| 8290 | 8365 |
| 8291 | 8366 |
| 8292 // This will not allocate (flatten the string), but it may run | 8367 // This will not allocate (flatten the string), but it may run |
| 8293 // very slowly for very deeply nested ConsStrings. For debugging use only. | 8368 // very slowly for very deeply nested ConsStrings. For debugging use only. |
| 8294 static MaybeObject* Runtime_GlobalPrint(Arguments args) { | 8369 static MaybeObject* Runtime_GlobalPrint(Arguments args) { |
| 8295 NoHandleAllocation ha; | 8370 NoHandleAllocation ha; |
| 8296 ASSERT(args.length() == 1); | 8371 ASSERT(args.length() == 1); |
| 8297 | 8372 |
| 8298 CONVERT_CHECKED(String, string, args[0]); | 8373 CONVERT_CHECKED(String, string, args[0]); |
| (...skipping 2763 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 11062 } else { | 11137 } else { |
| 11063 // Handle last resort GC and make sure to allow future allocations | 11138 // Handle last resort GC and make sure to allow future allocations |
| 11064 // to grow the heap without causing GCs (if possible). | 11139 // to grow the heap without causing GCs (if possible). |
| 11065 Counters::gc_last_resort_from_js.Increment(); | 11140 Counters::gc_last_resort_from_js.Increment(); |
| 11066 Heap::CollectAllGarbage(false); | 11141 Heap::CollectAllGarbage(false); |
| 11067 } | 11142 } |
| 11068 } | 11143 } |
| 11069 | 11144 |
| 11070 | 11145 |
| 11071 } } // namespace v8::internal | 11146 } } // namespace v8::internal |
| OLD | NEW |