| Index: src/builtins.cc
|
| diff --git a/src/builtins.cc b/src/builtins.cc
|
| index 620e4b36ce59fbef40713254c852c5220d68f086..6617fe1ad9738b80c72912c9984504a4d01ff3ce 100644
|
| --- a/src/builtins.cc
|
| +++ b/src/builtins.cc
|
| @@ -325,6 +325,18 @@ BUILTIN(ArrayCodeGeneric) {
|
| }
|
|
|
|
|
| +static void MoveDoubleElements(FixedDoubleArray* dst,
|
| + int dst_index,
|
| + FixedDoubleArray* src,
|
| + int src_index,
|
| + int len) {
|
| + if (len == 0) return;
|
| + memmove(dst->data_start() + dst_index,
|
| + src->data_start() + src_index,
|
| + len * kDoubleSize);
|
| +}
|
| +
|
| +
|
| static void MoveElements(Heap* heap,
|
| AssertNoAllocation* no_gc,
|
| FixedArray* dst,
|
| @@ -351,24 +363,39 @@ static void FillWithHoles(Heap* heap, FixedArray* dst, int from, int to) {
|
| }
|
|
|
|
|
| -static FixedArray* LeftTrimFixedArray(Heap* heap,
|
| - FixedArray* elms,
|
| - int to_trim) {
|
| +static void FillWithHoles(FixedDoubleArray* dst, int from, int to) {
|
| + for (int i = from; i < to; i++) {
|
| + dst->set_the_hole(i);
|
| + }
|
| +}
|
| +
|
| +
|
| +static FixedArrayBase* LeftTrimFixedArray(Heap* heap,
|
| + FixedArrayBase* elms,
|
| + int to_trim) {
|
| + Map* map = elms->map();
|
| + int entry_size;
|
| + if (elms->IsFixedArray()) {
|
| + entry_size = kPointerSize;
|
| + } else {
|
| + entry_size = kDoubleSize;
|
| + }
|
| ASSERT(elms->map() != HEAP->fixed_cow_array_map());
|
| // For now this trick is only applied to fixed arrays in new and paged space.
|
| // In large object space the object's start must coincide with chunk
|
| // and thus the trick is just not applicable.
|
| ASSERT(!HEAP->lo_space()->Contains(elms));
|
|
|
| - STATIC_ASSERT(FixedArray::kMapOffset == 0);
|
| - STATIC_ASSERT(FixedArray::kLengthOffset == kPointerSize);
|
| - STATIC_ASSERT(FixedArray::kHeaderSize == 2 * kPointerSize);
|
| + STATIC_ASSERT(FixedArrayBase::kMapOffset == 0);
|
| + STATIC_ASSERT(FixedArrayBase::kLengthOffset == kPointerSize);
|
| + STATIC_ASSERT(FixedArrayBase::kHeaderSize == 2 * kPointerSize);
|
|
|
| Object** former_start = HeapObject::RawField(elms, 0);
|
|
|
| const int len = elms->length();
|
|
|
| - if (to_trim > FixedArray::kHeaderSize / kPointerSize &&
|
| + if (to_trim > FixedArrayBase::kHeaderSize / entry_size &&
|
| + elms->IsFixedArray() &&
|
| !heap->new_space()->Contains(elms)) {
|
| // If we are doing a big trim in old space then we zap the space that was
|
| // formerly part of the array so that the GC (aided by the card-based
|
| @@ -382,14 +409,15 @@ static FixedArray* LeftTrimFixedArray(Heap* heap,
|
| // Technically in new space this write might be omitted (except for
|
| // debug mode which iterates through the heap), but to play safer
|
| // we still do it.
|
| - heap->CreateFillerObjectAt(elms->address(), to_trim * kPointerSize);
|
| + heap->CreateFillerObjectAt(elms->address(), to_trim * entry_size);
|
|
|
| - former_start[to_trim] = heap->fixed_array_map();
|
| - former_start[to_trim + 1] = Smi::FromInt(len - to_trim);
|
| + int new_start_index = to_trim * (entry_size / kPointerSize);
|
| + former_start[new_start_index] = map;
|
| + former_start[new_start_index + 1] = Smi::FromInt(len - to_trim);
|
|
|
| // Maintain marking consistency for HeapObjectIterator and
|
| // IncrementalMarking.
|
| - int size_delta = to_trim * kPointerSize;
|
| + int size_delta = to_trim * entry_size;
|
| if (heap->marking()->TransferMark(elms->address(),
|
| elms->address() + size_delta)) {
|
| MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta);
|
| @@ -397,8 +425,8 @@ static FixedArray* LeftTrimFixedArray(Heap* heap,
|
|
|
| HEAP_PROFILE(heap, ObjectMoveEvent(elms->address(),
|
| elms->address() + size_delta));
|
| - return FixedArray::cast(HeapObject::FromAddress(
|
| - elms->address() + to_trim * kPointerSize));
|
| + return FixedArrayBase::cast(HeapObject::FromAddress(
|
| + elms->address() + to_trim * entry_size));
|
| }
|
|
|
|
|
| @@ -427,19 +455,14 @@ static inline MaybeObject* EnsureJSArrayWithWritableFastElements(
|
| Map* map = elms->map();
|
| if (map == heap->fixed_array_map()) {
|
| if (args == NULL || array->HasFastObjectElements()) return elms;
|
| - if (array->HasFastDoubleElements()) {
|
| - ASSERT(elms == heap->empty_fixed_array());
|
| - MaybeObject* maybe_transition =
|
| - array->TransitionElementsKind(FAST_ELEMENTS);
|
| - if (maybe_transition->IsFailure()) return maybe_transition;
|
| - return elms;
|
| - }
|
| } else if (map == heap->fixed_cow_array_map()) {
|
| MaybeObject* maybe_writable_result = array->EnsureWritableFastElements();
|
| if (args == NULL || array->HasFastObjectElements() ||
|
| - maybe_writable_result->IsFailure()) {
|
| + !maybe_writable_result->To(&elms)) {
|
| return maybe_writable_result;
|
| }
|
| + } else if (map == heap->fixed_double_array_map()) {
|
| + if (args == NULL) return elms;
|
| } else {
|
| return NULL;
|
| }
|
| @@ -449,13 +472,28 @@ static inline MaybeObject* EnsureJSArrayWithWritableFastElements(
|
| int args_length = args->length();
|
| if (first_added_arg >= args_length) return array->elements();
|
|
|
| - MaybeObject* maybe_array = array->EnsureCanContainElements(
|
| - args,
|
| - first_added_arg,
|
| - args_length - first_added_arg,
|
| - DONT_ALLOW_DOUBLE_ELEMENTS);
|
| - if (maybe_array->IsFailure()) return maybe_array;
|
| - return array->elements();
|
| + ElementsKind origin_kind = array->map()->elements_kind();
|
| + ASSERT(!IsFastObjectElementsKind(origin_kind));
|
| + ElementsKind target_kind = origin_kind;
|
| + int arg_count = args->length() - first_added_arg;
|
| + Object** arguments = args->arguments() - first_added_arg - (arg_count - 1);
|
| + for (int i = 0; i < arg_count; i++) {
|
| + Object* arg = arguments[i];
|
| + if (arg->IsHeapObject()) {
|
| + if (arg->IsHeapNumber()) {
|
| + target_kind = FAST_DOUBLE_ELEMENTS;
|
| + } else {
|
| + target_kind = FAST_ELEMENTS;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + if (target_kind != origin_kind) {
|
| + MaybeObject* maybe_failure = array->TransitionElementsKind(target_kind);
|
| + if (maybe_failure->IsFailure()) return maybe_failure;
|
| + return array->elements();
|
| + }
|
| + return elms;
|
| }
|
|
|
|
|
| @@ -499,75 +537,131 @@ MUST_USE_RESULT static MaybeObject* CallJsBuiltin(
|
| BUILTIN(ArrayPush) {
|
| Heap* heap = isolate->heap();
|
| Object* receiver = *args.receiver();
|
| - Object* elms_obj;
|
| - { MaybeObject* maybe_elms_obj =
|
| - EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 1);
|
| - if (maybe_elms_obj == NULL) {
|
| - return CallJsBuiltin(isolate, "ArrayPush", args);
|
| - }
|
| - if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
|
| + FixedArrayBase* elms_obj;
|
| + MaybeObject* maybe_elms_obj =
|
| + EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 1);
|
| + if (maybe_elms_obj == NULL) {
|
| + return CallJsBuiltin(isolate, "ArrayPush", args);
|
| }
|
| - FixedArray* elms = FixedArray::cast(elms_obj);
|
| - JSArray* array = JSArray::cast(receiver);
|
| + if (!maybe_elms_obj->To(&elms_obj)) return maybe_elms_obj;
|
|
|
| - if (FLAG_harmony_observation && array->map()->is_observed()) {
|
| + if (FLAG_harmony_observation &&
|
| + JSObject::cast(receiver)->map()->is_observed()) {
|
| return CallJsBuiltin(isolate, "ArrayPush", args);
|
| }
|
|
|
| - int len = Smi::cast(array->length())->value();
|
| - int to_add = args.length() - 1;
|
| - if (to_add == 0) {
|
| - return Smi::FromInt(len);
|
| - }
|
| - // Currently fixed arrays cannot grow too big, so
|
| - // we should never hit this case.
|
| - ASSERT(to_add <= (Smi::kMaxValue - len));
|
| + JSArray* array = JSArray::cast(receiver);
|
| + ElementsKind kind = array->GetElementsKind();
|
|
|
| - int new_length = len + to_add;
|
| + if (IsFastSmiOrObjectElementsKind(kind)) {
|
| + FixedArray* elms = FixedArray::cast(elms_obj);
|
|
|
| - if (new_length > elms->length()) {
|
| - // New backing storage is needed.
|
| - int capacity = new_length + (new_length >> 1) + 16;
|
| - Object* obj;
|
| - { MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
|
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| + int len = Smi::cast(array->length())->value();
|
| + int to_add = args.length() - 1;
|
| + if (to_add == 0) {
|
| + return Smi::FromInt(len);
|
| }
|
| - FixedArray* new_elms = FixedArray::cast(obj);
|
| + // Currently fixed arrays cannot grow too big, so
|
| + // we should never hit this case.
|
| + ASSERT(to_add <= (Smi::kMaxValue - len));
|
|
|
| - ElementsKind kind = array->GetElementsKind();
|
| - CopyObjectToObjectElements(elms, kind, 0, new_elms, kind, 0, len);
|
| - FillWithHoles(heap, new_elms, new_length, capacity);
|
| + int new_length = len + to_add;
|
|
|
| - elms = new_elms;
|
| - }
|
| + if (new_length > elms->length()) {
|
| + // New backing storage is needed.
|
| + int capacity = new_length + (new_length >> 1) + 16;
|
| + FixedArray* new_elms;
|
| + MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
|
| + if (!maybe_obj->To(&new_elms)) return maybe_obj;
|
| +
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| + MaybeObject* maybe_failure =
|
| + accessor->CopyElements(array, 0, new_elms, kind, 0, len, elms_obj);
|
| + ASSERT(!maybe_failure->IsFailure());
|
| + USE(maybe_failure);
|
| + FillWithHoles(heap, new_elms, new_length, capacity);
|
|
|
| - // Add the provided values.
|
| - AssertNoAllocation no_gc;
|
| - WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
|
| - for (int index = 0; index < to_add; index++) {
|
| - elms->set(index + len, args[index + 1], mode);
|
| - }
|
| + elms = new_elms;
|
| + }
|
|
|
| - if (elms != array->elements()) {
|
| - array->set_elements(elms);
|
| - }
|
| + // Add the provided values.
|
| + AssertNoAllocation no_gc;
|
| + WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
|
| + for (int index = 0; index < to_add; index++) {
|
| + elms->set(index + len, args[index + 1], mode);
|
| + }
|
|
|
| - // Set the length.
|
| - array->set_length(Smi::FromInt(new_length));
|
| - return Smi::FromInt(new_length);
|
| + if (elms != array->elements()) {
|
| + array->set_elements(elms);
|
| + }
|
| +
|
| + // Set the length.
|
| + array->set_length(Smi::FromInt(new_length));
|
| + return Smi::FromInt(new_length);
|
| + } else {
|
| + int len = Smi::cast(array->length())->value();
|
| + int elms_len = elms_obj->length();
|
| +
|
| + int to_add = args.length() - 1;
|
| + if (to_add == 0) {
|
| + return Smi::FromInt(len);
|
| + }
|
| + // Currently fixed arrays cannot grow too big, so
|
| + // we should never hit this case.
|
| + ASSERT(to_add <= (Smi::kMaxValue - len));
|
| +
|
| + int new_length = len + to_add;
|
| +
|
| + FixedDoubleArray* new_elms;
|
| +
|
| + if (new_length > elms_len) {
|
| + // New backing storage is needed.
|
| + int capacity = new_length + (new_length >> 1) + 16;
|
| + MaybeObject* maybe_obj =
|
| + heap->AllocateUninitializedFixedDoubleArray(capacity);
|
| + if (!maybe_obj->To(&new_elms)) return maybe_obj;
|
| +
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| + MaybeObject* maybe_failure =
|
| + accessor->CopyElements(array, 0, new_elms, kind, 0, len, elms_obj);
|
| + ASSERT(!maybe_failure->IsFailure());
|
| + USE(maybe_failure);
|
| +
|
| + FillWithHoles(new_elms, len + to_add, new_elms->length());
|
| + } else {
|
| + // to_add is > 0 and new_length <= elms_len, so elms_obj cannot be the
|
| + // empty_fixed_array.
|
| + new_elms = FixedDoubleArray::cast(elms_obj);
|
| + }
|
| +
|
| + // Add the provided values.
|
| + AssertNoAllocation no_gc;
|
| + int index;
|
| + for (index = 0; index < to_add; index++) {
|
| + Object* arg = args[index + 1];
|
| + new_elms->set(index + len, arg->Number());
|
| + }
|
| +
|
| + if (new_elms != array->elements()) {
|
| + array->set_elements(new_elms);
|
| + }
|
| +
|
| + // Set the length.
|
| + array->set_length(Smi::FromInt(new_length));
|
| + return Smi::FromInt(new_length);
|
| + }
|
| }
|
|
|
|
|
| BUILTIN(ArrayPop) {
|
| Heap* heap = isolate->heap();
|
| Object* receiver = *args.receiver();
|
| - Object* elms_obj;
|
| - { MaybeObject* maybe_elms_obj =
|
| - EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
|
| - if (maybe_elms_obj == NULL) return CallJsBuiltin(isolate, "ArrayPop", args);
|
| - if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
|
| - }
|
| - FixedArray* elms = FixedArray::cast(elms_obj);
|
| + FixedArrayBase* elms_obj;
|
| + MaybeObject* maybe_elms =
|
| + EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
|
| + if (maybe_elms == NULL) return CallJsBuiltin(isolate, "ArrayPop", args);
|
| + if (!maybe_elms->To(&elms_obj)) return maybe_elms;
|
| +
|
| JSArray* array = JSArray::cast(receiver);
|
|
|
| if (FLAG_harmony_observation && array->map()->is_observed()) {
|
| @@ -577,18 +671,15 @@ BUILTIN(ArrayPop) {
|
| int len = Smi::cast(array->length())->value();
|
| if (len == 0) return heap->undefined_value();
|
|
|
| - // Get top element
|
| - Object* top = elms->get(len - 1);
|
| -
|
| - // Set the length.
|
| - array->set_length(Smi::FromInt(len - 1));
|
| -
|
| - if (!top->IsTheHole()) {
|
| - // Delete the top element.
|
| - elms->set_the_hole(len - 1);
|
| - return top;
|
| - }
|
| -
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| + int new_length = len - 1;
|
| + Object* result;
|
| + MaybeObject* maybe_result = accessor->Get(array, array, new_length);
|
| + if (!maybe_result->To(&result)) return maybe_result;
|
| + MaybeObject* maybe_failure =
|
| + accessor->SetLength(array, Smi::FromInt(new_length));
|
| + if (maybe_failure->IsFailure()) return maybe_failure;
|
| + if (!result->IsTheHole()) return result;
|
| return array->GetPrototype()->GetElement(len - 1);
|
| }
|
|
|
| @@ -596,19 +687,17 @@ BUILTIN(ArrayPop) {
|
| BUILTIN(ArrayShift) {
|
| Heap* heap = isolate->heap();
|
| Object* receiver = *args.receiver();
|
| - Object* elms_obj;
|
| - { MaybeObject* maybe_elms_obj =
|
| - EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
|
| - if (maybe_elms_obj == NULL)
|
| - return CallJsBuiltin(isolate, "ArrayShift", args);
|
| - if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
|
| - }
|
| + FixedArrayBase* elms_obj;
|
| + MaybeObject* maybe_elms_obj =
|
| + EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
|
| + if (maybe_elms_obj == NULL)
|
| + return CallJsBuiltin(isolate, "ArrayShift", args);
|
| + if (!maybe_elms_obj->To(&elms_obj)) return maybe_elms_obj;
|
| +
|
| if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
|
| return CallJsBuiltin(isolate, "ArrayShift", args);
|
| }
|
| - FixedArray* elms = FixedArray::cast(elms_obj);
|
| JSArray* array = JSArray::cast(receiver);
|
| - ASSERT(array->HasFastSmiOrObjectElements());
|
|
|
| if (FLAG_harmony_observation && array->map()->is_observed()) {
|
| return CallJsBuiltin(isolate, "ArrayShift", args);
|
| @@ -618,18 +707,25 @@ BUILTIN(ArrayShift) {
|
| if (len == 0) return heap->undefined_value();
|
|
|
| // Get first element
|
| - Object* first = elms->get(0);
|
| - if (first->IsTheHole()) {
|
| - first = heap->undefined_value();
|
| - }
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| + Object* first;
|
| + MaybeObject* maybe_first = accessor->Get(receiver, array, 0, elms_obj);
|
| + if (!maybe_first->To(&first)) return maybe_first;
|
|
|
| - if (!heap->lo_space()->Contains(elms)) {
|
| - array->set_elements(LeftTrimFixedArray(heap, elms, 1));
|
| + if (!heap->lo_space()->Contains(elms_obj)) {
|
| + array->set_elements(LeftTrimFixedArray(heap, elms_obj, 1));
|
| } else {
|
| // Shift the elements.
|
| - AssertNoAllocation no_gc;
|
| - MoveElements(heap, &no_gc, elms, 0, elms, 1, len - 1);
|
| - elms->set(len - 1, heap->the_hole_value());
|
| + if (elms_obj->IsFixedArray()) {
|
| + FixedArray* elms = FixedArray::cast(elms_obj);
|
| + AssertNoAllocation no_gc;
|
| + MoveElements(heap, &no_gc, elms, 0, elms, 1, len - 1);
|
| + elms->set(len - 1, heap->the_hole_value());
|
| + } else {
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
|
| + MoveDoubleElements(elms, 0, elms, 1, len - 1);
|
| + elms->set_the_hole(len - 1);
|
| + }
|
| }
|
|
|
| // Set the length.
|
| @@ -642,19 +738,21 @@ BUILTIN(ArrayShift) {
|
| BUILTIN(ArrayUnshift) {
|
| Heap* heap = isolate->heap();
|
| Object* receiver = *args.receiver();
|
| - Object* elms_obj;
|
| - { MaybeObject* maybe_elms_obj =
|
| - EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
|
| - if (maybe_elms_obj == NULL)
|
| - return CallJsBuiltin(isolate, "ArrayUnshift", args);
|
| - if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
|
| - }
|
| + FixedArrayBase* elms_obj;
|
| + MaybeObject* maybe_elms_obj =
|
| + EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
|
| + if (maybe_elms_obj == NULL)
|
| + return CallJsBuiltin(isolate, "ArrayUnshift", args);
|
| + if (!maybe_elms_obj->To(&elms_obj)) return maybe_elms_obj;
|
| +
|
| if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
|
| return CallJsBuiltin(isolate, "ArrayUnshift", args);
|
| }
|
| - FixedArray* elms = FixedArray::cast(elms_obj);
|
| JSArray* array = JSArray::cast(receiver);
|
| - ASSERT(array->HasFastSmiOrObjectElements());
|
| + if (!array->HasFastSmiOrObjectElements()) {
|
| + return CallJsBuiltin(isolate, "ArrayUnshift", args);
|
| + }
|
| + FixedArray* elms = FixedArray::cast(elms_obj);
|
|
|
| if (FLAG_harmony_observation && array->map()->is_observed()) {
|
| return CallJsBuiltin(isolate, "ArrayUnshift", args);
|
| @@ -675,13 +773,17 @@ BUILTIN(ArrayUnshift) {
|
| if (new_length > elms->length()) {
|
| // New backing storage is needed.
|
| int capacity = new_length + (new_length >> 1) + 16;
|
| - Object* obj;
|
| - { MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
|
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| - }
|
| - FixedArray* new_elms = FixedArray::cast(obj);
|
| + FixedArray* new_elms;
|
| + MaybeObject* maybe_elms = heap->AllocateUninitializedFixedArray(capacity);
|
| + if (!maybe_elms->To(&new_elms)) return maybe_elms;
|
| +
|
| ElementsKind kind = array->GetElementsKind();
|
| - CopyObjectToObjectElements(elms, kind, 0, new_elms, kind, to_add, len);
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| + MaybeObject* maybe_failure =
|
| + accessor->CopyElements(array, 0, new_elms, kind, to_add, len, elms);
|
| + ASSERT(!maybe_failure->IsFailure());
|
| + USE(maybe_failure);
|
| +
|
| FillWithHoles(heap, new_elms, new_length, capacity);
|
| elms = new_elms;
|
| array->set_elements(elms);
|
| @@ -706,16 +808,20 @@ BUILTIN(ArrayUnshift) {
|
| BUILTIN(ArraySlice) {
|
| Heap* heap = isolate->heap();
|
| Object* receiver = *args.receiver();
|
| - FixedArray* elms;
|
| + FixedArrayBase* elms;
|
| int len = -1;
|
| if (receiver->IsJSArray()) {
|
| JSArray* array = JSArray::cast(receiver);
|
| - if (!array->HasFastSmiOrObjectElements() ||
|
| - !IsJSArrayFastElementMovingAllowed(heap, array)) {
|
| + if (!IsJSArrayFastElementMovingAllowed(heap, array)) {
|
| + return CallJsBuiltin(isolate, "ArraySlice", args);
|
| + }
|
| +
|
| + if (array->HasFastElements()) {
|
| + elms = array->elements();
|
| + } else {
|
| return CallJsBuiltin(isolate, "ArraySlice", args);
|
| }
|
|
|
| - elms = FixedArray::cast(array->elements());
|
| len = Smi::cast(array->length())->value();
|
| } else {
|
| // Array.slice(arguments, ...) is quite a common idiom (notably more
|
| @@ -724,15 +830,19 @@ BUILTIN(ArraySlice) {
|
| isolate->context()->native_context()->arguments_boilerplate()->map();
|
|
|
| bool is_arguments_object_with_fast_elements =
|
| - receiver->IsJSObject()
|
| - && JSObject::cast(receiver)->map() == arguments_map
|
| - && JSObject::cast(receiver)->HasFastSmiOrObjectElements();
|
| + receiver->IsJSObject() &&
|
| + JSObject::cast(receiver)->map() == arguments_map;
|
| if (!is_arguments_object_with_fast_elements) {
|
| return CallJsBuiltin(isolate, "ArraySlice", args);
|
| }
|
| - elms = FixedArray::cast(JSObject::cast(receiver)->elements());
|
| - Object* len_obj = JSObject::cast(receiver)
|
| - ->InObjectPropertyAt(Heap::kArgumentsLengthIndex);
|
| + JSObject* object = JSObject::cast(receiver);
|
| +
|
| + if (object->HasFastElements()) {
|
| + elms = object->elements();
|
| + } else {
|
| + return CallJsBuiltin(isolate, "ArraySlice", args);
|
| + }
|
| + Object* len_obj = object->InObjectPropertyAt(Heap::kArgumentsLengthIndex);
|
| if (!len_obj->IsSmi()) {
|
| return CallJsBuiltin(isolate, "ArraySlice", args);
|
| }
|
| @@ -740,12 +850,27 @@ BUILTIN(ArraySlice) {
|
| if (len > elms->length()) {
|
| return CallJsBuiltin(isolate, "ArraySlice", args);
|
| }
|
| + }
|
| +
|
| + JSObject* object = JSObject::cast(receiver);
|
| + ElementsKind kind = object->GetElementsKind();
|
| +
|
| + if (IsHoleyElementsKind(kind)) {
|
| + bool packed = true;
|
| + ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
|
| for (int i = 0; i < len; i++) {
|
| - if (elms->get(i) == heap->the_hole_value()) {
|
| - return CallJsBuiltin(isolate, "ArraySlice", args);
|
| + if (!accessor->HasElement(object, object, i, elms)) {
|
| + packed = false;
|
| + break;
|
| }
|
| }
|
| + if (packed) {
|
| + kind = GetPackedElementsKind(kind);
|
| + } else if (!receiver->IsJSArray()) {
|
| + return CallJsBuiltin(isolate, "ArraySlice", args);
|
| + }
|
| }
|
| +
|
| ASSERT(len >= 0);
|
| int n_arguments = args.length() - 1;
|
|
|
| @@ -758,6 +883,12 @@ BUILTIN(ArraySlice) {
|
| Object* arg1 = args[1];
|
| if (arg1->IsSmi()) {
|
| relative_start = Smi::cast(arg1)->value();
|
| + } else if (arg1->IsHeapNumber()) {
|
| + double start = HeapNumber::cast(arg1)->value();
|
| + if (start < kMinInt || start > kMaxInt) {
|
| + return CallJsBuiltin(isolate, "ArraySlice", args);
|
| + }
|
| + relative_start = static_cast<int>(start);
|
| } else if (!arg1->IsUndefined()) {
|
| return CallJsBuiltin(isolate, "ArraySlice", args);
|
| }
|
| @@ -765,6 +896,12 @@ BUILTIN(ArraySlice) {
|
| Object* arg2 = args[2];
|
| if (arg2->IsSmi()) {
|
| relative_end = Smi::cast(arg2)->value();
|
| + } else if (arg2->IsHeapNumber()) {
|
| + double end = HeapNumber::cast(arg2)->value();
|
| + if (end < kMinInt || end > kMaxInt) {
|
| + return CallJsBuiltin(isolate, "ArraySlice", args);
|
| + }
|
| + relative_end = static_cast<int>(end);
|
| } else if (!arg2->IsUndefined()) {
|
| return CallJsBuiltin(isolate, "ArraySlice", args);
|
| }
|
| @@ -779,21 +916,21 @@ BUILTIN(ArraySlice) {
|
| int final = (relative_end < 0) ? Max(len + relative_end, 0)
|
| : Min(relative_end, len);
|
|
|
| - ElementsKind elements_kind = JSObject::cast(receiver)->GetElementsKind();
|
| -
|
| // Calculate the length of result array.
|
| int result_len = Max(final - k, 0);
|
|
|
| - MaybeObject* maybe_array =
|
| - heap->AllocateJSArrayAndStorage(elements_kind,
|
| - result_len,
|
| - result_len);
|
| JSArray* result_array;
|
| + MaybeObject* maybe_array = heap->AllocateJSArrayAndStorage(kind,
|
| + result_len,
|
| + result_len);
|
| if (!maybe_array->To(&result_array)) return maybe_array;
|
|
|
| - CopyObjectToObjectElements(elms, elements_kind, k,
|
| - FixedArray::cast(result_array->elements()),
|
| - elements_kind, 0, result_len);
|
| + ElementsAccessor* accessor = object->GetElementsAccessor();
|
| + MaybeObject* maybe_failure =
|
| + accessor->CopyElements(object, k, result_array->elements(),
|
| + kind, 0, result_len, elms);
|
| + ASSERT(!maybe_failure->IsFailure());
|
| + USE(maybe_failure);
|
|
|
| return result_array;
|
| }
|
| @@ -802,19 +939,18 @@ BUILTIN(ArraySlice) {
|
| BUILTIN(ArraySplice) {
|
| Heap* heap = isolate->heap();
|
| Object* receiver = *args.receiver();
|
| - Object* elms_obj;
|
| - { MaybeObject* maybe_elms_obj =
|
| - EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 3);
|
| - if (maybe_elms_obj == NULL)
|
| - return CallJsBuiltin(isolate, "ArraySplice", args);
|
| - if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
|
| + FixedArrayBase* elms_obj;
|
| + MaybeObject* maybe_elms =
|
| + EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 3);
|
| + if (maybe_elms == NULL) {
|
| + return CallJsBuiltin(isolate, "ArraySplice", args);
|
| }
|
| + if (!maybe_elms->To(&elms_obj)) return maybe_elms;
|
| +
|
| if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
|
| return CallJsBuiltin(isolate, "ArraySplice", args);
|
| }
|
| - FixedArray* elms = FixedArray::cast(elms_obj);
|
| JSArray* array = JSArray::cast(receiver);
|
| - ASSERT(array->HasFastSmiOrObjectElements());
|
|
|
| if (FLAG_harmony_observation && array->map()->is_observed()) {
|
| return CallJsBuiltin(isolate, "ArraySplice", args);
|
| @@ -829,6 +965,12 @@ BUILTIN(ArraySplice) {
|
| Object* arg1 = args[1];
|
| if (arg1->IsSmi()) {
|
| relative_start = Smi::cast(arg1)->value();
|
| + } else if (arg1->IsHeapNumber()) {
|
| + double start = HeapNumber::cast(arg1)->value();
|
| + if (start < kMinInt || start > kMaxInt) {
|
| + return CallJsBuiltin(isolate, "ArraySplice", args);
|
| + }
|
| + relative_start = static_cast<int>(start);
|
| } else if (!arg1->IsUndefined()) {
|
| return CallJsBuiltin(isolate, "ArraySplice", args);
|
| }
|
| @@ -858,51 +1000,83 @@ BUILTIN(ArraySplice) {
|
| actual_delete_count = Min(Max(value, 0), len - actual_start);
|
| }
|
|
|
| + ElementsKind elements_kind = array->GetElementsKind();
|
| +
|
| + int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
|
| + int new_length = len - actual_delete_count + item_count;
|
| +
|
| + // For double mode we do not support changing the length.
|
| + if (new_length > len && IsFastDoubleElementsKind(elements_kind)) {
|
| + return CallJsBuiltin(isolate, "ArraySplice", args);
|
| + }
|
| +
|
| + if (new_length == 0) {
|
| + MaybeObject* maybe_array = heap->AllocateJSArrayWithElements(
|
| + elms_obj, elements_kind, actual_delete_count);
|
| + if (maybe_array->IsFailure()) return maybe_array;
|
| + array->set_elements(heap->empty_fixed_array());
|
| + array->set_length(Smi::FromInt(0));
|
| + return maybe_array;
|
| + }
|
| +
|
| JSArray* result_array = NULL;
|
| - ElementsKind elements_kind =
|
| - JSObject::cast(receiver)->GetElementsKind();
|
| MaybeObject* maybe_array =
|
| heap->AllocateJSArrayAndStorage(elements_kind,
|
| actual_delete_count,
|
| actual_delete_count);
|
| if (!maybe_array->To(&result_array)) return maybe_array;
|
|
|
| - {
|
| - // Fill newly created array.
|
| - CopyObjectToObjectElements(elms, elements_kind, actual_start,
|
| - FixedArray::cast(result_array->elements()),
|
| - elements_kind, 0, actual_delete_count);
|
| + if (actual_delete_count > 0) {
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| + MaybeObject* maybe_failure =
|
| + accessor->CopyElements(array, actual_start, result_array->elements(),
|
| + elements_kind, 0, actual_delete_count, elms_obj);
|
| + // Cannot fail since the origin and target array are of the same elements
|
| + // kind.
|
| + ASSERT(!maybe_failure->IsFailure());
|
| + USE(maybe_failure);
|
| }
|
|
|
| - int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
|
| - int new_length = len - actual_delete_count + item_count;
|
| -
|
| bool elms_changed = false;
|
| if (item_count < actual_delete_count) {
|
| // Shrink the array.
|
| - const bool trim_array = !heap->lo_space()->Contains(elms) &&
|
| + const bool trim_array = !heap->lo_space()->Contains(elms_obj) &&
|
| ((actual_start + item_count) <
|
| (len - actual_delete_count - actual_start));
|
| if (trim_array) {
|
| const int delta = actual_delete_count - item_count;
|
|
|
| - {
|
| + if (elms_obj->IsFixedDoubleArray()) {
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
|
| + MoveDoubleElements(elms, delta, elms, 0, actual_start);
|
| + } else {
|
| + FixedArray* elms = FixedArray::cast(elms_obj);
|
| AssertNoAllocation no_gc;
|
| MoveElements(heap, &no_gc, elms, delta, elms, 0, actual_start);
|
| }
|
|
|
| - elms = LeftTrimFixedArray(heap, elms, delta);
|
| + elms_obj = LeftTrimFixedArray(heap, elms_obj, delta);
|
|
|
| elms_changed = true;
|
| } else {
|
| - AssertNoAllocation no_gc;
|
| - MoveElements(heap, &no_gc,
|
| - elms, actual_start + item_count,
|
| - elms, actual_start + actual_delete_count,
|
| - (len - actual_delete_count - actual_start));
|
| - FillWithHoles(heap, elms, new_length, len);
|
| + if (elms_obj->IsFixedDoubleArray()) {
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
|
| + MoveDoubleElements(elms, actual_start + item_count,
|
| + elms, actual_start + actual_delete_count,
|
| + (len - actual_delete_count - actual_start));
|
| + FillWithHoles(elms, new_length, len);
|
| + } else {
|
| + FixedArray* elms = FixedArray::cast(elms_obj);
|
| + AssertNoAllocation no_gc;
|
| + MoveElements(heap, &no_gc,
|
| + elms, actual_start + item_count,
|
| + elms, actual_start + actual_delete_count,
|
| + (len - actual_delete_count - actual_start));
|
| + FillWithHoles(heap, elms, new_length, len);
|
| + }
|
| }
|
| } else if (item_count > actual_delete_count) {
|
| + FixedArray* elms = FixedArray::cast(elms_obj);
|
| // Currently fixed arrays cannot grow too big, so
|
| // we should never hit this case.
|
| ASSERT((item_count - actual_delete_count) <= (Smi::kMaxValue - len));
|
| @@ -911,28 +1085,27 @@ BUILTIN(ArraySplice) {
|
| if (new_length > elms->length()) {
|
| // New backing storage is needed.
|
| int capacity = new_length + (new_length >> 1) + 16;
|
| - Object* obj;
|
| - { MaybeObject* maybe_obj =
|
| - heap->AllocateUninitializedFixedArray(capacity);
|
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| - }
|
| - FixedArray* new_elms = FixedArray::cast(obj);
|
| -
|
| - {
|
| - // Copy the part before actual_start as is.
|
| - ElementsKind kind = array->GetElementsKind();
|
| - CopyObjectToObjectElements(elms, kind, 0,
|
| - new_elms, kind, 0, actual_start);
|
| - const int to_copy = len - actual_delete_count - actual_start;
|
| - CopyObjectToObjectElements(elms, kind,
|
| - actual_start + actual_delete_count,
|
| - new_elms, kind,
|
| - actual_start + item_count, to_copy);
|
| - }
|
| + FixedArray* new_elms;
|
| + MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
|
| + if (!maybe_obj->To(&new_elms)) return maybe_obj;
|
| +
|
| + // Copy the part before actual_start as is.
|
| + ElementsKind kind = array->GetElementsKind();
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| + MaybeObject* maybe_failure = accessor->CopyElements(
|
| + array, 0, new_elms, kind, 0, actual_start, elms);
|
| + ASSERT(!maybe_failure->IsFailure());
|
| + USE(maybe_failure);
|
| + const int to_copy = len - actual_delete_count - actual_start;
|
| + maybe_failure = accessor->CopyElements(
|
| + array, actual_start + actual_delete_count, new_elms, kind,
|
| + actual_start + item_count, to_copy, elms);
|
| + ASSERT(!maybe_failure->IsFailure());
|
| + USE(maybe_failure);
|
|
|
| FillWithHoles(heap, new_elms, new_length, capacity);
|
|
|
| - elms = new_elms;
|
| + elms_obj = new_elms;
|
| elms_changed = true;
|
| } else {
|
| AssertNoAllocation no_gc;
|
| @@ -943,16 +1116,28 @@ BUILTIN(ArraySplice) {
|
| }
|
| }
|
|
|
| - AssertNoAllocation no_gc;
|
| - WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
|
| - for (int k = actual_start; k < actual_start + item_count; k++) {
|
| - elms->set(k, args[3 + k - actual_start], mode);
|
| + if (IsFastDoubleElementsKind(elements_kind)) {
|
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
|
| + for (int k = actual_start; k < actual_start + item_count; k++) {
|
| + Object* arg = args[3 + k - actual_start];
|
| + if (arg->IsSmi()) {
|
| + elms->set(k, Smi::cast(arg)->value());
|
| + } else {
|
| + elms->set(k, HeapNumber::cast(arg)->value());
|
| + }
|
| + }
|
| + } else {
|
| + FixedArray* elms = FixedArray::cast(elms_obj);
|
| + AssertNoAllocation no_gc;
|
| + WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
|
| + for (int k = actual_start; k < actual_start + item_count; k++) {
|
| + elms->set(k, args[3 + k - actual_start], mode);
|
| + }
|
| }
|
|
|
| if (elms_changed) {
|
| - array->set_elements(elms);
|
| + array->set_elements(elms_obj);
|
| }
|
| -
|
| // Set the length.
|
| array->set_length(Smi::FromInt(new_length));
|
|
|
| @@ -977,11 +1162,10 @@ BUILTIN(ArrayConcat) {
|
| for (int i = 0; i < n_arguments; i++) {
|
| Object* arg = args[i];
|
| if (!arg->IsJSArray() ||
|
| - !JSArray::cast(arg)->HasFastSmiOrObjectElements() ||
|
| + !JSArray::cast(arg)->HasFastElements() ||
|
| JSArray::cast(arg)->GetPrototype() != array_proto) {
|
| return CallJsBuiltin(isolate, "ArrayConcat", args);
|
| }
|
| -
|
| int len = Smi::cast(JSArray::cast(arg)->length())->value();
|
|
|
| // We shouldn't overflow when adding another len.
|
| @@ -991,27 +1175,24 @@ BUILTIN(ArrayConcat) {
|
| result_len += len;
|
| ASSERT(result_len >= 0);
|
|
|
| - if (result_len > FixedArray::kMaxLength) {
|
| + if (result_len > FixedDoubleArray::kMaxLength) {
|
| return CallJsBuiltin(isolate, "ArrayConcat", args);
|
| }
|
|
|
| - if (!JSArray::cast(arg)->HasFastSmiElements()) {
|
| - if (IsFastSmiElementsKind(elements_kind)) {
|
| - if (IsFastHoleyElementsKind(elements_kind)) {
|
| - elements_kind = FAST_HOLEY_ELEMENTS;
|
| - } else {
|
| - elements_kind = FAST_ELEMENTS;
|
| - }
|
| + ElementsKind arg_kind = JSArray::cast(arg)->map()->elements_kind();
|
| + ElementsKind packed_kind = GetPackedElementsKind(arg_kind);
|
| + if (IsMoreGeneralElementsKindTransition(
|
| + GetPackedElementsKind(elements_kind), packed_kind)) {
|
| + if (IsFastHoleyElementsKind(elements_kind)) {
|
| + elements_kind = GetHoleyElementsKind(arg_kind);
|
| + } else {
|
| + elements_kind = arg_kind;
|
| }
|
| }
|
| -
|
| - if (JSArray::cast(arg)->HasFastHoleyElements()) {
|
| - elements_kind = GetHoleyElementsKind(elements_kind);
|
| - }
|
| }
|
|
|
| - // Allocate result.
|
| JSArray* result_array;
|
| + // Allocate result.
|
| MaybeObject* maybe_array =
|
| heap->AllocateJSArrayAndStorage(elements_kind,
|
| result_len,
|
| @@ -1019,19 +1200,19 @@ BUILTIN(ArrayConcat) {
|
| if (!maybe_array->To(&result_array)) return maybe_array;
|
| if (result_len == 0) return result_array;
|
|
|
| - // Copy data.
|
| - int start_pos = 0;
|
| - FixedArray* result_elms(FixedArray::cast(result_array->elements()));
|
| + int j = 0;
|
| + FixedArrayBase* storage = result_array->elements();
|
| for (int i = 0; i < n_arguments; i++) {
|
| JSArray* array = JSArray::cast(args[i]);
|
| + ElementsAccessor* accessor = array->GetElementsAccessor();
|
| int len = Smi::cast(array->length())->value();
|
| - FixedArray* elms = FixedArray::cast(array->elements());
|
| - CopyObjectToObjectElements(elms, elements_kind, 0,
|
| - result_elms, elements_kind,
|
| - start_pos, len);
|
| - start_pos += len;
|
| + MaybeObject* maybe_failure =
|
| + accessor->CopyElements(array, 0, storage, elements_kind, j, len);
|
| + if (maybe_failure->IsFailure()) return maybe_failure;
|
| + j += len;
|
| }
|
| - ASSERT(start_pos == result_len);
|
| +
|
| + ASSERT(j == result_len);
|
|
|
| return result_array;
|
| }
|
|
|