blob: 3ea094d7dd22a343700488b8cbb9ac48ad801a46 [file] [log] [blame]
// Copyright (c) 2018, 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.graph;
import com.android.build.shrinker.r8integration.R8ResourceShrinkerState;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.contexts.CompilationContext;
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
import com.android.tools.r8.graph.lens.AppliedGraphLens;
import com.android.tools.r8.graph.lens.ClearCodeRewritingGraphLens;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.InitClassLens;
import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintFactory;
import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
import com.android.tools.r8.ir.analysis.proto.ProtoShrinker;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueConstantPropagationJoiner;
import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueFieldJoiner;
import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueParameterJoiner;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
import com.android.tools.r8.ir.optimize.info.MethodResolutionOptimizationInfoCollection;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.SeedMapper;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.optimize.compose.ComposeReferences;
import com.android.tools.r8.optimize.interfaces.collection.OpenClosedInterfacesCollection;
import com.android.tools.r8.profile.art.ArtProfileCollection;
import com.android.tools.r8.profile.startup.profile.StartupProfile;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.AssumeInfoCollection;
import com.android.tools.r8.shaking.KeepClassInfo;
import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.shaking.KeepInfo;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.KeepMemberInfo;
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.ProguardCompatibilityActions;
import com.android.tools.r8.shaking.RootSetUtils.MainDexRootSet;
import com.android.tools.r8.shaking.RootSetUtils.RootSet;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ResourceShrinkerUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.threads.ThreadTask;
import com.android.tools.r8.utils.threads.ThreadTaskUtils;
import com.android.tools.r8.verticalclassmerging.VerticallyMergedClasses;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class AppView<T extends AppInfo> implements DexDefinitionSupplier, LibraryModeledPredicate {
public enum WholeProgramOptimizations {
ON,
OFF;
public boolean isOn() {
return this == ON;
}
}
private T appInfo;
private AppInfoWithClassHierarchy appInfoForDesugaring;
private AppServices appServices;
private ArtProfileCollection artProfileCollection;
private AssumeInfoCollection assumeInfoCollection = AssumeInfoCollection.builder().build();
private final DontWarnConfiguration dontWarnConfiguration;
private final WholeProgramOptimizations wholeProgramOptimizations;
private GraphLens codeLens = GraphLens.getIdentityLens();
private GraphLens graphLens = GraphLens.getIdentityLens();
private GraphLens genericSignaturesLens = GraphLens.getIdentityLens();
private InitClassLens initClassLens;
private GraphLens kotlinMetadataLens = GraphLens.getIdentityLens();
private NamingLens namingLens = NamingLens.getIdentityLens();
private ProguardCompatibilityActions proguardCompatibilityActions;
private RootSet rootSet;
private MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection =
MethodResolutionOptimizationInfoCollection.empty();
private MainDexRootSet mainDexRootSet = null;
private StartupProfile startupProfile;
// This should preferably always be obtained via AppInfoWithLiveness.
// Currently however the liveness may be downgraded thus loosing the computed keep info.
private KeepInfoCollection keepInfo = null;
private ComposeReferences composeReferences = null;
private final AbstractValueFactory abstractValueFactory;
private final AbstractValueConstantPropagationJoiner abstractValueConstantPropagationJoiner;
private final AbstractValueFieldJoiner abstractValueFieldJoiner;
private final AbstractValueParameterJoiner abstractValueParameterJoiner;
private final InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory =
new InstanceFieldInitializationInfoFactory();
private final SimpleInliningConstraintFactory simpleInliningConstraintFactory =
new SimpleInliningConstraintFactory();
// Desugaring.
public final TypeRewriter typeRewriter;
// Modeling.
private final LibraryMethodSideEffectModelCollection libraryMethodSideEffectModelCollection;
// Optimizations.
private final ArgumentPropagator argumentPropagator;
private final LibraryMemberOptimizer libraryMemberOptimizer;
private final ProtoShrinker protoShrinker;
// Optimization results.
private boolean allCodeProcessed = false;
private Predicate<DexType> classesEscapingIntoLibrary = Predicates.alwaysTrue();
private InitializedClassesInInstanceMethods initializedClassesInInstanceMethods;
private HorizontallyMergedClasses horizontallyMergedClasses = HorizontallyMergedClasses.empty();
private VerticallyMergedClasses verticallyMergedClasses;
private EnumDataMap unboxedEnums = null;
private OpenClosedInterfacesCollection openClosedInterfacesCollection =
OpenClosedInterfacesCollection.getDefault();
// TODO(b/169115389): Remove
private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
private final Map<DexType, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
private final Map<DexType, String> sourceFileForPrunedTypes = new IdentityHashMap<>();
private SeedMapper applyMappingSeedMapper;
private R8ResourceShrinkerState resourceShrinkerState = null;
// When input has been (partially) desugared these are the classes which has been library
// desugared. This information is populated in the IR converter.
private Set<DexType> alreadyLibraryDesugared = null;
private final CompilationContext context;
private final Thread mainThread = Thread.currentThread();
private final AndroidApiLevelCompute apiLevelCompute;
private final ComputedApiLevel computedMinApiLevel;
private AppView(
T appInfo,
ArtProfileCollection artProfileCollection,
StartupProfile startupProfile,
WholeProgramOptimizations wholeProgramOptimizations,
TypeRewriter mapper) {
this(
appInfo,
artProfileCollection,
startupProfile,
wholeProgramOptimizations,
mapper,
Timing.empty());
}
private AppView(
T appInfo,
ArtProfileCollection artProfileCollection,
StartupProfile startupProfile,
WholeProgramOptimizations wholeProgramOptimizations,
TypeRewriter mapper,
Timing timing) {
assert appInfo != null;
this.appInfo = appInfo;
this.context =
timing.time(
"Compilation context", () -> CompilationContext.createInitialContext(options()));
this.wholeProgramOptimizations = wholeProgramOptimizations;
abstractValueFactory = new AbstractValueFactory();
abstractValueConstantPropagationJoiner = new AbstractValueConstantPropagationJoiner(this);
if (enableWholeProgramOptimizations()) {
abstractValueFieldJoiner = new AbstractValueFieldJoiner(withClassHierarchy());
abstractValueParameterJoiner = new AbstractValueParameterJoiner(withClassHierarchy());
} else {
abstractValueFieldJoiner = null;
abstractValueParameterJoiner = null;
}
this.artProfileCollection = artProfileCollection;
this.startupProfile = startupProfile;
this.dontWarnConfiguration =
timing.time(
"Dont warn config",
() -> DontWarnConfiguration.create(options().getProguardConfiguration()));
this.initClassLens = timing.time("Init class lens", InitClassLens::getThrowingInstance);
this.typeRewriter = mapper;
timing.begin("Create argument propagator");
if (enableWholeProgramOptimizations() && options().callSiteOptimizationOptions().isEnabled()) {
this.argumentPropagator = new ArgumentPropagator(withLiveness());
} else {
this.argumentPropagator = null;
}
if (enableWholeProgramOptimizations() && options().isOptimizedResourceShrinking()) {
resourceShrinkerState = ResourceShrinkerUtils.createResourceShrinkerState(this);
}
timing.end();
this.libraryMethodSideEffectModelCollection =
timing.time("Library side-effects", () -> new LibraryMethodSideEffectModelCollection(this));
this.libraryMemberOptimizer =
timing.time("Library optimizer", () -> new LibraryMemberOptimizer(this, timing));
this.protoShrinker = timing.time("Proto shrinker", () -> ProtoShrinker.create(withLiveness()));
this.apiLevelCompute =
timing.time("ApiLevel compute", () -> AndroidApiLevelCompute.create(this));
this.computedMinApiLevel =
timing.time(
"ApiLevel computed", () -> apiLevelCompute.computeInitialMinApiLevel(options()));
}
public boolean verifyMainThread() {
assert mainThread == Thread.currentThread();
return true;
}
@Override
public boolean isModeled(DexType type) {
return libraryMemberOptimizer.isModeled(type);
}
private static <T extends AppInfo> TypeRewriter defaultTypeRewriter(T appInfo) {
InternalOptions options = appInfo.options();
return options.getTypeRewriter();
}
public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
return new AppView<>(
appInfo,
ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
StartupProfile.empty(),
WholeProgramOptimizations.OFF,
defaultTypeRewriter(appInfo));
}
public static AppView<AppInfoWithClassHierarchy> createForSimulatingR8InD8(
DirectMappedDexApplication application, MainDexInfo mainDexInfo) {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
AppInfoWithClassHierarchy appInfo =
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
application,
classToFeatureSplitMap,
mainDexInfo,
GlobalSyntheticsStrategy.forSingleOutputMode());
return new AppView<>(
appInfo,
ArtProfileCollection.empty(),
StartupProfile.empty(),
WholeProgramOptimizations.ON,
defaultTypeRewriter(appInfo));
}
public static <T extends AppInfo> AppView<T> createForD8(
T appInfo, TypeRewriter mapper, Timing timing) {
return new AppView<>(
appInfo,
ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
StartupProfile.empty(),
WholeProgramOptimizations.OFF,
mapper,
timing);
}
public static AppView<AppInfoWithClassHierarchy> createForR8(DexApplication application) {
return createForR8(application, MainDexInfo.none());
}
public static AppView<AppInfoWithClassHierarchy> createForR8(
DexApplication application, MainDexInfo mainDexInfo) {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
AppInfoWithClassHierarchy appInfo =
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
application,
classToFeatureSplitMap,
mainDexInfo,
GlobalSyntheticsStrategy.forSingleOutputMode());
return new AppView<>(
appInfo,
ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
StartupProfile.createInitialStartupProfileForR8(application),
WholeProgramOptimizations.ON,
defaultTypeRewriter(appInfo));
}
public static <T extends AppInfo> AppView<T> createForL8(T appInfo, TypeRewriter mapper) {
return new AppView<>(
appInfo,
ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
StartupProfile.empty(),
WholeProgramOptimizations.OFF,
mapper);
}
public static <T extends AppInfo> AppView<T> createForRelocator(T appInfo) {
return new AppView<>(
appInfo,
ArtProfileCollection.empty(),
StartupProfile.empty(),
WholeProgramOptimizations.OFF,
defaultTypeRewriter(appInfo));
}
public static AppView<AppInfoWithClassHierarchy> createForTracer(
AppInfoWithClassHierarchy appInfo) {
return new AppView<>(
appInfo,
ArtProfileCollection.empty(),
StartupProfile.empty(),
WholeProgramOptimizations.ON,
defaultTypeRewriter(appInfo));
}
public AbstractValueFactory abstractValueFactory() {
return abstractValueFactory;
}
public AbstractValueConstantPropagationJoiner getAbstractValueConstantPropagationJoiner() {
return abstractValueConstantPropagationJoiner;
}
public AbstractValueFieldJoiner getAbstractValueFieldJoiner() {
return abstractValueFieldJoiner;
}
public AbstractValueParameterJoiner getAbstractValueParameterJoiner() {
return abstractValueParameterJoiner;
}
public void clearMethodResolutionOptimizationInfoCollection() {
methodResolutionOptimizationInfoCollection = MethodResolutionOptimizationInfoCollection.empty();
}
public MethodResolutionOptimizationInfoCollection
getMethodResolutionOptimizationInfoCollection() {
return methodResolutionOptimizationInfoCollection;
}
public void setMethodResolutionOptimizationInfoCollection(
MethodResolutionOptimizationInfoCollection getMethodResolutionOptimizationInfoCollection) {
this.methodResolutionOptimizationInfoCollection = getMethodResolutionOptimizationInfoCollection;
}
public InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory() {
return instanceFieldInitializationInfoFactory;
}
public SimpleInliningConstraintFactory simpleInliningConstraintFactory() {
return simpleInliningConstraintFactory;
}
public DexApplication app() {
return appInfo().app();
}
public T appInfo() {
assert !appInfo.hasClassHierarchy() || enableWholeProgramOptimizations();
return appInfo;
}
public AppInfoWithClassHierarchy appInfoWithClassHierarchy() {
return hasClassHierarchy() ? appInfo.withClassHierarchy() : null;
}
public AppInfoWithLiveness appInfoWithLiveness() {
return hasLiveness() ? appInfo.withLiveness() : null;
}
public AppInfoWithClassHierarchy appInfoForDesugaring() {
if (enableWholeProgramOptimizations()) {
assert appInfo.hasClassHierarchy();
return appInfo.withClassHierarchy();
}
assert !appInfo.hasClassHierarchy();
if (appInfoForDesugaring == null) {
appInfoForDesugaring = AppInfoWithClassHierarchy.createForDesugaring(appInfo());
}
return appInfoForDesugaring;
}
private void unsetAppInfoForDesugaring() {
appInfoForDesugaring = null;
}
public <U extends T> AppView<U> setAppInfo(U appInfo) {
assert !appInfo.isObsolete();
AppInfo previous = this.appInfo;
this.appInfo = appInfo;
unsetAppInfoForDesugaring();
if (appInfo != previous) {
previous.markObsolete();
}
if (appInfo.hasLiveness()) {
keepInfo = appInfo.withLiveness().getKeepInfo();
}
@SuppressWarnings("unchecked")
AppView<U> appViewWithSpecializedAppInfo = (AppView<U>) this;
return appViewWithSpecializedAppInfo;
}
public boolean isAllCodeProcessed() {
return allCodeProcessed;
}
public void setAllCodeProcessed() {
allCodeProcessed = true;
}
public void clearCodeRewritings(ExecutorService executorService, Timing timing)
throws ExecutionException {
timing.begin("Clear code rewritings");
setGraphLens(new ClearCodeRewritingGraphLens(withClassHierarchy()));
MemberRebindingIdentityLens memberRebindingIdentityLens =
MemberRebindingIdentityLensFactory.create(withClassHierarchy(), executorService);
setGraphLens(memberRebindingIdentityLens);
timing.end();
}
public void flattenGraphLenses() {
GraphLens graphLens = graphLens();
setGraphLens(GraphLens.getIdentityLens());
setGraphLens(new AppliedGraphLens(withClassHierarchy(), graphLens));
}
public AppServices appServices() {
return appServices;
}
public void setAppServices(AppServices appServices) {
this.appServices = appServices;
}
public ArtProfileCollection getArtProfileCollection() {
return artProfileCollection;
}
public void setArtProfileCollection(ArtProfileCollection artProfileCollection) {
this.artProfileCollection = artProfileCollection;
}
public StartupProfile getStartupProfile() {
return startupProfile;
}
public void setStartupProfile(StartupProfile startupProfile) {
this.startupProfile = startupProfile;
}
public AssumeInfoCollection getAssumeInfoCollection() {
return assumeInfoCollection;
}
public void setAssumeInfoCollection(AssumeInfoCollection assumeInfoCollection) {
this.assumeInfoCollection = assumeInfoCollection;
}
public DontWarnConfiguration getDontWarnConfiguration() {
return dontWarnConfiguration;
}
public boolean isClassEscapingIntoLibrary(DexType type) {
assert type.isClassType();
return classesEscapingIntoLibrary.test(type);
}
public void setClassesEscapingIntoLibrary(Predicate<DexType> classesEscapingIntoLibrary) {
this.classesEscapingIntoLibrary = classesEscapingIntoLibrary;
}
public void setSourceDebugExtensionForType(DexClass clazz, DexValueString sourceDebugExtension) {
sourceDebugExtensions.put(clazz.type, sourceDebugExtension);
}
public DexValueString getSourceDebugExtensionForType(DexClass clazz) {
return sourceDebugExtensions.get(clazz.type);
}
@Override
public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
return appInfo().contextIndependentDefinitionForWithResolutionResult(type);
}
@Override
public final DexClass definitionFor(DexType type) {
return appInfo().definitionFor(type);
}
public OptionalBool isInterface(DexType type) {
assert type.isClassType();
// Without whole program information we should not assume anything about any other class than
// the current holder in a given context.
if (enableWholeProgramOptimizations()) {
DexClass clazz = definitionFor(type);
if (clazz == null) {
return OptionalBool.unknown();
}
return OptionalBool.of(clazz.isInterface());
}
return OptionalBool.unknown();
}
@Override
public DexItemFactory dexItemFactory() {
return appInfo.dexItemFactory();
}
public ComposeReferences getComposeReferences() {
assert options().getJetpackComposeOptions().isAnyOptimizationsEnabled();
if (composeReferences == null) {
composeReferences = new ComposeReferences(dexItemFactory());
}
return composeReferences;
}
public boolean enableWholeProgramOptimizations() {
return wholeProgramOptimizations == WholeProgramOptimizations.ON;
}
public WholeProgramOptimizations getWholeProgramOptimizations() {
return wholeProgramOptimizations;
}
/**
* Create a new processor context.
*
* <p>The order of processor contexts for a compilation must be deterministic so this is required
* to be called on the main thread only.
*/
public ProcessorContext createProcessorContext() {
assert verifyMainThread();
return context.createProcessorContext();
}
public SyntheticItems getSyntheticItems() {
return appInfo.getSyntheticItems();
}
public <E extends Throwable> void withArgumentPropagator(
ThrowingConsumer<ArgumentPropagator, E> consumer) throws E {
if (argumentPropagator != null) {
consumer.accept(argumentPropagator);
}
}
public LibraryMemberOptimizer libraryMethodOptimizer() {
return libraryMemberOptimizer;
}
public LibraryMethodSideEffectModelCollection getLibraryMethodSideEffectModelCollection() {
return libraryMethodSideEffectModelCollection;
}
public ProtoShrinker protoShrinker() {
return protoShrinker;
}
public <E extends Throwable> void withProtoShrinker(ThrowingConsumer<ProtoShrinker, E> consumer)
throws E {
if (protoShrinker != null) {
consumer.accept(protoShrinker);
}
}
public <U> U withProtoShrinker(Function<ProtoShrinker, U> consumer, U defaultValue) {
if (protoShrinker != null) {
return consumer.apply(protoShrinker);
}
return defaultValue;
}
public <U> U withProtoEnumShrinker(Function<EnumLiteProtoShrinker, U> fn, U defaultValue) {
if (protoShrinker != null && options().protoShrinking().isEnumLiteProtoShrinkingEnabled()) {
return fn.apply(protoShrinker.enumLiteProtoShrinker);
}
return defaultValue;
}
public <E extends Throwable> void withGeneratedExtensionRegistryShrinker(
ThrowingConsumer<GeneratedExtensionRegistryShrinker, E> consumer) throws E {
if (protoShrinker != null && protoShrinker.generatedExtensionRegistryShrinker != null) {
consumer.accept(protoShrinker.generatedExtensionRegistryShrinker);
}
}
public <U> U withGeneratedExtensionRegistryShrinker(
Function<GeneratedExtensionRegistryShrinker, U> fn, U defaultValue) {
if (protoShrinker != null && protoShrinker.generatedExtensionRegistryShrinker != null) {
return fn.apply(protoShrinker.generatedExtensionRegistryShrinker);
}
return defaultValue;
}
public <E extends Throwable> void withGeneratedMessageLiteShrinker(
ThrowingConsumer<GeneratedMessageLiteShrinker, E> consumer) throws E {
if (protoShrinker != null && protoShrinker.generatedMessageLiteShrinker != null) {
consumer.accept(protoShrinker.generatedMessageLiteShrinker);
}
}
public <E extends Throwable> void withGeneratedMessageLiteBuilderShrinker(
ThrowingConsumer<GeneratedMessageLiteBuilderShrinker, E> consumer) throws E {
if (protoShrinker != null && protoShrinker.generatedMessageLiteBuilderShrinker != null) {
consumer.accept(protoShrinker.generatedMessageLiteBuilderShrinker);
}
}
public <U> U withGeneratedMessageLiteShrinker(
Function<GeneratedMessageLiteShrinker, U> fn, U defaultValue) {
if (protoShrinker != null && protoShrinker.generatedMessageLiteShrinker != null) {
return fn.apply(protoShrinker.generatedMessageLiteShrinker);
}
return defaultValue;
}
public <U> U withGeneratedMessageLiteBuilderShrinker(
Function<GeneratedMessageLiteBuilderShrinker, U> fn, U defaultValue) {
if (protoShrinker != null && protoShrinker.generatedMessageLiteBuilderShrinker != null) {
return fn.apply(protoShrinker.generatedMessageLiteBuilderShrinker);
}
return defaultValue;
}
public GraphLens codeLens() {
return codeLens;
}
public void setCodeLens(GraphLens codeLens) {
this.codeLens = codeLens;
}
public GraphLens graphLens() {
return graphLens;
}
/** @return true if the graph lens changed, otherwise false. */
public boolean setGraphLens(GraphLens graphLens) {
if (graphLens != this.graphLens) {
this.graphLens = graphLens;
// TODO(b/202368283): Currently, we always set an applied lens or a clear code rewriting lens
// when the graph lens has been fully applied to all code. Therefore, we implicitly update
// the code lens when these lenses are set. Now that we have an explicit code lens, the clear
// code rewriting lens is redundant and could be removed.
if (graphLens.isAppliedLens() || graphLens.isClearCodeRewritingLens()) {
setCodeLens(graphLens);
}
return true;
}
return false;
}
public GraphLens getGenericSignaturesLens() {
return genericSignaturesLens;
}
public void setGenericSignaturesLens(GraphLens genericSignaturesLens) {
this.genericSignaturesLens = genericSignaturesLens;
}
private boolean disallowFurtherInitClassUses = false;
public void dissallowFurtherInitClassUses() {
disallowFurtherInitClassUses = true;
}
public boolean canUseInitClass() {
return !disallowFurtherInitClassUses && options().isShrinking();
}
public InitClassLens initClassLens() {
return initClassLens;
}
public boolean hasInitClassLens() {
return initClassLens != null;
}
public void setInitClassLens(InitClassLens initClassLens) {
this.initClassLens = initClassLens;
}
public GraphLens getKotlinMetadataLens() {
return kotlinMetadataLens;
}
public void setKotlinMetadataLens(GraphLens kotlinMetadataLens) {
this.kotlinMetadataLens = kotlinMetadataLens;
}
public boolean hasInitializedClassesInInstanceMethods() {
return initializedClassesInInstanceMethods != null;
}
public InitializedClassesInInstanceMethods getInitializedClassesInInstanceMethods() {
return initializedClassesInInstanceMethods;
}
public void setInitializedClassesInInstanceMethods(
InitializedClassesInInstanceMethods initializedClassesInInstanceMethods) {
this.initializedClassesInInstanceMethods = initializedClassesInInstanceMethods;
}
public void setCfByteCodePassThrough(Set<DexMethod> cfByteCodePassThrough) {
assert options().enableCfByteCodePassThrough;
this.cfByteCodePassThrough = cfByteCodePassThrough;
}
public <U> U withInitializedClassesInInstanceMethods(
Function<InitializedClassesInInstanceMethods, U> fn, U defaultValue) {
if (initializedClassesInInstanceMethods != null) {
return fn.apply(initializedClassesInInstanceMethods);
}
return defaultValue;
}
public InternalOptions options() {
return appInfo.options();
}
public Reporter reporter() {
return options().reporter;
}
public TestingOptions testing() {
return options().testing;
}
public boolean hasRootSet() {
return rootSet != null;
}
public RootSet rootSet() {
return rootSet;
}
public void setRootSet(RootSet rootSet) {
this.rootSet = rootSet;
}
public void setMainDexRootSet(MainDexRootSet mainDexRootSet) {
assert mainDexRootSet != null : "Root set should never be recomputed";
this.mainDexRootSet = mainDexRootSet;
}
public boolean hasMainDexRootSet() {
return mainDexRootSet != null;
}
public MainDexRootSet getMainDexRootSet() {
return mainDexRootSet;
}
public boolean hasKeepInfo() {
return keepInfo != null;
}
public KeepInfoCollection getKeepInfo() {
assert hasKeepInfo();
return keepInfo;
}
public KeepInfo<?, ?> getKeepInfo(ProgramDefinition definition) {
return definition
.getReference()
.apply(
clazz -> getKeepInfo(definition.asProgramClass()),
field -> getKeepInfo(definition.asProgramField()),
method -> getKeepInfo(definition.asProgramMethod()));
}
public KeepClassInfo getKeepInfo(DexProgramClass clazz) {
return getKeepInfo().getClassInfo(clazz);
}
public KeepClassInfo getKeepInfoOrDefault(DexProgramClass clazz, KeepClassInfo defaultValue) {
return hasKeepInfo() ? getKeepInfo().getClassInfo(clazz) : defaultValue;
}
public KeepFieldInfo getKeepInfo(ProgramField field) {
return getKeepInfo().getFieldInfo(field);
}
public KeepMemberInfo<?, ?> getKeepInfo(ProgramMember<?, ?> member) {
return member.isField()
? getKeepInfo(member.asProgramField())
: getKeepInfo(member.asProgramMethod());
}
public KeepMethodInfo getKeepInfo(ProgramMethod method) {
return getKeepInfo().getMethodInfo(method);
}
public NamingLens getNamingLens() {
return namingLens;
}
public void setNamingLens(NamingLens namingLens) {
this.namingLens = namingLens;
}
public boolean hasProguardCompatibilityActions() {
return proguardCompatibilityActions != null;
}
public ProguardCompatibilityActions getProguardCompatibilityActions() {
return proguardCompatibilityActions;
}
public void setProguardCompatibilityActions(
ProguardCompatibilityActions proguardCompatibilityActions) {
assert options().forceProguardCompatibility;
this.proguardCompatibilityActions = proguardCompatibilityActions;
}
public MergedClassesCollection allMergedClasses() {
MergedClassesCollection collection = new MergedClassesCollection();
if (hasHorizontallyMergedClasses()) {
collection.add(horizontallyMergedClasses);
}
if (verticallyMergedClasses != null) {
collection.add(verticallyMergedClasses);
}
return collection;
}
public boolean hasHorizontallyMergedClasses() {
return !horizontallyMergedClasses.isEmpty();
}
/**
* Get the result of horizontal class merging. Returns null if horizontal class merging has not
* been run.
*/
public HorizontallyMergedClasses horizontallyMergedClasses() {
return horizontallyMergedClasses;
}
public void setHorizontallyMergedClasses(HorizontallyMergedClasses horizontallyMergedClasses) {
assert !hasHorizontallyMergedClasses();
this.horizontallyMergedClasses = horizontallyMergedClasses;
testing().horizontallyMergedClassesConsumer.accept(this, horizontallyMergedClasses);
}
public boolean hasVerticallyMergedClasses() {
return verticallyMergedClasses != null;
}
/**
* Get the result of vertical class merging. Returns null if vertical class merging has not been
* run.
*/
public VerticallyMergedClasses getVerticallyMergedClasses() {
return verticallyMergedClasses;
}
public void setVerticallyMergedClasses(
VerticallyMergedClasses verticallyMergedClasses, ClassMergerMode mode) {
if (mode.isInitial()) {
assert this.verticallyMergedClasses == null;
this.verticallyMergedClasses = verticallyMergedClasses;
testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
} else {
assert this.verticallyMergedClasses != null;
}
}
public OpenClosedInterfacesCollection getOpenClosedInterfacesCollection() {
return openClosedInterfacesCollection;
}
public void setOpenClosedInterfacesCollection(
OpenClosedInterfacesCollection openClosedInterfacesCollection) {
this.openClosedInterfacesCollection = openClosedInterfacesCollection;
}
public boolean hasUnboxedEnums() {
return unboxedEnums != null;
}
public EnumDataMap unboxedEnums() {
return hasUnboxedEnums() ? unboxedEnums : EnumDataMap.empty();
}
public void setUnboxedEnums(EnumDataMap unboxedEnums) {
assert !hasUnboxedEnums();
this.unboxedEnums = unboxedEnums;
testing().unboxedEnumsConsumer.accept(dexItemFactory(), unboxedEnums);
}
public R8ResourceShrinkerState getResourceShrinkerState() {
return resourceShrinkerState;
}
public boolean validateUnboxedEnumsHaveBeenPruned() {
for (DexType unboxedEnum : unboxedEnums.computeAllUnboxedEnums()) {
assert appInfo.definitionForWithoutExistenceAssert(unboxedEnum) == null
: "Enum " + unboxedEnum + " has been unboxed but is still in the program.";
assert appInfo().withLiveness().wasPruned(unboxedEnum)
: "Enum " + unboxedEnum + " has been unboxed but was not pruned.";
}
return true;
}
public boolean hasClassHierarchy() {
return appInfo().hasClassHierarchy();
}
@SuppressWarnings("unchecked")
public AppView<AppInfoWithClassHierarchy> withClassHierarchy() {
return appInfo.hasClassHierarchy()
? (AppView<AppInfoWithClassHierarchy>) this
: null;
}
@SuppressWarnings("unchecked")
public AppView<AppInfo> withoutClassHierarchy() {
assert !hasClassHierarchy();
return (AppView<AppInfo>) this;
}
public boolean hasLiveness() {
return appInfo().hasLiveness();
}
public AppView<AppInfoWithLiveness> withLiveness() {
@SuppressWarnings("unchecked")
AppView<AppInfoWithLiveness> appViewWithLiveness = (AppView<AppInfoWithLiveness>) this;
return appViewWithLiveness;
}
@SuppressWarnings("ReferenceEquality")
public OptionalBool isSubtype(DexType subtype, DexType supertype) {
// Even if we can compute isSubtype by having class hierarchy we may not be allowed to ask the
// question for all code paths in D8. Having the check for liveness ensure that we are in R8
// territory.
if (hasClassHierarchy()) {
return OptionalBool.of(appInfo().withClassHierarchy().isSubtype(subtype, supertype));
}
if (subtype == supertype || supertype == dexItemFactory().objectType) {
return OptionalBool.TRUE;
}
return OptionalBool.unknown();
}
public boolean isCfByteCodePassThrough(DexEncodedMethod method) {
if (!options().enableCfByteCodePassThrough) {
return false;
}
assert options().isGeneratingClassFiles();
if (cfByteCodePassThrough.contains(method.getReference())) {
return true;
}
return options().testing.cfByteCodePassThrough != null
&& options().testing.cfByteCodePassThrough.test(method.getReference());
}
public boolean hasCfByteCodePassThroughMethods() {
return !cfByteCodePassThrough.isEmpty();
}
public void pruneItems(PrunedItems prunedItems, ExecutorService executorService, Timing timing)
throws ExecutionException {
if (prunedItems.isEmpty()) {
assert appInfo().app() == prunedItems.getPrunedApp();
return;
}
timing.begin("Prune AppView");
if (appInfo.hasLiveness()) {
AppView<AppInfoWithLiveness> self = withLiveness();
self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems, executorService, timing));
} else if (appInfo.hasClassHierarchy()) {
AppView<AppInfoWithClassHierarchy> self = withClassHierarchy();
self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems, executorService, timing));
} else {
pruneAppInfo(prunedItems, this, executorService, timing);
}
if (appServices() != null) {
setAppServices(appServices().prunedCopy(prunedItems, timing));
}
setArtProfileCollection(getArtProfileCollection().withoutPrunedItems(prunedItems, timing));
setAssumeInfoCollection(getAssumeInfoCollection().withoutPrunedItems(prunedItems, timing));
if (hasProguardCompatibilityActions()) {
setProguardCompatibilityActions(
getProguardCompatibilityActions().withoutPrunedItems(prunedItems, timing));
}
if (hasRootSet()) {
rootSet.pruneItems(prunedItems, timing);
}
setStartupProfile(
getStartupProfile().withoutPrunedItems(prunedItems, getSyntheticItems(), timing));
if (hasMainDexRootSet()) {
setMainDexRootSet(mainDexRootSet.withoutPrunedItems(prunedItems, timing));
}
setOpenClosedInterfacesCollection(
openClosedInterfacesCollection.withoutPrunedItems(prunedItems, timing));
timing.end();
}
@SuppressWarnings("unchecked")
private static void pruneAppInfo(
PrunedItems prunedItems, AppView<?> appView, ExecutorService executorService, Timing timing)
throws ExecutionException {
((AppView<AppInfo>) appView)
.setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems, executorService, timing));
}
public void rewriteWithLens(
NonIdentityGraphLens lens, ExecutorService executorService, Timing timing)
throws ExecutionException {
rewriteWithLensAndApplication(lens, app().asDirect(), executorService, timing);
}
public void rewriteWithLensAndApplication(
NonIdentityGraphLens lens,
DirectMappedDexApplication application,
ExecutorService executorService,
Timing timing)
throws ExecutionException {
rewriteWithLensAndApplication(
lens, application, executorService, timing, withClassHierarchy(), lens.getPrevious());
assert verifyMovedMethodsHaveOriginalMethodPosition();
}
private static void rewriteWithLensAndApplication(
NonIdentityGraphLens lens,
DirectMappedDexApplication application,
ExecutorService executorService,
Timing timing,
AppView<? extends AppInfoWithClassHierarchy> appView,
GraphLens appliedLens)
throws ExecutionException {
assert lens != null;
assert application != null;
timing.begin("Rewrite AppView");
boolean changed = appView.setGraphLens(lens);
// Verify that the lens changed, except in the horizontal class merger case, where we install
// the lens prior to lens rewriting AppView.
assert changed || lens.isHorizontalClassMergerGraphLens();
assert application.verifyWithLens(appView.appInfo().app().asDirect(), lens);
// The application has already been rewritten with the given applied lens. Therefore, we
// temporarily replace that lens with a lens that does not have any rewritings to avoid the
// overhead of traversing the entire lens chain upon each lookup during the rewriting.
NonIdentityGraphLens firstUnappliedLens = computeFirstUnappliedLens(appView, lens, appliedLens);
// Insert a member rebinding lens above the first unapplied lens.
// TODO(b/182129249): Once the member rebinding phase has been removed, the MemberRebindingLens
// should be removed and all uses of FieldRebindingIdentityLens should be replaced by
// MemberRebindingIdentityLens.
GraphLens newMemberRebindingLens =
computeNewMemberRebindingLens(appView, appliedLens, firstUnappliedLens, timing);
firstUnappliedLens.withAlternativeParentLens(
newMemberRebindingLens,
() -> {
GraphLens appliedLensInModifiedLens = GraphLens.getIdentityLens();
ThreadTaskUtils.processTasks(
executorService,
appView.options(),
timing
.beginMerger("Rewrite AppView concurrently", executorService)
.disableSlowestReporting(),
new ThreadTask() {
private AppInfoWithClassHierarchy result;
@Override
public void run(Timing timing) {
if (appView.hasLiveness()) {
result =
appView
.appInfoWithLiveness()
.rewrittenWithLens(
application, lens, appliedLensInModifiedLens, timing);
} else {
assert appView.hasClassHierarchy();
AppView<AppInfoWithClassHierarchy> appViewWithClassHierarchy =
appView.withClassHierarchy();
AppInfoWithClassHierarchy appInfo = appViewWithClassHierarchy.appInfo();
MainDexInfo rewrittenMainDexInfo =
appInfo
.getMainDexInfo()
.rewrittenWithLens(appView.getSyntheticItems(), lens, timing);
result = appInfo.rebuildWithMainDexInfo(rewrittenMainDexInfo);
}
}
@Override
public void onJoin() {
appView.withClassHierarchy().setAppInfo(result);
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setAppServices(
appView.appServices().rewrittenWithLens(lens, threadTiming));
}
@Override
public boolean shouldRun() {
return !appView.appServices().isEmpty();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setArtProfileCollection(
appView
.getArtProfileCollection()
.rewrittenWithLens(appView, lens, threadTiming));
}
@Override
public boolean shouldRun() {
return !appView.getArtProfileCollection().isEmpty();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setAssumeInfoCollection(
appView
.getAssumeInfoCollection()
.rewrittenWithLens(
appView, lens, appliedLensInModifiedLens, threadTiming));
}
@Override
public boolean shouldRun() {
return !appView.getAssumeInfoCollection().isEmpty();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setInitClassLens(
appView.initClassLens().rewrittenWithLens(lens, threadTiming));
}
@Override
public boolean shouldRun() {
return appView.hasInitClassLens();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setProguardCompatibilityActions(
appView
.getProguardCompatibilityActions()
.rewrittenWithLens(lens, threadTiming));
}
@Override
public boolean shouldRun() {
return appView.hasProguardCompatibilityActions()
&& !appView.getProguardCompatibilityActions().isEmpty();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setMainDexRootSet(
appView.getMainDexRootSet().rewrittenWithLens(lens, threadTiming));
}
@Override
public boolean shouldRun() {
return appView.hasMainDexRootSet();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setOpenClosedInterfacesCollection(
appView
.getOpenClosedInterfacesCollection()
.rewrittenWithLens(lens, threadTiming));
}
@Override
public boolean shouldRun() {
return !appView.getOpenClosedInterfacesCollection().isEmpty();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setRootSet(appView.rootSet().rewrittenWithLens(lens, threadTiming));
}
@Override
public boolean shouldRun() {
return appView.hasRootSet();
}
},
new ThreadTask() {
@Override
public void run(Timing threadTiming) {
appView.setStartupProfile(
appView.getStartupProfile().rewrittenWithLens(lens, threadTiming));
}
@Override
public boolean shouldRun() {
return !appView.getStartupProfile().isEmpty();
}
},
new ThreadTask() {
@Override
public void run(Timing timing) {
ImmutableSet.Builder<DexMethod> cfByteCodePassThroughBuilder =
ImmutableSet.builder();
for (DexMethod method : appView.cfByteCodePassThrough) {
cfByteCodePassThroughBuilder.add(
lens.getRenamedMethodSignature(method, appliedLensInModifiedLens));
}
appView.cfByteCodePassThrough = cfByteCodePassThroughBuilder.build();
}
@Override
public boolean shouldRun() {
return !appView.cfByteCodePassThrough.isEmpty();
}
},
new ThreadTask() {
@Override
public void run(Timing timing) {
appView.setInitializedClassesInInstanceMethods(
appView
.getInitializedClassesInInstanceMethods()
.rewrittenWithLens(lens, appliedLens));
}
@Override
public boolean shouldRun() {
return appView.hasInitializedClassesInInstanceMethods();
}
});
});
timing.end(); // Rewrite AppView
}
private static NonIdentityGraphLens computeFirstUnappliedLens(
AppView<? extends AppInfoWithClassHierarchy> appView,
NonIdentityGraphLens lens,
GraphLens appliedLens) {
NonIdentityGraphLens firstUnappliedLens = lens;
while (firstUnappliedLens.getPrevious() != appliedLens) {
GraphLens previousLens = firstUnappliedLens.getPrevious();
assert previousLens.isNonIdentityLens();
assert previousLens != appView.codeLens();
firstUnappliedLens = previousLens.asNonIdentityLens();
}
return firstUnappliedLens;
}
private static GraphLens computeNewMemberRebindingLens(
AppView<? extends AppInfoWithClassHierarchy> appView,
GraphLens appliedLens,
NonIdentityGraphLens firstUnappliedLens,
Timing timing) {
timing.begin("Compute new member rebinding lens");
GraphLens newMemberRebindingLens = GraphLens.getIdentityLens();
if (!firstUnappliedLens.isMemberRebindingLens()
&& !firstUnappliedLens.isMemberRebindingIdentityLens()) {
NonIdentityGraphLens appliedMemberRebindingLens =
firstUnappliedLens.findPrevious(GraphLens::isMemberRebindingIdentityLens);
if (appliedMemberRebindingLens != null) {
newMemberRebindingLens =
appliedMemberRebindingLens
.asMemberRebindingIdentityLens()
.toRewrittenMemberRebindingIdentityLens(
appView, appliedLens, appliedMemberRebindingLens);
}
}
timing.end();
return newMemberRebindingLens;
}
public void rewriteWithD8Lens(NonIdentityGraphLens lens, Timing timing) {
rewriteWithD8Lens(lens, timing, withoutClassHierarchy());
}
private static void rewriteWithD8Lens(
NonIdentityGraphLens lens, Timing timing, AppView<AppInfo> appView) {
boolean changed = appView.setGraphLens(lens);
// Verify that the lens changed, except in the horizontal class merger case, where we install
// the lens prior to lens rewriting AppView.
assert changed || lens.isHorizontalClassMergerGraphLens();
appView.setArtProfileCollection(
appView.getArtProfileCollection().rewrittenWithLens(appView, lens, timing));
}
public void setAlreadyLibraryDesugared(Set<DexType> alreadyLibraryDesugared) {
assert this.alreadyLibraryDesugared == null;
this.alreadyLibraryDesugared = alreadyLibraryDesugared;
}
/**
* Called when an optimization that changes the app has finished. This allows easier diagnosing
* some failures, e.g., finding which optimization pass that adds/removes a given method.
*/
public void notifyOptimizationFinishedForTesting() {
// Intentionally empty.
}
public boolean isAlreadyLibraryDesugared(DexProgramClass clazz) {
if (!options().desugarSpecificOptions().allowAllDesugaredInput) {
return false;
}
assert alreadyLibraryDesugared != null;
return alreadyLibraryDesugared.contains(clazz.getType());
}
public void loadApplyMappingSeedMapper() throws IOException {
if (options().getProguardConfiguration().hasApplyMappingFile()) {
applyMappingSeedMapper =
SeedMapper.seedMapperFromFile(
options().reporter, options().getProguardConfiguration().getApplyMappingFile());
}
}
public SeedMapper getApplyMappingSeedMapper() {
return applyMappingSeedMapper;
}
public void clearApplyMappingSeedMapper() {
applyMappingSeedMapper = null;
}
public boolean checkForTesting(Supplier<Boolean> test) {
return testing().enableTestAssertions ? test.get() : true;
}
public AndroidApiLevelCompute apiLevelCompute() {
return apiLevelCompute;
}
public ComputedApiLevel computedMinApiLevel() {
return computedMinApiLevel;
}
public void addPrunedClassSourceFile(DexType prunedType, String sourceFile) {
sourceFileForPrunedTypes.put(prunedType, sourceFile);
}
public String getPrunedClassSourceFileInfo(DexType dexType) {
return sourceFileForPrunedTypes.get(dexType);
}
public boolean verifyMovedMethodsHaveOriginalMethodPosition() {
DirectMappedDexApplication application = app().asDirect();
application
.classesWithDeterministicOrder()
.forEach(
clazz ->
clazz.forEachProgramMethod(
method -> {
assert verifyOriginalMethodInPosition(method);
}));
return true;
}
private static boolean verifyOriginalMethodInPosition(ProgramMethod context) {
Code code = context.getDefinition().getCode();
if (code == null) {
return true;
}
DexMethod thisMethod = context.getReference();
code.forEachPosition(
context.getReference(),
context.getDefinition().isD8R8Synthesized(),
position -> {
DexMethod outerCaller = position.getOutermostCaller().getMethod();
assert thisMethod.isIdenticalTo(outerCaller);
});
return true;
}
}