Chromium Code Reviews| Index: src/builtins.cc | 
| diff --git a/src/builtins.cc b/src/builtins.cc | 
| index 620e4b36ce59fbef40713254c852c5220d68f086..4803fef8f52d0986b40b1f4438f5b8f07fa2fa87 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_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()) { | 
| @@ -578,17 +672,29 @@ BUILTIN(ArrayPop) { | 
| if (len == 0) return heap->undefined_value(); | 
| // Get top element | 
| 
 
danno
2012/11/15 10:34:28
Simplify both cases to (if performance allows)
El
 
 | 
| - Object* top = elms->get(len - 1); | 
| - | 
| + if (elms_obj->IsFixedArray()) { | 
| + FixedArray* elms = FixedArray::cast(elms_obj); | 
| + MaybeObject* top = elms->get(len - 1); | 
| + if (!top->IsTheHole()) { | 
| + // Delete the top element. | 
| + elms->set_the_hole(len - 1); | 
| + // Set the length. | 
| + array->set_length(Smi::FromInt(len - 1)); | 
| + return top; | 
| + } | 
| + } else { | 
| + FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj); | 
| + if (!elms->is_the_hole(len - 1)) { | 
| + MaybeObject* top = heap->AllocateHeapNumber(elms->get_scalar(len - 1)); | 
| + if (top->IsFailure()) return top; | 
| + elms->set_the_hole(len - 1); | 
| + // Set the length. | 
| + array->set_length(Smi::FromInt(len - 1)); | 
| + return top; | 
| + } | 
| + } | 
| // 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; | 
| - } | 
| - | 
| return array->GetPrototype()->GetElement(len - 1); | 
| } | 
| @@ -596,19 +702,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 +722,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 +753,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 +788,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,17 +823,22 @@ 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 | 
| // than 50% of invocations in Web apps). Treat it in C++ as well. | 
| @@ -724,15 +846,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 +866,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 +899,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 +912,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 +932,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 +955,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 +981,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, "ArraySlice", args); | 
| + } | 
| + relative_start = static_cast<int>(start); | 
| } else if (!arg1->IsUndefined()) { | 
| return CallJsBuiltin(isolate, "ArraySplice", args); | 
| } | 
| @@ -858,51 +1016,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 +1101,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 +1132,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 +1178,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 +1191,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 +1216,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; | 
| } |