blob: e0c7f8374a23dea7e47d93c1f0216c3fcddb28ae [file] [log] [blame]
// 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.DexType;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
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 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()) {
DexEncodedMethod[] newVirtualMethods =
processMethods(IterableUtils.ensureUnmodifiableList(holder.virtualMethods()));
if (newVirtualMethods != null) {
holder.setVirtualMethods(newVirtualMethods);
}
}
// TODO(b/154881041): Does this need the full subtype hierarchy of referenced types!?
subtypingInfo.forAllImmediateExtendsSubtypes(type, this::processClass);
scope = scope.getParent();
}
private DexEncodedMethod[] processMethods(List<DexEncodedMethod> virtualMethods) {
if (virtualMethods == null) {
return null;
}
// Removal of abstract methods is rare, ListUtils.filterOrElse does no copying if nothing is
// filtered out.
List<DexEncodedMethod> filteredMethods =
ListUtils.filterOrElse(virtualMethods, this::isNonAbstractPinnedOrWideningVisibility);
return filteredMethods == virtualMethods
? null
: filteredMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
}
private boolean isNonAbstractPinnedOrWideningVisibility(DexEncodedMethod method) {
if (!method.accessFlags.isAbstract()) {
return true;
}
// Check if the method widens visibility. Adding to the scope mutates it.
if (scope.addMethodIfMoreVisible(method) != AddMethodIfMoreVisibleResult.NOT_ADDED) {
return true;
}
if (appView.appInfo().isPinned(method.getReference())) {
return true;
}
// We will filter the method out since it is not pinned.
if (Log.ENABLED) {
Log.debug(getClass(), "Removing abstract method %s.", method.getReference());
}
return false;
}
}