| Index: src/runtime.cc
|
| ===================================================================
|
| --- src/runtime.cc (revision 611)
|
| +++ src/runtime.cc (working copy)
|
| @@ -3987,6 +3987,237 @@
|
| }
|
|
|
|
|
| +/**
|
| + * A simple visitor visits every element of Array's.
|
| + * The backend storage can be a fixed array for fast elements case,
|
| + * or a dictionary for sparse array. Since Dictionary is a subtype
|
| + * of FixedArray, the class can be used by both fast and slow cases.
|
| + * The second parameter of the constructor, fast_elements, specifies
|
| + * whether the storage is a FixedArray or Dictionary.
|
| + */
|
| +class ArrayConcatVisitor {
|
| + public:
|
| + ArrayConcatVisitor(Handle<FixedArray> storage, bool fast_elements)
|
| + : storage_(storage), fast_elements_(fast_elements), index_offset_(0) { }
|
| +
|
| + void visit(uint32_t i, Handle<Object> elm) {
|
| + uint32_t index = i + index_offset_;
|
| +
|
| + if (fast_elements_) {
|
| + ASSERT(index < static_cast<uint32_t>(storage_->length()));
|
| + storage_->set(index, *elm);
|
| +
|
| + } else {
|
| + Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
|
| + Handle<Dictionary> result =
|
| + Factory::DictionaryAtNumberPut(dict, index, elm);
|
| + if (!result.is_identical_to(dict))
|
| + storage_ = result;
|
| + }
|
| + }
|
| +
|
| + void increase_index_offset(uint32_t delta) {
|
| + index_offset_ += delta;
|
| + }
|
| +
|
| + private:
|
| + Handle<FixedArray> storage_;
|
| + bool fast_elements_;
|
| + uint32_t index_offset_;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * 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.
|
| + *
|
| + * It returns the number of visisted elements.
|
| + */
|
| +static uint32_t IterateElements(Handle<JSObject> receiver,
|
| + uint32_t range,
|
| + ArrayConcatVisitor* visitor) {
|
| + uint32_t num_of_elements = 0;
|
| +
|
| + if (receiver->HasFastElements()) {
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + } else {
|
| + Handle<Dictionary> 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)));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + 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.
|
| + */
|
| +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(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];
|
| + nof_elements +=
|
| + IterateElements(Handle<JSObject>::cast(obj), range, visitor);
|
| + }
|
| +
|
| + 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.
|
| + */
|
| +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++) {
|
| + Handle<Object> obj(arguments->GetElement(i));
|
| + 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.
|
| + visited_elements += (nof_elements > len) ? len : nof_elements;
|
| + if (visitor) visitor->increase_index_offset(len);
|
| +
|
| + } else {
|
| + if (visitor) {
|
| + visitor->visit(0, obj);
|
| + visitor->increase_index_offset(1);
|
| + }
|
| + visited_elements++;
|
| + }
|
| + }
|
| + return visited_elements;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Array::concat implementation.
|
| + * See ECMAScript 262, 15.4.4.4.
|
| + */
|
| +static Object* 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 = arguments->GetElement(i);
|
| + if (obj->IsJSArray()) {
|
| + result_length +=
|
| + static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
|
| + } else {
|
| + result_length++;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Allocate an empty array, will set 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 & space-efficient than a dictionary.
|
| + bool fast_case = (estimate_nof_elements * 2) >= result_length;
|
| +
|
| + Handle<FixedArray> storage;
|
| + if (fast_case) {
|
| + storage = Factory::NewFixedArray(result_length);
|
| +
|
| + } else {
|
| + // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
|
| + uint32_t at_least_space_for = estimate_nof_elements +
|
| + (estimate_nof_elements >> 2);
|
| + storage = Handle<FixedArray>::cast(
|
| + Factory::NewDictionary(at_least_space_for));
|
| + }
|
| +
|
| + Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
|
| +
|
| + ArrayConcatVisitor visitor(storage, fast_case);
|
| +
|
| + IterateArguments(arguments, &visitor);
|
| +
|
| + result->set_length(*len);
|
| + result->set_elements(*storage);
|
| +
|
| + return *result;
|
| +}
|
| +
|
| +
|
| // This will not allocate (flatten the string), but it may run
|
| // very slowly for very deeply nested ConsStrings. For debugging use only.
|
| static Object* Runtime_GlobalPrint(Arguments args) {
|
|
|