| // 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.DexClass; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.logging.Log; |
| import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * 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 AppInfoWithLiveness appInfo; |
| private ScopedDexMethodSet scope = new ScopedDexMethodSet(); |
| |
| public AbstractMethodRemover(AppInfoWithLiveness appInfo) { |
| this.appInfo = appInfo; |
| } |
| |
| public void run() { |
| assert scope.getParent() == null; |
| processClass(appInfo.dexItemFactory().objectType); |
| } |
| |
| private void processClass(DexType type) { |
| DexClass holder = appInfo.definitionFor(type); |
| scope = scope.newNestedScope(); |
| if (holder != null && holder.isProgramClass()) { |
| DexEncodedMethod[] newVirtualMethods = processMethods(holder.virtualMethods()); |
| if (newVirtualMethods != null) { |
| holder.setVirtualMethods(newVirtualMethods); |
| } |
| } |
| appInfo.forAllImmediateExtendsSubtypes(type, this::processClass); |
| scope = scope.getParent(); |
| } |
| |
| private DexEncodedMethod[] processMethods(List<DexEncodedMethod> virtualMethods) { |
| if (virtualMethods == null) { |
| return null; |
| } |
| // Removal of abstract methods is rare, so avoid copying the array until we find one. |
| List<DexEncodedMethod> methods = null; |
| for (int i = 0; i < virtualMethods.size(); i++) { |
| DexEncodedMethod method = virtualMethods.get(i); |
| if (scope.addMethodIfMoreVisible(method) != AddMethodIfMoreVisibleResult.NOT_ADDED |
| || !method.accessFlags.isAbstract() |
| || appInfo.isPinned(method.method)) { |
| if (methods != null) { |
| methods.add(method); |
| } |
| } else { |
| if (methods == null) { |
| methods = new ArrayList<>(virtualMethods.size() - 1); |
| for (int j = 0; j < i; j++) { |
| methods.add(virtualMethods.get(j)); |
| } |
| } |
| if (Log.ENABLED) { |
| Log.debug(getClass(), "Removing abstract method %s.", method.method); |
| } |
| } |
| } |
| if (methods != null) { |
| return methods.toArray(DexEncodedMethod.EMPTY_ARRAY); |
| } |
| return null; |
| } |
| |
| } |