| Index: src/builtins.cc
|
| ===================================================================
|
| --- src/builtins.cc (revision 3964)
|
| +++ src/builtins.cc (working copy)
|
| @@ -242,6 +242,109 @@
|
| }
|
|
|
|
|
| +static Object* AllocateJSArray() {
|
| + JSFunction* array_function =
|
| + Top::context()->global_context()->array_function();
|
| + Object* result = Heap::AllocateJSObject(array_function);
|
| + if (result->IsFailure()) return result;
|
| + return result;
|
| +}
|
| +
|
| +
|
| +static Object* AllocateEmptyJSArray() {
|
| + Object* result = AllocateJSArray();
|
| + if (result->IsFailure()) return result;
|
| + JSArray* result_array = JSArray::cast(result);
|
| + result_array->set_length(Smi::FromInt(0));
|
| + result_array->set_elements(Heap::empty_fixed_array());
|
| + return result_array;
|
| +}
|
| +
|
| +
|
| +static void CopyElements(AssertNoAllocation* no_gc,
|
| + FixedArray* dst,
|
| + int dst_index,
|
| + FixedArray* src,
|
| + int src_index,
|
| + int len) {
|
| + ASSERT(dst != src); // Use MoveElements instead.
|
| + memcpy(dst->data_start() + dst_index,
|
| + src->data_start() + src_index,
|
| + len * kPointerSize);
|
| + WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
|
| + if (mode == UPDATE_WRITE_BARRIER) {
|
| + Heap::RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
|
| + }
|
| +}
|
| +
|
| +
|
| +static void MoveElements(AssertNoAllocation* no_gc,
|
| + FixedArray* dst,
|
| + int dst_index,
|
| + FixedArray* src,
|
| + int src_index,
|
| + int len) {
|
| + memmove(dst->data_start() + dst_index,
|
| + src->data_start() + src_index,
|
| + len * kPointerSize);
|
| + WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
|
| + if (mode == UPDATE_WRITE_BARRIER) {
|
| + Heap::RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
|
| + }
|
| +}
|
| +
|
| +
|
| +static void FillWithHoles(FixedArray* dst, int from, int to) {
|
| + MemsetPointer(dst->data_start() + from, Heap::the_hole_value(), to - from);
|
| +}
|
| +
|
| +
|
| +static bool ArrayPrototypeHasNoElements() {
|
| + // This method depends on non writability of Object and Array prototype
|
| + // fields.
|
| + Context* global_context = Top::context()->global_context();
|
| + // Array.prototype
|
| + JSObject* proto =
|
| + JSObject::cast(global_context->array_function()->prototype());
|
| + if (proto->elements() != Heap::empty_fixed_array()) return false;
|
| + // Hidden prototype
|
| + proto = JSObject::cast(proto->GetPrototype());
|
| + ASSERT(proto->elements() == Heap::empty_fixed_array());
|
| + // Object.prototype
|
| + proto = JSObject::cast(proto->GetPrototype());
|
| + if (proto != global_context->initial_object_prototype()) return false;
|
| + if (proto->elements() != Heap::empty_fixed_array()) return false;
|
| + ASSERT(proto->GetPrototype()->IsNull());
|
| + return true;
|
| +}
|
| +
|
| +
|
| +static Object* CallJsBuiltin(const char* name,
|
| + BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
|
| + HandleScope handleScope;
|
| +
|
| + Handle<Object> js_builtin =
|
| + GetProperty(Handle<JSObject>(Top::global_context()->builtins()),
|
| + name);
|
| + ASSERT(js_builtin->IsJSFunction());
|
| + Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin));
|
| + Vector<Object**> argv(Vector<Object**>::New(args.length() - 1));
|
| + int n_args = args.length() - 1;
|
| + for (int i = 0; i < n_args; i++) {
|
| + argv[i] = &args[i + 1];
|
| + }
|
| + bool pending_exception = false;
|
| + Handle<Object> result = Execution::Call(function,
|
| + args.receiver(),
|
| + n_args,
|
| + argv.start(),
|
| + &pending_exception);
|
| + argv.Dispose();
|
| + if (pending_exception) return Failure::Exception();
|
| + return *result;
|
| +}
|
| +
|
| +
|
| BUILTIN(ArrayPush) {
|
| JSArray* array = JSArray::cast(*args.receiver());
|
| ASSERT(array->HasFastElements());
|
| @@ -261,22 +364,21 @@
|
| if (new_length > elms->length()) {
|
| // New backing storage is needed.
|
| int capacity = new_length + (new_length >> 1) + 16;
|
| - Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
|
| + Object* obj = Heap::AllocateUninitializedFixedArray(capacity);
|
| if (obj->IsFailure()) return obj;
|
| + FixedArray* new_elms = FixedArray::cast(obj);
|
|
|
| AssertNoAllocation no_gc;
|
| - FixedArray* new_elms = FixedArray::cast(obj);
|
| - WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
|
| - // Fill out the new array with old elements.
|
| - for (int i = 0; i < len; i++) new_elms->set(i, elms->get(i), mode);
|
| + CopyElements(&no_gc, new_elms, 0, elms, 0, len);
|
| + FillWithHoles(new_elms, new_length, capacity);
|
| +
|
| elms = new_elms;
|
| array->set_elements(elms);
|
| }
|
|
|
| + // Add the provided values.
|
| AssertNoAllocation no_gc;
|
| WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
|
| -
|
| - // Add the provided values.
|
| for (int index = 0; index < to_add; index++) {
|
| elms->set(index + len, args[index + 1], mode);
|
| }
|
| @@ -290,10 +392,9 @@
|
| BUILTIN(ArrayPop) {
|
| JSArray* array = JSArray::cast(*args.receiver());
|
| ASSERT(array->HasFastElements());
|
| - Object* undefined = Heap::undefined_value();
|
|
|
| int len = Smi::cast(array->length())->value();
|
| - if (len == 0) return undefined;
|
| + if (len == 0) return Heap::undefined_value();
|
|
|
| // Get top element
|
| FixedArray* elms = FixedArray::cast(array->elements());
|
| @@ -318,41 +419,28 @@
|
| }
|
|
|
|
|
| -static Object* GetElementToMove(uint32_t index,
|
| - FixedArray* elms,
|
| - JSObject* prototype) {
|
| - Object* e = elms->get(index);
|
| - if (e->IsTheHole() && prototype->HasElement(index)) {
|
| - e = prototype->GetElement(index);
|
| +BUILTIN(ArrayShift) {
|
| + if (!ArrayPrototypeHasNoElements()) {
|
| + return CallJsBuiltin("ArrayShift", args);
|
| }
|
| - return e;
|
| -}
|
|
|
| -
|
| -BUILTIN(ArrayShift) {
|
| JSArray* array = JSArray::cast(*args.receiver());
|
| ASSERT(array->HasFastElements());
|
|
|
| int len = Smi::cast(array->length())->value();
|
| if (len == 0) return Heap::undefined_value();
|
|
|
| - // Fetch the prototype.
|
| - JSFunction* array_function =
|
| - Top::context()->global_context()->array_function();
|
| - JSObject* prototype = JSObject::cast(array_function->prototype());
|
| -
|
| FixedArray* elms = FixedArray::cast(array->elements());
|
|
|
| // Get first element
|
| Object* first = elms->get(0);
|
| if (first->IsTheHole()) {
|
| - first = prototype->GetElement(0);
|
| + first = Heap::undefined_value();
|
| }
|
|
|
| // Shift the elements.
|
| - for (int i = 0; i < len - 1; i++) {
|
| - elms->set(i, GetElementToMove(i + 1, elms, prototype));
|
| - }
|
| + AssertNoAllocation no_gc;
|
| + MoveElements(&no_gc, elms, 0, elms, 1, len - 1);
|
| elms->set(len - 1, Heap::the_hole_value());
|
|
|
| // Set the length.
|
| @@ -363,6 +451,10 @@
|
|
|
|
|
| BUILTIN(ArrayUnshift) {
|
| + if (!ArrayPrototypeHasNoElements()) {
|
| + return CallJsBuiltin("ArrayUnshift", args);
|
| + }
|
| +
|
| JSArray* array = JSArray::cast(*args.receiver());
|
| ASSERT(array->HasFastElements());
|
|
|
| @@ -379,38 +471,22 @@
|
|
|
| FixedArray* elms = FixedArray::cast(array->elements());
|
|
|
| - // Fetch the prototype.
|
| - JSFunction* array_function =
|
| - Top::context()->global_context()->array_function();
|
| - JSObject* prototype = JSObject::cast(array_function->prototype());
|
| -
|
| if (new_length > elms->length()) {
|
| // New backing storage is needed.
|
| int capacity = new_length + (new_length >> 1) + 16;
|
| - Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
|
| + Object* obj = Heap::AllocateUninitializedFixedArray(capacity);
|
| if (obj->IsFailure()) return obj;
|
| + FixedArray* new_elms = FixedArray::cast(obj);
|
|
|
| AssertNoAllocation no_gc;
|
| - FixedArray* new_elms = FixedArray::cast(obj);
|
| - WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
|
| - // Fill out the new array with old elements.
|
| - for (int i = 0; i < len; i++)
|
| - new_elms->set(to_add + i,
|
| - GetElementToMove(i, elms, prototype),
|
| - mode);
|
| + CopyElements(&no_gc, new_elms, to_add, elms, 0, len);
|
| + FillWithHoles(new_elms, new_length, capacity);
|
|
|
| elms = new_elms;
|
| array->set_elements(elms);
|
| } else {
|
| AssertNoAllocation no_gc;
|
| - WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
|
| -
|
| - // Move elements to the right
|
| - for (int i = 0; i < len; i++) {
|
| - elms->set(new_length - i - 1,
|
| - GetElementToMove(len - i - 1, elms, prototype),
|
| - mode);
|
| - }
|
| + MoveElements(&no_gc, elms, to_add, elms, 0, len);
|
| }
|
|
|
| // Add the provided values.
|
| @@ -426,32 +502,11 @@
|
| }
|
|
|
|
|
| -static Object* CallJsBuiltin(const char* name,
|
| - BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
|
| - HandleScope handleScope;
|
| -
|
| - Handle<Object> js_builtin =
|
| - GetProperty(Handle<JSObject>(Top::global_context()->builtins()),
|
| - name);
|
| - ASSERT(js_builtin->IsJSFunction());
|
| - Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin));
|
| - Vector<Object**> argv(Vector<Object**>::New(args.length() - 1));
|
| - int n_args = args.length() - 1;
|
| - for (int i = 0; i < n_args; i++) {
|
| - argv[i] = &args[i + 1];
|
| +BUILTIN(ArraySlice) {
|
| + if (!ArrayPrototypeHasNoElements()) {
|
| + return CallJsBuiltin("ArraySlice", args);
|
| }
|
| - bool pending_exception = false;
|
| - Handle<Object> result = Execution::Call(function,
|
| - args.receiver(),
|
| - n_args,
|
| - argv.start(),
|
| - &pending_exception);
|
| - if (pending_exception) return Failure::Exception();
|
| - return *result;
|
| -}
|
|
|
| -
|
| -BUILTIN(ArraySlice) {
|
| JSArray* array = JSArray::cast(*args.receiver());
|
| ASSERT(array->HasFastElements());
|
|
|
| @@ -460,21 +515,21 @@
|
| int n_arguments = args.length() - 1;
|
|
|
| // Note carefully choosen defaults---if argument is missing,
|
| - // it's undefined which gets converted to 0 for relativeStart
|
| - // and to len for relativeEnd.
|
| - int relativeStart = 0;
|
| - int relativeEnd = len;
|
| + // it's undefined which gets converted to 0 for relative_start
|
| + // and to len for relative_end.
|
| + int relative_start = 0;
|
| + int relative_end = len;
|
| if (n_arguments > 0) {
|
| Object* arg1 = args[1];
|
| if (arg1->IsSmi()) {
|
| - relativeStart = Smi::cast(arg1)->value();
|
| + relative_start = Smi::cast(arg1)->value();
|
| } else if (!arg1->IsUndefined()) {
|
| return CallJsBuiltin("ArraySlice", args);
|
| }
|
| if (n_arguments > 1) {
|
| Object* arg2 = args[2];
|
| if (arg2->IsSmi()) {
|
| - relativeEnd = Smi::cast(arg2)->value();
|
| + relative_end = Smi::cast(arg2)->value();
|
| } else if (!arg2->IsUndefined()) {
|
| return CallJsBuiltin("ArraySlice", args);
|
| }
|
| @@ -482,44 +537,32 @@
|
| }
|
|
|
| // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
|
| - int k = (relativeStart < 0) ? Max(len + relativeStart, 0)
|
| - : Min(relativeStart, len);
|
| + int k = (relative_start < 0) ? Max(len + relative_start, 0)
|
| + : Min(relative_start, len);
|
|
|
| // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
|
| - int final = (relativeEnd < 0) ? Max(len + relativeEnd, 0)
|
| - : Min(relativeEnd, len);
|
| + int final = (relative_end < 0) ? Max(len + relative_end, 0)
|
| + : Min(relative_end, len);
|
|
|
| // Calculate the length of result array.
|
| int result_len = final - k;
|
| - if (result_len < 0) {
|
| - result_len = 0;
|
| + if (result_len <= 0) {
|
| + return AllocateEmptyJSArray();
|
| }
|
|
|
| - JSFunction* array_function =
|
| - Top::context()->global_context()->array_function();
|
| - Object* result = Heap::AllocateJSObject(array_function);
|
| + Object* result = AllocateJSArray();
|
| if (result->IsFailure()) return result;
|
| JSArray* result_array = JSArray::cast(result);
|
|
|
| - result = Heap::AllocateFixedArrayWithHoles(result_len);
|
| + result = Heap::AllocateUninitializedFixedArray(result_len);
|
| if (result->IsFailure()) return result;
|
| FixedArray* result_elms = FixedArray::cast(result);
|
|
|
| FixedArray* elms = FixedArray::cast(array->elements());
|
|
|
| - // Fetch the prototype.
|
| - JSObject* prototype = JSObject::cast(array_function->prototype());
|
| -
|
| AssertNoAllocation no_gc;
|
| - WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc);
|
| + CopyElements(&no_gc, result_elms, 0, elms, k, result_len);
|
|
|
| - // Fill newly created array.
|
| - for (int i = 0; i < result_len; i++) {
|
| - result_elms->set(i,
|
| - GetElementToMove(k + i, elms, prototype),
|
| - mode);
|
| - }
|
| -
|
| // Set elements.
|
| result_array->set_elements(result_elms);
|
|
|
| @@ -530,6 +573,10 @@
|
|
|
|
|
| BUILTIN(ArraySplice) {
|
| + if (!ArrayPrototypeHasNoElements()) {
|
| + return CallJsBuiltin("ArraySplice", args);
|
| + }
|
| +
|
| JSArray* array = JSArray::cast(*args.receiver());
|
| ASSERT(array->HasFastElements());
|
|
|
| @@ -546,118 +593,111 @@
|
| return Heap::undefined_value();
|
| }
|
|
|
| - int relativeStart = 0;
|
| + int relative_start = 0;
|
| Object* arg1 = args[1];
|
| if (arg1->IsSmi()) {
|
| - relativeStart = Smi::cast(arg1)->value();
|
| + relative_start = Smi::cast(arg1)->value();
|
| } else if (!arg1->IsUndefined()) {
|
| return CallJsBuiltin("ArraySplice", args);
|
| }
|
| - int actualStart = (relativeStart < 0) ? Max(len + relativeStart, 0)
|
| - : Min(relativeStart, len);
|
| + int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
|
| + : Min(relative_start, len);
|
|
|
| // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
|
| // given differently from when an undefined delete count is given.
|
| // This does not follow ECMA-262, but we do the same for
|
| // compatibility.
|
| - int deleteCount = len;
|
| + int delete_count = len;
|
| if (n_arguments > 1) {
|
| Object* arg2 = args[2];
|
| if (arg2->IsSmi()) {
|
| - deleteCount = Smi::cast(arg2)->value();
|
| + delete_count = Smi::cast(arg2)->value();
|
| } else {
|
| return CallJsBuiltin("ArraySplice", args);
|
| }
|
| }
|
| - int actualDeleteCount = Min(Max(deleteCount, 0), len - actualStart);
|
| + int actual_delete_count = Min(Max(delete_count, 0), len - actual_start);
|
|
|
| - JSFunction* array_function =
|
| - Top::context()->global_context()->array_function();
|
| + FixedArray* elms = FixedArray::cast(array->elements());
|
|
|
| - // Allocate result array.
|
| - Object* result = Heap::AllocateJSObject(array_function);
|
| - if (result->IsFailure()) return result;
|
| - JSArray* result_array = JSArray::cast(result);
|
| + JSArray* result_array = NULL;
|
| + if (actual_delete_count == 0) {
|
| + Object* result = AllocateEmptyJSArray();
|
| + if (result->IsFailure()) return result;
|
| + result_array = JSArray::cast(result);
|
| + } else {
|
| + // Allocate result array.
|
| + Object* result = AllocateJSArray();
|
| + if (result->IsFailure()) return result;
|
| + result_array = JSArray::cast(result);
|
|
|
| - result = Heap::AllocateFixedArrayWithHoles(actualDeleteCount);
|
| - if (result->IsFailure()) return result;
|
| - FixedArray* result_elms = FixedArray::cast(result);
|
| + result = Heap::AllocateUninitializedFixedArray(actual_delete_count);
|
| + if (result->IsFailure()) return result;
|
| + FixedArray* result_elms = FixedArray::cast(result);
|
|
|
| - FixedArray* elms = FixedArray::cast(array->elements());
|
| + AssertNoAllocation no_gc;
|
| + // Fill newly created array.
|
| + CopyElements(&no_gc,
|
| + result_elms, 0,
|
| + elms, actual_start,
|
| + actual_delete_count);
|
|
|
| - // Fetch the prototype.
|
| - JSObject* prototype = JSObject::cast(array_function->prototype());
|
| + // Set elements.
|
| + result_array->set_elements(result_elms);
|
|
|
| - AssertNoAllocation no_gc;
|
| - WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc);
|
| -
|
| - // Fill newly created array.
|
| - for (int k = 0; k < actualDeleteCount; k++) {
|
| - result_elms->set(k,
|
| - GetElementToMove(actualStart + k, elms, prototype),
|
| - mode);
|
| + // Set the length.
|
| + result_array->set_length(Smi::FromInt(actual_delete_count));
|
| }
|
|
|
| - // Set elements.
|
| - result_array->set_elements(result_elms);
|
| + int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
|
|
|
| - // Set the length.
|
| - result_array->set_length(Smi::FromInt(actualDeleteCount));
|
| + int new_length = len - actual_delete_count + item_count;
|
|
|
| - int itemCount = (n_arguments > 1) ? (n_arguments - 2) : 0;
|
| -
|
| - int new_length = len - actualDeleteCount + itemCount;
|
| -
|
| - mode = elms->GetWriteBarrierMode(no_gc);
|
| - if (itemCount < actualDeleteCount) {
|
| + if (item_count < actual_delete_count) {
|
| // Shrink the array.
|
| - for (int k = actualStart; k < (len - actualDeleteCount); k++) {
|
| - elms->set(k + itemCount,
|
| - GetElementToMove(k + actualDeleteCount, elms, prototype),
|
| - mode);
|
| - }
|
| -
|
| - for (int k = len; k > new_length; k--) {
|
| - elms->set(k - 1, Heap::the_hole_value());
|
| - }
|
| - } else if (itemCount > actualDeleteCount) {
|
| + AssertNoAllocation no_gc;
|
| + MoveElements(&no_gc,
|
| + elms, actual_start + item_count,
|
| + elms, actual_start + actual_delete_count,
|
| + (len - actual_delete_count - actual_start));
|
| + FillWithHoles(elms, new_length, len);
|
| + } else if (item_count > actual_delete_count) {
|
| // Currently fixed arrays cannot grow too big, so
|
| // we should never hit this case.
|
| - ASSERT((itemCount - actualDeleteCount) <= (Smi::kMaxValue - len));
|
| + ASSERT((item_count - actual_delete_count) <= (Smi::kMaxValue - len));
|
|
|
| - FixedArray* source_elms = elms;
|
| -
|
| // Check if array need to grow.
|
| if (new_length > elms->length()) {
|
| // New backing storage is needed.
|
| int capacity = new_length + (new_length >> 1) + 16;
|
| - Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
|
| + Object* obj = Heap::AllocateUninitializedFixedArray(capacity);
|
| if (obj->IsFailure()) return obj;
|
| -
|
| FixedArray* new_elms = FixedArray::cast(obj);
|
| - mode = new_elms->GetWriteBarrierMode(no_gc);
|
|
|
| - // Copy the part before actualStart as is.
|
| - for (int k = 0; k < actualStart; k++) {
|
| - new_elms->set(k, elms->get(k), mode);
|
| - }
|
| + AssertNoAllocation no_gc;
|
| + // Copy the part before actual_start as is.
|
| + CopyElements(&no_gc, new_elms, 0, elms, 0, actual_start);
|
| + CopyElements(&no_gc,
|
| + new_elms, actual_start + item_count,
|
| + elms, actual_start + actual_delete_count,
|
| + (len - actual_delete_count - actual_start));
|
| + FillWithHoles(new_elms, new_length, capacity);
|
|
|
| - source_elms = elms;
|
| elms = new_elms;
|
| array->set_elements(elms);
|
| + } else {
|
| + AssertNoAllocation no_gc;
|
| + MoveElements(&no_gc,
|
| + elms, actual_start + item_count,
|
| + elms, actual_start + actual_delete_count,
|
| + (len - actual_delete_count - actual_start));
|
| }
|
| -
|
| - for (int k = len - actualDeleteCount; k > actualStart; k--) {
|
| - elms->set(k + itemCount - 1,
|
| - GetElementToMove(k + actualDeleteCount - 1,
|
| - source_elms,
|
| - prototype),
|
| - mode);
|
| - }
|
| }
|
|
|
| - for (int k = actualStart; k < actualStart + itemCount; k++) {
|
| - elms->set(k, args[3 + k - actualStart], mode);
|
| + 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);
|
| }
|
|
|
| // Set the length.
|
|
|