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

Unified Diff: src/runtime.cc

Issue 6568007: Fix array concat to follow the specification in the presence of element getters. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge/build-ia32
Patch Set: git utd Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/objects-inl.h ('k') | test/mjsunit/array-concat.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/runtime.cc
diff --git a/src/runtime.cc b/src/runtime.cc
index 5a443efc3d0fb9b0445b50cf50a22f9472da78f9..d40565c95858ce16061405f555c86367b98dba50 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -8028,377 +8028,448 @@ static MaybeObject* Runtime_PushIfAbsent(Arguments args) {
class ArrayConcatVisitor {
public:
ArrayConcatVisitor(Handle<FixedArray> storage,
- uint32_t index_limit,
bool fast_elements) :
- storage_(storage), index_limit_(index_limit),
- index_offset_(0), fast_elements_(fast_elements) { }
+ storage_(storage),
+ index_offset_(0u),
+ fast_elements_(fast_elements) { }
void visit(uint32_t i, Handle<Object> elm) {
- if (i >= index_limit_ - index_offset_) return;
+ if (i >= JSObject::kMaxElementCount - index_offset_) return;
uint32_t index = index_offset_ + i;
if (fast_elements_) {
- ASSERT(index < static_cast<uint32_t>(storage_->length()));
- storage_->set(index, *elm);
-
- } else {
- Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
- Handle<NumberDictionary> result =
- Factory::DictionaryAtNumberPut(dict, index, elm);
- if (!result.is_identical_to(dict))
- storage_ = result;
+ if (index < static_cast<uint32_t>(storage_->length())) {
+ storage_->set(index, *elm);
+ return;
+ }
+ // Our initial estimate of length was foiled, possibly by
+ // getters on the arrays increasing the length of later arrays
+ // during iteration.
+ // This shouldn't happen in anything but pathological cases.
+ SetDictionaryMode(index);
+ // Fall-through to dictionary mode.
}
- }
+ ASSERT(!fast_elements_);
+ Handle<NumberDictionary> dict(storage_.cast<NumberDictionary>());
+ Handle<NumberDictionary> result =
+ Factory::DictionaryAtNumberPut(dict, index, elm);
+ if (!result.is_identical_to(dict)) {
+ storage_ = Handle<FixedArray>::cast(result);
+ }
+}
void increase_index_offset(uint32_t delta) {
- if (index_limit_ - index_offset_ < delta) {
- index_offset_ = index_limit_;
+ if (JSObject::kMaxElementCount - index_offset_ < delta) {
+ index_offset_ = JSObject::kMaxElementCount;
} else {
index_offset_ += delta;
}
}
- Handle<FixedArray> storage() { return storage_; }
+ Handle<JSArray> ToArray() {
+ Handle<JSArray> array = Factory::NewJSArray(0);
+ Handle<Object> length =
+ Factory::NewNumber(static_cast<double>(index_offset_));
+ Handle<Map> map;
+ if (fast_elements_) {
+ map = Factory::GetFastElementsMap(Handle<Map>(array->map()));
+ } else {
+ map = Factory::GetSlowElementsMap(Handle<Map>(array->map()));
+ }
+ array->set_map(*map);
+ array->set_length(*length);
+ array->set_elements(*storage_);
+ return array;
+ }
private:
- Handle<FixedArray> storage_;
- // Limit on the accepted indices. Elements with indices larger than the
- // limit are ignored by the visitor.
- uint32_t index_limit_;
- // Index after last seen index. Always less than or equal to index_limit_.
+ // Convert storage to dictionary mode.
+ void SetDictionaryMode(uint32_t index) {
+ ASSERT(fast_elements_);
+ Handle<FixedArray> current_storage(storage_.ToHandle());
+ HandleCell<NumberDictionary> slow_storage(
+ Factory::NewNumberDictionary(current_storage->length()));
+ uint32_t current_length = static_cast<uint32_t>(current_storage->length());
+ for (uint32_t i = 0; i < current_length; i++) {
+ HandleScope loop_scope;
+ Handle<Object> element(current_storage->get(i));
+ if (!element->IsTheHole()) {
+ slow_storage =
+ Factory::DictionaryAtNumberPut(slow_storage.ToHandle(), i, element);
+ }
+ }
+ storage_ = slow_storage.cast<FixedArray>();
+ fast_elements_ = false;
+ }
+
+ HandleCell<FixedArray> storage_;
+ // Index after last seen index. Always less than or equal to
+ // JSObject::kMaxElementCount.
uint32_t index_offset_;
- const bool fast_elements_;
+ bool fast_elements_;
};
+static uint32_t EstimateElementCount(Handle<JSArray> array) {
+ uint32_t length = static_cast<uint32_t>(array->length()->Number());
+ int element_count = 0;
+ switch (array->GetElementsKind()) {
+ case JSObject::FAST_ELEMENTS: {
+ // Fast elements can't have lengths that are not representable by
+ // a 32-bit signed integer.
+ ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0);
+ int fast_length = static_cast<int>(length);
+ Handle<FixedArray> elements(FixedArray::cast(array->elements()));
+ for (int i = 0; i < fast_length; i++) {
+ if (!elements->get(i)->IsTheHole()) element_count++;
+ }
+ break;
+ }
+ case JSObject::DICTIONARY_ELEMENTS: {
+ Handle<NumberDictionary> dictionary(
+ NumberDictionary::cast(array->elements()));
+ int capacity = dictionary->Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Handle<Object> key(dictionary->KeyAt(i));
+ if (dictionary->IsKey(*key)) {
+ element_count++;
+ }
+ }
+ break;
+ }
+ default:
+ // External arrays are always dense.
+ return length;
+ }
+ // As an estimate, we assume that the prototype doesn't contain any
+ // inherited elements.
+ return element_count;
+}
+
+
+
template<class ExternalArrayClass, class ElementType>
-static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
- bool elements_are_ints,
- bool elements_are_guaranteed_smis,
- uint32_t range,
- ArrayConcatVisitor* visitor) {
+static void IterateExternalArrayElements(Handle<JSObject> receiver,
+ bool elements_are_ints,
+ bool elements_are_guaranteed_smis,
+ ArrayConcatVisitor* visitor) {
Handle<ExternalArrayClass> array(
ExternalArrayClass::cast(receiver->elements()));
- uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
+ uint32_t len = static_cast<uint32_t>(array->length());
- if (visitor != NULL) {
- if (elements_are_ints) {
- if (elements_are_guaranteed_smis) {
- for (uint32_t j = 0; j < len; j++) {
- Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
+ ASSERT(visitor != NULL);
+ if (elements_are_ints) {
+ if (elements_are_guaranteed_smis) {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope;
+ Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
+ visitor->visit(j, e);
+ }
+ } else {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope;
+ int64_t val = static_cast<int64_t>(array->get(j));
+ if (Smi::IsValid(static_cast<intptr_t>(val))) {
+ Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
+ visitor->visit(j, e);
+ } else {
+ Handle<Object> e =
+ Factory::NewNumber(static_cast<ElementType>(val));
visitor->visit(j, e);
}
- } else {
- for (uint32_t j = 0; j < len; j++) {
- int64_t val = static_cast<int64_t>(array->get(j));
- if (Smi::IsValid(static_cast<intptr_t>(val))) {
- Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
- visitor->visit(j, e);
- } else {
- Handle<Object> e =
- Factory::NewNumber(static_cast<ElementType>(val));
- visitor->visit(j, e);
+ }
+ }
+ } else {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope;
+ Handle<Object> e = Factory::NewNumber(array->get(j));
+ visitor->visit(j, e);
+ }
+ }
+}
+
+
+// Used for sorting indices in a List<uint32_t>.
+static int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
+ uint32_t a = *ap;
+ uint32_t b = *bp;
+ return (a == b) ? 0 : (a < b) ? -1 : 1;
+}
+
+
+static void CollectElementIndices(Handle<JSObject> object,
+ uint32_t range,
+ List<uint32_t>* indices) {
+ JSObject::ElementsKind kind = object->GetElementsKind();
+ switch (kind) {
+ case JSObject::FAST_ELEMENTS: {
+ Handle<FixedArray> elements(FixedArray::cast(object->elements()));
+ uint32_t length = static_cast<uint32_t>(elements->length());
+ if (range < length) length = range;
+ for (uint32_t i = 0; i < length; i++) {
+ if (!elements->get(i)->IsTheHole()) {
+ indices->Add(i);
+ }
+ }
+ break;
+ }
+ case JSObject::DICTIONARY_ELEMENTS: {
+ Handle<NumberDictionary> dict(NumberDictionary::cast(object->elements()));
+ uint32_t capacity = dict->Capacity();
+ for (uint32_t j = 0; j < capacity; j++) {
+ HandleScope loop_scope;
+ Handle<Object> k(dict->KeyAt(j));
+ if (dict->IsKey(*k)) {
+ ASSERT(k->IsNumber());
+ uint32_t index = static_cast<uint32_t>(k->Number());
+ if (index < range) {
+ indices->Add(index);
}
}
}
- } else {
- for (uint32_t j = 0; j < len; j++) {
- Handle<Object> e = Factory::NewNumber(array->get(j));
- visitor->visit(j, e);
+ break;
+ }
+ default: {
+ int dense_elements_length;
+ switch (kind) {
+ case JSObject::PIXEL_ELEMENTS: {
+ dense_elements_length =
+ PixelArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_BYTE_ELEMENTS: {
+ dense_elements_length =
+ ExternalByteArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedByteArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_SHORT_ELEMENTS: {
+ dense_elements_length =
+ ExternalShortArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedShortArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_INT_ELEMENTS: {
+ dense_elements_length =
+ ExternalIntArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedIntArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
+ dense_elements_length =
+ ExternalFloatArray::cast(object->elements())->length();
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ uint32_t length = static_cast<uint32_t>(dense_elements_length);
+ if (range <= length) {
+ length = range;
+ // We will add all indices, so we might as well clear it first
+ // and avoid duplicates.
+ indices->Clear();
}
+ for (uint32_t i = 0; i < length; i++) {
+ indices->Add(i);
+ }
+ if (length == range) return; // All indices accounted for already.
+ break;
}
}
- return len;
+ Handle<Object> prototype(object->GetPrototype());
+ if (prototype->IsJSObject()) {
+ // The prototype will usually have no inherited element indices,
+ // but we have to check.
+ CollectElementIndices(Handle<JSObject>::cast(prototype), range, indices);
+ }
}
+
/**
- * A helper function that visits elements of a JSObject. Only elements
- * whose index between 0 and range (exclusive) are visited.
- *
- * If the third parameter, visitor, is not NULL, the visitor is called
- * with parameters, 'visitor_index_offset + element index' and the element.
+ * A helper function that visits elements of a JSArray in numerical
+ * order.
*
- * It returns the number of visisted elements.
+ * The visitor argument called for each existing element in the array
+ * with the element index and the element's value.
+ * Afterwards it increments the base-index of the visitor by the array
+ * length.
*/
-static uint32_t IterateElements(Handle<JSObject> receiver,
- uint32_t range,
- ArrayConcatVisitor* visitor) {
- uint32_t num_of_elements = 0;
-
+static void IterateElements(Handle<JSArray> receiver,
+ ArrayConcatVisitor* visitor) {
+ uint32_t length = static_cast<uint32_t>(receiver->length()->Number());
switch (receiver->GetElementsKind()) {
case JSObject::FAST_ELEMENTS: {
+ // Run through the elements FixedArray and use HasElement and GetElement
+ // to check the prototype for missing elements.
Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
- uint32_t len = elements->length();
- if (range < len) {
- len = range;
- }
-
- for (uint32_t j = 0; j < len; j++) {
- Handle<Object> e(elements->get(j));
- if (!e->IsTheHole()) {
- num_of_elements++;
- if (visitor) {
- visitor->visit(j, e);
- }
+ int fast_length = static_cast<int>(length);
+ ASSERT(fast_length <= elements->length());
+ for (int j = 0; j < fast_length; j++) {
+ HandleScope loop_scope;
+ Handle<Object> element_value(elements->get(j));
+ if (!element_value->IsTheHole()) {
+ visitor->visit(j, element_value);
+ } else if (receiver->HasElement(j)) {
+ // Call GetElement on receiver, not its prototype, or getters won't
+ // have the correct receiver.
+ element_value = GetElement(receiver, j);
+ visitor->visit(j, element_value);
}
}
break;
}
+ case JSObject::DICTIONARY_ELEMENTS: {
+ Handle<NumberDictionary> dict(receiver->element_dictionary());
+ List<uint32_t> indices(dict->Capacity() / 2);
+ // Collect all indices in the object and the prototypes less
+ // than length. This might introduce duplicates in the indices list.
+ CollectElementIndices(receiver, length, &indices);
+ indices.Sort(&compareUInt32);
+ int j = 0;
+ int n = indices.length();
+ while (j < n) {
+ HandleScope loop_scope;
+ uint32_t index = indices[j];
+ Handle<Object> element = GetElement(receiver, index);
+ visitor->visit(index, element);
+ // Skip to next different index (i.e., omit duplicates).
+ do {
+ j++;
+ } while (j < n && indices[j] == index);
+ }
+ break;
+ }
case JSObject::PIXEL_ELEMENTS: {
Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
- uint32_t len = pixels->length();
- if (range < len) {
- len = range;
- }
-
- for (uint32_t j = 0; j < len; j++) {
- num_of_elements++;
- if (visitor != NULL) {
- Handle<Smi> e(Smi::FromInt(pixels->get(j)));
- visitor->visit(j, e);
- }
+ for (uint32_t j = 0; j < length; j++) {
+ Handle<Smi> e(Smi::FromInt(pixels->get(j)));
+ visitor->visit(j, e);
}
break;
}
case JSObject::EXTERNAL_BYTE_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalByteArray, int8_t>(
- receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalByteArray, int8_t>(
+ receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
- receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
+ receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_SHORT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalShortArray, int16_t>(
- receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalShortArray, int16_t>(
+ receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
- receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
+ receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_INT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalIntArray, int32_t>(
- receiver, true, false, range, visitor);
+ IterateExternalArrayElements<ExternalIntArray, int32_t>(
+ receiver, true, false, visitor);
break;
}
case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
- receiver, true, false, range, visitor);
+ IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
+ receiver, true, false, visitor);
break;
}
case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalFloatArray, float>(
- receiver, false, false, range, visitor);
- break;
- }
- case JSObject::DICTIONARY_ELEMENTS: {
- Handle<NumberDictionary> dict(receiver->element_dictionary());
- uint32_t capacity = dict->Capacity();
- for (uint32_t j = 0; j < capacity; j++) {
- Handle<Object> k(dict->KeyAt(j));
- if (dict->IsKey(*k)) {
- ASSERT(k->IsNumber());
- uint32_t index = static_cast<uint32_t>(k->Number());
- if (index < range) {
- num_of_elements++;
- if (visitor) {
- visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
- }
- }
- }
- }
+ IterateExternalArrayElements<ExternalFloatArray, float>(
+ receiver, false, false, visitor);
break;
}
default:
UNREACHABLE();
break;
}
-
- return num_of_elements;
-}
-
-
-/**
- * A helper function that visits elements of an Array object, and elements
- * on its prototypes.
- *
- * Elements on prototypes are visited first, and only elements whose indices
- * less than Array length are visited.
- *
- * If a ArrayConcatVisitor object is given, the visitor is called with
- * parameters, element's index + visitor_index_offset and the element.
- *
- * The returned number of elements is an upper bound on the actual number
- * of elements added. If the same element occurs in more than one object
- * in the array's prototype chain, it will be counted more than once, but
- * will only occur once in the result.
- */
-static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
- ArrayConcatVisitor* visitor) {
- uint32_t range = static_cast<uint32_t>(array->length()->Number());
- Handle<Object> obj = array;
-
- static const int kEstimatedPrototypes = 3;
- List< Handle<JSObject> > objects(kEstimatedPrototypes);
-
- // Visit prototype first. If an element on the prototype is shadowed by
- // the inheritor using the same index, the ArrayConcatVisitor visits
- // the prototype element before the shadowing element.
- // The visitor can simply overwrite the old value by new value using
- // the same index. This follows Array::concat semantics.
- while (!obj->IsNull()) {
- objects.Add(Handle<JSObject>::cast(obj));
- obj = Handle<Object>(obj->GetPrototype());
- }
-
- uint32_t nof_elements = 0;
- for (int i = objects.length() - 1; i >= 0; i--) {
- Handle<JSObject> obj = objects[i];
- uint32_t encountered_elements =
- IterateElements(Handle<JSObject>::cast(obj), range, visitor);
-
- if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
- nof_elements = JSObject::kMaxElementCount;
- } else {
- nof_elements += encountered_elements;
- }
- }
-
- return nof_elements;
-}
-
-
-/**
- * A helper function of Runtime_ArrayConcat.
- *
- * The first argument is an Array of arrays and objects. It is the
- * same as the arguments array of Array::concat JS function.
- *
- * If an argument is an Array object, the function visits array
- * elements. If an argument is not an Array object, the function
- * visits the object as if it is an one-element array.
- *
- * If the result array index overflows 32-bit unsigned integer, the rounded
- * non-negative number is used as new length. For example, if one
- * array length is 2^32 - 1, second array length is 1, the
- * concatenated array length is 0.
- * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
- * is one more than the last array index to get a value assigned).
- */
-static uint32_t IterateArguments(Handle<JSArray> arguments,
- ArrayConcatVisitor* visitor) {
- uint32_t visited_elements = 0;
- uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
-
- for (uint32_t i = 0; i < num_of_args; i++) {
- Object *element;
- MaybeObject* maybe_element = arguments->GetElement(i);
- // This if() is not expected to fail, but we have the check in the
- // interest of hardening the runtime calls.
- if (maybe_element->ToObject(&element)) {
- Handle<Object> obj(element);
- if (obj->IsJSArray()) {
- Handle<JSArray> array = Handle<JSArray>::cast(obj);
- uint32_t len = static_cast<uint32_t>(array->length()->Number());
- uint32_t nof_elements =
- IterateArrayAndPrototypeElements(array, visitor);
- // Total elements of array and its prototype chain can be more than
- // the array length, but ArrayConcat can only concatenate at most
- // the array length number of elements. We use the length as an estimate
- // for the actual number of elements added.
- uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
- if (JSArray::kMaxElementCount - visited_elements < added_elements) {
- visited_elements = JSArray::kMaxElementCount;
- } else {
- visited_elements += added_elements;
- }
- if (visitor) visitor->increase_index_offset(len);
- } else {
- if (visitor) {
- visitor->visit(0, obj);
- visitor->increase_index_offset(1);
- }
- if (visited_elements < JSArray::kMaxElementCount) {
- visited_elements++;
- }
- }
- }
- }
- return visited_elements;
+ visitor->increase_index_offset(length);
}
/**
* Array::concat implementation.
* See ECMAScript 262, 15.4.4.4.
- * TODO(lrn): Fix non-compliance for very large concatenations and update to
+ * TODO(581): Fix non-compliance for very large concatenations and update to
* following the ECMAScript 5 specification.
*/
static MaybeObject* Runtime_ArrayConcat(Arguments args) {
ASSERT(args.length() == 1);
HandleScope handle_scope;
- CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
- Handle<JSArray> arguments(arg_arrays);
-
- // Pass 1: estimate the number of elements of the result
- // (it could be more than real numbers if prototype has elements).
- uint32_t result_length = 0;
- uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
-
- { AssertNoAllocation nogc;
- for (uint32_t i = 0; i < num_of_args; i++) {
- Object* obj;
- MaybeObject* maybe_object = arguments->GetElement(i);
- // This if() is not expected to fail, but we have the check in the
- // interest of hardening the runtime calls.
- if (maybe_object->ToObject(&obj)) {
- uint32_t length_estimate;
- if (obj->IsJSArray()) {
- length_estimate =
- static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
- } else {
- length_estimate = 1;
- }
- if (JSObject::kMaxElementCount - result_length < length_estimate) {
- result_length = JSObject::kMaxElementCount;
- break;
- }
- result_length += length_estimate;
+ CONVERT_ARG_CHECKED(JSArray, arguments, 0);
+ int argument_count = static_cast<int>(arguments->length()->Number());
+ RUNTIME_ASSERT(arguments->HasFastElements());
+ Handle<FixedArray> elements(FixedArray::cast(arguments->elements()));
+
+ // Pass 1: estimate the length and number of elements of the result.
+ // The actual length can be larger if any of the arguments have getters
+ // that mutate other arguments (but will otherwise be precise).
+ // The number of elements is precise if there are no inherited elements.
+
+ uint32_t estimate_result_length = 0;
+ uint32_t estimate_nof_elements = 0;
+ {
+ for (int i = 0; i < argument_count; i++) {
+ HandleScope loop_scope;
+ Handle<Object> obj(elements->get(i));
+ uint32_t length_estimate;
+ uint32_t element_estimate;
+ if (obj->IsJSArray()) {
+ Handle<JSArray> array(Handle<JSArray>::cast(obj));
+ length_estimate =
+ static_cast<uint32_t>(array->length()->Number());
+ element_estimate =
+ EstimateElementCount(array);
+ } else {
+ length_estimate = 1;
+ element_estimate = 1;
+ }
+ // Avoid overflows by capping at kMaxElementCount.
+ if (JSObject::kMaxElementCount - estimate_result_length <
+ length_estimate) {
+ estimate_result_length = JSObject::kMaxElementCount;
+ } else {
+ estimate_result_length += length_estimate;
+ }
+ if (JSObject::kMaxElementCount - estimate_nof_elements <
+ element_estimate) {
+ estimate_nof_elements = JSObject::kMaxElementCount;
+ } else {
+ estimate_nof_elements += element_estimate;
}
}
}
- // Allocate an empty array, will set map, length, and content later.
- Handle<JSArray> result = Factory::NewJSArray(0);
-
- uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
// If estimated number of elements is more than half of length, a
// fixed array (fast case) is more time and space-efficient than a
// dictionary.
- bool fast_case = (estimate_nof_elements * 2) >= result_length;
+ bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length;
- Handle<Map> map;
Handle<FixedArray> storage;
if (fast_case) {
// The backing storage array must have non-existing elements to
// preserve holes across concat operations.
- map = Factory::GetFastElementsMap(Handle<Map>(result->map()));
- storage = Factory::NewFixedArrayWithHoles(result_length);
+ storage = Factory::NewFixedArrayWithHoles(estimate_result_length);
} else {
- map = Factory::GetSlowElementsMap(Handle<Map>(result->map()));
// TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
uint32_t at_least_space_for = estimate_nof_elements +
(estimate_nof_elements >> 2);
@@ -8406,21 +8477,20 @@ static MaybeObject* Runtime_ArrayConcat(Arguments args) {
Factory::NewNumberDictionary(at_least_space_for));
}
- Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
-
- ArrayConcatVisitor visitor(storage, result_length, fast_case);
-
- IterateArguments(arguments, &visitor);
+ ArrayConcatVisitor visitor(storage, fast_case);
- // Please note:
- // - the storage might have been changed in the visitor;
- // - the map and the storage must be set together to avoid breaking
- // the invariant that the map describes the array's elements.
- result->set_map(*map);
- result->set_length(*len);
- result->set_elements(*visitor.storage());
+ for (int i = 0; i < argument_count; i++) {
+ Handle<Object> obj(elements->get(i));
+ if (obj->IsJSArray()) {
+ Handle<JSArray> array = Handle<JSArray>::cast(obj);
+ IterateElements(array, &visitor);
+ } else {
+ visitor.visit(0, obj);
+ visitor.increase_index_offset(1);
+ }
+ }
- return *result;
+ return *visitor.ToArray();
}
« no previous file with comments | « src/objects-inl.h ('k') | test/mjsunit/array-concat.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698