| Index: src/runtime.cc
|
| diff --git a/src/runtime.cc b/src/runtime.cc
|
| index 50ea60f055de29afa5138cfc8dee75d4ddcad2c3..76ea4bb7ee0110fc6ecc2d536192a09c5f3723fa 100644
|
| --- a/src/runtime.cc
|
| +++ b/src/runtime.cc
|
| @@ -5371,8 +5371,8 @@ class ArrayConcatVisitor {
|
| fast_elements_(fast_elements), index_offset_(0) { }
|
|
|
| void visit(uint32_t i, Handle<Object> elm) {
|
| - uint32_t index = i + index_offset_;
|
| - if (index >= index_limit_) return;
|
| + if (i >= index_limit_ - index_offset_) return;
|
| + uint32_t index = index_offset_ + i;
|
|
|
| if (fast_elements_) {
|
| ASSERT(index < static_cast<uint32_t>(storage_->length()));
|
| @@ -5388,16 +5388,23 @@ class ArrayConcatVisitor {
|
| }
|
|
|
| void increase_index_offset(uint32_t delta) {
|
| - index_offset_ += delta;
|
| + if (index_limit_ - index_offset_ < delta) {
|
| + index_offset_ = index_limit_;
|
| + } else {
|
| + index_offset_ += delta;
|
| + }
|
| }
|
|
|
| Handle<FixedArray> storage() { return storage_; }
|
|
|
| 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_;
|
| - bool fast_elements_;
|
| + // Index after last seen index. Always less than or equal to index_limit_.
|
| uint32_t index_offset_;
|
| + bool fast_elements_;
|
| };
|
|
|
|
|
| @@ -5569,6 +5576,11 @@ static uint32_t IterateElements(Handle<JSObject> receiver,
|
| *
|
| * 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) {
|
| @@ -5591,8 +5603,14 @@ static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
|
| uint32_t nof_elements = 0;
|
| for (int i = objects.length() - 1; i >= 0; i--) {
|
| Handle<JSObject> obj = objects[i];
|
| - nof_elements +=
|
| + 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;
|
| @@ -5609,13 +5627,16 @@ static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> 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 integer, the rounded
|
| + * 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) {
|
| + const uint32_t max_length = JSObject::kMaxElementCount;
|
| uint32_t visited_elements = 0;
|
| uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
|
|
|
| @@ -5628,16 +5649,23 @@ static uint32_t IterateArguments(Handle<JSArray> arguments,
|
| 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.
|
| - visited_elements += (nof_elements > len) ? len : nof_elements;
|
| + // 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);
|
| }
|
| - visited_elements++;
|
| + if (visited_elements < JSArray::kMaxElementCount) {
|
| + visited_elements++;
|
| + }
|
| }
|
| }
|
| return visited_elements;
|
| @@ -5647,6 +5675,8 @@ static uint32_t IterateArguments(Handle<JSArray> arguments,
|
| /**
|
| * Array::concat implementation.
|
| * See ECMAScript 262, 15.4.4.4.
|
| + * TODO(lrn): Fix non-compliance for very large concatenations and update to
|
| + * following the ECMAScript 5 specification.
|
| */
|
| static Object* Runtime_ArrayConcat(Arguments args) {
|
| ASSERT(args.length() == 1);
|
| @@ -5663,12 +5693,18 @@ static Object* Runtime_ArrayConcat(Arguments args) {
|
| { AssertNoAllocation nogc;
|
| for (uint32_t i = 0; i < num_of_args; i++) {
|
| Object* obj = arguments->GetElement(i);
|
| + uint32_t length_estimate;
|
| if (obj->IsJSArray()) {
|
| - result_length +=
|
| + length_estimate =
|
| static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
|
| } else {
|
| - result_length++;
|
| + length_estimate = 1;
|
| + }
|
| + if (JSObject::kMaxElementCount - result_length < length_estimate) {
|
| + result_length = JSObject::kMaxElementCount;
|
| + break;
|
| }
|
| + result_length += length_estimate;
|
| }
|
| }
|
|
|
|
|