Files
sdk/pkg/vm/lib/transformations/call_site_annotator.dart
T
Alexander Markov 293cc6475a [vm/aot/tfa] Whole-program propagation of closure values
This change adds propagation of closure values to TFA.
For now, inferred closure values are used only when they are
constant (tear-off of a static method or a constructor).

Arbitrary closures can now be referenced from unrelated
members via closure-id metadata.

In addition, this change fixes an incorrect stack trace when
an implicit closure (tear-off) was propagated and its call was
inlined. Inlining interval was not recorded because of the missing
source position of a call to a target method within implicit closure.

TEST=pkg/vm/testcases/transformations/type_flow/transformer/closures.dart
Issue: https://github.com/dart-lang/sdk/issues/39692

Change-Id: I3590da91b6057e0b55a8614382dba1bbcc267b39
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/325447
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
2023-10-12 16:16:21 +00:00

116 lines
4.1 KiB
Dart

// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This transformation annotates call sites with the receiver type.
// This is done to avoid reimplementing [Expression.getStaticType] in
// C++.
// We don't annotate all call-sites, but only those where VM could benefit from
// knowing static type of the receiver.
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/type_environment.dart'
show StaticTypeContext, TypeEnvironment;
import '../metadata/call_site_attributes.dart';
CallSiteAttributesMetadataRepository addRepositoryTo(Component component) {
return component.metadata.putIfAbsent(
CallSiteAttributesMetadataRepository.repositoryTag,
() => new CallSiteAttributesMetadataRepository())
as CallSiteAttributesMetadataRepository;
}
void transformLibraries(Component component, List<Library> libraries,
CoreTypes coreTypes, ClassHierarchy hierarchy) {
final transformer =
new AnnotateWithStaticTypes(component, coreTypes, hierarchy);
libraries.forEach(transformer.visitLibrary);
}
class AnnotateWithStaticTypes extends RecursiveVisitor {
final CallSiteAttributesMetadataRepository _metadata;
final TypeEnvironment env;
StaticTypeContext? _staticTypeContext;
AnnotateWithStaticTypes(
Component component, CoreTypes coreTypes, ClassHierarchy hierarchy)
: _metadata = addRepositoryTo(component),
env = new TypeEnvironment(coreTypes, hierarchy);
@override
defaultMember(Member node) {
_staticTypeContext = new StaticTypeContext(node, env);
super.defaultMember(node);
_staticTypeContext = null;
}
void annotateWithReceiver(TreeNode node, Expression receiver) {
annotateWithReceiverType(node, receiver.getStaticType(_staticTypeContext!));
}
void annotateWithReceiverType(TreeNode node, DartType receiverType) {
_metadata.mapping[node] =
new CallSiteAttributesMetadata(receiverType: receiverType);
}
@override
visitInstanceSet(InstanceSet node) {
super.visitInstanceSet(node);
if (hasGenericCovariantParameters(node.interfaceTarget)) {
annotateWithReceiver(node, node.receiver);
}
}
@override
visitInstanceInvocation(InstanceInvocation node) {
super.visitInstanceInvocation(node);
final DartType receiverType =
node.receiver.getStaticType(_staticTypeContext!);
if (receiverType is FunctionType && node.name.text == 'call') {
throw 'Node ${node.runtimeType}: $node at ${node.location} has receiver'
' static type $receiverType and selector \'call\'';
}
// TODO(34162): We don't need to save the type here for calls, just whether
// or not it's a statically-checked call.
if (hasGenericCovariantParameters(node.interfaceTarget)) {
annotateWithReceiverType(node, receiverType);
}
}
@override
visitEqualsCall(EqualsCall node) {
super.visitEqualsCall(node);
// TODO(34162): We don't need to save the type here for calls, just whether
// or not it's a statically-checked call.
if (hasGenericCovariantParameters(node.interfaceTarget)) {
annotateWithReceiver(node, node.left);
}
}
/// Return [true] if the given list of [VariableDeclaration] contains
/// any annotated with generic-covariant-impl.
static bool containsGenericCovariantImpl(List<VariableDeclaration> decls) =>
decls.any((p) => p.isCovariantByClass);
/// Returns [true] if the given [member] has any parameters annotated with
/// generic-covariant-impl attribute.
static bool hasGenericCovariantParameters(Member? member) {
if (member is Procedure) {
return containsGenericCovariantImpl(
member.function.positionalParameters) ||
containsGenericCovariantImpl(member.function.namedParameters);
} else if (member is Field) {
return member.isCovariantByClass;
}
return false;
}
}