From: bmeurer Date: Wed, 23 Sep 2015 05:43:57 +0000 (-0700) Subject: [builtin] Refactor Invoke to deal with any kind of callable. X-Git-Tag: upstream/4.7.83~166 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=634d1d86d8e98584097c6cb548a68ecf705c44f7;p=platform%2Fupstream%2Fv8.git [builtin] Refactor Invoke to deal with any kind of callable. Now both Execution::Call and Execution::New can deal with any kind of target and will raise a proper exception if the target is not callable (which is not yet spec compliant for New, as we would have to check IsConstructor instead, which we don't have yet). Now we no longer need to do any of these weird call/construct delegate gymnastics in C++, and we finally have a single true bottleneck for Call/Construct abstract operations in the code base, with only a few special handlings left in the compilers to optimize the JSFunction case. R=jarin@chromium.org BUG=v8:4430, v8:4413 LOG=n Review URL: https://codereview.chromium.org/1360793002 Cr-Commit-Position: refs/heads/master@{#30874} --- diff --git a/src/api.cc b/src/api.cc index 62e50dd..1cb00f2 100644 --- a/src/api.cc +++ b/src/api.cc @@ -4244,18 +4244,9 @@ MaybeLocal Object::CallAsFunction(Local context, auto recv_obj = Utils::OpenHandle(*recv); STATIC_ASSERT(sizeof(v8::Local) == sizeof(i::Object**)); i::Handle* args = reinterpret_cast*>(argv); - i::Handle fun; - if (self->IsJSFunction()) { - fun = i::Handle::cast(self); - } else { - has_pending_exception = - !i::Execution::GetFunctionDelegate(isolate, self).ToHandle(&fun); - RETURN_ON_FAILED_EXECUTION(Value); - recv_obj = self; - } Local result; has_pending_exception = !ToLocal( - i::Execution::Call(isolate, fun, recv_obj, argc, args), &result); + i::Execution::Call(isolate, self, recv_obj, argc, args), &result); RETURN_ON_FAILED_EXECUTION(Value); RETURN_ESCAPED(result); } @@ -4278,21 +4269,9 @@ MaybeLocal Object::CallAsConstructor(Local context, int argc, auto self = Utils::OpenHandle(this); STATIC_ASSERT(sizeof(v8::Local) == sizeof(i::Object**)); i::Handle* args = reinterpret_cast*>(argv); - if (self->IsJSFunction()) { - auto fun = i::Handle::cast(self); - Local result; - has_pending_exception = - !ToLocal(i::Execution::New(fun, argc, args), &result); - RETURN_ON_FAILED_EXECUTION(Value); - RETURN_ESCAPED(result); - } - i::Handle fun; - has_pending_exception = - !i::Execution::GetConstructorDelegate(isolate, self).ToHandle(&fun); - RETURN_ON_FAILED_EXECUTION(Value); Local result; has_pending_exception = !ToLocal( - i::Execution::Call(isolate, fun, self, argc, args), &result); + i::Execution::New(isolate, self, self, argc, args), &result); RETURN_ON_FAILED_EXECUTION(Value); RETURN_ESCAPED(result); } diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 9b87243..715118c 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -1585,13 +1585,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // -- r1 : the target to call (can be any Object). // ----------------------------------- - Label non_smi, non_function; - __ JumpIfSmi(r1, &non_function); + Label non_callable, non_function, non_smi; + __ JumpIfSmi(r1, &non_callable); __ bind(&non_smi); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); + __ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE); __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET, eq); - __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE)); + __ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE)); __ b(ne, &non_function); // 1. Call to function proxy. @@ -1603,27 +1603,23 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). __ bind(&non_function); - // TODO(bmeurer): I wonder why we prefer to have slow API calls? This could - // be awesome instead; i.e. a trivial improvement would be to call into the - // runtime and just deal with the API function there instead of returning a - // delegate from a runtime call that just jumps back to the runtime once - // called. Or, bonus points, call directly into the C API function here, as - // we do in some Crankshaft fast cases. - // Overwrite the original receiver with the (original) target. + // Check if target has a [[Call]] internal method. + __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ tst(r4, Operand(1 << Map::kIsCallable)); + __ b(eq, &non_callable); + // Overwrite the original receiver the (original) target. __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, r1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(r0); - __ Push(r0, r1); - __ CallRuntime(Runtime::kGetFunctionDelegate, 1); - __ mov(r1, r0); - __ Pop(r0); - __ SmiUntag(r0); + __ Push(r1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(r1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1658,32 +1654,40 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // the JSFunction on which new was invoked initially) // ----------------------------------- - Label slow; - __ JumpIfSmi(r1, &slow); - __ CompareObjectType(r1, r5, r5, JS_FUNCTION_TYPE); + Label non_callable, non_function; + __ JumpIfSmi(r1, &non_callable); + __ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE); __ Jump(masm->isolate()->builtins()->ConstructFunction(), RelocInfo::CODE_TARGET, eq); __ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE)); - __ b(ne, &slow); + __ b(ne, &non_function); + // 1. Construct of function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies. __ ldr(r1, FieldMemOperand(r1, JSFunctionProxy::kConstructTrapOffset)); __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - __ bind(&slow); + // 2. Construct of something that else, which might have a [[Construct]] + // internal method (if not we raise an exception). + __ bind(&non_function); + // Check if target has a [[Call]] internal method. + // TODO(bmeurer): This shoud use IsConstructor once available. + __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ tst(r4, Operand(1 << Map::kIsCallable)); + __ b(eq, &non_callable); + // Overwrite the original receiver the (original) target. + __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, r1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Construct of something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(r0); - __ Push(r0, r1); - __ CallRuntime(Runtime::kGetConstructorDelegate, 1); - __ mov(r1, r0); - __ Pop(r0); - __ SmiUntag(r0); + __ Push(r1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(r1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 7a21635..53cf0ec 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -1647,14 +1647,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // -- x1 : the target to call (can be any Object). // ----------------------------------- - Label non_smi, non_jsfunction, non_function; - __ JumpIfSmi(x1, &non_function); + Label non_callable, non_function, non_smi; + __ JumpIfSmi(x1, &non_callable); __ Bind(&non_smi); - __ CompareObjectType(x1, x2, x2, JS_FUNCTION_TYPE); - __ B(ne, &non_jsfunction); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); - __ Bind(&non_jsfunction); - __ Cmp(x2, JS_FUNCTION_PROXY_TYPE); + __ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET, + eq); + __ Cmp(x5, JS_FUNCTION_PROXY_TYPE); __ B(ne, &non_function); // 1. Call to function proxy. @@ -1666,27 +1665,22 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). __ Bind(&non_function); - // TODO(bmeurer): I wonder why we prefer to have slow API calls? This could - // be awesome instead; i.e. a trivial improvement would be to call into the - // runtime and just deal with the API function there instead of returning a - // delegate from a runtime call that just jumps back to the runtime once - // called. Or, bonus points, call directly into the C API function here, as - // we do in some Crankshaft fast cases. + // Check if target has a [[Call]] internal method. + __ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset)); + __ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable); // Overwrite the original receiver with the (original) target. __ Poke(x1, Operand(x0, LSL, kXRegSizeLog2)); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, x1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(x0); - __ Push(x0, x1); - __ CallRuntime(Runtime::kGetFunctionDelegate, 1); - __ Mov(x1, x0); - __ Pop(x0); - __ SmiUntag(x0); + __ Push(x1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(x1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1722,32 +1716,39 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // the JSFunction on which new was invoked initially) // ----------------------------------- - Label slow; - __ JumpIfSmi(x1, &slow); - __ CompareObjectType(x1, x5, x5, JS_FUNCTION_TYPE); + Label non_callable, non_function; + __ JumpIfSmi(x1, &non_callable); + __ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE); __ Jump(masm->isolate()->builtins()->ConstructFunction(), RelocInfo::CODE_TARGET, eq); - __ Cmp(x5, Operand(JS_FUNCTION_PROXY_TYPE)); - __ B(ne, &slow); + __ Cmp(x5, JS_FUNCTION_PROXY_TYPE); + __ B(ne, &non_function); + // 1. Construct of function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies. __ Ldr(x1, FieldMemOperand(x1, JSFunctionProxy::kConstructTrapOffset)); __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - __ Bind(&slow); + // 2. Construct of something that else, which might have a [[Construct]] + // internal method (if not we raise an exception). + __ Bind(&non_function); + // Check if target has a [[Call]] internal method. + // TODO(bmeurer): This shoud use IsConstructor once available. + __ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset)); + __ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable); + // Overwrite the original receiver with the (original) target. + __ Poke(x1, Operand(x0, LSL, kXRegSizeLog2)); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, x1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Construct of something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(x0); - __ Push(x0, x1); - __ CallRuntime(Runtime::kGetConstructorDelegate, 1); - __ Mov(x1, x0); - __ Pop(x0); - __ SmiUntag(x0); + __ Push(x1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(x1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } diff --git a/src/arm64/simulator-arm64.cc b/src/arm64/simulator-arm64.cc index f4e4839..4e6a9d9 100644 --- a/src/arm64/simulator-arm64.cc +++ b/src/arm64/simulator-arm64.cc @@ -178,13 +178,13 @@ double Simulator::CallDouble(byte* entry, CallArgument* args) { int64_t Simulator::CallJS(byte* entry, Object* new_target, - JSFunction* func, + Object* target, Object* revc, int64_t argc, Object*** argv) { CallArgument args[] = { CallArgument(new_target), - CallArgument(func), + CallArgument(target), CallArgument(revc), CallArgument(argc), CallArgument(argv), @@ -193,6 +193,7 @@ int64_t Simulator::CallJS(byte* entry, return CallInt64(entry, args); } + int64_t Simulator::CallRegExp(byte* entry, String* input, int64_t start_offset, diff --git a/src/arm64/simulator-arm64.h b/src/arm64/simulator-arm64.h index cd734ad..e4d9a81 100644 --- a/src/arm64/simulator-arm64.h +++ b/src/arm64/simulator-arm64.h @@ -188,7 +188,7 @@ class Simulator : public DecoderVisitor { // which set up the simulator state and grab the result on return. int64_t CallJS(byte* entry, Object* new_target, - JSFunction* func, + Object* target, Object* revc, int64_t argc, Object*** argv); diff --git a/src/execution.cc b/src/execution.cc index c214e7c..9330de0 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -9,8 +9,6 @@ #include "src/deoptimizer.h" #include "src/isolate-inl.h" #include "src/messages.h" -#include "src/parser.h" -#include "src/prettyprinter.h" #include "src/vm-state-inl.h" namespace v8 { @@ -55,47 +53,12 @@ static void PrintDeserializedCodeInfo(Handle function) { namespace { -MUST_USE_RESULT MaybeHandle Invoke(bool is_construct, - Handle function, +MUST_USE_RESULT MaybeHandle Invoke(Isolate* isolate, bool is_construct, + Handle target, Handle receiver, int argc, Handle args[], Handle new_target) { - Isolate* const isolate = function->GetIsolate(); - - // Convert calls on global objects to be calls on the global - // receiver instead to avoid having a 'this' pointer which refers - // directly to a global object. - if (receiver->IsGlobalObject()) { - receiver = - handle(Handle::cast(receiver)->global_proxy(), isolate); - } - - // api callbacks can be called directly. - if (!is_construct && function->shared()->IsApiFunction()) { - SaveContext save(isolate); - isolate->set_context(function->context()); - // Do proper receiver conversion for non-strict mode api functions. - if (!receiver->IsJSReceiver() && - is_sloppy(function->shared()->language_mode())) { - if (receiver->IsUndefined() || receiver->IsNull()) { - receiver = handle(function->global_proxy(), isolate); - } else { - ASSIGN_RETURN_ON_EXCEPTION( - isolate, receiver, Execution::ToObject(isolate, receiver), Object); - } - } - DCHECK(function->context()->global_object()->IsGlobalObject()); - auto value = Builtins::InvokeApiFunction(function, receiver, argc, args); - bool has_exception = value.is_null(); - DCHECK(has_exception == isolate->has_pending_exception()); - if (has_exception) { - isolate->ReportPendingMessages(); - return MaybeHandle(); - } else { - isolate->clear_pending_message(); - } - return value; - } + DCHECK(!receiver->IsGlobalObject()); // Entering JavaScript. VMState state(isolate); @@ -109,7 +72,7 @@ MUST_USE_RESULT MaybeHandle Invoke(bool is_construct, // Placeholder for return value. Object* value = NULL; - typedef Object* (*JSEntryFunction)(Object* new_target, Object* function, + typedef Object* (*JSEntryFunction)(Object* new_target, Object* target, Object* receiver, int argc, Object*** args); @@ -117,10 +80,6 @@ MUST_USE_RESULT MaybeHandle Invoke(bool is_construct, ? isolate->factory()->js_construct_entry_code() : isolate->factory()->js_entry_code(); - // Make sure that the global object of the context we're about to - // make the current one is indeed a global object. - DCHECK(function->context()->global_object()->IsGlobalObject()); - { // Save and restore context around invocation and block the // allocation of handles without explicit handle scopes. @@ -130,10 +89,12 @@ MUST_USE_RESULT MaybeHandle Invoke(bool is_construct, // Call the function through the right JS entry stub. Object* orig_func = *new_target; - JSFunction* func = *function; + Object* func = *target; Object* recv = *receiver; Object*** argv = reinterpret_cast(args); - if (FLAG_profile_deserialization) PrintDeserializedCodeInfo(function); + if (FLAG_profile_deserialization && target->IsJSFunction()) { + PrintDeserializedCodeInfo(Handle::cast(target)); + } value = CALL_GENERATED_CODE(stub_entry, orig_func, func, recv, argc, argv); } @@ -163,30 +124,64 @@ MUST_USE_RESULT MaybeHandle Invoke(bool is_construct, } // namespace +// static MaybeHandle Execution::Call(Isolate* isolate, Handle callable, Handle receiver, int argc, Handle argv[]) { - if (!callable->IsJSFunction()) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, callable, - GetFunctionDelegate(isolate, callable), Object); + // Convert calls on global objects to be calls on the global + // receiver instead to avoid having a 'this' pointer which refers + // directly to a global object. + if (receiver->IsGlobalObject()) { + receiver = + handle(Handle::cast(receiver)->global_proxy(), isolate); + } + + // api callbacks can be called directly. + if (callable->IsJSFunction() && + Handle::cast(callable)->shared()->IsApiFunction()) { + Handle function = Handle::cast(callable); + SaveContext save(isolate); + isolate->set_context(function->context()); + // Do proper receiver conversion for non-strict mode api functions. + if (!receiver->IsJSReceiver() && + is_sloppy(function->shared()->language_mode())) { + if (receiver->IsUndefined() || receiver->IsNull()) { + receiver = handle(function->global_proxy(), isolate); + } else { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, receiver, Execution::ToObject(isolate, receiver), Object); + } + } + DCHECK(function->context()->global_object()->IsGlobalObject()); + auto value = Builtins::InvokeApiFunction(function, receiver, argc, argv); + bool has_exception = value.is_null(); + DCHECK(has_exception == isolate->has_pending_exception()); + if (has_exception) { + isolate->ReportPendingMessages(); + return MaybeHandle(); + } else { + isolate->clear_pending_message(); + } + return value; } - Handle func = Handle::cast(callable); - return Invoke(false, func, receiver, argc, argv, + return Invoke(isolate, false, callable, receiver, argc, argv, isolate->factory()->undefined_value()); } +// static MaybeHandle Execution::New(Handle constructor, int argc, Handle argv[]) { - return New(constructor, constructor, argc, argv); + return New(constructor->GetIsolate(), constructor, constructor, argc, argv); } -MaybeHandle Execution::New(Handle constructor, - Handle new_target, int argc, +// static +MaybeHandle Execution::New(Isolate* isolate, Handle constructor, + Handle new_target, int argc, Handle argv[]) { - return Invoke(true, constructor, handle(constructor->global_proxy()), argc, - argv, new_target); + return Invoke(isolate, true, constructor, + isolate->factory()->undefined_value(), argc, argv, new_target); } @@ -234,98 +229,6 @@ MaybeHandle Execution::TryCall(Handle func, } -// static -MaybeHandle Execution::GetFunctionDelegate(Isolate* isolate, - Handle object) { - DCHECK(!object->IsJSFunction()); - if (object->IsHeapObject()) { - DisallowHeapAllocation no_gc; - - // If object is a function proxy, get its handler. Iterate if necessary. - Object* fun = *object; - while (fun->IsJSFunctionProxy()) { - fun = JSFunctionProxy::cast(fun)->call_trap(); - } - if (fun->IsJSFunction()) { - return handle(JSFunction::cast(fun), isolate); - } - - // We can also have exotic objects with [[Call]] internal methods. - if (fun->IsCallable()) { - return handle(isolate->native_context()->call_as_function_delegate(), - isolate); - } - } - - // If the Object doesn't have an instance-call handler we should - // throw a non-callable exception. - Handle callsite = RenderCallSite(isolate, object); - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kCalledNonCallable, callsite), - JSFunction); -} - - -// static -MaybeHandle Execution::GetConstructorDelegate( - Isolate* isolate, Handle object) { - // If you return a function from here, it will be called when an - // attempt is made to call the given object as a constructor. - - DCHECK(!object->IsJSFunction()); - if (object->IsHeapObject()) { - DisallowHeapAllocation no_gc; - - // If object is a function proxies, get its handler. Iterate if necessary. - Object* fun = *object; - while (fun->IsJSFunctionProxy()) { - // TODO(bmeurer): This should work based on [[Construct]]; our proxies - // are screwed. - fun = JSFunctionProxy::cast(fun)->call_trap(); - } - if (fun->IsJSFunction()) { - return handle(JSFunction::cast(fun), isolate); - } - - // We can also have exotic objects with [[Construct]] internal methods. - // TODO(bmeurer): This should use IsConstructor() as dictacted by the spec. - if (fun->IsCallable()) { - return handle(isolate->native_context()->call_as_constructor_delegate(), - isolate); - } - } - - // If the Object doesn't have an instance-call handler we should - // throw a non-callable exception. - Handle callsite = RenderCallSite(isolate, object); - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kCalledNonCallable, callsite), - JSFunction); -} - - -// static -Handle Execution::RenderCallSite(Isolate* isolate, - Handle object) { - MessageLocation location; - if (isolate->ComputeLocation(&location)) { - Zone zone; - base::SmartPointer info( - location.function()->shared()->is_function() - ? new ParseInfo(&zone, location.function()) - : new ParseInfo(&zone, location.script())); - if (Parser::ParseStatic(info.get())) { - CallPrinter printer(isolate, &zone); - const char* string = printer.Print(info->literal(), location.start_pos()); - return isolate->factory()->NewStringFromAsciiChecked(string); - } else { - isolate->clear_pending_exception(); - } - } - return Object::TypeOf(isolate, object); -} - - void StackGuard::SetStackLimit(uintptr_t limit) { ExecutionAccess access(isolate_); // If the current limits are special (e.g. due to a pending interrupt) then diff --git a/src/execution.h b/src/execution.h index 5c25082..3822cbb 100644 --- a/src/execution.h +++ b/src/execution.h @@ -35,8 +35,9 @@ class Execution final : public AllStatic { MUST_USE_RESULT static MaybeHandle New(Handle constructor, int argc, Handle argv[]); - MUST_USE_RESULT static MaybeHandle New(Handle constructor, - Handle new_target, + MUST_USE_RESULT static MaybeHandle New(Isolate* isolate, + Handle constructor, + Handle new_target, int argc, Handle argv[]); @@ -88,20 +89,6 @@ class Execution final : public AllStatic { Handle fun, Handle pos, Handle is_global); - - // Get a function delegate for the given non-function object. - // Used for support calling objects as functions. - MUST_USE_RESULT static MaybeHandle GetFunctionDelegate( - Isolate* isolate, Handle object); - - // Get a function delegate (or undefined) for the given non-function - // object. Used for support calling objects as constructors. - MUST_USE_RESULT static MaybeHandle GetConstructorDelegate( - Isolate* isolate, Handle object); - - private: - MUST_USE_RESULT static Handle RenderCallSite(Isolate* isolate, - Handle object); }; diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index a6d29cc..817b425 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -1516,13 +1516,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // -- edi : the target to call (can be any Object). // ----------------------------------- - Label non_smi, non_function; - __ JumpIfSmi(edi, &non_function); + Label non_callable, non_function, non_smi; + __ JumpIfSmi(edi, &non_callable); __ bind(&non_smi); - __ CmpObjectType(edi, JS_FUNCTION_TYPE, edx); + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ j(equal, masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); - __ CmpInstanceType(edx, JS_FUNCTION_PROXY_TYPE); + __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE); __ j(not_equal, &non_function); // 1. Call to function proxy. @@ -1534,28 +1534,22 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). __ bind(&non_function); - // TODO(bmeurer): I wonder why we prefer to have slow API calls? This could - // be awesome instead; i.e. a trivial improvement would be to call into the - // runtime and just deal with the API function there instead of returning a - // delegate from a runtime call that just jumps back to the runtime once - // called. Or, bonus points, call directly into the C API function here, as - // we do in some Crankshaft fast cases. + // Check if target has a [[Call]] internal method. + __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsCallable); + __ j(zero, &non_callable, Label::kNear); // Overwrite the original receiver with the (original) target. __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, edi); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(eax); - __ Push(eax); __ Push(edi); - __ CallRuntime(Runtime::kGetFunctionDelegate, 1); - __ mov(edi, eax); - __ Pop(eax); - __ SmiUntag(eax); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(edi); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1591,33 +1585,39 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // -- edi : the constructor to call (can be any Object) // ----------------------------------- - Label slow; - __ JumpIfSmi(edi, &slow, Label::kNear); + Label non_callable, non_function; + __ JumpIfSmi(edi, &non_callable); __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ j(equal, masm->isolate()->builtins()->ConstructFunction(), RelocInfo::CODE_TARGET); __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE); - __ j(not_equal, &slow, Label::kNear); + __ j(not_equal, &non_function, Label::kNear); + // 1. Construct of function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies. __ mov(edi, FieldOperand(edi, JSFunctionProxy::kConstructTrapOffset)); __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - __ bind(&slow); + // 2. Construct of something else, which might have a [[Construct]] internal + // method (if not we raise an exception). + __ bind(&non_function); + // Check if target has a [[Call]] internal method. + // TODO(bmeurer): This shoud use IsConstructor once available. + __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsCallable); + __ j(zero, &non_callable, Label::kNear); + // Overwrite the original receiver with the (original) target. + __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, edi); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Construct of something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(eax); - __ Push(eax); __ Push(edi); - __ CallRuntime(Runtime::kGetConstructorDelegate, 1); - __ mov(edi, eax); - __ Pop(eax); - __ SmiUntag(eax); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(edi); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index 912b860..939ef05 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -1596,14 +1596,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // -- a1 : the target to call (can be any Object). // ----------------------------------- - Label non_smi, non_function; - __ JumpIfSmi(a1, &non_function); + Label non_callable, non_function, non_smi; + __ JumpIfSmi(a1, &non_callable); __ bind(&non_smi); - __ GetObjectType(a1, a2, a2); + __ GetObjectType(a1, t1, t2); __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET, - eq, a2, Operand(JS_FUNCTION_TYPE)); - __ Branch(&non_function, ne, a2, Operand(JS_FUNCTION_PROXY_TYPE)); - + eq, t2, Operand(JS_FUNCTION_TYPE)); + __ Branch(&non_function, ne, t2, Operand(JS_FUNCTION_PROXY_TYPE)); // 1. Call to function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies. @@ -1614,29 +1613,25 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). __ bind(&non_function); - // TODO(bmeurer): I wonder why we prefer to have slow API calls? This could - // be awesome instead; i.e. a trivial improvement would be to call into the - // runtime and just deal with the API function there instead of returning a - // delegate from a runtime call that just jumps back to the runtime once - // called. Or, bonus points, call directly into the C API function here, as - // we do in some Crankshaft fast cases. + // Check if target has a [[Call]] internal method. + __ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset)); + __ And(t1, t1, Operand(1 << Map::kIsCallable)); + __ Branch(&non_callable, eq, t1, Operand(zero_reg)); // Overwrite the original receiver with the (original) target. __ sll(at, a0, kPointerSizeLog2); __ addu(at, sp, at); __ sw(a1, MemOperand(at)); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, a1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). - FrameScope scope(masm, StackFrame::INTERNAL); - __ sll(a0, a0, kSmiTagSize); // Smi tagged. - __ Push(a0, a1); - __ CallRuntime(Runtime::kGetFunctionDelegate, 1); - __ mov(a1, v0); - __ Pop(a0); - __ sra(a0, a0, kSmiTagSize); // Un-tag. + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(a1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(a1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1672,31 +1667,41 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // the JSFunction on which new was invoked initially) // ----------------------------------- - Label slow; - __ JumpIfSmi(a1, &slow); - __ GetObjectType(a1, t1, t1); + Label non_callable, non_function; + __ JumpIfSmi(a1, &non_callable); + __ GetObjectType(a1, t1, t2); __ Jump(masm->isolate()->builtins()->ConstructFunction(), - RelocInfo::CODE_TARGET, eq, t1, Operand(JS_FUNCTION_TYPE)); - __ Branch(&slow, ne, t1, Operand(JS_FUNCTION_PROXY_TYPE)); + RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE)); + __ Branch(&non_function, ne, t2, Operand(JS_FUNCTION_PROXY_TYPE)); + // 1. Construct of function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies. __ lw(a1, FieldMemOperand(a1, JSFunctionProxy::kConstructTrapOffset)); __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - __ bind(&slow); + // 2. Construct of something that else, which might have a [[Construct]] + // internal method (if not we raise an exception). + __ bind(&non_function); + // Check if target has a [[Call]] internal method. + // TODO(bmeurer): This shoud use IsConstructor once available. + __ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset)); + __ And(t1, t1, Operand(1 << Map::kIsCallable)); + __ Branch(&non_callable, eq, t1, Operand(zero_reg)); + // Overwrite the original receiver with the (original) target. + __ sll(at, a0, kPointerSizeLog2); + __ addu(at, sp, at); + __ sw(a1, MemOperand(at)); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, a1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Construct of something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). - FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(a0); - __ Push(a0, a1); - __ CallRuntime(Runtime::kGetConstructorDelegate, 1); - __ mov(a1, v0); - __ Pop(a0); - __ SmiUntag(a0); + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(a1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(a1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } diff --git a/src/mips64/builtins-mips64.cc b/src/mips64/builtins-mips64.cc index d23a0f1..b588f78 100644 --- a/src/mips64/builtins-mips64.cc +++ b/src/mips64/builtins-mips64.cc @@ -1593,13 +1593,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // -- a1 : the target to call (can be any Object). // ----------------------------------- - Label non_smi, non_function; - __ JumpIfSmi(a1, &non_function); + Label non_callable, non_function, non_smi; + __ JumpIfSmi(a1, &non_callable); __ bind(&non_smi); - __ GetObjectType(a1, a2, a2); + __ GetObjectType(a1, t1, t2); __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET, - eq, a2, Operand(JS_FUNCTION_TYPE)); - __ Branch(&non_function, ne, a2, Operand(JS_FUNCTION_PROXY_TYPE)); + eq, t2, Operand(JS_FUNCTION_TYPE)); + __ Branch(&non_function, ne, t2, Operand(JS_FUNCTION_PROXY_TYPE)); // 1. Call to function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies. @@ -1610,29 +1610,25 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). __ bind(&non_function); - // TODO(bmeurer): I wonder why we prefer to have slow API calls? This could - // be awesome instead; i.e. a trivial improvement would be to call into the - // runtime and just deal with the API function there instead of returning a - // delegate from a runtime call that just jumps back to the runtime once - // called. Or, bonus points, call directly into the C API function here, as - // we do in some Crankshaft fast cases. + // Check if target has a [[Call]] internal method. + __ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset)); + __ And(t1, t1, Operand(1 << Map::kIsCallable)); + __ Branch(&non_callable, eq, t1, Operand(zero_reg)); // Overwrite the original receiver with the (original) target. __ dsll(at, a0, kPointerSizeLog2); __ daddu(at, sp, at); __ sd(a1, MemOperand(at)); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, a1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). - FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(a0); - __ Push(a0, a1); - __ CallRuntime(Runtime::kGetFunctionDelegate, 1); - __ mov(a1, v0); - __ Pop(a0); - __ SmiUntag(a0); + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(a1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(a1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1667,31 +1663,41 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // the JSFunction on which new was invoked initially) // ----------------------------------- - Label slow; - __ JumpIfSmi(a1, &slow); - __ GetObjectType(a1, a5, a5); + Label non_callable, non_function; + __ JumpIfSmi(a1, &non_callable); + __ GetObjectType(a1, t1, t2); __ Jump(masm->isolate()->builtins()->ConstructFunction(), - RelocInfo::CODE_TARGET, eq, a5, Operand(JS_FUNCTION_TYPE)); - __ Branch(&slow, ne, a5, Operand(JS_FUNCTION_PROXY_TYPE)); + RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE)); + __ Branch(&non_function, ne, t2, Operand(JS_FUNCTION_PROXY_TYPE)); + // 1. Construct of function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies. __ ld(a1, FieldMemOperand(a1, JSFunctionProxy::kConstructTrapOffset)); __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - __ bind(&slow); + // 2. Construct of something that else, which might have a [[Construct]] + // internal method (if not we raise an exception). + __ bind(&non_function); + // Check if target has a [[Call]] internal method. + // TODO(bmeurer): This shoud use IsConstructor once available. + __ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset)); + __ And(t1, t1, Operand(1 << Map::kIsCallable)); + __ Branch(&non_callable, eq, t1, Operand(zero_reg)); + // Overwrite the original receiver with the (original) target. + __ dsll(at, a0, kPointerSizeLog2); + __ daddu(at, sp, at); + __ sd(a1, MemOperand(at)); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, a1); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Construct of something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). - FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(a0); - __ Push(a0, a1); - __ CallRuntime(Runtime::kGetConstructorDelegate, 1); - __ mov(a1, v0); - __ Pop(a0); - __ SmiUntag(a0); + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(a1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(a1); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } diff --git a/src/runtime/runtime-classes.cc b/src/runtime/runtime-classes.cc index 050da01..50c2cca 100644 --- a/src/runtime/runtime-classes.cc +++ b/src/runtime/runtime-classes.cc @@ -520,8 +520,9 @@ RUNTIME_FUNCTION(Runtime_DefaultConstructorCallSuper) { Handle result; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, Execution::New(super_constructor, original_constructor, - argument_count, arguments.get())); + isolate, result, + Execution::New(isolate, super_constructor, original_constructor, + argument_count, arguments.get())); return *result; } diff --git a/src/runtime/runtime-function.cc b/src/runtime/runtime-function.cc index 6585b4f..5600166 100644 --- a/src/runtime/runtime-function.cc +++ b/src/runtime/runtime-function.cc @@ -494,16 +494,9 @@ RUNTIME_FUNCTION(Runtime_NewObjectFromBound) { bound_args->get(JSFunction::kBoundArgumentsStartIndex + i), isolate); } - if (!bound_function->IsJSFunction()) { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, bound_function, - Execution::GetConstructorDelegate(isolate, bound_function)); - } - DCHECK(bound_function->IsJSFunction()); - Handle result; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, Execution::New(Handle::cast(bound_function), + isolate, result, Execution::New(isolate, bound_function, bound_function, total_argc, param_data.get())); return *result; } @@ -564,30 +557,6 @@ RUNTIME_FUNCTION(Runtime_Apply) { } -RUNTIME_FUNCTION(Runtime_GetFunctionDelegate) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); - RUNTIME_ASSERT(!object->IsJSFunction()); - Handle result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, Execution::GetFunctionDelegate(isolate, object)); - return *result; -} - - -RUNTIME_FUNCTION(Runtime_GetConstructorDelegate) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); - RUNTIME_ASSERT(!object->IsJSFunction()); - Handle result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, Execution::GetConstructorDelegate(isolate, object)); - return *result; -} - - RUNTIME_FUNCTION(Runtime_GetOriginalConstructor) { SealHandleScope shs(isolate); DCHECK(args.length() == 0); diff --git a/src/runtime/runtime-internal.cc b/src/runtime/runtime-internal.cc index 18b9481..90d5532 100644 --- a/src/runtime/runtime-internal.cc +++ b/src/runtime/runtime-internal.cc @@ -11,6 +11,8 @@ #include "src/frames-inl.h" #include "src/isolate-inl.h" #include "src/messages.h" +#include "src/parser.h" +#include "src/prettyprinter.h" namespace v8 { namespace internal { @@ -409,5 +411,39 @@ RUNTIME_FUNCTION(Runtime_GetCodeStubExportsObject) { return isolate->heap()->code_stub_exports_object(); } + +namespace { + +Handle RenderCallSite(Isolate* isolate, Handle object) { + MessageLocation location; + if (isolate->ComputeLocation(&location)) { + Zone zone; + base::SmartPointer info( + location.function()->shared()->is_function() + ? new ParseInfo(&zone, location.function()) + : new ParseInfo(&zone, location.script())); + if (Parser::ParseStatic(info.get())) { + CallPrinter printer(isolate, &zone); + const char* string = printer.Print(info->literal(), location.start_pos()); + return isolate->factory()->NewStringFromAsciiChecked(string); + } else { + isolate->clear_pending_exception(); + } + } + return Object::TypeOf(isolate, object); +} + +} // namespace + + +RUNTIME_FUNCTION(Runtime_ThrowCalledNonCallable) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); + Handle callsite = RenderCallSite(isolate, object); + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kCalledNonCallable, callsite)); +} + } // namespace internal } // namespace v8 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index c1f72bb..b22a36f 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -244,8 +244,6 @@ namespace internal { F(NewObjectFromBound, 1, 1) \ F(Call, -1 /* >= 2 */, 1) \ F(Apply, 5, 1) \ - F(GetFunctionDelegate, 1, 1) \ - F(GetConstructorDelegate, 1, 1) \ F(GetOriginalConstructor, 0, 1) \ F(CallFunction, -1 /* receiver + n args + function */, 1) \ F(IsConstructCall, 0, 1) \ @@ -341,7 +339,8 @@ namespace internal { F(HarmonyToString, 0, 1) \ F(GetTypeFeedbackVector, 1, 1) \ F(GetCallerJSFunction, 0, 1) \ - F(GetCodeStubExportsObject, 0, 1) + F(GetCodeStubExportsObject, 0, 1) \ + F(ThrowCalledNonCallable, 1, 1) #define FOR_EACH_INTRINSIC_JSON(F) \ diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index e8cc438..41c224f 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -1717,14 +1717,15 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // -- rax : the number of arguments (not including the receiver) // -- rdi : the target to call (can be any Object) // ----------------------------------- + StackArgumentsAccessor args(rsp, rax); - Label non_smi, non_function; - __ JumpIfSmi(rdi, &non_function); + Label non_callable, non_function, non_smi; + __ JumpIfSmi(rdi, &non_callable); __ bind(&non_smi); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rdx); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); __ j(equal, masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); - __ CmpInstanceType(rdx, JS_FUNCTION_PROXY_TYPE); + __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE); __ j(not_equal, &non_function); // 1. Call to function proxy. @@ -1736,29 +1737,23 @@ void Builtins::Generate_Call(MacroAssembler* masm) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). __ bind(&non_function); - // TODO(bmeurer): I wonder why we prefer to have slow API calls? This could - // be awesome instead; i.e. a trivial improvement would be to call into the - // runtime and just deal with the API function there instead of returning a - // delegate from a runtime call that just jumps back to the runtime once - // called. Or, bonus points, call directly into the C API function here, as - // we do in some Crankshaft fast cases. - StackArgumentsAccessor args(rsp, rax); + // Check if target has a [[Call]] internal method. + __ testb(FieldOperand(rcx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsCallable)); + __ j(zero, &non_callable, Label::kNear); // Overwrite the original receiver with the (original) target. __ movp(args.GetReceiverOperand(), rdi); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, rdi); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameScope scope(masm, StackFrame::INTERNAL); - __ Integer32ToSmi(rax, rax); - __ Push(rax); __ Push(rdi); - __ CallRuntime(Runtime::kGetFunctionDelegate, 1); - __ movp(rdi, rax); - __ Pop(rax); - __ SmiToInteger32(rax, rax); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(rdi); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1793,34 +1788,42 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // the JSFunction on which new was invoked initially) // -- rdi : the constructor to call (can be any Object) // ----------------------------------- + StackArgumentsAccessor args(rsp, rax); - Label slow; - __ JumpIfSmi(rdi, &slow, Label::kNear); + Label non_callable, non_function; + __ JumpIfSmi(rdi, &non_callable); __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); __ j(equal, masm->isolate()->builtins()->ConstructFunction(), RelocInfo::CODE_TARGET); __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE); - __ j(not_equal, &slow, Label::kNear); + __ j(not_equal, &non_function, Label::kNear); + // 1. Construct of function proxy. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies. __ movp(rdi, FieldOperand(rdi, JSFunctionProxy::kConstructTrapOffset)); __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - __ bind(&slow); + // 2. Construct of something else, which might have a [[Construct]] internal + // method (if not we raise an exception). + __ bind(&non_function); + // Check if target has a [[Call]] internal method. + // TODO(bmeurer): This shoud use IsConstructor once available. + __ testb(FieldOperand(rcx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsCallable)); + __ j(zero, &non_callable, Label::kNear); + // Overwrite the original receiver with the (original) target. + __ movp(args.GetReceiverOperand(), rdi); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, rdi); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Construct of something that is not callable. + __ bind(&non_callable); { - // Determine the delegate for the target (if any). FrameScope scope(masm, StackFrame::INTERNAL); - __ Integer32ToSmi(rax, rax); - __ Push(rax); __ Push(rdi); - __ CallRuntime(Runtime::kGetConstructorDelegate, 1); - __ movp(rdi, rax); - __ Pop(rax); - __ SmiToInteger32(rax, rax); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); } - // The delegate is always a regular function. - __ AssertFunction(rdi); - __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); }