| // 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); |
| } |
| } |