| // 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; |
| } |
| } |