| Index: src/builtins.cc
|
| ===================================================================
|
| --- src/builtins.cc (revision 3935)
|
| +++ src/builtins.cc (working copy)
|
| @@ -168,28 +168,6 @@
|
| // ----------------------------------------------------------------------------
|
|
|
|
|
| -Handle<Code> Builtins::GetCode(JavaScript id, bool* resolved) {
|
| - Code* code = Builtins::builtin(Builtins::Illegal);
|
| - *resolved = false;
|
| -
|
| - if (Top::context() != NULL) {
|
| - Object* object = Top::builtins()->javascript_builtin(id);
|
| - if (object->IsJSFunction()) {
|
| - Handle<SharedFunctionInfo> shared(JSFunction::cast(object)->shared());
|
| - // Make sure the number of parameters match the formal parameter count.
|
| - ASSERT(shared->formal_parameter_count() ==
|
| - Builtins::GetArgumentsCount(id));
|
| - if (EnsureCompiled(shared, CLEAR_EXCEPTION)) {
|
| - code = shared->code();
|
| - *resolved = true;
|
| - }
|
| - }
|
| - }
|
| -
|
| - return Handle<Code>(code);
|
| -}
|
| -
|
| -
|
| BUILTIN(Illegal) {
|
| UNREACHABLE();
|
| return Heap::undefined_value(); // Make compiler happy.
|
| @@ -268,19 +246,19 @@
|
| JSArray* array = JSArray::cast(*args.receiver());
|
| ASSERT(array->HasFastElements());
|
|
|
| - // Make sure we have space for the elements.
|
| 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));
|
|
|
| - // Set new length.
|
| - int new_length = len + args.length() - 1;
|
| + int new_length = len + to_add;
|
| FixedArray* elms = FixedArray::cast(array->elements());
|
|
|
| - if (new_length <= elms->length()) {
|
| - // Backing storage has extra space for the provided values.
|
| - for (int index = 0; index < args.length() - 1; index++) {
|
| - elms->set(index + len, args[index+1]);
|
| - }
|
| - } else {
|
| + if (new_length > elms->length()) {
|
| // New backing storage is needed.
|
| int capacity = new_length + (new_length >> 1) + 16;
|
| Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
|
| @@ -291,16 +269,21 @@
|
| 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);
|
| - // Add the provided values.
|
| - for (int index = 0; index < args.length() - 1; index++) {
|
| - new_elms->set(index + len, args[index+1], mode);
|
| - }
|
| - // Set the new backing storage.
|
| - array->set_elements(new_elms);
|
| + elms = new_elms;
|
| + array->set_elements(elms);
|
| }
|
| +
|
| + 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);
|
| + }
|
| +
|
| // Set the length.
|
| array->set_length(Smi::FromInt(new_length));
|
| - return array->length();
|
| + return Smi::FromInt(new_length);
|
| }
|
|
|
|
|
| @@ -335,6 +318,355 @@
|
| }
|
|
|
|
|
| +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);
|
| + }
|
| + 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);
|
| + }
|
| +
|
| + // Shift the elements.
|
| + for (int i = 0; i < len - 1; i++) {
|
| + elms->set(i, GetElementToMove(i + 1, elms, prototype));
|
| + }
|
| + elms->set(len - 1, Heap::the_hole_value());
|
| +
|
| + // Set the length.
|
| + array->set_length(Smi::FromInt(len - 1));
|
| +
|
| + return first;
|
| +}
|
| +
|
| +
|
| +BUILTIN(ArrayUnshift) {
|
| + JSArray* array = JSArray::cast(*args.receiver());
|
| + ASSERT(array->HasFastElements());
|
| +
|
| + int len = Smi::cast(array->length())->value();
|
| + int to_add = args.length() - 1;
|
| + // Note that we cannot quit early if to_add == 0 as
|
| + // values should be lifted from prototype into
|
| + // the array.
|
| +
|
| + int new_length = len + to_add;
|
| + // Currently fixed arrays cannot grow too big, so
|
| + // we should never hit this case.
|
| + ASSERT(to_add <= (Smi::kMaxValue - len));
|
| +
|
| + 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);
|
| + if (obj->IsFailure()) return 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);
|
| +
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + // Add the provided values.
|
| + AssertNoAllocation no_gc;
|
| + WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
|
| + for (int i = 0; i < to_add; i++) {
|
| + elms->set(i, args[i + 1], mode);
|
| + }
|
| +
|
| + // Set the length.
|
| + array->set_length(Smi::FromInt(new_length));
|
| + return Smi::FromInt(new_length);
|
| +}
|
| +
|
| +
|
| +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);
|
| + if (pending_exception) return Failure::Exception();
|
| + return *result;
|
| +}
|
| +
|
| +
|
| +BUILTIN(ArraySlice) {
|
| + JSArray* array = JSArray::cast(*args.receiver());
|
| + ASSERT(array->HasFastElements());
|
| +
|
| + int len = Smi::cast(array->length())->value();
|
| +
|
| + 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;
|
| + if (n_arguments > 0) {
|
| + Object* arg1 = args[1];
|
| + if (arg1->IsSmi()) {
|
| + relativeStart = 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();
|
| + } else if (!arg2->IsUndefined()) {
|
| + return CallJsBuiltin("ArraySlice", args);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
|
| + int k = (relativeStart < 0) ? Max(len + relativeStart, 0)
|
| + : Min(relativeStart, len);
|
| +
|
| + // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
|
| + int final = (relativeEnd < 0) ? Max(len + relativeEnd, 0)
|
| + : Min(relativeEnd, len);
|
| +
|
| + // Calculate the length of result array.
|
| + int result_len = final - k;
|
| + if (result_len < 0) {
|
| + result_len = 0;
|
| + }
|
| +
|
| + JSFunction* array_function =
|
| + Top::context()->global_context()->array_function();
|
| + Object* result = Heap::AllocateJSObject(array_function);
|
| + if (result->IsFailure()) return result;
|
| + JSArray* result_array = JSArray::cast(result);
|
| +
|
| + result = Heap::AllocateFixedArrayWithHoles(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);
|
| +
|
| + // 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);
|
| +
|
| + // Set the length.
|
| + result_array->set_length(Smi::FromInt(result_len));
|
| + return result_array;
|
| +}
|
| +
|
| +
|
| +BUILTIN(ArraySplice) {
|
| + JSArray* array = JSArray::cast(*args.receiver());
|
| + ASSERT(array->HasFastElements());
|
| +
|
| + int len = Smi::cast(array->length())->value();
|
| +
|
| + int n_arguments = args.length() - 1;
|
| +
|
| + // SpiderMonkey and JSC return undefined in the case where no
|
| + // arguments are given instead of using the implicit undefined
|
| + // arguments. This does not follow ECMA-262, but we do the same for
|
| + // compatibility.
|
| + // TraceMonkey follows ECMA-262 though.
|
| + if (n_arguments == 0) {
|
| + return Heap::undefined_value();
|
| + }
|
| +
|
| + int relativeStart = 0;
|
| + Object* arg1 = args[1];
|
| + if (arg1->IsSmi()) {
|
| + relativeStart = Smi::cast(arg1)->value();
|
| + } else if (!arg1->IsUndefined()) {
|
| + return CallJsBuiltin("ArraySplice", args);
|
| + }
|
| + int actualStart = (relativeStart < 0) ? Max(len + relativeStart, 0)
|
| + : Min(relativeStart, 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;
|
| + if (n_arguments > 1) {
|
| + Object* arg2 = args[2];
|
| + if (arg2->IsSmi()) {
|
| + deleteCount = Smi::cast(arg2)->value();
|
| + } else {
|
| + return CallJsBuiltin("ArraySplice", args);
|
| + }
|
| + }
|
| + int actualDeleteCount = Min(Max(deleteCount, 0), len - actualStart);
|
| +
|
| + JSFunction* array_function =
|
| + Top::context()->global_context()->array_function();
|
| +
|
| + // Allocate result array.
|
| + Object* result = Heap::AllocateJSObject(array_function);
|
| + if (result->IsFailure()) return result;
|
| + JSArray* result_array = JSArray::cast(result);
|
| +
|
| + result = Heap::AllocateFixedArrayWithHoles(actualDeleteCount);
|
| + 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);
|
| +
|
| + // Fill newly created array.
|
| + for (int k = 0; k < actualDeleteCount; k++) {
|
| + result_elms->set(k,
|
| + GetElementToMove(actualStart + k, elms, prototype),
|
| + mode);
|
| + }
|
| +
|
| + // Set elements.
|
| + result_array->set_elements(result_elms);
|
| +
|
| + // Set the length.
|
| + result_array->set_length(Smi::FromInt(actualDeleteCount));
|
| +
|
| + int itemCount = (n_arguments > 1) ? (n_arguments - 2) : 0;
|
| +
|
| + int new_length = len - actualDeleteCount + itemCount;
|
| +
|
| + mode = elms->GetWriteBarrierMode(no_gc);
|
| + if (itemCount < actualDeleteCount) {
|
| + // 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) {
|
| + // Currently fixed arrays cannot grow too big, so
|
| + // we should never hit this case.
|
| + ASSERT((itemCount - actualDeleteCount) <= (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);
|
| + 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);
|
| + }
|
| +
|
| + source_elms = elms;
|
| + elms = new_elms;
|
| + array->set_elements(elms);
|
| + }
|
| +
|
| + 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);
|
| + }
|
| +
|
| + // Set the length.
|
| + array->set_length(Smi::FromInt(new_length));
|
| +
|
| + return result_array;
|
| +}
|
| +
|
| +
|
| // -----------------------------------------------------------------------------
|
| //
|
|
|
| @@ -474,6 +806,76 @@
|
| }
|
|
|
|
|
| +#ifdef DEBUG
|
| +
|
| +static void VerifyTypeCheck(Handle<JSObject> object,
|
| + Handle<JSFunction> function) {
|
| + FunctionTemplateInfo* info =
|
| + FunctionTemplateInfo::cast(function->shared()->function_data());
|
| + if (info->signature()->IsUndefined()) return;
|
| + SignatureInfo* signature = SignatureInfo::cast(info->signature());
|
| + Object* receiver_type = signature->receiver();
|
| + if (receiver_type->IsUndefined()) return;
|
| + FunctionTemplateInfo* type = FunctionTemplateInfo::cast(receiver_type);
|
| + ASSERT(object->IsInstanceOf(type));
|
| +}
|
| +
|
| +#endif
|
| +
|
| +
|
| +BUILTIN(FastHandleApiCall) {
|
| + ASSERT(!CalledAsConstructor());
|
| + const bool is_construct = false;
|
| +
|
| + // We expect four more arguments: function, callback, call data, and holder.
|
| + const int args_length = args.length() - 4;
|
| + ASSERT(args_length >= 0);
|
| +
|
| + Handle<JSFunction> function = args.at<JSFunction>(args_length);
|
| + Object* callback_obj = args[args_length + 1];
|
| + Handle<Object> data_handle = args.at<Object>(args_length + 2);
|
| + Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3);
|
| +
|
| +#ifdef DEBUG
|
| + VerifyTypeCheck(checked_holder, function);
|
| +#endif
|
| +
|
| + v8::Local<v8::Object> holder = v8::Utils::ToLocal(checked_holder);
|
| + v8::Local<v8::Function> callee = v8::Utils::ToLocal(function);
|
| + v8::InvocationCallback callback =
|
| + v8::ToCData<v8::InvocationCallback>(callback_obj);
|
| + v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
|
| +
|
| + v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
|
| + data,
|
| + holder,
|
| + callee,
|
| + is_construct,
|
| + reinterpret_cast<void**>(&args[0] - 1),
|
| + args_length - 1);
|
| +
|
| + HandleScope scope;
|
| + Object* result;
|
| + v8::Handle<v8::Value> value;
|
| + {
|
| + // Leaving JavaScript.
|
| + VMState state(EXTERNAL);
|
| +#ifdef ENABLE_LOGGING_AND_PROFILING
|
| + state.set_external_callback(v8::ToCData<Address>(callback_obj));
|
| +#endif
|
| + value = callback(new_args);
|
| + }
|
| + if (value.IsEmpty()) {
|
| + result = Heap::undefined_value();
|
| + } else {
|
| + result = *reinterpret_cast<Object**>(*value);
|
| + }
|
| +
|
| + RETURN_IF_SCHEDULED_EXCEPTION();
|
| + return result;
|
| +}
|
| +
|
| +
|
| // Helper function to handle calls to non-function objects created through the
|
| // API. The object can be called as either a constructor (using new) or just as
|
| // a function (without new).
|
| @@ -657,7 +1059,11 @@
|
| KeyedLoadIC::GeneratePreMonomorphic(masm);
|
| }
|
|
|
| +static void Generate_KeyedLoadIC_IndexedInterceptor(MacroAssembler* masm) {
|
| + KeyedLoadIC::GenerateIndexedInterceptor(masm);
|
| +}
|
|
|
| +
|
| static void Generate_StoreIC_Initialize(MacroAssembler* masm) {
|
| StoreIC::GenerateInitialize(masm);
|
| }
|
| @@ -668,15 +1074,16 @@
|
| }
|
|
|
|
|
| -static void Generate_StoreIC_ExtendStorage(MacroAssembler* masm) {
|
| - StoreIC::GenerateExtendStorage(masm);
|
| -}
|
| -
|
| static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) {
|
| StoreIC::GenerateMegamorphic(masm);
|
| }
|
|
|
|
|
| +static void Generate_StoreIC_ArrayLength(MacroAssembler* masm) {
|
| + StoreIC::GenerateArrayLength(masm);
|
| +}
|
| +
|
| +
|
| static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) {
|
| KeyedStoreIC::GenerateGeneric(masm);
|
| }
|
| @@ -720,11 +1127,6 @@
|
| }
|
|
|
|
|
| -static void Generate_KeyedStoreIC_ExtendStorage(MacroAssembler* masm) {
|
| - KeyedStoreIC::GenerateExtendStorage(masm);
|
| -}
|
| -
|
| -
|
| static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
|
| KeyedStoreIC::GenerateMiss(masm);
|
| }
|
| @@ -869,9 +1271,6 @@
|
| v8::internal::V8::FatalProcessOutOfMemory("CreateCode");
|
| }
|
| }
|
| - // Add any unresolved jumps or calls to the fixup list in the
|
| - // bootstrapper.
|
| - Bootstrapper::AddFixup(Code::cast(code), &masm);
|
| // Log the event and add the code to the builtins array.
|
| LOG(CodeCreateEvent(Logger::BUILTIN_TAG,
|
| Code::cast(code), functions[i].s_name));
|
|
|