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

Unified Diff: src/runtime.cc

Issue 7990: Implement Array::concat function in C++.... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 12 years, 2 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/runtime.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
===================================================================
--- 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) {
« no previous file with comments | « src/runtime.h ('k') | test/mjsunit/array-concat.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698