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