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, ...@@ -4244,18 +4244,9 @@ MaybeLocal<Value> Object::CallAsFunction(Local<Context> context,
auto recv_obj = Utils::OpenHandle(*recv); auto recv_obj = Utils::OpenHandle(*recv);
STATIC_ASSERT(sizeof(v8::Local<v8::Value>) == sizeof(i::Object**)); 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::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; Local<Value> result;
has_pending_exception = !ToLocal<Value>( 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_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result); RETURN_ESCAPED(result);
} }
...@@ -4278,21 +4269,9 @@ MaybeLocal<Value> Object::CallAsConstructor(Local<Context> context, int argc, ...@@ -4278,21 +4269,9 @@ MaybeLocal<Value> Object::CallAsConstructor(Local<Context> context, int argc,
auto self = Utils::OpenHandle(this); auto self = Utils::OpenHandle(this);
STATIC_ASSERT(sizeof(v8::Local<v8::Value>) == sizeof(i::Object**)); 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::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; Local<Value> result;
has_pending_exception = !ToLocal<Value>( 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_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result); RETURN_ESCAPED(result);
} }
......
...@@ -1585,13 +1585,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1585,13 +1585,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// -- r1 : the target to call (can be any Object). // -- r1 : the target to call (can be any Object).
// ----------------------------------- // -----------------------------------
Label non_smi, non_function; Label non_callable, non_function, non_smi;
__ JumpIfSmi(r1, &non_function); __ JumpIfSmi(r1, &non_callable);
__ bind(&non_smi); __ 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, __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET,
eq); eq);
__ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE)); __ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE));
__ b(ne, &non_function); __ b(ne, &non_function);
// 1. Call to function proxy. // 1. Call to function proxy.
...@@ -1603,27 +1603,23 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1603,27 +1603,23 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// 2. Call to something else, which might have a [[Call]] internal method (if // 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception). // not we raise an exception).
__ bind(&non_function); __ bind(&non_function);
// TODO(bmeurer): I wonder why we prefer to have slow API calls? This could // Check if target has a [[Call]] internal method.
// be awesome instead; i.e. a trivial improvement would be to call into the __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
// runtime and just deal with the API function there instead of returning a __ tst(r4, Operand(1 << Map::kIsCallable));
// delegate from a runtime call that just jumps back to the runtime once __ b(eq, &non_callable);
// called. Or, bonus points, call directly into the C API function here, as // Overwrite the original receiver the (original) target.
// we do in some Crankshaft fast cases.
// Overwrite the original receiver with the (original) target.
__ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); __ 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); FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(r0); __ Push(r1);
__ Push(r0, r1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ CallRuntime(Runtime::kGetFunctionDelegate, 1);
__ mov(r1, r0);
__ Pop(r0);
__ SmiUntag(r0);
} }
// 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) { ...@@ -1658,32 +1654,40 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// the JSFunction on which new was invoked initially) // the JSFunction on which new was invoked initially)
// ----------------------------------- // -----------------------------------
Label slow; Label non_callable, non_function;
__ JumpIfSmi(r1, &slow); __ JumpIfSmi(r1, &non_callable);
__ CompareObjectType(r1, r5, r5, JS_FUNCTION_TYPE); __ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE);
__ Jump(masm->isolate()->builtins()->ConstructFunction(), __ Jump(masm->isolate()->builtins()->ConstructFunction(),
RelocInfo::CODE_TARGET, eq); RelocInfo::CODE_TARGET, eq);
__ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE)); __ 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. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ ldr(r1, FieldMemOperand(r1, JSFunctionProxy::kConstructTrapOffset)); __ ldr(r1, FieldMemOperand(r1, JSFunctionProxy::kConstructTrapOffset));
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); __ 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); FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(r0); __ Push(r1);
__ Push(r0, r1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1);
__ mov(r1, r0);
__ Pop(r0);
__ SmiUntag(r0);
} }
// 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) { ...@@ -1647,14 +1647,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// -- x1 : the target to call (can be any Object). // -- x1 : the target to call (can be any Object).
// ----------------------------------- // -----------------------------------
Label non_smi, non_jsfunction, non_function; Label non_callable, non_function, non_smi;
__ JumpIfSmi(x1, &non_function); __ JumpIfSmi(x1, &non_callable);
__ Bind(&non_smi); __ Bind(&non_smi);
__ CompareObjectType(x1, x2, x2, JS_FUNCTION_TYPE); __ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE);
__ B(ne, &non_jsfunction); __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET,
__ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); eq);
__ Bind(&non_jsfunction); __ Cmp(x5, JS_FUNCTION_PROXY_TYPE);
__ Cmp(x2, JS_FUNCTION_PROXY_TYPE);
__ B(ne, &non_function); __ B(ne, &non_function);
// 1. Call to function proxy. // 1. Call to function proxy.
...@@ -1666,27 +1665,22 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1666,27 +1665,22 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// 2. Call to something else, which might have a [[Call]] internal method (if // 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception). // not we raise an exception).
__ Bind(&non_function); __ Bind(&non_function);
// TODO(bmeurer): I wonder why we prefer to have slow API calls? This could // Check if target has a [[Call]] internal method.
// be awesome instead; i.e. a trivial improvement would be to call into the __ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset));
// runtime and just deal with the API function there instead of returning a __ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable);
// 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. // Overwrite the original receiver with the (original) target.
__ Poke(x1, Operand(x0, LSL, kXRegSizeLog2)); __ 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); FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(x0); __ Push(x1);
__ Push(x0, x1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ CallRuntime(Runtime::kGetFunctionDelegate, 1);
__ Mov(x1, x0);
__ Pop(x0);
__ SmiUntag(x0);
} }
// 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) { ...@@ -1722,32 +1716,39 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// the JSFunction on which new was invoked initially) // the JSFunction on which new was invoked initially)
// ----------------------------------- // -----------------------------------
Label slow; Label non_callable, non_function;
__ JumpIfSmi(x1, &slow); __ JumpIfSmi(x1, &non_callable);
__ CompareObjectType(x1, x5, x5, JS_FUNCTION_TYPE); __ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE);
__ Jump(masm->isolate()->builtins()->ConstructFunction(), __ Jump(masm->isolate()->builtins()->ConstructFunction(),
RelocInfo::CODE_TARGET, eq); RelocInfo::CODE_TARGET, eq);
__ Cmp(x5, Operand(JS_FUNCTION_PROXY_TYPE)); __ Cmp(x5, 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. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ Ldr(x1, FieldMemOperand(x1, JSFunctionProxy::kConstructTrapOffset)); __ Ldr(x1, FieldMemOperand(x1, JSFunctionProxy::kConstructTrapOffset));
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); __ 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); FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(x0); __ Push(x1);
__ Push(x0, x1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1);
__ Mov(x1, x0);
__ Pop(x0);
__ SmiUntag(x0);
} }
// 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) { ...@@ -178,13 +178,13 @@ double Simulator::CallDouble(byte* entry, CallArgument* args) {
int64_t Simulator::CallJS(byte* entry, int64_t Simulator::CallJS(byte* entry,
Object* new_target, Object* new_target,
JSFunction* func, Object* target,
Object* revc, Object* revc,
int64_t argc, int64_t argc,
Object*** argv) { Object*** argv) {
CallArgument args[] = { CallArgument args[] = {
CallArgument(new_target), CallArgument(new_target),
CallArgument(func), CallArgument(target),
CallArgument(revc), CallArgument(revc),
CallArgument(argc), CallArgument(argc),
CallArgument(argv), CallArgument(argv),
...@@ -193,6 +193,7 @@ int64_t Simulator::CallJS(byte* entry, ...@@ -193,6 +193,7 @@ int64_t Simulator::CallJS(byte* entry,
return CallInt64(entry, args); return CallInt64(entry, args);
} }
int64_t Simulator::CallRegExp(byte* entry, int64_t Simulator::CallRegExp(byte* entry,
String* input, String* input,
int64_t start_offset, int64_t start_offset,
......
...@@ -188,7 +188,7 @@ class Simulator : public DecoderVisitor { ...@@ -188,7 +188,7 @@ class Simulator : public DecoderVisitor {
// which set up the simulator state and grab the result on return. // which set up the simulator state and grab the result on return.
int64_t CallJS(byte* entry, int64_t CallJS(byte* entry,
Object* new_target, Object* new_target,
JSFunction* func, Object* target,
Object* revc, Object* revc,
int64_t argc, int64_t argc,
Object*** argv); Object*** argv);
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
#include "src/deoptimizer.h" #include "src/deoptimizer.h"
#include "src/isolate-inl.h" #include "src/isolate-inl.h"
#include "src/messages.h" #include "src/messages.h"
#include "src/parser.h"
#include "src/prettyprinter.h"
#include "src/vm-state-inl.h" #include "src/vm-state-inl.h"
namespace v8 { namespace v8 {
...@@ -55,47 +53,12 @@ static void PrintDeserializedCodeInfo(Handle<JSFunction> function) { ...@@ -55,47 +53,12 @@ static void PrintDeserializedCodeInfo(Handle<JSFunction> function) {
namespace { namespace {
MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct, MUST_USE_RESULT MaybeHandle<Object> Invoke(Isolate* isolate, bool is_construct,
Handle<JSFunction> function, Handle<Object> target,
Handle<Object> receiver, int argc, Handle<Object> receiver, int argc,
Handle<Object> args[], Handle<Object> args[],
Handle<Object> new_target) { Handle<Object> new_target) {
Isolate* const isolate = function->GetIsolate(); DCHECK(!receiver->IsGlobalObject());
// 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<GlobalObject>::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<Object>();
} else {
isolate->clear_pending_message();
}
return value;
}
// Entering JavaScript. // Entering JavaScript.
VMState<JS> state(isolate); VMState<JS> state(isolate);
...@@ -109,7 +72,7 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct, ...@@ -109,7 +72,7 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct,
// Placeholder for return value. // Placeholder for return value.
Object* value = NULL; Object* value = NULL;
typedef Object* (*JSEntryFunction)(Object* new_target, Object* function, typedef Object* (*JSEntryFunction)(Object* new_target, Object* target,
Object* receiver, int argc, Object* receiver, int argc,
Object*** args); Object*** args);
...@@ -117,10 +80,6 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct, ...@@ -117,10 +80,6 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct,
? isolate->factory()->js_construct_entry_code() ? isolate->factory()->js_construct_entry_code()
: isolate->factory()->js_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 // Save and restore context around invocation and block the
// allocation of handles without explicit handle scopes. // allocation of handles without explicit handle scopes.
...@@ -130,10 +89,12 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct, ...@@ -130,10 +89,12 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct,
// Call the function through the right JS entry stub. // Call the function through the right JS entry stub.
Object* orig_func = *new_target; Object* orig_func = *new_target;
JSFunction* func = *function; Object* func = *target;
Object* recv = *receiver; Object* recv = *receiver;
Object*** argv = reinterpret_cast<Object***>(args); Object*** argv = reinterpret_cast<Object***>(args);
if (FLAG_profile_deserialization) PrintDeserializedCodeInfo(function); if (FLAG_profile_deserialization && target->IsJSFunction()) {
PrintDeserializedCodeInfo(Handle<JSFunction>::cast(target));
}
value = CALL_GENERATED_CODE(stub_entry, orig_func, func, recv, argc, argv); value = CALL_GENERATED_CODE(stub_entry, orig_func, func, recv, argc, argv);
} }
...@@ -163,30 +124,64 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct, ...@@ -163,30 +124,64 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(bool is_construct,
} // namespace } // namespace
// static
MaybeHandle<Object> Execution::Call(Isolate* isolate, Handle<Object> callable, MaybeHandle<Object> Execution::Call(Isolate* isolate, Handle<Object> callable,
Handle<Object> receiver, int argc, Handle<Object> receiver, int argc,
Handle<Object> argv[]) { Handle<Object> argv[]) {
if (!callable->IsJSFunction()) { // Convert calls on global objects to be calls on the global
ASSIGN_RETURN_ON_EXCEPTION(isolate, callable, // receiver instead to avoid having a 'this' pointer which refers
GetFunctionDelegate(isolate, callable), Object); // directly to a global object.
if (receiver->IsGlobalObject()) {
receiver =
handle(Handle<GlobalObject>::cast(receiver)->global_proxy(), isolate);
}
// api callbacks can be called directly.
if (callable->IsJSFunction() &&
Handle<JSFunction>::cast(callable)->shared()->IsApiFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::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<Object>();
} else {
isolate->clear_pending_message();
}
return value;
} }
Handle<JSFunction> func = Handle<JSFunction>::cast(callable); return Invoke(isolate, false, callable, receiver, argc, argv,
return Invoke(false, func, receiver, argc, argv,
isolate->factory()->undefined_value()); isolate->factory()->undefined_value());
} }
// static
MaybeHandle<Object> Execution::New(Handle<JSFunction> constructor, int argc, MaybeHandle<Object> Execution::New(Handle<JSFunction> constructor, int argc,
Handle<Object> argv[]) { Handle<Object> argv[]) {
return New(constructor, constructor, argc, argv); return New(constructor->GetIsolate(), constructor, constructor, argc, argv);
} }
MaybeHandle<Object> Execution::New(Handle<JSFunction> constructor, // static
Handle<JSFunction> new_target, int argc, MaybeHandle<Object> Execution::New(Isolate* isolate, Handle<Object> constructor,
Handle<Object> new_target, int argc,
Handle<Object> argv[]) { Handle<Object> argv[]) {
return Invoke(true, constructor, handle(constructor->global_proxy()), argc, return Invoke(isolate, true, constructor,
argv, new_target); isolate->factory()->undefined_value(), argc, argv, new_target);
} }
...@@ -234,98 +229,6 @@ MaybeHandle<Object> Execution::TryCall(Handle<JSFunction> func, ...@@ -234,98 +229,6 @@ MaybeHandle<Object> Execution::TryCall(Handle<JSFunction> func,
} }
// static
MaybeHandle<JSFunction> Execution::GetFunctionDelegate(Isolate* isolate,
Handle<Object> 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<String> callsite = RenderCallSite(isolate, object);
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kCalledNonCallable, callsite),
JSFunction);
}
// static
MaybeHandle<JSFunction> Execution::GetConstructorDelegate(
Isolate* isolate, Handle<Object> 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<String> callsite = RenderCallSite(isolate, object);
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kCalledNonCallable, callsite),
JSFunction);
}
// static
Handle<String> Execution::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);
}
void StackGuard::SetStackLimit(uintptr_t limit) { void StackGuard::SetStackLimit(uintptr_t limit) {
ExecutionAccess access(isolate_); ExecutionAccess access(isolate_);
// If the current limits are special (e.g. due to a pending interrupt) then // If the current limits are special (e.g. due to a pending interrupt) then
......
...@@ -35,8 +35,9 @@ class Execution final : public AllStatic { ...@@ -35,8 +35,9 @@ class Execution final : public AllStatic {
MUST_USE_RESULT static MaybeHandle<Object> New(Handle<JSFunction> constructor, MUST_USE_RESULT static MaybeHandle<Object> New(Handle<JSFunction> constructor,
int argc, int argc,
Handle<Object> argv[]); Handle<Object> argv[]);
MUST_USE_RESULT static MaybeHandle<Object> New(Handle<JSFunction> constructor, MUST_USE_RESULT static MaybeHandle<Object> New(Isolate* isolate,
Handle<JSFunction> new_target, Handle<Object> constructor,
Handle<Object> new_target,
int argc, int argc,
Handle<Object> argv[]); Handle<Object> argv[]);
...@@ -88,20 +89,6 @@ class Execution final : public AllStatic { ...@@ -88,20 +89,6 @@ class Execution final : public AllStatic {
Handle<JSFunction> fun, Handle<JSFunction> fun,
Handle<Object> pos, Handle<Object> pos,
Handle<Object> is_global); 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) { ...@@ -1516,13 +1516,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// -- edi : the target to call (can be any Object). // -- edi : the target to call (can be any Object).
// ----------------------------------- // -----------------------------------
Label non_smi, non_function; Label non_callable, non_function, non_smi;
__ JumpIfSmi(edi, &non_function); __ JumpIfSmi(edi, &non_callable);
__ bind(&non_smi); __ bind(&non_smi);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, edx); __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(equal, masm->isolate()->builtins()->CallFunction(), __ j(equal, masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
__ CmpInstanceType(edx, JS_FUNCTION_PROXY_TYPE); __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
__ j(not_equal, &non_function); __ j(not_equal, &non_function);
// 1. Call to function proxy. // 1. Call to function proxy.
...@@ -1534,28 +1534,22 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1534,28 +1534,22 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// 2. Call to something else, which might have a [[Call]] internal method (if // 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception). // not we raise an exception).
__ bind(&non_function); __ bind(&non_function);
// TODO(bmeurer): I wonder why we prefer to have slow API calls? This could // Check if target has a [[Call]] internal method.
// be awesome instead; i.e. a trivial improvement would be to call into the __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsCallable);
// runtime and just deal with the API function there instead of returning a __ j(zero, &non_callable, Label::kNear);
// 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. // Overwrite the original receiver with the (original) target.
__ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); __ 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); FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(eax);
__ Push(eax);
__ Push(edi); __ Push(edi);
__ CallRuntime(Runtime::kGetFunctionDelegate, 1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ mov(edi, eax);
__ Pop(eax);
__ SmiUntag(eax);
} }
// 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) { ...@@ -1591,33 +1585,39 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// -- edi : the constructor to call (can be any Object) // -- edi : the constructor to call (can be any Object)
// ----------------------------------- // -----------------------------------
Label slow; Label non_callable, non_function;
__ JumpIfSmi(edi, &slow, Label::kNear); __ JumpIfSmi(edi, &non_callable);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(equal, masm->isolate()->builtins()->ConstructFunction(), __ j(equal, masm->isolate()->builtins()->ConstructFunction(),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
__ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE); __ 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. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ mov(edi, FieldOperand(edi, JSFunctionProxy::kConstructTrapOffset)); __ mov(edi, FieldOperand(edi, JSFunctionProxy::kConstructTrapOffset));
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); __ 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); FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(eax);
__ Push(eax);
__ Push(edi); __ Push(edi);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ mov(edi, eax);
__ Pop(eax);
__ SmiUntag(eax);
} }
// 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) { ...@@ -1596,14 +1596,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// -- a1 : the target to call (can be any Object). // -- a1 : the target to call (can be any Object).
// ----------------------------------- // -----------------------------------
Label non_smi, non_function; Label non_callable, non_function, non_smi;
__ JumpIfSmi(a1, &non_function); __ JumpIfSmi(a1, &non_callable);
__ bind(&non_smi); __ bind(&non_smi);
__ GetObjectType(a1, a2, a2); __ GetObjectType(a1, t1, t2);
__ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET, __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET,
eq, a2, Operand(JS_FUNCTION_TYPE)); eq, t2, Operand(JS_FUNCTION_TYPE));
__ Branch(&non_function, ne, a2, Operand(JS_FUNCTION_PROXY_TYPE)); __ Branch(&non_function, ne, t2, Operand(JS_FUNCTION_PROXY_TYPE));
// 1. Call to function proxy. // 1. Call to function proxy.
// TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies. // TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies.
...@@ -1614,29 +1613,25 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1614,29 +1613,25 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// 2. Call to something else, which might have a [[Call]] internal method (if // 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception). // not we raise an exception).
__ bind(&non_function); __ bind(&non_function);
// TODO(bmeurer): I wonder why we prefer to have slow API calls? This could // Check if target has a [[Call]] internal method.
// be awesome instead; i.e. a trivial improvement would be to call into the __ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset));
// runtime and just deal with the API function there instead of returning a __ And(t1, t1, Operand(1 << Map::kIsCallable));
// delegate from a runtime call that just jumps back to the runtime once __ Branch(&non_callable, eq, t1, Operand(zero_reg));
// 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. // Overwrite the original receiver with the (original) target.
__ sll(at, a0, kPointerSizeLog2); __ sll(at, a0, kPointerSizeLog2);
__ addu(at, sp, at); __ addu(at, sp, at);
__ sw(a1, MemOperand(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). FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
FrameScope scope(masm, StackFrame::INTERNAL); __ Push(a1);
__ sll(a0, a0, kSmiTagSize); // Smi tagged. __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ Push(a0, a1);
__ CallRuntime(Runtime::kGetFunctionDelegate, 1);
__ mov(a1, v0);
__ Pop(a0);
__ sra(a0, a0, kSmiTagSize); // Un-tag.
} }
// 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) { ...@@ -1672,31 +1667,41 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// the JSFunction on which new was invoked initially) // the JSFunction on which new was invoked initially)
// ----------------------------------- // -----------------------------------
Label slow; Label non_callable, non_function;
__ JumpIfSmi(a1, &slow); __ JumpIfSmi(a1, &non_callable);
__ GetObjectType(a1, t1, t1); __ GetObjectType(a1, t1, t2);
__ Jump(masm->isolate()->builtins()->ConstructFunction(), __ Jump(masm->isolate()->builtins()->ConstructFunction(),
RelocInfo::CODE_TARGET, eq, t1, Operand(JS_FUNCTION_TYPE)); RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
__ Branch(&slow, ne, t1, Operand(JS_FUNCTION_PROXY_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. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ lw(a1, FieldMemOperand(a1, JSFunctionProxy::kConstructTrapOffset)); __ lw(a1, FieldMemOperand(a1, JSFunctionProxy::kConstructTrapOffset));
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); __ 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). FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
FrameScope scope(masm, StackFrame::INTERNAL); __ Push(a1);
__ SmiTag(a0); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ Push(a0, a1);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1);
__ mov(a1, v0);
__ Pop(a0);
__ SmiUntag(a0);
} }
// 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) { ...@@ -1593,13 +1593,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// -- a1 : the target to call (can be any Object). // -- a1 : the target to call (can be any Object).
// ----------------------------------- // -----------------------------------
Label non_smi, non_function; Label non_callable, non_function, non_smi;
__ JumpIfSmi(a1, &non_function); __ JumpIfSmi(a1, &non_callable);
__ bind(&non_smi); __ bind(&non_smi);
__ GetObjectType(a1, a2, a2); __ GetObjectType(a1, t1, t2);
__ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET, __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET,
eq, a2, Operand(JS_FUNCTION_TYPE)); eq, t2, Operand(JS_FUNCTION_TYPE));
__ Branch(&non_function, ne, a2, Operand(JS_FUNCTION_PROXY_TYPE)); __ Branch(&non_function, ne, t2, Operand(JS_FUNCTION_PROXY_TYPE));
// 1. Call to function proxy. // 1. Call to function proxy.
// TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies. // TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies.
...@@ -1610,29 +1610,25 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1610,29 +1610,25 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// 2. Call to something else, which might have a [[Call]] internal method (if // 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception). // not we raise an exception).
__ bind(&non_function); __ bind(&non_function);
// TODO(bmeurer): I wonder why we prefer to have slow API calls? This could // Check if target has a [[Call]] internal method.
// be awesome instead; i.e. a trivial improvement would be to call into the __ lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset));
// runtime and just deal with the API function there instead of returning a __ And(t1, t1, Operand(1 << Map::kIsCallable));
// delegate from a runtime call that just jumps back to the runtime once __ Branch(&non_callable, eq, t1, Operand(zero_reg));
// 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. // Overwrite the original receiver with the (original) target.
__ dsll(at, a0, kPointerSizeLog2); __ dsll(at, a0, kPointerSizeLog2);
__ daddu(at, sp, at); __ daddu(at, sp, at);
__ sd(a1, MemOperand(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). FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
FrameScope scope(masm, StackFrame::INTERNAL); __ Push(a1);
__ SmiTag(a0); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ Push(a0, a1);
__ CallRuntime(Runtime::kGetFunctionDelegate, 1);
__ mov(a1, v0);
__ Pop(a0);
__ SmiUntag(a0);
} }
// 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) { ...@@ -1667,31 +1663,41 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// the JSFunction on which new was invoked initially) // the JSFunction on which new was invoked initially)
// ----------------------------------- // -----------------------------------
Label slow; Label non_callable, non_function;
__ JumpIfSmi(a1, &slow); __ JumpIfSmi(a1, &non_callable);
__ GetObjectType(a1, a5, a5); __ GetObjectType(a1, t1, t2);
__ Jump(masm->isolate()->builtins()->ConstructFunction(), __ Jump(masm->isolate()->builtins()->ConstructFunction(),
RelocInfo::CODE_TARGET, eq, a5, Operand(JS_FUNCTION_TYPE)); RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
__ Branch(&slow, ne, a5, Operand(JS_FUNCTION_PROXY_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. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ ld(a1, FieldMemOperand(a1, JSFunctionProxy::kConstructTrapOffset)); __ ld(a1, FieldMemOperand(a1, JSFunctionProxy::kConstructTrapOffset));
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); __ 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). FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
FrameScope scope(masm, StackFrame::INTERNAL); __ Push(a1);
__ SmiTag(a0); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ Push(a0, a1);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1);
__ mov(a1, v0);
__ Pop(a0);
__ SmiUntag(a0);
} }
// 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) { ...@@ -520,8 +520,9 @@ RUNTIME_FUNCTION(Runtime_DefaultConstructorCallSuper) {
Handle<Object> result; Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION( ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, Execution::New(super_constructor, original_constructor, isolate, result,
argument_count, arguments.get())); Execution::New(isolate, super_constructor, original_constructor,
argument_count, arguments.get()));
return *result; return *result;
} }
......
...@@ -494,16 +494,9 @@ RUNTIME_FUNCTION(Runtime_NewObjectFromBound) { ...@@ -494,16 +494,9 @@ RUNTIME_FUNCTION(Runtime_NewObjectFromBound) {
bound_args->get(JSFunction::kBoundArgumentsStartIndex + i), isolate); 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; Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 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())); total_argc, param_data.get()));
return *result; return *result;
} }
...@@ -564,30 +557,6 @@ RUNTIME_FUNCTION(Runtime_Apply) { ...@@ -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) { RUNTIME_FUNCTION(Runtime_GetOriginalConstructor) {
SealHandleScope shs(isolate); SealHandleScope shs(isolate);
DCHECK(args.length() == 0); DCHECK(args.length() == 0);
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include "src/frames-inl.h" #include "src/frames-inl.h"
#include "src/isolate-inl.h" #include "src/isolate-inl.h"
#include "src/messages.h" #include "src/messages.h"
#include "src/parser.h"
#include "src/prettyprinter.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -409,5 +411,39 @@ RUNTIME_FUNCTION(Runtime_GetCodeStubExportsObject) { ...@@ -409,5 +411,39 @@ RUNTIME_FUNCTION(Runtime_GetCodeStubExportsObject) {
return isolate->heap()->code_stub_exports_object(); 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 internal
} // namespace v8 } // namespace v8
...@@ -244,8 +244,6 @@ namespace internal { ...@@ -244,8 +244,6 @@ namespace internal {
F(NewObjectFromBound, 1, 1) \ F(NewObjectFromBound, 1, 1) \
F(Call, -1 /* >= 2 */, 1) \ F(Call, -1 /* >= 2 */, 1) \
F(Apply, 5, 1) \ F(Apply, 5, 1) \
F(GetFunctionDelegate, 1, 1) \
F(GetConstructorDelegate, 1, 1) \
F(GetOriginalConstructor, 0, 1) \ F(GetOriginalConstructor, 0, 1) \
F(CallFunction, -1 /* receiver + n args + function */, 1) \ F(CallFunction, -1 /* receiver + n args + function */, 1) \
F(IsConstructCall, 0, 1) \ F(IsConstructCall, 0, 1) \
...@@ -341,7 +339,8 @@ namespace internal { ...@@ -341,7 +339,8 @@ namespace internal {
F(HarmonyToString, 0, 1) \ F(HarmonyToString, 0, 1) \
F(GetTypeFeedbackVector, 1, 1) \ F(GetTypeFeedbackVector, 1, 1) \
F(GetCallerJSFunction, 0, 1) \ F(GetCallerJSFunction, 0, 1) \
F(GetCodeStubExportsObject, 0, 1) F(GetCodeStubExportsObject, 0, 1) \
F(ThrowCalledNonCallable, 1, 1)
#define FOR_EACH_INTRINSIC_JSON(F) \ #define FOR_EACH_INTRINSIC_JSON(F) \
......
...@@ -1717,14 +1717,15 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1717,14 +1717,15 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// -- rax : the number of arguments (not including the receiver) // -- rax : the number of arguments (not including the receiver)
// -- rdi : the target to call (can be any Object) // -- rdi : the target to call (can be any Object)
// ----------------------------------- // -----------------------------------
StackArgumentsAccessor args(rsp, rax);
Label non_smi, non_function; Label non_callable, non_function, non_smi;
__ JumpIfSmi(rdi, &non_function); __ JumpIfSmi(rdi, &non_callable);
__ bind(&non_smi); __ bind(&non_smi);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rdx); __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
__ j(equal, masm->isolate()->builtins()->CallFunction(), __ j(equal, masm->isolate()->builtins()->CallFunction(),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
__ CmpInstanceType(rdx, JS_FUNCTION_PROXY_TYPE); __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
__ j(not_equal, &non_function); __ j(not_equal, &non_function);
// 1. Call to function proxy. // 1. Call to function proxy.
...@@ -1736,29 +1737,23 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1736,29 +1737,23 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
// 2. Call to something else, which might have a [[Call]] internal method (if // 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception). // not we raise an exception).
__ bind(&non_function); __ bind(&non_function);
// TODO(bmeurer): I wonder why we prefer to have slow API calls? This could // Check if target has a [[Call]] internal method.
// be awesome instead; i.e. a trivial improvement would be to call into the __ testb(FieldOperand(rcx, Map::kBitFieldOffset),
// runtime and just deal with the API function there instead of returning a Immediate(1 << Map::kIsCallable));
// delegate from a runtime call that just jumps back to the runtime once __ j(zero, &non_callable, Label::kNear);
// called. Or, bonus points, call directly into the C API function here, as
// we do in some Crankshaft fast cases.
StackArgumentsAccessor args(rsp, rax);
// Overwrite the original receiver with the (original) target. // Overwrite the original receiver with the (original) target.
__ movp(args.GetReceiverOperand(), rdi); __ 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); FrameScope scope(masm, StackFrame::INTERNAL);
__ Integer32ToSmi(rax, rax);
__ Push(rax);
__ Push(rdi); __ Push(rdi);
__ CallRuntime(Runtime::kGetFunctionDelegate, 1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ movp(rdi, rax);
__ Pop(rax);
__ SmiToInteger32(rax, rax);
} }
// 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) { ...@@ -1793,34 +1788,42 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// the JSFunction on which new was invoked initially) // the JSFunction on which new was invoked initially)
// -- rdi : the constructor to call (can be any Object) // -- rdi : the constructor to call (can be any Object)
// ----------------------------------- // -----------------------------------
StackArgumentsAccessor args(rsp, rax);
Label slow; Label non_callable, non_function;
__ JumpIfSmi(rdi, &slow, Label::kNear); __ JumpIfSmi(rdi, &non_callable);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
__ j(equal, masm->isolate()->builtins()->ConstructFunction(), __ j(equal, masm->isolate()->builtins()->ConstructFunction(),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
__ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE); __ 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. // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ movp(rdi, FieldOperand(rdi, JSFunctionProxy::kConstructTrapOffset)); __ movp(rdi, FieldOperand(rdi, JSFunctionProxy::kConstructTrapOffset));
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); __ 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); FrameScope scope(masm, StackFrame::INTERNAL);
__ Integer32ToSmi(rax, rax);
__ Push(rax);
__ Push(rdi); __ Push(rdi);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1); __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
__ movp(rdi, rax);
__ Pop(rax);
__ SmiToInteger32(rax, rax);
} }
// 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