23cde7f4b8
TEST=reformatting only Change-Id: I310e2568661e919a2c8354e01e19d18f7109f54c Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399261 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
253 lines
7.3 KiB
Dart
253 lines
7.3 KiB
Dart
// Copyright (c) 2017, 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.
|
|
|
|
library vm.transformations.cha_devirtualization;
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/core_types.dart' show CoreTypes;
|
|
import 'package:kernel/class_hierarchy.dart'
|
|
show ClassHierarchy, ClassHierarchySubtypes, ClosedWorldClassHierarchy;
|
|
|
|
import '../metadata/direct_call.dart';
|
|
|
|
/// Devirtualization of method invocations based on the class hierarchy
|
|
/// analysis. Assumes strong mode and closed world.
|
|
Component transformComponent(CoreTypes coreTypes, Component component) {
|
|
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
|
|
ClosedWorldClassHierarchy hierarchy =
|
|
new ClassHierarchy(
|
|
component,
|
|
coreTypes,
|
|
onAmbiguousSupertypes: ignoreAmbiguousSupertypes,
|
|
)
|
|
as ClosedWorldClassHierarchy;
|
|
final hierarchySubtypes = hierarchy.computeSubtypesInformation();
|
|
new CHADevirtualization(
|
|
coreTypes,
|
|
component,
|
|
hierarchy,
|
|
hierarchySubtypes,
|
|
).visitComponent(component);
|
|
return component;
|
|
}
|
|
|
|
/// Base class for implementing devirtualization of method invocations.
|
|
/// Subclasses should implement particular devirtualization strategy in
|
|
/// [getDirectCall] method. Once direct target is determined, the invocation
|
|
/// node is annotated with direct call metadata.
|
|
abstract class Devirtualization extends RecursiveVisitor {
|
|
/// Toggles tracing (useful for debugging).
|
|
static const _trace = const bool.fromEnvironment('trace.devirtualization');
|
|
|
|
final DirectCallMetadataRepository _metadata;
|
|
final Set<Name> _objectMemberNames;
|
|
|
|
Devirtualization(
|
|
CoreTypes coreTypes,
|
|
Component component,
|
|
ClassHierarchy hierarchy,
|
|
) : _metadata = new DirectCallMetadataRepository(),
|
|
_objectMemberNames = new Set<Name>.from(
|
|
hierarchy
|
|
.getInterfaceMembers(coreTypes.objectClass)
|
|
.map((Member m) => m.name),
|
|
) {
|
|
component.addMetadataRepository(_metadata);
|
|
}
|
|
|
|
bool isMethod(Member member) => (member is Procedure) && !member.isGetter;
|
|
|
|
bool isFieldOrGetter(Member member) =>
|
|
(member is Field) || ((member is Procedure) && member.isGetter);
|
|
|
|
bool isLegalTargetForMethodInvocation(Member target, Arguments arguments) {
|
|
final FunctionNode func = target.function!;
|
|
|
|
final positionalArgs = arguments.positional.length;
|
|
if ((positionalArgs < func.requiredParameterCount) ||
|
|
(positionalArgs > func.positionalParameters.length)) {
|
|
return false;
|
|
}
|
|
|
|
if (arguments.named.isNotEmpty || func.namedParameters.isNotEmpty) {
|
|
final names = arguments.named.map((v) => v.name).toSet();
|
|
for (var param in func.namedParameters) {
|
|
final passed = names.remove(param.name);
|
|
if (param.isRequired && !passed) {
|
|
return false;
|
|
}
|
|
}
|
|
if (names.isNotEmpty) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (arguments.types.isNotEmpty &&
|
|
arguments.types.length != func.typeParameters.length) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool hasExtraTargetForNull(DirectCallMetadata directCall) =>
|
|
directCall.checkReceiverForNull &&
|
|
_objectMemberNames.contains(directCall.targetMember!.name);
|
|
|
|
DirectCallMetadata? getDirectCall(
|
|
TreeNode node,
|
|
Member? interfaceTarget, {
|
|
bool setter = false,
|
|
});
|
|
|
|
makeDirectCall(TreeNode node, Member? target, DirectCallMetadata directCall) {
|
|
if (_trace) {
|
|
print(
|
|
"[devirt] Resolving ${target} to ${directCall.targetMember}"
|
|
" at ${node.location}",
|
|
);
|
|
}
|
|
_metadata.mapping[node] = directCall;
|
|
}
|
|
|
|
@override
|
|
visitLibrary(Library node) {
|
|
if (_trace) {
|
|
print("[devirt] Processing library ${node.name}");
|
|
}
|
|
super.visitLibrary(node);
|
|
}
|
|
|
|
void _handleMethodInvocation(
|
|
TreeNode node,
|
|
Member? target,
|
|
Arguments arguments,
|
|
) {
|
|
if (target != null && !isMethod(target)) {
|
|
return;
|
|
}
|
|
|
|
final DirectCallMetadata? directCall = getDirectCall(node, target);
|
|
|
|
// TODO(alexmarkov): Convert _isLegalTargetForMethodInvocation()
|
|
// check into an assertion once front-end implements all override checks.
|
|
if ((directCall != null) &&
|
|
isMethod(directCall.targetMember!) &&
|
|
isLegalTargetForMethodInvocation(directCall.targetMember!, arguments) &&
|
|
!hasExtraTargetForNull(directCall)) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
@override
|
|
visitInstanceInvocation(InstanceInvocation node) {
|
|
super.visitInstanceInvocation(node);
|
|
_handleMethodInvocation(node, node.interfaceTarget, node.arguments);
|
|
}
|
|
|
|
@override
|
|
visitDynamicInvocation(DynamicInvocation node) {
|
|
super.visitDynamicInvocation(node);
|
|
_handleMethodInvocation(node, null, node.arguments);
|
|
}
|
|
|
|
@override
|
|
visitEqualsCall(EqualsCall node) {
|
|
super.visitEqualsCall(node);
|
|
|
|
final target = node.interfaceTarget;
|
|
final DirectCallMetadata? directCall = getDirectCall(node, target);
|
|
if (directCall != null && !directCall.checkReceiverForNull) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
void _handlePropertyGet(TreeNode node, Member? target) {
|
|
if (target != null && !isFieldOrGetter(target)) {
|
|
return;
|
|
}
|
|
|
|
final DirectCallMetadata? directCall = getDirectCall(node, target);
|
|
|
|
if ((directCall != null) &&
|
|
isFieldOrGetter(directCall.targetMember!) &&
|
|
!hasExtraTargetForNull(directCall)) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
@override
|
|
visitInstanceGet(InstanceGet node) {
|
|
super.visitInstanceGet(node);
|
|
_handlePropertyGet(node, node.interfaceTarget);
|
|
}
|
|
|
|
@override
|
|
visitDynamicGet(DynamicGet node) {
|
|
super.visitDynamicGet(node);
|
|
_handlePropertyGet(node, null);
|
|
}
|
|
|
|
void _handlePropertySet(TreeNode node, Member? target) {
|
|
final DirectCallMetadata? directCall = getDirectCall(
|
|
node,
|
|
target,
|
|
setter: true,
|
|
);
|
|
if (directCall != null) {
|
|
makeDirectCall(node, target, directCall);
|
|
}
|
|
}
|
|
|
|
@override
|
|
visitInstanceSet(InstanceSet node) {
|
|
super.visitInstanceSet(node);
|
|
_handlePropertySet(node, node.interfaceTarget);
|
|
}
|
|
|
|
@override
|
|
visitDynamicSet(DynamicSet node) {
|
|
super.visitDynamicSet(node);
|
|
_handlePropertySet(node, null);
|
|
}
|
|
|
|
@override
|
|
visitFunctionInvocation(FunctionInvocation node) {
|
|
super.visitFunctionInvocation(node);
|
|
final DirectCallMetadata? directCall = getDirectCall(node, null);
|
|
if (directCall != null) {
|
|
makeDirectCall(node, null, directCall);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Devirtualization based on the closed-world class hierarchy analysis.
|
|
class CHADevirtualization extends Devirtualization {
|
|
final ClassHierarchySubtypes _hierarchySubtype;
|
|
|
|
CHADevirtualization(
|
|
CoreTypes coreTypes,
|
|
Component component,
|
|
ClosedWorldClassHierarchy hierarchy,
|
|
this._hierarchySubtype,
|
|
) : super(coreTypes, component, hierarchy);
|
|
|
|
@override
|
|
DirectCallMetadata? getDirectCall(
|
|
TreeNode node,
|
|
Member? interfaceTarget, {
|
|
bool setter = false,
|
|
}) {
|
|
if (interfaceTarget == null) {
|
|
return null;
|
|
}
|
|
Member? singleTarget = _hierarchySubtype
|
|
.getSingleTargetForInterfaceInvocation(interfaceTarget, setter: setter);
|
|
if (singleTarget == null) {
|
|
return null;
|
|
}
|
|
return DirectCallMetadata.targetMember(singleTarget, true);
|
|
}
|
|
}
|