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(
CodeStubArguments args(this, argc);
Label throw_illegal_invocation(this, Label::kDeferred);
// For API callbacks we need to call ToObject on the receiver.
// And in case the receiver is a JSObject already, we might
// need to perform access checks in the current {context},
// depending on whether the "needs access check" bit is
// set on the receiver _and_ the {function_template_info}
// doesn't have the "accepts any receiver" bit set.
TVARIABLE(Object, var_receiver, args.GetReceiver());
if (mode == CallFunctionTemplateMode::kCheckCompatibleReceiver) {
// We are only interested to see that receiver is compatible
// for the {function_template_info}, and don't need to bother
// doing any access checks. So ensure that the receiver is
// 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),
// For API callbacks the receiver is always a JSReceiver (since
// they are treated like sloppy mode functions). We might need
// to perform access checks int the current {context}, depending
// on whether the "needs access check" bit is set on the receiver
// _and_ the {function_template_info} doesn't have the "accepts
// any receiver" bit set.
TNode<JSReceiver> receiver = CAST(args.GetReceiver());
if (mode == CallFunctionTemplateMode::kCheckAccess ||
mode == CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver) {
TNode<Map> receiver_map = LoadMap(receiver);
Label receiver_needs_access_check(this, Label::kDeferred),
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(
IsSetWord32<Map::IsAccessCheckNeededBit>(LoadMapBitField(receiver_map)),
&receiver_done);
......@@ -531,32 +519,14 @@ void CallOrConstructBuiltinsAssembler::CallFunctionTemplate(
1 << FunctionTemplateInfo::kAcceptAnyReceiver),
&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);
{
CallRuntime(Runtime::kAccessCheck, context, var_receiver.value());
CallRuntime(Runtime::kAccessCheck, context, receiver);
Goto(&receiver_done);
}
BIND(&receiver_done);
}
TNode<JSReceiver> receiver = CAST(var_receiver.value());
// Figure out the API holder for the {receiver} depending on the
// {mode} and the signature on the {function_template_info}.
......
......@@ -2834,6 +2834,13 @@ Reduction JSCallReducer::ReduceCallApiFunction(
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
receiver, global_proxy, effect, control);
} 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
// and/or the compatible receiver check, so use the generic builtin
// that does those checks dynamically. This is still significantly
......@@ -2853,6 +2860,8 @@ Reduction JSCallReducer::ReduceCallApiFunction(
jsgraph()->HeapConstant(callable.code()));
node->ReplaceInput(1, jsgraph()->HeapConstant(function_template_info));
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));
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