Skip to content

Commit 556f0fb

Browse files
sstricklcommit-bot@chromium.org
authored andcommitted
[vm] Remove non-covariant checks from closure bodies (part 1)
This change only affects compilation when running in non-precompiled mode with --no-lazy-dispatchers enabled. Instead of always compiling in non-covariant checks, even for closures not called dynamically, remove the non-covariant checks from the closure and instead do the non-covariant checks for dynamic calls during the NoSuchMethodForCallStub fallback by calling Function::DoArgumentTypesMatch. Adds two overloads for Function::DoArgumentTypesMatch, one which takes a function type argument vector and one which takes neither an instantiator type argument vector or a function type argument vector. For the versions that are not explicitly passed a type argument vector, an appropriate one is constructed using the arguments. If there is not enough information in the arguments, then we fall back to assuming the empty type argument vector for the instantiator case and instantiating to bounds in the function type argument case. Fixes Function::DoArgumentTypesMatch to handle generic functions and to check arguments appropriately according to the active null safety mode. For generic functions, the provided or resulting function type vector has non-covariant checks performed against the type parameter bounds. This change uncovered one test that was incorrectly passing in strong mode, see #42688 for details. Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try,vm-dartkb-linux-release-x64-try Change-Id: I5030a46b356778448b51a243f16406eacf1c04bf Bug: #40813 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153202 Commit-Queue: Tess Strickland <[email protected]> Reviewed-by: Régis Crelier <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent c326744 commit 556f0fb

20 files changed

+526
-141
lines changed

runtime/lib/mirrors.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,12 @@ DEFINE_NATIVE_ENTRY(ClassMirror_invokeConstructor, 0, 5) {
15211521
InvocationMirror::kMethod);
15221522
UNREACHABLE();
15231523
}
1524+
#if defined(DEBUG)
1525+
// Make sure the receiver is the null value, so that DoArgumentTypesMatch does
1526+
// not attempt to retrieve the instantiator type arguments from the receiver.
1527+
explicit_argument = args.At(args_descriptor.FirstArgIndex());
1528+
ASSERT(explicit_argument.IsNull());
1529+
#endif
15241530
const Object& type_error =
15251531
Object::Handle(redirected_constructor.DoArgumentTypesMatch(
15261532
args, args_descriptor, type_arguments));

runtime/vm/compiler/frontend/prologue_builder.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ bool PrologueBuilder::PrologueSkippableOnUncheckedEntry(
4040

4141
bool PrologueBuilder::HasEmptyPrologue(const Function& function) {
4242
return !function.HasOptionalParameters() && !function.IsGeneric() &&
43-
!function.CanReceiveDynamicInvocation();
43+
!function.CanReceiveDynamicInvocation() &&
44+
!function.IsClosureFunction();
4445
}
4546

4647
BlockEntryInstr* PrologueBuilder::BuildPrologue(BlockEntryInstr* entry,
@@ -81,6 +82,8 @@ BlockEntryInstr* PrologueBuilder::BuildPrologue(BlockEntryInstr* entry,
8182
}
8283

8384
const bool is_empty_prologue = prologue.entry == prologue.current;
85+
// Double-check we create empty prologues when HasEmptyPrologue returns true.
86+
ASSERT(!HasEmptyPrologue(function_) || is_empty_prologue);
8487

8588
// Always do this to preserve deoptid numbering.
8689
JoinEntryInstr* normal_code = BuildJoinEntry();

runtime/vm/compiler/frontend/scope_builder.cc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,7 @@ bool MethodCanSkipTypeChecksForNonCovariantArguments(
3232
// In AOT mode we don't dynamically generate such trampolines but instead rely
3333
// on a static analysis to discover which methods can be invoked dynamically,
3434
// and generate the necessary trampolines during precompilation.
35-
if (method.name() == Symbols::Call().raw() ||
36-
method.CanReceiveDynamicInvocation()) {
37-
// Currently we consider all call methods to be invoked dynamically and
38-
// don't mangle their names.
39-
// TODO(vegorov) remove this once we also introduce special type checking
40-
// entry point for closures.
41-
return false;
42-
}
43-
return true;
35+
return !method.CanReceiveDynamicInvocation();
4436
}
4537

4638
ScopeBuilder::ScopeBuilder(ParsedFunction* parsed_function)

runtime/vm/dart_api_impl.cc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3806,11 +3806,14 @@ static Dart_Handle NewExternalTypedData(
38063806
CHECK_LENGTH(length, ExternalTypedData::MaxElements(cid));
38073807
Zone* zone = thread->zone();
38083808
intptr_t bytes = length * ExternalTypedData::ElementSizeInBytes(cid);
3809-
const ExternalTypedData& result = ExternalTypedData::Handle(
3810-
zone,
3811-
ExternalTypedData::New(cid, reinterpret_cast<uint8_t*>(data), length,
3812-
thread->heap()->SpaceForExternal(bytes)));
3813-
if (callback != NULL) {
3809+
auto& cls = Class::Handle(zone, thread->isolate()->class_table()->At(cid));
3810+
auto& result = Object::Handle(zone, cls.EnsureIsAllocateFinalized(thread));
3811+
if (result.IsError()) {
3812+
return Api::NewHandle(thread, result.raw());
3813+
}
3814+
result = ExternalTypedData::New(cid, reinterpret_cast<uint8_t*>(data), length,
3815+
thread->heap()->SpaceForExternal(bytes));
3816+
if (callback != nullptr) {
38143817
AllocateFinalizableHandle(thread, result, peer, external_allocation_size,
38153818
callback);
38163819
}

runtime/vm/dart_entry.cc

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,20 @@ ObjectPtr DartEntry::ResolveCallable(const Array& arguments,
226226
// The null instance cannot resolve to a callable, so we can stop there.
227227
for (instance ^= arguments.At(receiver_index); !instance.IsNull();
228228
instance ^= arguments.At(receiver_index)) {
229-
// If the current instance is a compatible callable, return its function.
230-
if (instance.IsCallable(&function) &&
231-
function.AreValidArgumentCounts(type_args_len, args_count,
232-
named_args_count, nullptr)) {
233-
return function.raw();
229+
// The instance is a callable, so check that its function is compatible.
230+
if (instance.IsCallable(&function)) {
231+
bool matches = function.AreValidArgumentCounts(type_args_len, args_count,
232+
named_args_count, nullptr);
233+
234+
if (matches && type_args_len > 0 && function.IsClosureFunction()) {
235+
// Though the closure function is generic, the closure itself may
236+
// not be because it closes over delayed function type arguments.
237+
matches = Closure::Cast(instance).IsGeneric(thread);
238+
}
239+
240+
if (matches) {
241+
return function.raw();
242+
}
234243
}
235244

236245
// Special case: closures are implemented with a call getter instead of a

0 commit comments

Comments
 (0)