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

Unified Diff: src/runtime/runtime-array.cc

Issue 1330483003: Adding ElementsAccessor::Concat (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@2015-09-01_array_builtins_shift
Patch Set: merging master Created 5 years, 3 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/runtime.h ('k') | test/mjsunit/array-natives-elements.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/runtime/runtime-array.cc
diff --git a/src/runtime/runtime-array.cc b/src/runtime/runtime-array.cc
index 49eb1ad7024d9b643bccf150a56664bee0cc56a4..4b41bd876efc61e174f0efbcd59a5829fd589324 100644
--- a/src/runtime/runtime-array.cc
+++ b/src/runtime/runtime-array.cc
@@ -52,7 +52,6 @@ RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
- InstallBuiltin(isolate, holder, "concat", Builtins::kArrayConcat);
return *holder;
}
@@ -111,779 +110,6 @@ RUNTIME_FUNCTION(Runtime_PushIfAbsent) {
}
-/**
- * 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.
- *
- * An index limit is used to deal with the situation that a result array
- * length overflows 32-bit non-negative integer.
- */
-class ArrayConcatVisitor {
- public:
- ArrayConcatVisitor(Isolate* isolate, Handle<FixedArray> storage,
- bool fast_elements)
- : isolate_(isolate),
- storage_(Handle<FixedArray>::cast(
- isolate->global_handles()->Create(*storage))),
- index_offset_(0u),
- bit_field_(FastElementsField::encode(fast_elements) |
- ExceedsLimitField::encode(false)) {}
-
- ~ArrayConcatVisitor() { clear_storage(); }
-
- void visit(uint32_t i, Handle<Object> elm) {
- if (i >= JSObject::kMaxElementCount - index_offset_) {
- set_exceeds_array_limit(true);
- return;
- }
- uint32_t index = index_offset_ + i;
-
- if (fast_elements()) {
- 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();
- // Fall-through to dictionary mode.
- }
- DCHECK(!fast_elements());
- Handle<SeededNumberDictionary> dict(
- SeededNumberDictionary::cast(*storage_));
- // The object holding this backing store has just been allocated, so
- // it cannot yet be used as a prototype.
- Handle<SeededNumberDictionary> result =
- SeededNumberDictionary::AtNumberPut(dict, index, elm, false);
- if (!result.is_identical_to(dict)) {
- // Dictionary needed to grow.
- clear_storage();
- set_storage(*result);
- }
- }
-
- void increase_index_offset(uint32_t delta) {
- if (JSObject::kMaxElementCount - index_offset_ < delta) {
- index_offset_ = JSObject::kMaxElementCount;
- } else {
- index_offset_ += delta;
- }
- // If the initial length estimate was off (see special case in visit()),
- // but the array blowing the limit didn't contain elements beyond the
- // provided-for index range, go to dictionary mode now.
- if (fast_elements() &&
- index_offset_ >
- static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) {
- SetDictionaryMode();
- }
- }
-
- bool exceeds_array_limit() const {
- return ExceedsLimitField::decode(bit_field_);
- }
-
- Handle<JSArray> ToArray() {
- Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
- Handle<Object> length =
- isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
- Handle<Map> map = JSObject::GetElementsTransitionMap(
- array, fast_elements() ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS);
- array->set_map(*map);
- array->set_length(*length);
- array->set_elements(*storage_);
- return array;
- }
-
- private:
- // Convert storage to dictionary mode.
- void SetDictionaryMode() {
- DCHECK(fast_elements());
- Handle<FixedArray> current_storage(*storage_);
- Handle<SeededNumberDictionary> slow_storage(
- SeededNumberDictionary::New(isolate_, 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(isolate_);
- Handle<Object> element(current_storage->get(i), isolate_);
- if (!element->IsTheHole()) {
- // The object holding this backing store has just been allocated, so
- // it cannot yet be used as a prototype.
- Handle<SeededNumberDictionary> new_storage =
- SeededNumberDictionary::AtNumberPut(slow_storage, i, element,
- false);
- if (!new_storage.is_identical_to(slow_storage)) {
- slow_storage = loop_scope.CloseAndEscape(new_storage);
- }
- }
- }
- clear_storage();
- set_storage(*slow_storage);
- set_fast_elements(false);
- }
-
- inline void clear_storage() {
- GlobalHandles::Destroy(Handle<Object>::cast(storage_).location());
- }
-
- inline void set_storage(FixedArray* storage) {
- storage_ =
- Handle<FixedArray>::cast(isolate_->global_handles()->Create(storage));
- }
-
- class FastElementsField : public BitField<bool, 0, 1> {};
- class ExceedsLimitField : public BitField<bool, 1, 1> {};
-
- bool fast_elements() const { return FastElementsField::decode(bit_field_); }
- void set_fast_elements(bool fast) {
- bit_field_ = FastElementsField::update(bit_field_, fast);
- }
- void set_exceeds_array_limit(bool exceeds) {
- bit_field_ = ExceedsLimitField::update(bit_field_, exceeds);
- }
-
- Isolate* isolate_;
- Handle<FixedArray> storage_; // Always a global handle.
- // Index after last seen index. Always less than or equal to
- // JSObject::kMaxElementCount.
- uint32_t index_offset_;
- uint32_t bit_field_;
-};
-
-
-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 FAST_SMI_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_HOLEY_ELEMENTS: {
- // Fast elements can't have lengths that are not representable by
- // a 32-bit signed integer.
- DCHECK(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 FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS: {
- // Fast elements can't have lengths that are not representable by
- // a 32-bit signed integer.
- DCHECK(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0);
- int fast_length = static_cast<int>(length);
- if (array->elements()->IsFixedArray()) {
- DCHECK(FixedArray::cast(array->elements())->length() == 0);
- break;
- }
- Handle<FixedDoubleArray> elements(
- FixedDoubleArray::cast(array->elements()));
- for (int i = 0; i < fast_length; i++) {
- if (!elements->is_the_hole(i)) element_count++;
- }
- break;
- }
- case DICTIONARY_ELEMENTS: {
- Handle<SeededNumberDictionary> dictionary(
- SeededNumberDictionary::cast(array->elements()));
- int capacity = dictionary->Capacity();
- for (int i = 0; i < capacity; i++) {
- Handle<Object> key(dictionary->KeyAt(i), array->GetIsolate());
- if (dictionary->IsKey(*key)) {
- element_count++;
- }
- }
- break;
- }
- case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
- case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
-#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case TYPE##_ELEMENTS:
-
- TYPED_ARRAYS(TYPED_ARRAY_CASE)
-#undef TYPED_ARRAY_CASE
- // 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 void IterateTypedArrayElements(Isolate* isolate,
- Handle<JSObject> receiver,
- bool elements_are_ints,
- bool elements_are_guaranteed_smis,
- ArrayConcatVisitor* visitor) {
- Handle<ExternalArrayClass> array(
- ExternalArrayClass::cast(receiver->elements()));
- uint32_t len = static_cast<uint32_t>(array->length());
-
- DCHECK(visitor != NULL);
- if (elements_are_ints) {
- if (elements_are_guaranteed_smis) {
- for (uint32_t j = 0; j < len; j++) {
- HandleScope loop_scope(isolate);
- Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get_scalar(j))),
- isolate);
- visitor->visit(j, e);
- }
- } else {
- for (uint32_t j = 0; j < len; j++) {
- HandleScope loop_scope(isolate);
- int64_t val = static_cast<int64_t>(array->get_scalar(j));
- if (Smi::IsValid(static_cast<intptr_t>(val))) {
- Handle<Smi> e(Smi::FromInt(static_cast<int>(val)), isolate);
- visitor->visit(j, e);
- } else {
- Handle<Object> e =
- isolate->factory()->NewNumber(static_cast<ElementType>(val));
- visitor->visit(j, e);
- }
- }
- }
- } else {
- for (uint32_t j = 0; j < len; j++) {
- HandleScope loop_scope(isolate);
- Handle<Object> e = isolate->factory()->NewNumber(array->get_scalar(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) {
- Isolate* isolate = object->GetIsolate();
- ElementsKind kind = object->GetElementsKind();
- switch (kind) {
- case FAST_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_HOLEY_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 FAST_HOLEY_DOUBLE_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS: {
- if (object->elements()->IsFixedArray()) {
- DCHECK(object->elements()->length() == 0);
- break;
- }
- Handle<FixedDoubleArray> elements(
- FixedDoubleArray::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->is_the_hole(i)) {
- indices->Add(i);
- }
- }
- break;
- }
- case DICTIONARY_ELEMENTS: {
- Handle<SeededNumberDictionary> dict(
- SeededNumberDictionary::cast(object->elements()));
- uint32_t capacity = dict->Capacity();
- for (uint32_t j = 0; j < capacity; j++) {
- HandleScope loop_scope(isolate);
- Handle<Object> k(dict->KeyAt(j), isolate);
- if (dict->IsKey(*k)) {
- DCHECK(k->IsNumber());
- uint32_t index = static_cast<uint32_t>(k->Number());
- if (index < range) {
- indices->Add(index);
- }
- }
- }
- break;
- }
-#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
- case TYPE##_ELEMENTS: \
-
- TYPED_ARRAYS(TYPED_ARRAY_CASE)
-#undef TYPED_ARRAY_CASE
- {
- uint32_t length = static_cast<uint32_t>(
- FixedArrayBase::cast(object->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;
- }
- case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
- case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
- ElementsAccessor* accessor = object->GetElementsAccessor();
- for (uint32_t i = 0; i < range; i++) {
- if (accessor->HasElement(object, i)) {
- indices->Add(i);
- }
- }
- break;
- }
- }
-
- PrototypeIterator iter(isolate, object);
- if (!iter.IsAtEnd()) {
- // The prototype will usually have no inherited element indices,
- // but we have to check.
- CollectElementIndices(
- Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), range,
- indices);
- }
-}
-
-
-static bool IterateElementsSlow(Isolate* isolate, Handle<JSObject> receiver,
- uint32_t length, ArrayConcatVisitor* visitor) {
- for (uint32_t i = 0; i < length; ++i) {
- HandleScope loop_scope(isolate);
- Maybe<bool> maybe = JSReceiver::HasElement(receiver, i);
- if (!maybe.IsJust()) return false;
- if (maybe.FromJust()) {
- Handle<Object> element_value;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_value,
- Object::GetElement(isolate, receiver, i),
- false);
- visitor->visit(i, element_value);
- }
- }
- visitor->increase_index_offset(length);
- return true;
-}
-
-
-/**
- * A helper function that visits elements of a JSObject in numerical
- * order.
- *
- * 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.
- * Returns false if any access threw an exception, otherwise true.
- */
-static bool IterateElements(Isolate* isolate, Handle<JSObject> receiver,
- ArrayConcatVisitor* visitor) {
- uint32_t length = 0;
-
- if (receiver->IsJSArray()) {
- Handle<JSArray> array(Handle<JSArray>::cast(receiver));
- length = static_cast<uint32_t>(array->length()->Number());
- } else {
- Handle<Object> val;
- Handle<Object> key(isolate->heap()->length_string(), isolate);
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val,
- Runtime::GetObjectProperty(isolate, receiver, key), false);
- // TODO(caitp): Support larger element indexes (up to 2^53-1).
- if (!val->ToUint32(&length)) {
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val,
- Execution::ToLength(isolate, val), false);
- val->ToUint32(&length);
- }
- }
-
- if (!(receiver->IsJSArray() || receiver->IsJSTypedArray())) {
- // For classes which are not known to be safe to access via elements alone,
- // use the slow case.
- return IterateElementsSlow(isolate, receiver, length, visitor);
- }
-
- switch (receiver->GetElementsKind()) {
- case FAST_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_HOLEY_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()));
- int fast_length = static_cast<int>(length);
- DCHECK(fast_length <= elements->length());
- for (int j = 0; j < fast_length; j++) {
- HandleScope loop_scope(isolate);
- Handle<Object> element_value(elements->get(j), isolate);
- if (!element_value->IsTheHole()) {
- visitor->visit(j, element_value);
- } else {
- Maybe<bool> maybe = JSReceiver::HasElement(receiver, j);
- if (!maybe.IsJust()) return false;
- if (maybe.FromJust()) {
- // Call GetElement on receiver, not its prototype, or getters won't
- // have the correct receiver.
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element_value,
- Object::GetElement(isolate, receiver, j), false);
- visitor->visit(j, element_value);
- }
- }
- }
- break;
- }
- case FAST_HOLEY_DOUBLE_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS: {
- // Empty array is FixedArray but not FixedDoubleArray.
- if (length == 0) break;
- // Run through the elements FixedArray and use HasElement and GetElement
- // to check the prototype for missing elements.
- if (receiver->elements()->IsFixedArray()) {
- DCHECK(receiver->elements()->length() == 0);
- break;
- }
- Handle<FixedDoubleArray> elements(
- FixedDoubleArray::cast(receiver->elements()));
- int fast_length = static_cast<int>(length);
- DCHECK(fast_length <= elements->length());
- for (int j = 0; j < fast_length; j++) {
- HandleScope loop_scope(isolate);
- if (!elements->is_the_hole(j)) {
- double double_value = elements->get_scalar(j);
- Handle<Object> element_value =
- isolate->factory()->NewNumber(double_value);
- visitor->visit(j, element_value);
- } else {
- Maybe<bool> maybe = JSReceiver::HasElement(receiver, j);
- if (!maybe.IsJust()) return false;
- if (maybe.FromJust()) {
- // Call GetElement on receiver, not its prototype, or getters won't
- // have the correct receiver.
- Handle<Object> element_value;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element_value,
- Object::GetElement(isolate, receiver, j), false);
- visitor->visit(j, element_value);
- }
- }
- }
- break;
- }
- case DICTIONARY_ELEMENTS: {
- Handle<SeededNumberDictionary> 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(isolate);
- uint32_t index = indices[j];
- Handle<Object> element;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element, Object::GetElement(isolate, receiver, index),
- false);
- visitor->visit(index, element);
- // Skip to next different index (i.e., omit duplicates).
- do {
- j++;
- } while (j < n && indices[j] == index);
- }
- break;
- }
- case UINT8_CLAMPED_ELEMENTS: {
- Handle<FixedUint8ClampedArray> pixels(
- FixedUint8ClampedArray::cast(receiver->elements()));
- for (uint32_t j = 0; j < length; j++) {
- Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate);
- visitor->visit(j, e);
- }
- break;
- }
- case INT8_ELEMENTS: {
- IterateTypedArrayElements<FixedInt8Array, int8_t>(
- isolate, receiver, true, true, visitor);
- break;
- }
- case UINT8_ELEMENTS: {
- IterateTypedArrayElements<FixedUint8Array, uint8_t>(
- isolate, receiver, true, true, visitor);
- break;
- }
- case INT16_ELEMENTS: {
- IterateTypedArrayElements<FixedInt16Array, int16_t>(
- isolate, receiver, true, true, visitor);
- break;
- }
- case UINT16_ELEMENTS: {
- IterateTypedArrayElements<FixedUint16Array, uint16_t>(
- isolate, receiver, true, true, visitor);
- break;
- }
- case INT32_ELEMENTS: {
- IterateTypedArrayElements<FixedInt32Array, int32_t>(
- isolate, receiver, true, false, visitor);
- break;
- }
- case UINT32_ELEMENTS: {
- IterateTypedArrayElements<FixedUint32Array, uint32_t>(
- isolate, receiver, true, false, visitor);
- break;
- }
- case FLOAT32_ELEMENTS: {
- IterateTypedArrayElements<FixedFloat32Array, float>(
- isolate, receiver, false, false, visitor);
- break;
- }
- case FLOAT64_ELEMENTS: {
- IterateTypedArrayElements<FixedFloat64Array, double>(
- isolate, receiver, false, false, visitor);
- break;
- }
- case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
- case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
- for (uint32_t index = 0; index < length; index++) {
- HandleScope loop_scope(isolate);
- Handle<Object> element;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate, element, Object::GetElement(isolate, receiver, index),
- false);
- visitor->visit(index, element);
- }
- break;
- }
- }
- visitor->increase_index_offset(length);
- return true;
-}
-
-
-static bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
- HandleScope handle_scope(isolate);
- if (!obj->IsSpecObject()) return false;
- if (FLAG_harmony_concat_spreadable) {
- Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol());
- Handle<Object> value;
- MaybeHandle<Object> maybeValue =
- i::Runtime::GetObjectProperty(isolate, obj, key);
- if (maybeValue.ToHandle(&value)) {
- if (!value->IsUndefined()) {
- return value->BooleanValue();
- }
- }
- }
- return obj->IsJSArray();
-}
-
-
-/**
- * Array::concat implementation.
- * See ECMAScript 262, 15.4.4.4.
- * TODO(581): Fix non-compliance for very large concatenations and update to
- * following the ECMAScript 5 specification.
- */
-RUNTIME_FUNCTION(Runtime_ArrayConcat) {
- HandleScope handle_scope(isolate);
- DCHECK(args.length() == 1);
-
- CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0);
- int argument_count = static_cast<int>(arguments->length()->Number());
- RUNTIME_ASSERT(arguments->HasFastObjectElements());
- 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.
-
- ElementsKind kind = FAST_SMI_ELEMENTS;
-
- uint32_t estimate_result_length = 0;
- uint32_t estimate_nof_elements = 0;
- for (int i = 0; i < argument_count; i++) {
- HandleScope loop_scope(isolate);
- Handle<Object> obj(elements->get(i), isolate);
- 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());
- if (length_estimate != 0) {
- kind = GetMoreGeneralElementsKind(
- kind, GetPackedElementsKind(array->map()->elements_kind()));
- }
- element_estimate = EstimateElementCount(array);
- } else {
- if (obj->IsHeapObject()) {
- if (obj->IsNumber()) {
- kind = GetMoreGeneralElementsKind(kind, FAST_DOUBLE_ELEMENTS);
- } else {
- kind = GetMoreGeneralElementsKind(kind, FAST_ELEMENTS);
- }
- }
- 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;
- }
- }
-
- // 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) >= estimate_result_length;
-
- if (fast_case && kind == FAST_DOUBLE_ELEMENTS) {
- Handle<FixedArrayBase> storage =
- isolate->factory()->NewFixedDoubleArray(estimate_result_length);
- int j = 0;
- bool failure = false;
- if (estimate_result_length > 0) {
- Handle<FixedDoubleArray> double_storage =
- Handle<FixedDoubleArray>::cast(storage);
- for (int i = 0; i < argument_count; i++) {
- Handle<Object> obj(elements->get(i), isolate);
- if (obj->IsSmi()) {
- double_storage->set(j, Smi::cast(*obj)->value());
- j++;
- } else if (obj->IsNumber()) {
- double_storage->set(j, obj->Number());
- j++;
- } else {
- JSArray* array = JSArray::cast(*obj);
- uint32_t length = static_cast<uint32_t>(array->length()->Number());
- switch (array->map()->elements_kind()) {
- case FAST_HOLEY_DOUBLE_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS: {
- // Empty array is FixedArray but not FixedDoubleArray.
- if (length == 0) break;
- FixedDoubleArray* elements =
- FixedDoubleArray::cast(array->elements());
- for (uint32_t i = 0; i < length; i++) {
- if (elements->is_the_hole(i)) {
- // TODO(jkummerow/verwaest): We could be a bit more clever
- // here: Check if there are no elements/getters on the
- // prototype chain, and if so, allow creation of a holey
- // result array.
- // Same thing below (holey smi case).
- failure = true;
- break;
- }
- double double_value = elements->get_scalar(i);
- double_storage->set(j, double_value);
- j++;
- }
- break;
- }
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_SMI_ELEMENTS: {
- FixedArray* elements(FixedArray::cast(array->elements()));
- for (uint32_t i = 0; i < length; i++) {
- Object* element = elements->get(i);
- if (element->IsTheHole()) {
- failure = true;
- break;
- }
- int32_t int_value = Smi::cast(element)->value();
- double_storage->set(j, int_value);
- j++;
- }
- break;
- }
- case FAST_HOLEY_ELEMENTS:
- case FAST_ELEMENTS:
- case DICTIONARY_ELEMENTS:
- DCHECK_EQ(0u, length);
- break;
- default:
- UNREACHABLE();
- }
- }
- if (failure) break;
- }
- }
- if (!failure) {
- Handle<JSArray> array = isolate->factory()->NewJSArray(0);
- Smi* length = Smi::FromInt(j);
- Handle<Map> map;
- map = JSObject::GetElementsTransitionMap(array, kind);
- array->set_map(*map);
- array->set_length(length);
- array->set_elements(*storage);
- return *array;
- }
- // In case of failure, fall through.
- }
-
- Handle<FixedArray> storage;
- if (fast_case) {
- // The backing storage array must have non-existing elements to preserve
- // holes across concat operations.
- storage =
- isolate->factory()->NewFixedArrayWithHoles(estimate_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(
- SeededNumberDictionary::New(isolate, at_least_space_for));
- }
-
- ArrayConcatVisitor visitor(isolate, storage, fast_case);
-
- for (int i = 0; i < argument_count; i++) {
- Handle<Object> obj(elements->get(i), isolate);
- bool spreadable = IsConcatSpreadable(isolate, obj);
- if (isolate->has_pending_exception()) return isolate->heap()->exception();
- if (spreadable) {
- Handle<JSObject> object = Handle<JSObject>::cast(obj);
- if (!IterateElements(isolate, object, &visitor)) {
- return isolate->heap()->exception();
- }
- } else {
- visitor.visit(0, obj);
- visitor.increase_index_offset(1);
- }
- }
-
- if (visitor.exceeds_array_limit()) {
- THROW_NEW_ERROR_RETURN_FAILURE(
- isolate, NewRangeError(MessageTemplate::kInvalidArrayLength));
- }
- return *visitor.ToArray();
-}
-
-
// Moves all own elements of an object, that are below a limit, to positions
// starting at zero. All undefined values are placed after non-undefined values,
// and are followed by non-existing element. Does not change the length
« no previous file with comments | « src/runtime/runtime.h ('k') | test/mjsunit/array-natives-elements.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698