| // 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 CoreTypes coreTypes; | |
| final DirectCallMetadataRepository _metadata; | |
| final Set<Name> _objectMemberNames; | |
| Devirtualization( | |
| this.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.parameterName); | |
| 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; | |
| } | |
| final bool checkReceiverForNull = | |
| singleTarget.enclosingClass != coreTypes.objectClass; | |
| return DirectCallMetadata.targetMember(singleTarget, checkReceiverForNull); | |
| } | |
| } |