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 8010 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
8021 * of FixedArray, the class can be used by both fast and slow cases. | 8021 * of FixedArray, the class can be used by both fast and slow cases. |
8022 * The second parameter of the constructor, fast_elements, specifies | 8022 * The second parameter of the constructor, fast_elements, specifies |
8023 * whether the storage is a FixedArray or Dictionary. | 8023 * whether the storage is a FixedArray or Dictionary. |
8024 * | 8024 * |
8025 * An index limit is used to deal with the situation that a result array | 8025 * An index limit is used to deal with the situation that a result array |
8026 * length overflows 32-bit non-negative integer. | 8026 * length overflows 32-bit non-negative integer. |
8027 */ | 8027 */ |
8028 class ArrayConcatVisitor { | 8028 class ArrayConcatVisitor { |
8029 public: | 8029 public: |
8030 ArrayConcatVisitor(Handle<FixedArray> storage, | 8030 ArrayConcatVisitor(Handle<FixedArray> storage, |
8031 uint32_t index_limit, | |
8032 bool fast_elements) : | 8031 bool fast_elements) : |
8033 storage_(storage), index_limit_(index_limit), | 8032 storage_(storage), |
8034 index_offset_(0), fast_elements_(fast_elements) { } | 8033 index_offset_(0u), |
8034 fast_elements_(fast_elements) { } | |
8035 | 8035 |
8036 void visit(uint32_t i, Handle<Object> elm) { | 8036 void visit(uint32_t i, Handle<Object> elm) { |
8037 if (i >= index_limit_ - index_offset_) return; | 8037 if (i >= JSObject::kMaxElementCount - index_offset_) return; |
8038 uint32_t index = index_offset_ + i; | 8038 uint32_t index = index_offset_ + i; |
8039 | 8039 |
8040 if (fast_elements_) { | 8040 if (fast_elements_) { |
8041 ASSERT(index < static_cast<uint32_t>(storage_->length())); | 8041 if (index < static_cast<uint32_t>(storage_->length())) { |
8042 storage_->set(index, *elm); | 8042 storage_->set(index, *elm); |
8043 | 8043 return; |
8044 } else { | 8044 } |
8045 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_); | 8045 // Our initial estimate of length was foiled, possibly by |
8046 Handle<NumberDictionary> result = | 8046 // getters on the arrays extending later arrays. |
8047 Factory::DictionaryAtNumberPut(dict, index, elm); | 8047 // This shouldn't happen in anything but pathological cases. |
8048 if (!result.is_identical_to(dict)) | 8048 if (index < static_cast<uint32_t>(storage_->length()) * 2) { |
8049 storage_ = result; | 8049 Handle<FixedArray> new_array = |
8050 } | 8050 Factory::NewFixedArrayWithHoles(storage_->length() * 2); |
8051 } | 8051 memcpy(new_array->data_start(), |
Erik Corry
2011/02/24 10:36:57
Seems like there is a missing write barrier here.
Lasse Reichstein
2011/02/24 13:44:41
True. This overflow can only happen if an array ch
| |
8052 storage_->data_start(), | |
8053 storage_->length() * kPointerSize); | |
8054 storage_ = new_array; | |
8055 storage_->set(index, *elm); | |
8056 return; | |
8057 } | |
8058 // In even more pathological cases, we go directly to slow-case. | |
8059 SetDictionaryMode(index); | |
8060 // Fall-through to dictionary mode. | |
8061 } | |
8062 ASSERT(!fast_elements_); | |
8063 Handle<NumberDictionary> dict(storage_.cast<NumberDictionary>()); | |
8064 Handle<NumberDictionary> result = | |
8065 Factory::DictionaryAtNumberPut(dict, index, elm); | |
8066 if (!result.is_identical_to(dict)) { | |
8067 storage_ = Handle<FixedArray>::cast(result); | |
8068 } | |
8069 } | |
8052 | 8070 |
8053 void increase_index_offset(uint32_t delta) { | 8071 void increase_index_offset(uint32_t delta) { |
8054 if (index_limit_ - index_offset_ < delta) { | 8072 if (JSObject::kMaxElementCount - index_offset_ < delta) { |
8055 index_offset_ = index_limit_; | 8073 index_offset_ = JSObject::kMaxElementCount; |
8056 } else { | 8074 } else { |
8057 index_offset_ += delta; | 8075 index_offset_ += delta; |
8058 } | 8076 } |
8059 } | 8077 } |
8060 | 8078 |
8061 Handle<FixedArray> storage() { return storage_; } | 8079 Handle<JSArray> ToArray() { |
8080 Handle<JSArray> array = Factory::NewJSArray(0); | |
8081 Handle<Object> length = | |
8082 Factory::NewNumber(static_cast<double>(index_offset_)); | |
8083 Handle<Map> map; | |
8084 if (fast_elements_) { | |
8085 map = Factory::GetFastElementsMap(Handle<Map>(array->map())); | |
8086 } else { | |
8087 map = Factory::GetSlowElementsMap(Handle<Map>(array->map())); | |
8088 } | |
8089 array->set_map(*map); | |
8090 array->set_length(*length); | |
8091 array->set_elements(*storage_); | |
8092 return array; | |
8093 } | |
8062 | 8094 |
8063 private: | 8095 private: |
8064 Handle<FixedArray> storage_; | 8096 // Convert storage to dictionary mode. |
8065 // Limit on the accepted indices. Elements with indices larger than the | 8097 void SetDictionaryMode(uint32_t index) { |
8066 // limit are ignored by the visitor. | 8098 ASSERT(fast_elements_); |
8067 uint32_t index_limit_; | 8099 Handle<FixedArray> current_storage(storage_.ToHandle()); |
8068 // Index after last seen index. Always less than or equal to index_limit_. | 8100 uint32_t current_length = static_cast<uint32_t>(current_storage->length()); |
8101 // Add 25% for good measure. | |
Erik Corry
2011/02/24 10:36:57
HashTable<Shape, Key>::Allocate already rounds up
Lasse Reichstein
2011/02/24 13:44:41
I'll remove the extra 25% here, and later where we
| |
8102 int capacity = current_length + (current_length >> 2); | |
Erik Corry
2011/02/24 10:36:57
If there is any point to the uint32_t then this sh
Lasse Reichstein
2011/02/24 13:44:41
Then it will be int here. When used as an array el
| |
8103 RootVar<NumberDictionary> slow_storage( | |
8104 Factory::NewNumberDictionary(capacity)); | |
8105 for (uint32_t i = 0; i < current_length; i++) { | |
8106 HandleScope loop_scope; | |
8107 Handle<Object> element(current_storage->get(i)); | |
8108 if (!element->IsTheHole()) { | |
8109 Handle<NumberDictionary> result = | |
8110 Factory::DictionaryAtNumberPut(slow_storage.ToHandle(), i, element); | |
8111 if (*result != *slow_storage) { | |
Erik Corry
2011/02/24 10:36:57
It seems like there's not much point to the if her
Lasse Reichstein
2011/02/24 13:44:41
Will remove the if.
| |
8112 slow_storage = result; | |
8113 } | |
8114 } | |
8115 } | |
8116 storage_ = slow_storage.cast<FixedArray>(); | |
8117 fast_elements_ = false; | |
8118 } | |
8119 | |
8120 RootVar<FixedArray> storage_; | |
8121 // Index after last seen index. Always less than or equal to | |
8122 // JSObject::kMaxElementCount. | |
8069 uint32_t index_offset_; | 8123 uint32_t index_offset_; |
8070 const bool fast_elements_; | 8124 bool fast_elements_; |
8071 }; | 8125 }; |
8072 | 8126 |
8073 | 8127 |
8128 static uint32_t EstimateElementCount(Handle<JSArray> array) { | |
Erik Corry
2011/02/24 10:36:57
Lots of use of unsigned and fixed bit-width types
Lasse Reichstein
2011/02/24 13:44:41
Not when the value is used as an array element ind
| |
8129 uint32_t length = static_cast<uint32_t>(array->length()->Number()); | |
8130 int element_count = 0; | |
8131 switch (array->GetElementsKind()) { | |
8132 case JSObject::FAST_ELEMENTS: { | |
8133 // Fast elements can't have lengths that are not representable by | |
8134 // a 32-bit signed integer. | |
8135 ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); | |
8136 int fast_length = static_cast<int>(length); | |
8137 Handle<FixedArray> elements(FixedArray::cast(array->elements())); | |
8138 for (int i = 0; i < fast_length; i++) { | |
8139 if (!elements->get(i)->IsTheHole()) element_count++; | |
8140 } | |
8141 break; | |
8142 } | |
8143 case JSObject::DICTIONARY_ELEMENTS: { | |
8144 Handle<NumberDictionary> dictionary( | |
8145 NumberDictionary::cast(array->elements())); | |
8146 int capacity = dictionary->Capacity(); | |
8147 for (int i = 0; i < capacity; i++) { | |
8148 Handle<Object> key(dictionary->KeyAt(i)); | |
8149 if (dictionary->IsKey(*key)) { | |
8150 element_count++; | |
8151 } | |
8152 } | |
8153 break; | |
8154 } | |
8155 default: | |
8156 // External arrays are always dense. | |
8157 return length; | |
8158 } | |
8159 // As an estimate, we assume that the prototype doesn't contain any | |
8160 // intherited elements. | |
Erik Corry
2011/02/24 10:36:57
intherited -> inherited
Lasse Reichstein
2011/02/24 13:45:49
Fixed.
| |
8161 return element_count; | |
8162 } | |
8163 | |
8164 | |
8165 | |
8074 template<class ExternalArrayClass, class ElementType> | 8166 template<class ExternalArrayClass, class ElementType> |
8075 static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver, | 8167 static void IterateExternalArrayElements(Handle<JSObject> receiver, |
8076 bool elements_are_ints, | 8168 bool elements_are_ints, |
8077 bool elements_are_guaranteed_smis, | 8169 bool elements_are_guaranteed_smis, |
8078 uint32_t range, | 8170 ArrayConcatVisitor* visitor) { |
8079 ArrayConcatVisitor* visitor) { | |
8080 Handle<ExternalArrayClass> array( | 8171 Handle<ExternalArrayClass> array( |
8081 ExternalArrayClass::cast(receiver->elements())); | 8172 ExternalArrayClass::cast(receiver->elements())); |
8082 uint32_t len = Min(static_cast<uint32_t>(array->length()), range); | 8173 uint32_t len = static_cast<uint32_t>(array->length()); |
8083 | 8174 |
8084 if (visitor != NULL) { | 8175 ASSERT(visitor != NULL); |
8085 if (elements_are_ints) { | 8176 if (elements_are_ints) { |
8086 if (elements_are_guaranteed_smis) { | 8177 if (elements_are_guaranteed_smis) { |
8087 for (uint32_t j = 0; j < len; j++) { | 8178 for (uint32_t j = 0; j < len; j++) { |
8088 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j)))); | 8179 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j)))); |
Erik Corry
2011/02/24 10:36:57
Not something introduced by you, but don't we have
Lasse Reichstein
2011/02/24 13:44:41
Good catch.
Before I changed the visitor to hold t
| |
8089 visitor->visit(j, e); | 8180 visitor->visit(j, e); |
8090 } | |
8091 } else { | |
8092 for (uint32_t j = 0; j < len; j++) { | |
8093 int64_t val = static_cast<int64_t>(array->get(j)); | |
8094 if (Smi::IsValid(static_cast<intptr_t>(val))) { | |
8095 Handle<Smi> e(Smi::FromInt(static_cast<int>(val))); | |
8096 visitor->visit(j, e); | |
8097 } else { | |
8098 Handle<Object> e = | |
8099 Factory::NewNumber(static_cast<ElementType>(val)); | |
8100 visitor->visit(j, e); | |
8101 } | |
8102 } | |
8103 } | 8181 } |
8104 } else { | 8182 } else { |
8105 for (uint32_t j = 0; j < len; j++) { | 8183 for (uint32_t j = 0; j < len; j++) { |
8106 Handle<Object> e = Factory::NewNumber(array->get(j)); | 8184 int64_t val = static_cast<int64_t>(array->get(j)); |
8107 visitor->visit(j, e); | 8185 if (Smi::IsValid(static_cast<intptr_t>(val))) { |
8108 } | 8186 Handle<Smi> e(Smi::FromInt(static_cast<int>(val))); |
8109 } | 8187 visitor->visit(j, e); |
8110 } | 8188 } else { |
8111 | 8189 Handle<Object> e = |
8112 return len; | 8190 Factory::NewNumber(static_cast<ElementType>(val)); |
8113 } | 8191 visitor->visit(j, e); |
8114 | 8192 } |
8115 /** | 8193 } |
8116 * A helper function that visits elements of a JSObject. Only elements | 8194 } |
8117 * whose index between 0 and range (exclusive) are visited. | 8195 } else { |
8118 * | 8196 for (uint32_t j = 0; j < len; j++) { |
8119 * If the third parameter, visitor, is not NULL, the visitor is called | 8197 Handle<Object> e = Factory::NewNumber(array->get(j)); |
8120 * with parameters, 'visitor_index_offset + element index' and the element. | 8198 visitor->visit(j, e); |
8121 * | 8199 } |
8122 * It returns the number of visisted elements. | 8200 } |
8123 */ | 8201 } |
8124 static uint32_t IterateElements(Handle<JSObject> receiver, | 8202 |
8125 uint32_t range, | 8203 |
8126 ArrayConcatVisitor* visitor) { | 8204 // Used for sorting indices in a List<uint32_t>. |
8127 uint32_t num_of_elements = 0; | 8205 static int compareUInt32(const uint32_t* ap, const uint32_t* bp) { |
8128 | 8206 uint32_t a = *ap; |
8129 switch (receiver->GetElementsKind()) { | 8207 uint32_t b = *bp; |
8208 return (a == b) ? 0 : (a < b) ? -1 : 1; | |
8209 } | |
8210 | |
8211 | |
8212 static void CollectElementIndices(Handle<JSObject> object, | |
8213 uint32_t range, | |
8214 List<uint32_t>* indices) { | |
8215 JSObject::ElementsKind kind = object->GetElementsKind(); | |
8216 switch (kind) { | |
8130 case JSObject::FAST_ELEMENTS: { | 8217 case JSObject::FAST_ELEMENTS: { |
8131 Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); | 8218 Handle<FixedArray> elements(FixedArray::cast(object->elements())); |
8132 uint32_t len = elements->length(); | 8219 uint32_t length = static_cast<uint32_t>(elements->length()); |
8133 if (range < len) { | 8220 if (range < length) length = range; |
8134 len = range; | 8221 for (uint32_t i = 0; i < length; i++) { |
8135 } | 8222 if (!elements->get(i)->IsTheHole()) { |
8136 | 8223 indices->Add(i); |
8137 for (uint32_t j = 0; j < len; j++) { | 8224 } |
8138 Handle<Object> e(elements->get(j)); | 8225 } |
8139 if (!e->IsTheHole()) { | |
8140 num_of_elements++; | |
8141 if (visitor) { | |
8142 visitor->visit(j, e); | |
8143 } | |
8144 } | |
8145 } | |
8146 break; | |
8147 } | |
8148 case JSObject::PIXEL_ELEMENTS: { | |
8149 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements())); | |
8150 uint32_t len = pixels->length(); | |
8151 if (range < len) { | |
8152 len = range; | |
8153 } | |
8154 | |
8155 for (uint32_t j = 0; j < len; j++) { | |
8156 num_of_elements++; | |
8157 if (visitor != NULL) { | |
8158 Handle<Smi> e(Smi::FromInt(pixels->get(j))); | |
8159 visitor->visit(j, e); | |
8160 } | |
8161 } | |
8162 break; | |
8163 } | |
8164 case JSObject::EXTERNAL_BYTE_ELEMENTS: { | |
8165 num_of_elements = | |
8166 IterateExternalArrayElements<ExternalByteArray, int8_t>( | |
8167 receiver, true, true, range, visitor); | |
8168 break; | |
8169 } | |
8170 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { | |
8171 num_of_elements = | |
8172 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>( | |
8173 receiver, true, true, range, visitor); | |
8174 break; | |
8175 } | |
8176 case JSObject::EXTERNAL_SHORT_ELEMENTS: { | |
8177 num_of_elements = | |
8178 IterateExternalArrayElements<ExternalShortArray, int16_t>( | |
8179 receiver, true, true, range, visitor); | |
8180 break; | |
8181 } | |
8182 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { | |
8183 num_of_elements = | |
8184 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>( | |
8185 receiver, true, true, range, visitor); | |
8186 break; | |
8187 } | |
8188 case JSObject::EXTERNAL_INT_ELEMENTS: { | |
8189 num_of_elements = | |
8190 IterateExternalArrayElements<ExternalIntArray, int32_t>( | |
8191 receiver, true, false, range, visitor); | |
8192 break; | |
8193 } | |
8194 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { | |
8195 num_of_elements = | |
8196 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>( | |
8197 receiver, true, false, range, visitor); | |
8198 break; | |
8199 } | |
8200 case JSObject::EXTERNAL_FLOAT_ELEMENTS: { | |
8201 num_of_elements = | |
8202 IterateExternalArrayElements<ExternalFloatArray, float>( | |
8203 receiver, false, false, range, visitor); | |
8204 break; | 8226 break; |
8205 } | 8227 } |
8206 case JSObject::DICTIONARY_ELEMENTS: { | 8228 case JSObject::DICTIONARY_ELEMENTS: { |
8207 Handle<NumberDictionary> dict(receiver->element_dictionary()); | 8229 Handle<NumberDictionary> dict(NumberDictionary::cast(object->elements())); |
8208 uint32_t capacity = dict->Capacity(); | 8230 uint32_t capacity = dict->Capacity(); |
8209 for (uint32_t j = 0; j < capacity; j++) { | 8231 for (uint32_t j = 0; j < capacity; j++) { |
8232 HandleScope loop_scope; | |
8210 Handle<Object> k(dict->KeyAt(j)); | 8233 Handle<Object> k(dict->KeyAt(j)); |
8211 if (dict->IsKey(*k)) { | 8234 if (dict->IsKey(*k)) { |
8212 ASSERT(k->IsNumber()); | 8235 ASSERT(k->IsNumber()); |
8213 uint32_t index = static_cast<uint32_t>(k->Number()); | 8236 uint32_t index = static_cast<uint32_t>(k->Number()); |
8214 if (index < range) { | 8237 if (index < range) { |
8215 num_of_elements++; | 8238 indices->Add(index); |
8216 if (visitor) { | |
8217 visitor->visit(index, Handle<Object>(dict->ValueAt(j))); | |
8218 } | |
8219 } | 8239 } |
8220 } | 8240 } |
8221 } | 8241 } |
8222 break; | 8242 break; |
8223 } | 8243 } |
8244 default: { | |
8245 int dense_elements_length; | |
8246 switch (kind) { | |
8247 case JSObject::PIXEL_ELEMENTS: { | |
8248 dense_elements_length = | |
8249 PixelArray::cast(object->elements())->length(); | |
8250 break; | |
8251 } | |
8252 case JSObject::EXTERNAL_BYTE_ELEMENTS: { | |
8253 dense_elements_length = | |
8254 ExternalByteArray::cast(object->elements())->length(); | |
8255 break; | |
8256 } | |
8257 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { | |
8258 dense_elements_length = | |
8259 ExternalUnsignedByteArray::cast(object->elements())->length(); | |
8260 break; | |
8261 } | |
8262 case JSObject::EXTERNAL_SHORT_ELEMENTS: { | |
8263 dense_elements_length = | |
8264 ExternalShortArray::cast(object->elements())->length(); | |
8265 break; | |
8266 } | |
8267 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { | |
8268 dense_elements_length = | |
8269 ExternalUnsignedShortArray::cast(object->elements())->length(); | |
8270 break; | |
8271 } | |
8272 case JSObject::EXTERNAL_INT_ELEMENTS: { | |
8273 dense_elements_length = | |
8274 ExternalIntArray::cast(object->elements())->length(); | |
8275 break; | |
8276 } | |
8277 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { | |
8278 dense_elements_length = | |
8279 ExternalUnsignedIntArray::cast(object->elements())->length(); | |
8280 break; | |
8281 } | |
8282 case JSObject::EXTERNAL_FLOAT_ELEMENTS: { | |
8283 dense_elements_length = | |
8284 ExternalFloatArray::cast(object->elements())->length(); | |
8285 break; | |
8286 } | |
8287 default: | |
8288 UNREACHABLE(); | |
8289 break; | |
8290 } | |
8291 uint32_t length = static_cast<uint32_t>(dense_elements_length); | |
8292 if (range <= length) { | |
8293 length = range; | |
8294 // We will add all indices, so we might as well clear it first | |
8295 // and avoid duplicates. | |
8296 indices->Clear(); | |
8297 } | |
8298 for (uint32_t i = 0; i < length; i++) { | |
8299 indices->Add(i); | |
8300 } | |
8301 if (length == range) return; // All indices accounted for already. | |
8302 break; | |
8303 } | |
8304 } | |
8305 | |
8306 Handle<Object> prototype(object->GetPrototype()); | |
8307 if (prototype->IsJSObject()) { | |
8308 // The prototype will usually have no inherited element indices, | |
8309 // but we have to check. | |
8310 CollectElementIndices(Handle<JSObject>::cast(prototype), range, indices); | |
8311 } | |
8312 } | |
8313 | |
8314 | |
8315 /** | |
8316 * A helper function that visits elements of a JSArray in numerical | |
8317 * order. | |
8318 * | |
8319 * The visitor argument called for each existing element in the array | |
8320 * with the element index and the element's value. | |
8321 * Afterwards it increments the base-index of the visitor by the array | |
8322 * length. | |
8323 */ | |
8324 static void IterateElements(Handle<JSArray> receiver, | |
8325 ArrayConcatVisitor* visitor) { | |
8326 uint32_t length = static_cast<uint32_t>(receiver->length()->Number()); | |
8327 switch (receiver->GetElementsKind()) { | |
8328 case JSObject::FAST_ELEMENTS: { | |
8329 // Run through the elements FixedArray and use HasElement and GetElement | |
8330 // to check the prototype for missing elements. | |
8331 Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); | |
8332 int fast_length = static_cast<int>(length); | |
8333 ASSERT(fast_length <= elements->length()); | |
8334 for (int j = 0; j < fast_length; j++) { | |
8335 HandleScope loop_scope; | |
8336 Handle<Object> element_value(elements->get(j)); | |
8337 if (!element_value->IsTheHole()) { | |
8338 visitor->visit(j, element_value); | |
8339 } else if (receiver->HasElement(j)) { | |
8340 // Call GetElement on receiver, not its prototype, or getters won't | |
8341 // have the correct receiver. | |
8342 element_value = GetElement(receiver, j); | |
8343 visitor->visit(j, element_value); | |
8344 } | |
8345 } | |
8346 break; | |
8347 } | |
8348 case JSObject::DICTIONARY_ELEMENTS: { | |
8349 Handle<NumberDictionary> dict(receiver->element_dictionary()); | |
8350 List<uint32_t> indices(dict->Capacity() / 2); | |
8351 // Collect all indices in the object and the prototypes less | |
8352 // than length. This might introduce duplicates in the indices list. | |
8353 CollectElementIndices(receiver, length, &indices); | |
8354 indices.Sort(&compareUInt32); | |
8355 int j = 0; | |
8356 int n = indices.length(); | |
8357 while (j < n) { | |
8358 HandleScope loop_scope; | |
8359 uint32_t index = indices[j]; | |
8360 Handle<Object> element = GetElement(receiver, index); | |
8361 visitor->visit(index, element); | |
8362 // Skip to next different index (i.e., omit duplicates). | |
8363 do { | |
8364 j++; | |
8365 } while (j < n && indices[j] == index); | |
Erik Corry
2011/02/24 10:36:57
So if we find a dupe shouldn't we update the lengt
Lasse Reichstein
2011/02/24 13:44:41
No, a dupe in the indices means that we have an in
| |
8366 } | |
8367 break; | |
8368 } | |
8369 case JSObject::PIXEL_ELEMENTS: { | |
8370 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements())); | |
8371 for (uint32_t j = 0; j < length; j++) { | |
8372 Handle<Smi> e(Smi::FromInt(pixels->get(j))); | |
8373 visitor->visit(j, e); | |
8374 } | |
8375 break; | |
8376 } | |
8377 case JSObject::EXTERNAL_BYTE_ELEMENTS: { | |
8378 IterateExternalArrayElements<ExternalByteArray, int8_t>( | |
8379 receiver, true, true, visitor); | |
8380 break; | |
8381 } | |
8382 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { | |
8383 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>( | |
8384 receiver, true, true, visitor); | |
8385 break; | |
8386 } | |
8387 case JSObject::EXTERNAL_SHORT_ELEMENTS: { | |
8388 IterateExternalArrayElements<ExternalShortArray, int16_t>( | |
8389 receiver, true, true, visitor); | |
8390 break; | |
8391 } | |
8392 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { | |
8393 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>( | |
8394 receiver, true, true, visitor); | |
8395 break; | |
8396 } | |
8397 case JSObject::EXTERNAL_INT_ELEMENTS: { | |
8398 IterateExternalArrayElements<ExternalIntArray, int32_t>( | |
8399 receiver, true, false, visitor); | |
8400 break; | |
8401 } | |
8402 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { | |
8403 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>( | |
8404 receiver, true, false, visitor); | |
8405 break; | |
8406 } | |
8407 case JSObject::EXTERNAL_FLOAT_ELEMENTS: { | |
8408 IterateExternalArrayElements<ExternalFloatArray, float>( | |
8409 receiver, false, false, visitor); | |
8410 break; | |
8411 } | |
8224 default: | 8412 default: |
8225 UNREACHABLE(); | 8413 UNREACHABLE(); |
8226 break; | 8414 break; |
8227 } | 8415 } |
8228 | 8416 visitor->increase_index_offset(length); |
8229 return num_of_elements; | 8417 } |
8230 } | 8418 |
8231 | 8419 |
8232 | |
8233 /** | |
8234 * A helper function that visits elements of an Array object, and elements | |
8235 * on its prototypes. | |
8236 * | |
8237 * Elements on prototypes are visited first, and only elements whose indices | |
8238 * less than Array length are visited. | |
8239 * | |
8240 * If a ArrayConcatVisitor object is given, the visitor is called with | |
8241 * parameters, element's index + visitor_index_offset and the element. | |
8242 * | |
8243 * The returned number of elements is an upper bound on the actual number | |
8244 * of elements added. If the same element occurs in more than one object | |
8245 * in the array's prototype chain, it will be counted more than once, but | |
8246 * will only occur once in the result. | |
8247 */ | |
8248 static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array, | |
8249 ArrayConcatVisitor* visitor) { | |
8250 uint32_t range = static_cast<uint32_t>(array->length()->Number()); | |
8251 Handle<Object> obj = array; | |
8252 | |
8253 static const int kEstimatedPrototypes = 3; | |
8254 List< Handle<JSObject> > objects(kEstimatedPrototypes); | |
8255 | |
8256 // Visit prototype first. If an element on the prototype is shadowed by | |
8257 // the inheritor using the same index, the ArrayConcatVisitor visits | |
8258 // the prototype element before the shadowing element. | |
8259 // The visitor can simply overwrite the old value by new value using | |
8260 // the same index. This follows Array::concat semantics. | |
8261 while (!obj->IsNull()) { | |
8262 objects.Add(Handle<JSObject>::cast(obj)); | |
8263 obj = Handle<Object>(obj->GetPrototype()); | |
8264 } | |
8265 | |
8266 uint32_t nof_elements = 0; | |
8267 for (int i = objects.length() - 1; i >= 0; i--) { | |
8268 Handle<JSObject> obj = objects[i]; | |
8269 uint32_t encountered_elements = | |
8270 IterateElements(Handle<JSObject>::cast(obj), range, visitor); | |
8271 | |
8272 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) { | |
8273 nof_elements = JSObject::kMaxElementCount; | |
8274 } else { | |
8275 nof_elements += encountered_elements; | |
8276 } | |
8277 } | |
8278 | |
8279 return nof_elements; | |
8280 } | |
8281 | |
8282 | |
8283 /** | |
8284 * A helper function of Runtime_ArrayConcat. | |
8285 * | |
8286 * The first argument is an Array of arrays and objects. It is the | |
8287 * same as the arguments array of Array::concat JS function. | |
8288 * | |
8289 * If an argument is an Array object, the function visits array | |
8290 * elements. If an argument is not an Array object, the function | |
8291 * visits the object as if it is an one-element array. | |
8292 * | |
8293 * If the result array index overflows 32-bit unsigned integer, the rounded | |
8294 * non-negative number is used as new length. For example, if one | |
8295 * array length is 2^32 - 1, second array length is 1, the | |
8296 * concatenated array length is 0. | |
8297 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length | |
8298 * is one more than the last array index to get a value assigned). | |
8299 */ | |
8300 static uint32_t IterateArguments(Handle<JSArray> arguments, | |
8301 ArrayConcatVisitor* visitor) { | |
8302 uint32_t visited_elements = 0; | |
8303 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number()); | |
8304 | |
8305 for (uint32_t i = 0; i < num_of_args; i++) { | |
8306 Object *element; | |
8307 MaybeObject* maybe_element = arguments->GetElement(i); | |
8308 // This if() is not expected to fail, but we have the check in the | |
8309 // interest of hardening the runtime calls. | |
8310 if (maybe_element->ToObject(&element)) { | |
8311 Handle<Object> obj(element); | |
8312 if (obj->IsJSArray()) { | |
8313 Handle<JSArray> array = Handle<JSArray>::cast(obj); | |
8314 uint32_t len = static_cast<uint32_t>(array->length()->Number()); | |
8315 uint32_t nof_elements = | |
8316 IterateArrayAndPrototypeElements(array, visitor); | |
8317 // Total elements of array and its prototype chain can be more than | |
8318 // the array length, but ArrayConcat can only concatenate at most | |
8319 // the array length number of elements. We use the length as an estimate | |
8320 // for the actual number of elements added. | |
8321 uint32_t added_elements = (nof_elements > len) ? len : nof_elements; | |
8322 if (JSArray::kMaxElementCount - visited_elements < added_elements) { | |
8323 visited_elements = JSArray::kMaxElementCount; | |
8324 } else { | |
8325 visited_elements += added_elements; | |
8326 } | |
8327 if (visitor) visitor->increase_index_offset(len); | |
8328 } else { | |
8329 if (visitor) { | |
8330 visitor->visit(0, obj); | |
8331 visitor->increase_index_offset(1); | |
8332 } | |
8333 if (visited_elements < JSArray::kMaxElementCount) { | |
8334 visited_elements++; | |
8335 } | |
8336 } | |
8337 } | |
8338 } | |
8339 return visited_elements; | |
8340 } | |
8341 | |
8342 | |
8343 /** | 8420 /** |
8344 * Array::concat implementation. | 8421 * Array::concat implementation. |
8345 * See ECMAScript 262, 15.4.4.4. | 8422 * See ECMAScript 262, 15.4.4.4. |
8346 * TODO(lrn): Fix non-compliance for very large concatenations and update to | 8423 * TODO(lrn): Fix non-compliance for very large concatenations and update to |
Erik Corry
2011/02/24 10:36:57
Is there a bug number for this one?
Lasse Reichstein
2011/02/24 13:44:41
There is: 581.
Changing comment to match.
| |
8347 * following the ECMAScript 5 specification. | 8424 * following the ECMAScript 5 specification. |
8348 */ | 8425 */ |
8349 static MaybeObject* Runtime_ArrayConcat(Arguments args) { | 8426 static MaybeObject* Runtime_ArrayConcat(Arguments args) { |
8350 ASSERT(args.length() == 1); | 8427 ASSERT(args.length() == 1); |
8351 HandleScope handle_scope; | 8428 HandleScope handle_scope; |
8352 | 8429 |
8353 CONVERT_CHECKED(JSArray, arg_arrays, args[0]); | 8430 CONVERT_ARG_CHECKED(JSArray, arguments, 0); |
8354 Handle<JSArray> arguments(arg_arrays); | 8431 int argument_count = static_cast<int>(arguments->length()->Number()); |
8432 RUNTIME_ASSERT(arguments->HasFastElements()); | |
8433 Handle<FixedArray> elements(FixedArray::cast(arguments->elements())); | |
8355 | 8434 |
8356 // Pass 1: estimate the number of elements of the result | 8435 // Pass 1: estimate the length and number of elements of the result. |
8357 // (it could be more than real numbers if prototype has elements). | 8436 // The actual length can be larger if any of the arguments have getters |
8358 uint32_t result_length = 0; | 8437 // that mutate other arguments (but will otherwise be precise). |
Erik Corry
2011/02/24 10:36:57
Parenthetic remark not accurate in the presence of
Lasse Reichstein
2011/02/24 13:44:41
The length is indifferent to inherited elements. W
| |
8359 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number()); | 8438 // The number of elements is precise if there are no inherited elements. |
8360 | 8439 |
8361 { AssertNoAllocation nogc; | 8440 uint32_t estimate_result_length = 0; |
8362 for (uint32_t i = 0; i < num_of_args; i++) { | 8441 uint32_t estimate_nof_elements = 0; |
8363 Object* obj; | 8442 { |
8364 MaybeObject* maybe_object = arguments->GetElement(i); | 8443 for (int i = 0; i < argument_count; i++) { |
8365 // This if() is not expected to fail, but we have the check in the | 8444 HandleScope loop_scope; |
8366 // interest of hardening the runtime calls. | 8445 Handle<Object> obj(elements->get(i)); |
8367 if (maybe_object->ToObject(&obj)) { | 8446 uint32_t length_estimate; |
8368 uint32_t length_estimate; | 8447 uint32_t element_estimate; |
8369 if (obj->IsJSArray()) { | 8448 if (obj->IsJSArray()) { |
8370 length_estimate = | 8449 Handle<JSArray> array(Handle<JSArray>::cast(obj)); |
8371 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number()); | 8450 length_estimate = |
8372 } else { | 8451 static_cast<uint32_t>(array->length()->Number()); |
8373 length_estimate = 1; | 8452 element_estimate = |
8374 } | 8453 EstimateElementCount(array); |
8375 if (JSObject::kMaxElementCount - result_length < length_estimate) { | 8454 } else { |
8376 result_length = JSObject::kMaxElementCount; | 8455 length_estimate = 1; |
8377 break; | 8456 element_estimate = 1; |
8378 } | 8457 } |
8379 result_length += length_estimate; | 8458 // Avoid overflows by capping at kMaxElementCount. |
8459 if (JSObject::kMaxElementCount - estimate_result_length < | |
8460 length_estimate) { | |
8461 estimate_result_length = JSObject::kMaxElementCount; | |
8462 } else { | |
8463 estimate_result_length += length_estimate; | |
8464 } | |
8465 if (JSObject::kMaxElementCount - estimate_nof_elements < | |
8466 element_estimate) { | |
8467 estimate_nof_elements = JSObject::kMaxElementCount; | |
8468 } else { | |
8469 estimate_nof_elements += element_estimate; | |
8380 } | 8470 } |
8381 } | 8471 } |
8382 } | 8472 } |
8383 | 8473 |
8384 // Allocate an empty array, will set map, length, and content later. | |
8385 Handle<JSArray> result = Factory::NewJSArray(0); | |
8386 | |
8387 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL); | |
8388 // If estimated number of elements is more than half of length, a | 8474 // If estimated number of elements is more than half of length, a |
8389 // fixed array (fast case) is more time and space-efficient than a | 8475 // fixed array (fast case) is more time and space-efficient than a |
8390 // dictionary. | 8476 // dictionary. |
8391 bool fast_case = (estimate_nof_elements * 2) >= result_length; | 8477 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; |
8392 | 8478 |
8393 Handle<Map> map; | |
8394 Handle<FixedArray> storage; | 8479 Handle<FixedArray> storage; |
8395 if (fast_case) { | 8480 if (fast_case) { |
8396 // The backing storage array must have non-existing elements to | 8481 // The backing storage array must have non-existing elements to |
8397 // preserve holes across concat operations. | 8482 // preserve holes across concat operations. |
8398 map = Factory::GetFastElementsMap(Handle<Map>(result->map())); | 8483 storage = Factory::NewFixedArrayWithHoles(estimate_result_length); |
8399 storage = Factory::NewFixedArrayWithHoles(result_length); | |
8400 } else { | 8484 } else { |
8401 map = Factory::GetSlowElementsMap(Handle<Map>(result->map())); | |
8402 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate | 8485 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate |
8403 uint32_t at_least_space_for = estimate_nof_elements + | 8486 uint32_t at_least_space_for = estimate_nof_elements + |
8404 (estimate_nof_elements >> 2); | 8487 (estimate_nof_elements >> 2); |
8405 storage = Handle<FixedArray>::cast( | 8488 storage = Handle<FixedArray>::cast( |
8406 Factory::NewNumberDictionary(at_least_space_for)); | 8489 Factory::NewNumberDictionary(at_least_space_for)); |
8407 } | 8490 } |
8408 | 8491 |
8409 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length)); | 8492 ArrayConcatVisitor visitor(storage, fast_case); |
8410 | 8493 |
8411 ArrayConcatVisitor visitor(storage, result_length, fast_case); | 8494 for (int i = 0; i < argument_count; i++) { |
8495 Handle<Object> obj(elements->get(i)); | |
8496 if (obj->IsJSArray()) { | |
8497 Handle<JSArray> array = Handle<JSArray>::cast(obj); | |
8498 IterateElements(array, &visitor); | |
8499 } else { | |
8500 visitor.visit(0, obj); | |
8501 visitor.increase_index_offset(1); | |
8502 } | |
8503 } | |
8412 | 8504 |
8413 IterateArguments(arguments, &visitor); | 8505 return *visitor.ToArray(); |
8414 | |
8415 // Please note: | |
8416 // - the storage might have been changed in the visitor; | |
8417 // - the map and the storage must be set together to avoid breaking | |
8418 // the invariant that the map describes the array's elements. | |
8419 result->set_map(*map); | |
8420 result->set_length(*len); | |
8421 result->set_elements(*visitor.storage()); | |
8422 | |
8423 return *result; | |
8424 } | 8506 } |
8425 | 8507 |
8426 | 8508 |
8427 // This will not allocate (flatten the string), but it may run | 8509 // This will not allocate (flatten the string), but it may run |
8428 // very slowly for very deeply nested ConsStrings. For debugging use only. | 8510 // very slowly for very deeply nested ConsStrings. For debugging use only. |
8429 static MaybeObject* Runtime_GlobalPrint(Arguments args) { | 8511 static MaybeObject* Runtime_GlobalPrint(Arguments args) { |
8430 NoHandleAllocation ha; | 8512 NoHandleAllocation ha; |
8431 ASSERT(args.length() == 1); | 8513 ASSERT(args.length() == 1); |
8432 | 8514 |
8433 CONVERT_CHECKED(String, string, args[0]); | 8515 CONVERT_CHECKED(String, string, args[0]); |
(...skipping 2787 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
11221 } else { | 11303 } else { |
11222 // Handle last resort GC and make sure to allow future allocations | 11304 // Handle last resort GC and make sure to allow future allocations |
11223 // to grow the heap without causing GCs (if possible). | 11305 // to grow the heap without causing GCs (if possible). |
11224 Counters::gc_last_resort_from_js.Increment(); | 11306 Counters::gc_last_resort_from_js.Increment(); |
11225 Heap::CollectAllGarbage(false); | 11307 Heap::CollectAllGarbage(false); |
11226 } | 11308 } |
11227 } | 11309 } |
11228 | 11310 |
11229 | 11311 |
11230 } } // namespace v8::internal | 11312 } } // namespace v8::internal |
OLD | NEW |