Commit 634d1d86 authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent c281c15d
......@@ -4244,18 +4244,9 @@ MaybeLocal<Value> Object::CallAsFunction(Local<Context> context,
auto recv_obj = Utils::OpenHandle(*recv);
STATIC_ASSERT(sizeof(v8::Local<v8::Value>) == sizeof(i::Object**));
i::Handle<i::Object>* args = reinterpret_cast<i::Handle<i::Object>*>(argv);
i::Handle<i::JSFunction> fun;
if (self->IsJSFunction()) {
fun = i::Handle<i::JSFunction>::cast(self);
} else {
has_pending_exception =
!i::Execution::GetFunctionDelegate(isolate, self).ToHandle(&fun);
RETURN_ON_FAILED_EXECUTION(Value);
recv_obj = self;
}
Local<Value> result;
has_pending_exception = !ToLocal<Value>(
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<Value> Object::CallAsConstructor(Local<Context> context, int argc,
auto self = Utils::OpenHandle(this);
STATIC_ASSERT(sizeof(v8::Local<v8::Value>) == sizeof(i::Object**));
i::Handle<i::Object>* args = reinterpret_cast<i::Handle<i::Object>*>(argv);
if (self->IsJSFunction()) {
auto fun = i::Handle<i::JSFunction>::cast(self);
Local<Value> result;
has_pending_exception =
!ToLocal<Value>(i::Execution::New(fun, argc, args), &result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
i::Handle<i::JSFunction> fun;
has_pending_exception =
!i::Execution::GetConstructorDelegate(isolate, self).ToHandle(&fun);
RETURN_ON_FAILED_EXECUTION(Value);
Local<Value> result;
has_pending_exception = !ToLocal<Value>(
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);
}
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -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,
......
......@@ -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);
......
This diff is collapsed.
......@@ -35,8 +35,9 @@ class Execution final : public AllStatic {
MUST_USE_RESULT static MaybeHandle<Object> New(Handle<JSFunction> constructor,
int argc,
Handle<Object> argv[]);
MUST_USE_RESULT static MaybeHandle<Object> New(Handle<JSFunction> constructor,
Handle<JSFunction> new_target,
MUST_USE_RESULT static MaybeHandle<Object> New(Isolate* isolate,
Handle<Object> constructor,
Handle<Object> new_target,
int argc,
Handle<Object> argv[]);
......@@ -88,20 +89,6 @@ class Execution final : public AllStatic {
Handle<JSFunction> fun,
Handle<Object> pos,
Handle<Object> is_global);
// Get a function delegate for the given non-function object.
// Used for support calling objects as functions.
MUST_USE_RESULT static MaybeHandle<JSFunction> GetFunctionDelegate(
Isolate* isolate, Handle<Object> 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<JSFunction> GetConstructorDelegate(
Isolate* isolate, Handle<Object> object);
private:
MUST_USE_RESULT static Handle<String> RenderCallSite(Isolate* isolate,
Handle<Object> object);
};
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -520,8 +520,9 @@ RUNTIME_FUNCTION(Runtime_DefaultConstructorCallSuper) {
Handle<Object> 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;
}
......
......@@ -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<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, Execution::New(Handle<JSFunction>::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<JSFunction> 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<JSFunction> 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);
......
......@@ -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<String> RenderCallSite(Isolate* isolate, Handle<Object> object) {
MessageLocation location;
if (isolate->ComputeLocation(&location)) {
Zone zone;
base::SmartPointer<ParseInfo> 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<String> callsite = RenderCallSite(isolate, object);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kCalledNonCallable, callsite));
}
} // namespace internal
} // namespace v8
......@@ -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) \
......
......@@ -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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment