|  | // Copyright (c) 2016, 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.optimize; | 
|  |  | 
|  | import static com.android.tools.r8.dex.Constants.ACC_PRIVATE; | 
|  | import static com.android.tools.r8.dex.Constants.ACC_PROTECTED; | 
|  | import static com.android.tools.r8.dex.Constants.ACC_PUBLIC; | 
|  | import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; | 
|  |  | 
|  | import com.android.tools.r8.graph.AppView; | 
|  | import com.android.tools.r8.graph.DexApplication; | 
|  | import com.android.tools.r8.graph.DexClass; | 
|  | import com.android.tools.r8.graph.DexEncodedMethod; | 
|  | import com.android.tools.r8.graph.DexProgramClass; | 
|  | import com.android.tools.r8.graph.DexType; | 
|  | import com.android.tools.r8.graph.GraphLens; | 
|  | import com.android.tools.r8.graph.InnerClassAttribute; | 
|  | import com.android.tools.r8.graph.MethodAccessFlags; | 
|  | import com.android.tools.r8.graph.SubtypingInfo; | 
|  | import com.android.tools.r8.ir.optimize.MethodPoolCollection; | 
|  | import com.android.tools.r8.optimize.PublicizerLens.PublicizedLensBuilder; | 
|  | import com.android.tools.r8.shaking.AppInfoWithLiveness; | 
|  | import com.android.tools.r8.utils.OptionalBool; | 
|  | import com.android.tools.r8.utils.Timing; | 
|  | import java.util.LinkedHashSet; | 
|  | import java.util.Set; | 
|  | import java.util.concurrent.ExecutionException; | 
|  | import java.util.concurrent.ExecutorService; | 
|  |  | 
|  | public final class ClassAndMemberPublicizer { | 
|  |  | 
|  | private final DexApplication application; | 
|  | private final AppView<AppInfoWithLiveness> appView; | 
|  | private final SubtypingInfo subtypingInfo; | 
|  | private final MethodPoolCollection methodPoolCollection; | 
|  |  | 
|  | private final PublicizedLensBuilder lensBuilder = PublicizerLens.createBuilder(); | 
|  |  | 
|  | private ClassAndMemberPublicizer( | 
|  | DexApplication application, | 
|  | AppView<AppInfoWithLiveness> appView, | 
|  | SubtypingInfo subtypingInfo) { | 
|  | this.application = application; | 
|  | this.appView = appView; | 
|  | this.subtypingInfo = subtypingInfo; | 
|  | this.methodPoolCollection = | 
|  | // We will add private instance methods when we promote them. | 
|  | new MethodPoolCollection( | 
|  | appView, subtypingInfo, MethodPoolCollection::excludesPrivateInstanceMethod); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Marks all package private and protected methods and fields as public. Makes all private static | 
|  | * methods public. Makes private instance methods public final instance methods, if possible. | 
|  | * | 
|  | * <p>This will destructively update the DexApplication passed in as argument. | 
|  | */ | 
|  | public static GraphLens run( | 
|  | ExecutorService executorService, | 
|  | Timing timing, | 
|  | DexApplication application, | 
|  | AppView<AppInfoWithLiveness> appView, | 
|  | SubtypingInfo subtypingInfo) | 
|  | throws ExecutionException { | 
|  | return new ClassAndMemberPublicizer(application, appView, subtypingInfo) | 
|  | .run(executorService, timing); | 
|  | } | 
|  |  | 
|  | private GraphLens run(ExecutorService executorService, Timing timing) throws ExecutionException { | 
|  | // Phase 1: Collect methods to check if private instance methods don't have conflicts. | 
|  | methodPoolCollection.buildAll(executorService, timing); | 
|  |  | 
|  | // Phase 2: Visit classes and promote class/member to public if possible. | 
|  | timing.begin("Phase 2: promoteToPublic"); | 
|  | for (DexClass iface : appView.appInfo().computeReachableInterfaces()) { | 
|  | publicizeType(iface.type); | 
|  | } | 
|  | publicizeType(appView.dexItemFactory().objectType); | 
|  | timing.end(); | 
|  |  | 
|  | return lensBuilder.build(appView); | 
|  | } | 
|  |  | 
|  | private void publicizeType(DexType type) { | 
|  | DexProgramClass clazz = asProgramClassOrNull(application.definitionFor(type)); | 
|  | if (clazz != null) { | 
|  | publicizeClass(clazz); | 
|  | } | 
|  | subtypingInfo.forAllImmediateExtendsSubtypes(type, this::publicizeType); | 
|  | } | 
|  |  | 
|  | private void publicizeClass(DexProgramClass clazz) { | 
|  | clazz.accessFlags.promoteToPublic(); | 
|  |  | 
|  | // Publicize fields. | 
|  | clazz.forEachField( | 
|  | field -> { | 
|  | if (field.isPublic()) { | 
|  | return; | 
|  | } | 
|  | if (!appView.appInfo().isAccessModificationAllowed(field.field)) { | 
|  | // TODO(b/131130038): Also do not publicize package-private and protected fields that | 
|  | //  are kept. | 
|  | if (field.isPrivate()) { | 
|  | return; | 
|  | } | 
|  | } | 
|  | field.accessFlags.promoteToPublic(); | 
|  | }); | 
|  |  | 
|  | // Publicize methods. | 
|  | Set<DexEncodedMethod> privateInstanceMethods = new LinkedHashSet<>(); | 
|  | clazz.forEachMethod( | 
|  | method -> { | 
|  | if (publicizeMethod(clazz, method)) { | 
|  | privateInstanceMethods.add(method); | 
|  | } | 
|  | }); | 
|  | if (!privateInstanceMethods.isEmpty()) { | 
|  | clazz.virtualizeMethods(privateInstanceMethods); | 
|  | } | 
|  |  | 
|  | // Publicize inner class attribute. | 
|  | InnerClassAttribute attr = clazz.getInnerClassAttributeForThisClass(); | 
|  | if (attr != null) { | 
|  | int accessFlags = ((attr.getAccess() | ACC_PUBLIC) & ~ACC_PRIVATE) & ~ACC_PROTECTED; | 
|  | clazz.replaceInnerClassAttributeForThisClass( | 
|  | new InnerClassAttribute( | 
|  | accessFlags, attr.getInner(), attr.getOuter(), attr.getInnerName())); | 
|  | } | 
|  | } | 
|  |  | 
|  | private boolean publicizeMethod(DexProgramClass holder, DexEncodedMethod method) { | 
|  | MethodAccessFlags accessFlags = method.accessFlags; | 
|  | if (accessFlags.isPublic()) { | 
|  | return false; | 
|  | } | 
|  | // If this method is mentioned in keep rules, do not transform (rule applications changed). | 
|  | if (!appView.appInfo().isAccessModificationAllowed(method.method)) { | 
|  | // TODO(b/131130038): Also do not publicize package-private and protected methods that are | 
|  | //  kept. | 
|  | if (method.isPrivate()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!accessFlags.isPrivate() || appView.dexItemFactory().isConstructor(method.method)) { | 
|  | // TODO(b/150589374): This should check for dispatch targets or just abandon in | 
|  | //  package-private. | 
|  | accessFlags.promoteToPublic(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!accessFlags.isStatic()) { | 
|  |  | 
|  | // We can't publicize private instance methods in interfaces or methods that are copied from | 
|  | // interfaces to lambda-desugared classes because this will be added as a new default method. | 
|  | // TODO(b/111118390): It might be possible to transform it into static methods, though. | 
|  | if (holder.isInterface() || accessFlags.isSynthetic()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | boolean wasSeen = methodPoolCollection.markIfNotSeen(holder, method.method); | 
|  | if (wasSeen) { | 
|  | // We can't do anything further because even renaming is not allowed due to the keep rule. | 
|  | if (!appView.appInfo().isMinificationAllowed(method.method)) { | 
|  | return false; | 
|  | } | 
|  | // TODO(b/111118390): Renaming will enable more private instance methods to be publicized. | 
|  | return false; | 
|  | } | 
|  | lensBuilder.add(method.method); | 
|  | accessFlags.promoteToFinal(); | 
|  | accessFlags.promoteToPublic(); | 
|  | // The method just became public and is therefore not a library override. | 
|  | method.setLibraryMethodOverride(OptionalBool.FALSE); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // For private static methods we can just relax the access to public, since | 
|  | // even though JLS prevents from declaring static method in derived class if | 
|  | // an instance method with same signature exists in superclass, JVM actually | 
|  | // does not take into account access of the static methods. | 
|  | accessFlags.promoteToPublic(); | 
|  | return false; | 
|  | } | 
|  | } |