// 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 _objectMemberNames; Devirtualization( CoreTypes coreTypes, Component component, ClassHierarchy hierarchy, ) : _metadata = new DirectCallMetadataRepository(), _objectMemberNames = new Set.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); } }