Skip to content

Commit 4305541

Browse files
alexmarkovCommit Queue
authored andcommitted
[vm] Support implicit dynamic calls
According to the spec, a call in the form e(a0,...,aN) where static type of 'e' is 'dynamic' should succeed only if (1) 'e' evaluates to a function, or (2) runtime type of 'e' has a 'call' *method*. If runtime type of 'e' has a 'call' getter this invocation should fail with NSM. This behavior is different from 'e.call(a0,...,aN)' which accepts 'call' getters. --- In order to implement this behavior in the VM, a special 'dyn:implicit:call' selector is added. It behaves similarly to 'dyn:call' except when looking for a getter target. This selector is used when CFE sets FlagImplicitCall on a DynamicInvocation node. TEST=co19/Language/Expressions/Function_Invocation/Function_Expression_Invocation/call_A04_t01 TEST=co19/Language/Expressions/Function_Invocation/Function_Expression_Invocation/call_A04_t02 Fixes #59965 Issue #59952 Issue #51517 Issue dart-lang/language#3482 Change-Id: Ic45f7743ad75571476642dcec9c91e6a77e8e321 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407161 Reviewed-by: Slava Egorov <[email protected]> Commit-Queue: Alexander Markov <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent 052e03c commit 4305541

File tree

8 files changed

+60
-37
lines changed

8 files changed

+60
-37
lines changed

runtime/vm/compiler/aot/precompiler.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@ void Precompiler::AddCalleesOfHelper(const Object& entry,
993993
const auto& call_site = UnlinkedCall::Cast(entry);
994994
// A dynamic call.
995995
*temp_selector = call_site.target_name();
996+
*temp_selector = Function::DropImplicitCallPrefix(*temp_selector).ptr();
996997
AddSelector(*temp_selector);
997998
if (IsPotentialClosureCall(*temp_selector)) {
998999
const Array& arguments_descriptor =
@@ -1005,6 +1006,7 @@ void Precompiler::AddCalleesOfHelper(const Object& entry,
10051006
// A dynamic call.
10061007
const auto& cache = MegamorphicCache::Cast(entry);
10071008
*temp_selector = cache.target_name();
1009+
*temp_selector = Function::DropImplicitCallPrefix(*temp_selector).ptr();
10081010
AddSelector(*temp_selector);
10091011
if (IsPotentialClosureCall(*temp_selector)) {
10101012
const Array& arguments_descriptor =

runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,7 +2818,10 @@ Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p,
28182818
// read flags.
28192819
const uint8_t flags = ReadFlags();
28202820
bool is_invariant = false;
2821-
if (!is_dynamic) {
2821+
bool is_implicit_call = false;
2822+
if (is_dynamic) {
2823+
is_implicit_call = (flags & kDynamicInvocationFlagImplicitCall) != 0;
2824+
} else {
28222825
is_invariant = (flags & kInstanceInvocationFlagInvariant) != 0;
28232826
}
28242827

@@ -2939,14 +2942,17 @@ Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p,
29392942
// at the entry because the parameter is marked covariant, neither of
29402943
// those cases require a dynamic invocation forwarder.
29412944
const Function* direct_call_target = &direct_call.target_;
2942-
if (H.IsRoot(itarget_name) &&
2943-
(name.ptr() != Symbols::EqualOperator().ptr())) {
2945+
if (is_dynamic && (name.ptr() != Symbols::EqualOperator().ptr())) {
29442946
mangled_name = &String::ZoneHandle(
29452947
Z, Function::CreateDynamicInvocationForwarderName(name));
29462948
if (!direct_call_target->IsNull()) {
29472949
direct_call_target = &Function::ZoneHandle(
29482950
direct_call_target->GetDynamicInvocationForwarder(*mangled_name));
29492951
}
2952+
if (is_implicit_call) {
2953+
ASSERT(mangled_name->ptr() == Symbols::DynamicCall().ptr());
2954+
mangled_name = &Symbols::DynamicImplicitCall();
2955+
}
29502956
}
29512957

29522958
if (!direct_call_target->IsNull()) {

runtime/vm/compiler/frontend/kernel_to_il.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3640,6 +3640,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfInvokeFieldDispatcher(
36403640
const Class& owner = Class::Handle(Z, function.Owner());
36413641
ASSERT(!owner.IsNull());
36423642
auto& field_name = String::Handle(Z, function.name());
3643+
ASSERT(field_name.ptr() != Symbols::DynamicImplicitCall().ptr());
36433644
// If the field name has a dyn: tag, then remove it. We don't add dynamic
36443645
// invocation forwarders for field getters used for invoking, we just use
36453646
// the tag in the name of the invoke field dispatcher to detect dynamic calls.

runtime/vm/object.cc

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3989,6 +3989,7 @@ FunctionPtr Class::CreateInvocationDispatcher(
39893989
const String& target_name,
39903990
const Array& args_desc,
39913991
UntaggedFunction::Kind kind) const {
3992+
ASSERT(target_name.ptr() != Symbols::DynamicImplicitCall().ptr());
39923993
Thread* thread = Thread::Current();
39933994
Zone* zone = thread->zone();
39943995
FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
@@ -4329,12 +4330,22 @@ bool Function::IsDynamicInvocationForwarderName(StringPtr name) {
43294330
}
43304331

43314332
StringPtr Function::DemangleDynamicInvocationForwarderName(const String& name) {
4333+
if (name.ptr() == Symbols::DynamicImplicitCall().ptr()) {
4334+
return Symbols::call().ptr();
4335+
}
43324336
const intptr_t kDynamicPrefixLength = 4; // "dyn:"
43334337
ASSERT(Symbols::DynamicPrefix().Length() == kDynamicPrefixLength);
43344338
return Symbols::New(Thread::Current(), name, kDynamicPrefixLength,
43354339
name.Length() - kDynamicPrefixLength);
43364340
}
43374341

4342+
const String& Function::DropImplicitCallPrefix(const String& name) {
4343+
if (name.ptr() == Symbols::DynamicImplicitCall().ptr()) {
4344+
return Symbols::DynamicCall();
4345+
}
4346+
return name;
4347+
}
4348+
43384349
StringPtr Function::CreateDynamicInvocationForwarderName(const String& name) {
43394350
return Symbols::FromConcat(Thread::Current(), Symbols::DynamicPrefix(), name);
43404351
}
@@ -4395,6 +4406,7 @@ FunctionPtr Function::CreateDynamicInvocationForwarder(
43954406
FunctionPtr Function::GetDynamicInvocationForwarder(
43964407
const String& mangled_name) const {
43974408
ASSERT(IsDynamicInvocationForwarderName(mangled_name));
4409+
ASSERT(mangled_name.ptr() != Symbols::DynamicImplicitCall().ptr());
43984410
auto thread = Thread::Current();
43994411
auto zone = thread->zone();
44004412
const Class& owner = Class::Handle(zone, Owner());
@@ -10806,12 +10818,6 @@ bool Function::IsClosureCallDispatcher() const {
1080610818
return name() == Symbols::call().ptr();
1080710819
}
1080810820

10809-
bool Function::IsClosureCallGetter() const {
10810-
if (!IsGetterFunction()) return false;
10811-
if (!Class::IsClosureClass(Owner())) return false;
10812-
return name() == Symbols::GetCall().ptr();
10813-
}
10814-
1081510821
FunctionPtr Function::ImplicitClosureFunction() const {
1081610822
// Return the existing implicit closure function if any.
1081710823
if (implicit_closure_function() != Function::null()) {

runtime/vm/object.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3343,10 +3343,6 @@ class Function : public Object {
33433343
// Function interface for closures.
33443344
bool IsClosureCallDispatcher() const;
33453345

3346-
// Returns true if this function is _Closure.get:call, which returns the
3347-
// closure object for invocation.
3348-
bool IsClosureCallGetter() const;
3349-
33503346
bool IsDynamicInvocationForwarder() const {
33513347
return kind() == UntaggedFunction::kDynamicInvocationForwarder;
33523348
}
@@ -4063,6 +4059,7 @@ class Function : public Object {
40634059
static bool IsDynamicInvocationForwarderName(StringPtr name);
40644060

40654061
static StringPtr DemangleDynamicInvocationForwarderName(const String& name);
4062+
static const String& DropImplicitCallPrefix(const String& name);
40664063

40674064
static StringPtr CreateDynamicInvocationForwarderName(const String& name);
40684065

runtime/vm/resolver.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ static FunctionPtr ResolveDynamicAnyArgsWithCustomLookup(
4040
zone,
4141
Function::DemangleDynamicInvocationForwarderName(function_name))
4242
: &function_name;
43+
const String& dispatcher_name =
44+
Function::DropImplicitCallPrefix(function_name);
4345

4446
const bool is_getter = Field::IsGetterName(*demangled_name);
4547
const String* const method_name_to_extract =
@@ -53,7 +55,7 @@ static FunctionPtr ResolveDynamicAnyArgsWithCustomLookup(
5355
if (is_dyn_call) {
5456
// If a dyn:* forwarder already exists, return it.
5557
function = cls.GetInvocationDispatcher(
56-
function_name, Array::null_array(),
58+
dispatcher_name, Array::null_array(),
5759
UntaggedFunction::kDynamicInvocationForwarder,
5860
/*create_if_absent=*/false);
5961
if (!function.IsNull()) return function.ptr();
@@ -67,7 +69,7 @@ static FunctionPtr ResolveDynamicAnyArgsWithCustomLookup(
6769
#if !defined(DART_PRECOMPILED_RUNTIME)
6870
if (allow_add && is_dyn_call && !function.IsNull()) {
6971
// In JIT mode, lazily create a dyn:* forwarder if one is required.
70-
function = function.GetDynamicInvocationForwarder(function_name);
72+
function = function.GetDynamicInvocationForwarder(dispatcher_name);
7173
}
7274
#endif
7375
if (!function.IsNull()) return function.ptr();

runtime/vm/runtime_entry.cc

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,9 +1700,10 @@ static bool ResolveCallThroughGetter(const Class& receiver_class,
17001700
// We do this on the target_name, _not_ on the demangled name, so that
17011701
// FlowGraphBuilder::BuildGraphOfInvokeFieldDispatcher can detect dynamic
17021702
// calls from the dyn: tag on the name of the dispatcher.
1703+
const String& dispatcher_name = Function::DropImplicitCallPrefix(target_name);
17031704
const Function& target_function =
17041705
Function::Handle(receiver_class.GetInvocationDispatcher(
1705-
target_name, arguments_descriptor,
1706+
dispatcher_name, arguments_descriptor,
17061707
UntaggedFunction::kInvokeFieldDispatcher, create_if_absent));
17071708
ASSERT(!create_if_absent || !target_function.IsNull());
17081709
if (FLAG_trace_ic) {
@@ -1728,13 +1729,15 @@ FunctionPtr InlineCacheMissHelper(const Class& receiver_class,
17281729
Function::DemangleDynamicInvocationForwarderName(target_name));
17291730
}
17301731
const bool is_getter = Field::IsGetterName(*demangled);
1732+
const bool is_dyn_implicit_call =
1733+
target_name.ptr() == Symbols::DynamicImplicitCall().ptr();
17311734
Function& result = Function::Handle();
17321735
#if defined(DART_PRECOMPILED_RUNTIME)
17331736
const bool create_if_absent = false;
17341737
#else
17351738
const bool create_if_absent = true;
17361739
#endif
1737-
if (is_getter ||
1740+
if (is_getter || (is_dyn_implicit_call && !receiver_class.IsClosureClass()) ||
17381741
!ResolveCallThroughGetter(receiver_class, target_name, *demangled,
17391742
args_descriptor, &result)) {
17401743
ArgumentsDescriptor desc(args_descriptor);
@@ -2915,6 +2918,8 @@ static ObjectPtr InvokeCallThroughGetterOrNoSuchMethod(
29152918
const Array& orig_arguments_desc) {
29162919
const bool is_dynamic_call =
29172920
Function::IsDynamicInvocationForwarderName(target_name);
2921+
const bool is_dyn_implicit_call =
2922+
target_name.ptr() == Symbols::DynamicImplicitCall().ptr();
29182923
String& demangled_target_name = String::Handle(zone, target_name.ptr());
29192924
if (is_dynamic_call) {
29202925
demangled_target_name =
@@ -2976,7 +2981,7 @@ static ObjectPtr InvokeCallThroughGetterOrNoSuchMethod(
29762981
}
29772982

29782983
// Dynamic call sites have to use the dynamic getter as well (if it was
2979-
// created).
2984+
// created), unless its a dynamic implicit call to 'call'.
29802985
const auto& getter_name =
29812986
String::Handle(zone, Field::GetterName(demangled_target_name));
29822987
const auto& dyn_getter_name = String::Handle(
@@ -3003,31 +3008,34 @@ static ObjectPtr InvokeCallThroughGetterOrNoSuchMethod(
30033008
}
30043009
}
30053010

3006-
// If there is a getter we need to call-through-getter.
3007-
if (is_dynamic_call) {
3008-
function = Resolver::ResolveDynamicFunction(zone, cls, dyn_getter_name);
3009-
}
3010-
if (function.IsNull()) {
3011-
function = Resolver::ResolveDynamicFunction(zone, cls, getter_name);
3012-
}
3013-
if (!function.IsNull()) {
3014-
const Array& getter_arguments = Array::Handle(Array::New(1));
3015-
getter_arguments.SetAt(0, receiver);
3016-
const Object& getter_result = Object::Handle(
3017-
zone, DartEntry::InvokeFunction(function, getter_arguments));
3018-
if (getter_result.IsError()) {
3019-
return getter_result.ptr();
3011+
if (!is_dyn_implicit_call) {
3012+
// If there is a getter we need to call-through-getter.
3013+
if (is_dynamic_call) {
3014+
function =
3015+
Resolver::ResolveDynamicFunction(zone, cls, dyn_getter_name);
30203016
}
3021-
ASSERT(getter_result.IsNull() || getter_result.IsInstance());
3017+
if (function.IsNull()) {
3018+
function = Resolver::ResolveDynamicFunction(zone, cls, getter_name);
3019+
}
3020+
if (!function.IsNull()) {
3021+
const Array& getter_arguments = Array::Handle(Array::New(1));
3022+
getter_arguments.SetAt(0, receiver);
3023+
const Object& getter_result = Object::Handle(
3024+
zone, DartEntry::InvokeFunction(function, getter_arguments));
3025+
if (getter_result.IsError()) {
3026+
return getter_result.ptr();
3027+
}
3028+
ASSERT(getter_result.IsNull() || getter_result.IsInstance());
30223029

3023-
orig_arguments.SetAt(args_desc.FirstArgIndex(), getter_result);
3024-
return DartEntry::InvokeClosure(thread, orig_arguments,
3025-
orig_arguments_desc);
3030+
orig_arguments.SetAt(args_desc.FirstArgIndex(), getter_result);
3031+
return DartEntry::InvokeClosure(thread, orig_arguments,
3032+
orig_arguments_desc);
3033+
}
30263034
}
30273035
cls = cls.SuperClass();
30283036
}
30293037

3030-
if (receiver.IsRecord()) {
3038+
if (receiver.IsRecord() && !is_dyn_implicit_call) {
30313039
const Record& record = Record::Cast(receiver);
30323040
const intptr_t field_index =
30333041
record.GetFieldIndexByName(thread, demangled_target_name);

runtime/vm/symbols.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class ObjectPointerVisitor;
9494
V(DynamicCallCurrentParamIndexVar, ":dyn_call_current_param_index") \
9595
V(DynamicCallCurrentTypeParamVar, ":dyn_call_current_type_param") \
9696
V(DynamicCallFunctionTypeArgsVar, ":dyn_call_function_type_args") \
97+
V(DynamicImplicitCall, "dyn:implicit:call") \
9798
V(DynamicPrefix, "dyn:") \
9899
V(EntryPointsTemp, ":entry_points_temp") \
99100
V(EqualOperator, "==") \

0 commit comments

Comments
 (0)