blob: dfc170e7c7948c38079b034b5be23d652d93a233 [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.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.shaking.KeepInfo.Joiner;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public class DiscardedChecker {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final InternalOptions options;
private final List<ProgramDefinition> failed = new ArrayList<>();
private DiscardedChecker(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
this.options = appView.options();
}
public static DiscardedChecker create(AppView<? extends AppInfoWithClassHierarchy> appView) {
return new DiscardedChecker(appView);
}
public static DiscardedChecker createForMainDex(
AppView<? extends AppInfoWithClassHierarchy> appView) {
MinimumKeepInfoCollection unconditionalKeepInfo =
appView
.getMainDexRootSet()
.getDependentMinimumKeepInfo()
.getOrCreateUnconditionalMinimumKeepInfo();
return new DiscardedChecker(appView) {
@Override
boolean isCheckDiscardedEnabled(ProgramDefinition definition) {
return unconditionalKeepInfo.hasMinimumKeepInfoThatMatches(
definition.getReference(), Joiner::isCheckDiscardedEnabled);
}
};
}
public List<ProgramDefinition> run(
Iterable<DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
assert failed.isEmpty();
// TODO(b/131668850): Consider only iterating the items matched by a -checkdiscard rule.
ThreadUtils.processItems(classes, this::checkClassAndMembers, executorService);
// Sort the failures for determinism.
failed.sort((item, other) -> item.getReference().compareTo(other.getReference()));
return failed;
}
boolean isCheckDiscardedEnabled(ProgramDefinition definition) {
return appView.getKeepInfo().getInfo(definition).isCheckDiscardedEnabled(options);
}
private void checkClassAndMembers(DexProgramClass clazz) {
// Only look for -checkdiscard failures for members if the class itself did not fail a
// -checkdiscard check
if (check(clazz)) {
clazz.forEachProgramMember(this::check);
}
}
/** Returns true if the check succeeded (i.e., no -checkdiscard failure was found). */
private boolean check(ProgramDefinition item) {
if (isCheckDiscardedEnabled(item)) {
// We expect few check discarded failures thus locking here should be OK.
synchronized (failed) {
failed.add(item);
}
return false;
}
return true;
}
}