| // 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.AppInfoWithSubtyping; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.logging.Log; |
| import com.android.tools.r8.utils.MethodSignatureEquivalence; |
| import com.google.common.base.Equivalence; |
| import com.google.common.base.Equivalence.Wrapper; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| public class AbstractMethodRemover { |
| |
| private final AppInfoWithSubtyping appInfo; |
| private ScopedDexItemSet scope; |
| |
| public AbstractMethodRemover(AppInfoWithSubtyping appInfo) { |
| this.appInfo = appInfo; |
| } |
| |
| public void run() { |
| assert scope == null; |
| processClass(appInfo.dexItemFactory.objectType); |
| } |
| |
| private void processClass(DexType type) { |
| DexClass holder = appInfo.definitionFor(type); |
| scope = new ScopedDexItemSet(scope); |
| if (holder != null) { |
| holder.setVirtualMethods(processMethods(holder.virtualMethods())); |
| } |
| type.forAllExtendsSubtypes(this::processClass); |
| scope = scope.getParent(); |
| } |
| |
| private DexEncodedMethod[] processMethods(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.length; i++) { |
| DexEncodedMethod method = virtualMethods[i]; |
| if (scope.addMethod(method.method) || !method.accessFlags.isAbstract()) { |
| if (methods != null) { |
| methods.add(method); |
| } |
| } else { |
| if (methods == null) { |
| methods = new ArrayList<>(virtualMethods.length - 1); |
| for (int j = 0; j < i; j++) { |
| methods.add(virtualMethods[j]); |
| } |
| } |
| if (Log.ENABLED) { |
| Log.debug(getClass(), "Removing abstract method %s.", method.method); |
| } |
| } |
| } |
| return methods == null ? virtualMethods : methods.toArray(new DexEncodedMethod[methods.size()]); |
| } |
| |
| private static class ScopedDexItemSet { |
| private static Equivalence<DexMethod> METHOD_EQUIVALENCE = MethodSignatureEquivalence.get(); |
| |
| private final ScopedDexItemSet parent; |
| private final Set<Wrapper<DexMethod>> items = new HashSet<>(); |
| |
| private ScopedDexItemSet(ScopedDexItemSet parent) { |
| this.parent = parent; |
| } |
| |
| private boolean contains(Wrapper<DexMethod> item) { |
| return items.contains(item) |
| || ((parent != null) && parent.contains(item)); |
| } |
| |
| boolean addMethod(DexMethod method) { |
| Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method); |
| return !contains(wrapped) && items.add(wrapped); |
| } |
| |
| ScopedDexItemSet getParent() { |
| return parent; |
| } |
| } |
| } |