| // Copyright (c) 2017, the R8 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. |
| package com.android.tools.r8.shaking; |
| |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.graph.SubtypingInfo; |
| import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * Removes abstract methods if they only shadow methods of the same signature in a superclass. |
| * <p> |
| * We do not consider classes from the library for this optimization, as the program might run |
| * against a different version of the library where methods are missing. |
| * <p> |
| * This optimization is beneficial mostly as it removes superfluous abstract methods that are |
| * created by the {@link TreePruner}. |
| */ |
| public class AbstractMethodRemover { |
| |
| private final AppView<AppInfoWithLiveness> appView; |
| private final SubtypingInfo subtypingInfo; |
| private ScopedDexMethodSet scope = new ScopedDexMethodSet(); |
| |
| public AbstractMethodRemover(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo) { |
| this.appView = appView; |
| this.subtypingInfo = subtypingInfo; |
| } |
| |
| public void run() { |
| assert scope.getParent() == null; |
| processClass(appView.dexItemFactory().objectType); |
| } |
| |
| private void processClass(DexType type) { |
| DexClass holder = appView.definitionFor(type); |
| scope = scope.newNestedScope(); |
| if (holder != null && holder.isProgramClass()) { |
| processMethods(holder.asProgramClass()); |
| } |
| // TODO(b/154881041): Does this need the full subtype hierarchy of referenced types!? |
| subtypingInfo.forAllImmediateExtendsSubtypes(type, this::processClass); |
| scope = scope.getParent(); |
| } |
| |
| private void processMethods(DexProgramClass clazz) { |
| Set<DexEncodedMethod> toRemove = null; |
| for (ProgramMethod method : clazz.virtualProgramMethods()) { |
| if (!isNonAbstractPinnedOrWideningVisibility(method)) { |
| if (toRemove == null) { |
| toRemove = new HashSet<>(); |
| } |
| toRemove.add(method.getDefinition()); |
| } |
| } |
| if (toRemove != null) { |
| clazz.getMethodCollection().removeMethods(toRemove); |
| } |
| } |
| |
| private boolean isNonAbstractPinnedOrWideningVisibility(ProgramMethod method) { |
| if (!method.getAccessFlags().isAbstract()) { |
| return true; |
| } |
| // Check if the method widens visibility. Adding to the scope mutates it. |
| if (scope.addMethodIfMoreVisible(method.getDefinition()) |
| != AddMethodIfMoreVisibleResult.NOT_ADDED) { |
| return true; |
| } |
| if (appView.appInfo().isPinned(method)) { |
| return true; |
| } |
| // We will filter the method out since it is not pinned. |
| return false; |
| } |
| |
| } |