|  | // Copyright (c) 2019, 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.ir.optimize; | 
|  |  | 
|  | import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; | 
|  |  | 
|  | import com.android.tools.r8.graph.AppView; | 
|  | import com.android.tools.r8.graph.DexEncodedMember; | 
|  | import com.android.tools.r8.graph.DexProgramClass; | 
|  | import com.android.tools.r8.shaking.AppInfoWithLiveness; | 
|  | import com.android.tools.r8.utils.BooleanBox; | 
|  | import com.android.tools.r8.utils.IterableUtils; | 
|  | import com.android.tools.r8.utils.ThreadUtils; | 
|  | import com.android.tools.r8.utils.Timing; | 
|  | import com.google.common.collect.Sets; | 
|  | import java.util.Set; | 
|  | import java.util.concurrent.ExecutionException; | 
|  | import java.util.concurrent.ExecutorService; | 
|  |  | 
|  | /** | 
|  | * This pass: | 
|  | * | 
|  | * <ul> | 
|  | *   <li>cleans the nests: it removes missing nest host/members from the input, | 
|  | *   <li>clears nests which do not use nest based access control to allow other optimizations such | 
|  | *       as class merging to perform better. | 
|  | * </ul> | 
|  | */ | 
|  | public class NestReducer { | 
|  |  | 
|  | private AppView<AppInfoWithLiveness> appView; | 
|  |  | 
|  | public NestReducer(AppView<AppInfoWithLiveness> appView) { | 
|  | this.appView = appView; | 
|  | } | 
|  |  | 
|  | public void run(ExecutorService executorService, Timing timing) throws ExecutionException { | 
|  | timing.begin("NestReduction"); | 
|  | if (appView.options().shouldDesugarNests()) { | 
|  | removeNests(); | 
|  | } else { | 
|  | reduceNests(executorService); | 
|  | } | 
|  | timing.end(); | 
|  | } | 
|  |  | 
|  | private void removeNests() { | 
|  | for (DexProgramClass clazz : appView.appInfo().classes()) { | 
|  | if (clazz.isInANest()) { | 
|  | if (clazz.isNestHost()) { | 
|  | clazz.clearNestMembers(); | 
|  | } else { | 
|  | clazz.clearNestHost(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void reduceNests(ExecutorService executorService) throws ExecutionException { | 
|  | Set<DexProgramClass> nestHosts = Sets.newIdentityHashSet(); | 
|  | Set<DexProgramClass> nestMembers = Sets.newIdentityHashSet(); | 
|  | for (DexProgramClass clazz : appView.appInfo().classes()) { | 
|  | if (clazz.isInANest()) { | 
|  | if (clazz.isNestHost()) { | 
|  | nestHosts.add(clazz); | 
|  | } else { | 
|  | nestMembers.add(clazz); | 
|  | } | 
|  | } | 
|  | } | 
|  | ThreadUtils.processItems(nestHosts, this::processNestHost, executorService); | 
|  | ThreadUtils.processItems(nestMembers, this::processNestMember, executorService); | 
|  | } | 
|  |  | 
|  | private void processNestHost(DexProgramClass clazz) { | 
|  | BooleanBox nestHasPrivateMembers = | 
|  | new BooleanBox(IterableUtils.hasNext(clazz.members(DexEncodedMember::isPrivate))); | 
|  | clazz | 
|  | .getNestMembersClassAttributes() | 
|  | .removeIf( | 
|  | attribute -> { | 
|  | DexProgramClass member = | 
|  | asProgramClassOrNull(appView.definitionFor(attribute.getNestMember(), clazz)); | 
|  | if (member == null) { | 
|  | return true; | 
|  | } | 
|  | nestHasPrivateMembers.computeIfNotSet( | 
|  | () -> IterableUtils.hasNext(member.members(DexEncodedMember::isPrivate))); | 
|  | return false; | 
|  | }); | 
|  | if (nestHasPrivateMembers.isFalse() && appView.options().enableNestReduction) { | 
|  | clazz.getNestMembersClassAttributes().clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void processNestMember(DexProgramClass clazz) { | 
|  | DexProgramClass hostClass = | 
|  | asProgramClassOrNull(appView.definitionFor(clazz.getNestHost(), clazz)); | 
|  | if (hostClass == null || !hostClass.isNestHost()) { | 
|  | clazz.clearNestHost(); | 
|  | } | 
|  | } | 
|  | } |