Commit 0e5ed533 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Always pass correct receiver to CallFunctionTemplate.

This changes the contract of the CallFunctionTemplate builtins such that
the receiver must always be a JSReceiver, which simplifies the logic and
also makes it possible to have TurboFan completely optimize away the
ConvertReceiver operation, while at the same time ensuring that we
always pass the correct receiver to API functions.

Bug: chromium:961199, v8:8820
Change-Id: Ied9dee3f504bd5b711c29f1e2c00f0434c731d19
Cq-Include-Trybots: luci.chromium.try:linux-rel,win7-rel
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1611803
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61563}
parent fbe70b38
...@@ -498,30 +498,18 @@ void CallOrConstructBuiltinsAssembler::CallFunctionTemplate( ...@@ -498,30 +498,18 @@ void CallOrConstructBuiltinsAssembler::CallFunctionTemplate(
CodeStubArguments args(this, argc); CodeStubArguments args(this, argc);
Label throw_illegal_invocation(this, Label::kDeferred); Label throw_illegal_invocation(this, Label::kDeferred);
// For API callbacks we need to call ToObject on the receiver. // For API callbacks the receiver is always a JSReceiver (since
// And in case the receiver is a JSObject already, we might // they are treated like sloppy mode functions). We might need
// need to perform access checks in the current {context}, // to perform access checks int the current {context}, depending
// depending on whether the "needs access check" bit is // on whether the "needs access check" bit is set on the receiver
// set on the receiver _and_ the {function_template_info} // _and_ the {function_template_info} doesn't have the "accepts
// doesn't have the "accepts any receiver" bit set. // any receiver" bit set.
TVARIABLE(Object, var_receiver, args.GetReceiver()); TNode<JSReceiver> receiver = CAST(args.GetReceiver());
if (mode == CallFunctionTemplateMode::kCheckCompatibleReceiver) { if (mode == CallFunctionTemplateMode::kCheckAccess ||
// We are only interested to see that receiver is compatible mode == CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver) {
// for the {function_template_info}, and don't need to bother TNode<Map> receiver_map = LoadMap(receiver);
// doing any access checks. So ensure that the receiver is Label receiver_needs_access_check(this, Label::kDeferred),
// actually a JSReceiver.
var_receiver = ToObject_Inline(context, var_receiver.value());
} else {
Label receiver_is_primitive(this, Label::kDeferred),
receiver_needs_access_check(this, &var_receiver, Label::kDeferred),
receiver_done(this); receiver_done(this);
// Check if the receiver needs to be converted, or if it's already
// a JSReceiver, see if the "needs access check" bit is set _and_
// the {function_template_info} doesn't just accept any receiver.
GotoIf(TaggedIsSmi(var_receiver.value()), &receiver_is_primitive);
TNode<Map> receiver_map = LoadMap(CAST(var_receiver.value()));
GotoIfNot(IsJSReceiverMap(receiver_map), &receiver_is_primitive);
GotoIfNot( GotoIfNot(
IsSetWord32<Map::IsAccessCheckNeededBit>(LoadMapBitField(receiver_map)), IsSetWord32<Map::IsAccessCheckNeededBit>(LoadMapBitField(receiver_map)),
&receiver_done); &receiver_done);
...@@ -531,32 +519,14 @@ void CallOrConstructBuiltinsAssembler::CallFunctionTemplate( ...@@ -531,32 +519,14 @@ void CallOrConstructBuiltinsAssembler::CallFunctionTemplate(
1 << FunctionTemplateInfo::kAcceptAnyReceiver), 1 << FunctionTemplateInfo::kAcceptAnyReceiver),
&receiver_done, &receiver_needs_access_check); &receiver_done, &receiver_needs_access_check);
BIND(&receiver_is_primitive);
{
// Convert primitives to wrapper objects as necessary. In case
// null or undefined were passed, we need to do the access check
// on the global proxy here.
var_receiver = ToObject(context, var_receiver.value());
args.SetReceiver(var_receiver.value());
GotoIfNot(IsSetWord32<Map::IsAccessCheckNeededBit>(
LoadMapBitField(LoadMap(CAST(var_receiver.value())))),
&receiver_done);
TNode<WordT> function_template_info_flags = LoadAndUntagObjectField(
function_template_info, FunctionTemplateInfo::kFlagOffset);
Branch(IsSetWord(function_template_info_flags,
1 << FunctionTemplateInfo::kAcceptAnyReceiver),
&receiver_done, &receiver_needs_access_check);
}
BIND(&receiver_needs_access_check); BIND(&receiver_needs_access_check);
{ {
CallRuntime(Runtime::kAccessCheck, context, var_receiver.value()); CallRuntime(Runtime::kAccessCheck, context, receiver);
Goto(&receiver_done); Goto(&receiver_done);
} }
BIND(&receiver_done); BIND(&receiver_done);
} }
TNode<JSReceiver> receiver = CAST(var_receiver.value());
// Figure out the API holder for the {receiver} depending on the // Figure out the API holder for the {receiver} depending on the
// {mode} and the signature on the {function_template_info}. // {mode} and the signature on the {function_template_info}.
......
...@@ -2834,6 +2834,13 @@ Reduction JSCallReducer::ReduceCallApiFunction( ...@@ -2834,6 +2834,13 @@ Reduction JSCallReducer::ReduceCallApiFunction(
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()), graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
receiver, global_proxy, effect, control); receiver, global_proxy, effect, control);
} else { } else {
// The CallFunctionTemplate builtin requires the {receiver} to be
// an actual JSReceiver, so make sure we do the proper conversion
// first if necessary.
receiver = holder = effect =
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
receiver, global_proxy, effect, control);
// We don't have enough information to eliminate the access check // We don't have enough information to eliminate the access check
// and/or the compatible receiver check, so use the generic builtin // and/or the compatible receiver check, so use the generic builtin
// that does those checks dynamically. This is still significantly // that does those checks dynamically. This is still significantly
...@@ -2853,6 +2860,8 @@ Reduction JSCallReducer::ReduceCallApiFunction( ...@@ -2853,6 +2860,8 @@ Reduction JSCallReducer::ReduceCallApiFunction(
jsgraph()->HeapConstant(callable.code())); jsgraph()->HeapConstant(callable.code()));
node->ReplaceInput(1, jsgraph()->HeapConstant(function_template_info)); node->ReplaceInput(1, jsgraph()->HeapConstant(function_template_info));
node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc)); node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc));
node->ReplaceInput(3, receiver); // Update receiver input.
node->ReplaceInput(6 + argc, effect); // Update effect input.
NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
return Changed(node); return Changed(node);
} }
......
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