blob: e653a7aaa5d8ec3d7fb9dfe101930d1578c88f53 [file] [log] [blame]
// 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.utils;
import static com.android.tools.r8.utils.AndroidApiLevel.B;
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DataResourceConsumer;
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.GlobalSyntheticsConsumer;
import com.android.tools.r8.MapIdProvider;
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.SourceFileProvider;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Backend;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.dex.MixedSectionLayoutStrategy;
import com.android.tools.r8.dex.VirtualFile;
import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.errors.InvalidLibrarySuperclassDiagnostic;
import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.experimental.startup.StartupOptions;
import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
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.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.Policy;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.desugar.TypeRewriter.MachineDesugarPrefixRewritingMapper;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.profile.art.ArtProfileOptions;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.repackaging.Repackaging.DefaultRepackagingConfiguration;
import com.android.tools.r8.repackaging.Repackaging.RepackagingConfiguration;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.GlobalKeepInfoConfiguration;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.objectweb.asm.Opcodes;
public class InternalOptions implements GlobalKeepInfoConfiguration {
// Set to true to run compilation in a single thread and without randomly shuffling the input.
// This makes life easier when running R8 in a debugger.
public static final boolean DETERMINISTIC_DEBUGGING =
System.getProperty("com.android.tools.r8.deterministicdebugging") != null;
// Use a MethodCollection where most interleavings between reading and mutating is caught.
public static final boolean USE_METHOD_COLLECTION_CONCURRENCY_CHECKED = false;
public enum LineNumberOptimization {
OFF,
ON
}
public enum DesugarState {
OFF,
ON;
public boolean isOff() {
return this == OFF;
}
public boolean isOn() {
return this == ON;
}
}
public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V19;
public static final int SUPPORTED_DEX_VERSION =
AndroidApiLevel.LATEST.getDexVersion().getIntValue();
public static final int EXPERIMENTAL_DEX_VERSION = DexVersion.V40.getIntValue();
public static final int ASM_VERSION = Opcodes.ASM9;
public final DexItemFactory itemFactory;
public DexItemFactory dexItemFactory() {
return itemFactory;
}
public boolean hasProguardConfiguration() {
return proguardConfiguration != null;
}
public ProguardConfiguration getProguardConfiguration() {
return proguardConfiguration;
}
private final ProguardConfiguration proguardConfiguration;
public final Reporter reporter;
// TODO(zerny): Make this private-final once we have full program-consumer support.
public ProgramConsumer programConsumer = null;
public ProgramClassConflictResolver programClassConflictResolver = null;
private GlobalSyntheticsConsumer globalSyntheticsConsumer = null;
public DataResourceConsumer dataResourceConsumer;
public FeatureSplitConfiguration featureSplitConfiguration;
public List<Consumer<InspectorImpl>> outputInspections = Collections.emptyList();
// Constructor for testing and/or other utilities.
public InternalOptions() {
reporter = new Reporter();
itemFactory = new DexItemFactory();
proguardConfiguration = null;
enableTreeShaking = false;
enableMinification = false;
}
// Constructor for D8, L8, Lint and other non-shrinkers.
public InternalOptions(DexItemFactory factory, Reporter reporter) {
assert reporter != null;
assert factory != null;
this.reporter = reporter;
itemFactory = factory;
proguardConfiguration = null;
enableTreeShaking = false;
enableMinification = false;
disableGlobalOptimizations();
}
// Constructor for R8.
public InternalOptions(
CompilationMode mode, ProguardConfiguration proguardConfiguration, Reporter reporter) {
assert reporter != null;
assert proguardConfiguration != null;
this.debug = mode == CompilationMode.DEBUG;
this.reporter = reporter;
this.proguardConfiguration = proguardConfiguration;
itemFactory = proguardConfiguration.getDexItemFactory();
enableTreeShaking = proguardConfiguration.isShrinking();
enableMinification = proguardConfiguration.isObfuscating();
if (!proguardConfiguration.isOptimizing()) {
// TODO(b/171457102): Avoid the need for this.
// -dontoptimize disables optimizations by flipping related flags.
disableAllOptimizations();
}
if (debug) {
assert !isMinifying();
assert !isOptimizing();
keepDebugRelatedInformation();
}
configurationDebugging = proguardConfiguration.isConfigurationDebugging();
if (proguardConfiguration.isProtoShrinkingEnabled()) {
enableProtoShrinking();
}
}
private void keepDebugRelatedInformation() {
assert !proguardConfiguration.isObfuscating();
getProguardConfiguration().getKeepAttributes().sourceFile = true;
getProguardConfiguration().getKeepAttributes().sourceDebugExtension = true;
getProguardConfiguration().getKeepAttributes().lineNumberTable = true;
getProguardConfiguration().getKeepAttributes().localVariableTable = true;
getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true;
}
void enableProtoShrinking() {
enableFieldBitAccessAnalysis = true;
protoShrinking.enableGeneratedMessageLiteShrinking = true;
protoShrinking.enableGeneratedMessageLiteBuilderShrinking = true;
protoShrinking.enableGeneratedExtensionRegistryShrinking = true;
protoShrinking.enableEnumLiteProtoShrinking = true;
}
void disableAllOptimizations() {
disableGlobalOptimizations();
enableNameReflectionOptimization = false;
enableStringConcatenationOptimization = false;
}
public void disableGlobalOptimizations() {
inlinerOptions.enableInlining = false;
enableClassInlining = false;
enableDevirtualization = false;
enableVerticalClassMerging = false;
enableEnumUnboxing = false;
outline.enabled = false;
enableEnumValueOptimization = false;
enableSideEffectAnalysis = false;
enableTreeShakingOfLibraryMethodOverrides = false;
enableInitializedClassesAnalysis = false;
callSiteOptimizationOptions.disableOptimization();
horizontalClassMergerOptions.setRestrictToSynthetics();
}
public void configureAndroidPlatformBuild(boolean isAndroidPlatformBuild) {
assert !androidPlatformBuild;
if (isAndroidPlatformBuildOrMinApiPlatform()) {
apiModelingOptions().disableApiModeling();
}
if (!isAndroidPlatformBuild) {
return;
}
// Configure options according to platform build assumptions.
// See go/r8platformflag and b/232073181.
androidPlatformBuild = isAndroidPlatformBuild;
enableBackportMethods = false;
}
public boolean isAndroidPlatformBuild() {
return androidPlatformBuild;
}
public boolean isAndroidPlatformBuildOrMinApiPlatform() {
return androidPlatformBuild || minApiLevel.isPlatform();
}
public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
// To print memory one also have to enable printtimes.
public boolean printMemory = System.getProperty("com.android.tools.r8.printmemory") != null;
// Flag to toggle if DEX code objects should pass-through without IR processing.
public boolean passthroughDexCode = false;
public static class NeverMergeGroup<T> {
private final List<T> prefixes;
private final List<T> exceptionPrefixes;
NeverMergeGroup(List<T> prefixes, List<T> exceptionPrefixes) {
this.prefixes = prefixes;
this.exceptionPrefixes = exceptionPrefixes;
}
public List<T> getPrefixes() {
return prefixes;
}
public List<T> getExceptionPrefixes() {
return exceptionPrefixes;
}
public <R> NeverMergeGroup<R> map(Function<T, R> fn) {
return new NeverMergeGroup<>(
prefixes.stream().map(fn).collect(Collectors.toList()),
exceptionPrefixes.stream().map(fn).collect(Collectors.toList()));
}
}
// Flag to toggle if the prefix based merge restriction should be enforced.
public boolean enableNeverMergePrefixes = true;
public NeverMergeGroup<String> neverMerge =
new NeverMergeGroup<>(ImmutableList.of("j$."), ImmutableList.of("java."));
public boolean classpathInterfacesMayHaveStaticInitialization = false;
public boolean libraryInterfacesMayHaveStaticInitialization = false;
// Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations.
public boolean enableFieldBitAccessAnalysis =
System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
public boolean enableVerticalClassMerging = true;
public boolean enableUnusedInterfaceRemoval = true;
public boolean enableDevirtualization = true;
public boolean enableEnumUnboxing = true;
public boolean enableSimpleInliningConstraints = true;
public final int simpleInliningConstraintThreshold = 0;
public boolean enableClassInlining = true;
public boolean enableClassStaticizer = true;
public boolean enableInitializedClassesAnalysis = true;
public boolean enableSideEffectAnalysis = true;
public boolean enableDeterminismAnalysis = true;
public boolean enableServiceLoaderRewriting = true;
public boolean enableNameReflectionOptimization = true;
public boolean enableStringConcatenationOptimization = true;
public boolean enableTreeShakingOfLibraryMethodOverrides = false;
public boolean encodeChecksums = false;
public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
public boolean forceAnnotateSynthetics = false;
public boolean readDebugSetFileEvent = false;
public boolean disableL8AnnotationRemoval =
System.getProperty("com.android.tools.r8.disableL8AnnotationRemoval") != null;
public int callGraphLikelySpuriousCallEdgeThreshold = 50;
public int verificationSizeLimitInBytes() {
if (testing.verificationSizeLimitInBytesOverride > -1) {
return testing.verificationSizeLimitInBytesOverride;
}
// For CF we use the defined limit in the spec. For DEX we use the limit of the static verifier
// https://android.googlesource.com/platform/art/+/android10-release/compiler/compiler.cc#48
return isGeneratingClassFiles() ? 65534 : 16383;
}
public int minimumVerificationSizeLimitInBytes() {
if (testing.verificationSizeLimitInBytesOverride > -1) {
return testing.verificationSizeLimitInBytesOverride;
}
return 16383;
}
// We assume options will always be created on the main thread.
public Thread mainThread = Thread.currentThread();
public boolean enableSwitchRewriting = true;
public boolean enableStringSwitchConversion = true;
public int minimumStringSwitchSize = 3;
public boolean enableEnumValueOptimization = true;
public boolean enableEnumSwitchMapRemoval = true;
public final OutlineOptions outline = new OutlineOptions();
public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
public boolean enableRedundantFieldLoadElimination = true;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
public boolean enableLoopUnrolling = true;
// TODO(b/237567012): Remove when resolved.
public boolean enableCheckAllInstructionsDuringStackMapVerification = false;
public String synthesizedClassPrefix = "";
// Number of threads to use while processing the dex files.
public int threadCount = DETERMINISTIC_DEBUGGING ? 1 : ThreadUtils.NOT_SPECIFIED;
// Print smali disassembly.
public boolean useSmaliSyntax = false;
// Verbose output.
public boolean verbose = false;
// Silencing output.
public boolean quiet = false;
// Throw exception if there is a warning about invalid debug info.
public boolean invalidDebugInfoFatal = false;
// Don't gracefully recover from invalid debug info.
public boolean invalidDebugInfoStrict =
System.getProperty("com.android.tools.r8.strictdebuginfo") != null;
public boolean ignoreJavaLibraryOverride = false;
// When dexsplitting we ignore main dex classes missing in the application. These will be
// fused together by play store when shipped for pre-L devices.
public boolean ignoreMainDexMissingClasses = false;
// Boolean value indicating that byte code pass through may be enabled.
public boolean enableCfByteCodePassThrough = false;
// Flag to control the representation of stateless lambdas.
// See b/222081665 for context.
public boolean createSingletonsForStatelessLambdas =
System.getProperty("com.android.tools.r8.createSingletonsForStatelessLambdas") != null;
// Flag to allow record annotations in DEX. See b/231930852 for context.
public boolean emitRecordAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitRecordAnnotationsInDex") != null;
// Flag to allow nest annotations in DEX. See b/231930852 for context.
public boolean emitNestAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitNestAnnotationsInDex") != null;
// Flag to allow permitted subclasses annotations in DEX. See b/231930852 for context.
public boolean emitPermittedSubclassesAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitPermittedSubclassesAnnotationsInDex") != null;
private DumpInputFlags dumpInputFlags = DumpInputFlags.getDefault();
// Contain the contents of the build properties file from the compiler command.
public DumpOptions dumpOptions;
// Hidden marker for classes.dex
private boolean hasMarker = false;
private Marker marker;
public void setMarker(Marker marker) {
this.hasMarker = true;
this.marker = marker;
}
public Marker getMarker(Tool tool) {
if (hasMarker) {
return marker;
}
return createMarker(tool);
}
// Compute the marker to be placed in the main dex file.
private Marker createMarker(Tool tool) {
Marker marker =
new Marker(tool)
.setVersion(Version.LABEL)
.setCompilationMode(debug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
.setBackend(isGeneratingClassFiles() ? Backend.CF : Backend.DEX)
.setHasChecksums(encodeChecksums);
// The marker records the min API if any desugaring happens or if the compiler generates dex
// since the output depends on the min API in this case. There is basically no min API entry
// in R8 cf to cf.
if (isGeneratingDex() || desugarState == DesugarState.ON) {
marker.setMinApi(getMinApiLevel().getLevel());
}
if (machineDesugaredLibrarySpecification.getIdentifier() != null) {
marker.setDesugaredLibraryIdentifiers(machineDesugaredLibrarySpecification.getIdentifier());
}
if (Version.isDevelopmentVersion()) {
marker.setSha1(VersionProperties.INSTANCE.getSha());
}
if (tool == Tool.R8) {
marker.setR8Mode(forceProguardCompatibility ? "compatibility" : "full");
}
if (androidPlatformBuild) {
marker.setAndroidPlatformBuild();
}
return marker;
}
public void setDumpInputFlags(DumpInputFlags dumpInputFlags) {
this.dumpInputFlags = dumpInputFlags;
}
public boolean hasConsumer() {
return programConsumer != null;
}
public InternalOutputMode getInternalOutputMode() {
assert hasConsumer();
if (isGeneratingDexIndexed()) {
return InternalOutputMode.DexIndexed;
} else if (isGeneratingDexFilePerClassFile()) {
return InternalOutputMode.DexFilePerClassFile;
} else if (isGeneratingClassFiles()) {
return InternalOutputMode.ClassFile;
}
throw new UnsupportedOperationException("Cannot find internal output mode.");
}
public boolean hasGlobalSyntheticsConsumer() {
return globalSyntheticsConsumer != null;
}
public GlobalSyntheticsConsumer getGlobalSyntheticsConsumer() {
return globalSyntheticsConsumer;
}
public void setGlobalSyntheticsConsumer(GlobalSyntheticsConsumer globalSyntheticsConsumer) {
this.globalSyntheticsConsumer = globalSyntheticsConsumer;
}
public boolean isDesugaredLibraryCompilation() {
return machineDesugaredLibrarySpecification.isLibraryCompilation();
}
public boolean isRelocatorCompilation() {
return relocatorCompilation;
}
public boolean shouldKeepStackMapTable() {
assert isRelocatorCompilation() || getProguardConfiguration() != null;
return isRelocatorCompilation() || getProguardConfiguration().getKeepAttributes().stackMapTable;
}
public boolean shouldRerunEnqueuer() {
return isShrinking() || isMinifying() || getProguardConfiguration().hasApplyMappingFile();
}
public boolean isGeneratingDex() {
return isGeneratingDexIndexed() || isGeneratingDexFilePerClassFile();
}
public boolean isGeneratingDexIndexed() {
return programConsumer instanceof DexIndexedConsumer;
}
public boolean isGeneratingDexFilePerClassFile() {
return programConsumer instanceof DexFilePerClassFileConsumer;
}
public boolean isGeneratingClassFiles() {
return programConsumer instanceof ClassFileConsumer;
}
public boolean isDesugaring() {
return desugarState.isOn();
}
public boolean isCfDesugaring() {
return isGeneratingClassFiles() && desugarState.isOn();
}
public DexIndexedConsumer getDexIndexedConsumer() {
return (DexIndexedConsumer) programConsumer;
}
public DexFilePerClassFileConsumer getDexFilePerClassFileConsumer() {
return (DexFilePerClassFileConsumer) programConsumer;
}
public ClassFileConsumer getClassFileConsumer() {
return (ClassFileConsumer) programConsumer;
}
public void signalFinishedToConsumers() {
if (programConsumer != null) {
programConsumer.finished(reporter);
if (dataResourceConsumer != null) {
dataResourceConsumer.finished(reporter);
}
}
if (featureSplitConfiguration != null) {
for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
ProgramConsumer programConsumer = featureSplit.getProgramConsumer();
programConsumer.finished(reporter);
DataResourceConsumer dataResourceConsumer = programConsumer.getDataResourceConsumer();
if (dataResourceConsumer != null) {
dataResourceConsumer.finished(reporter);
}
}
}
if (desugarGraphConsumer != null) {
desugarGraphConsumer.finished();
}
}
public boolean shouldDesugarNests() {
return !canUseNestBasedAccess();
}
public boolean shouldDesugarRecords() {
return desugarState.isOn() && !canUseRecords();
}
public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
public Set<String> extensiveInterfaceMethodMinifierLoggingFilter =
getExtensiveInterfaceMethodMinifierLoggingFilter();
public List<String> methodsFilter = ImmutableList.of();
private AndroidApiLevel minApiLevel = AndroidApiLevel.getDefault();
// Skipping min_api check and compiling an intermediate result intended for later merging.
// Intermediate builds also emits or update synthesized classes mapping.
public boolean intermediate = false;
private boolean androidPlatformBuild = false;
public boolean retainCompileTimeAnnotations = true;
public boolean ignoreBootClasspathEnumsForMaindexTracing =
System.getProperty("com.android.tools.r8.ignoreBootClasspathEnumsForMaindexTracing") != null;
public boolean pruneNonVissibleAnnotationClasses =
System.getProperty("com.android.tools.r8.pruneNonVissibleAnnotationClasses") != null;
public List<String> logArgumentsFilter = ImmutableList.of();
// Flag to turn on/offLoad/store optimization in the Cf back-end.
public boolean enableLoadStoreOptimization = true;
// Flag to turn on/off desugaring in D8/R8.
public DesugarState desugarState = DesugarState.ON;
// Flag to turn on/off backport methods.
public boolean enableBackportMethods = true;
// Flag to turn on/off reduction of nest to improve class merging optimizations.
public boolean enableNestReduction = true;
// Defines interface method rewriter behavior.
public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Auto;
// Defines try-with-resources rewriter behavior.
public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Auto;
// Flag to turn on/off processing of @dalvik.annotation.codegen.CovariantReturnType and
// @dalvik.annotation.codegen.CovariantReturnType$CovariantReturnTypes.
public boolean processCovariantReturnTypeAnnotations = true;
// Flag to control library/program class lookup order.
// TODO(120884788): Enable this flag as the default.
public boolean lookupLibraryBeforeProgram = false;
// TODO(120884788): Leave this system property as a stop-gap for some time.
// public boolean lookupLibraryBeforeProgram =
// System.getProperty("com.android.tools.r8.lookupProgramBeforeLibrary") == null;
public boolean enableEnqueuerDeferredTracing =
System.getProperty("com.android.tools.r8.disableEnqueuerDeferredTracing") == null;
public boolean loadAllClassDefinitions = false;
// Whether or not to check for valid multi-dex builds.
//
// For min-api levels that did not support native multi-dex the user should provide a main dex
// list. However, DX, didn't check that this was the case. Therefore, for CompatDX we have a flag
// to disable the check that the build makes sense for multi-dexing.
public boolean enableMainDexListCheck = true;
private final boolean enableTreeShaking;
private final boolean enableMinification;
public AndroidApiLevel getMinApiLevel() {
// If compiling to CF with no desugaring then we should not inspect the min-api.
// For now we assert the API level for non-desugared CF is B, but it would be better to never
// access the min-api in those cases.
assert desugarState.isOn() || isGeneratingDex() || minApiLevel.equals(AndroidApiLevel.B);
return minApiLevel;
}
public void setMinApiLevel(AndroidApiLevel minApiLevel) {
assert minApiLevel != null;
this.minApiLevel = minApiLevel;
}
public boolean isOptimizing() {
return hasProguardConfiguration() && getProguardConfiguration().isOptimizing();
}
public boolean isRelease() {
return !debug;
}
public boolean isShrinking() {
assert proguardConfiguration == null
|| enableTreeShaking == proguardConfiguration.isShrinking();
return enableTreeShaking;
}
public boolean isMinifying() {
assert proguardConfiguration == null
|| enableMinification == proguardConfiguration.isObfuscating();
return enableMinification;
}
@Override
public boolean isAnnotationRemovalEnabled() {
return !isForceProguardCompatibilityEnabled();
}
@Override
public boolean isTreeShakingEnabled() {
return isShrinking();
}
@Override
public boolean isMinificationEnabled() {
return isMinifying();
}
@Override
public boolean isOptimizationEnabled() {
return isOptimizing();
}
@Override
public boolean isRepackagingEnabled() {
return !debug
&& proguardConfiguration != null
&& proguardConfiguration.getPackageObfuscationMode().isSome()
&& (isMinifying() || !isForceProguardCompatibilityEnabled());
}
@Override
public boolean isForceProguardCompatibilityEnabled() {
return forceProguardCompatibility;
}
public boolean parseSignatureAttribute() {
return isKeepAttributesSignatureEnabled();
}
@Override
public boolean isKeepAttributesSignatureEnabled() {
return proguardConfiguration == null || proguardConfiguration.getKeepAttributes().signature;
}
@Override
public boolean isKeepEnclosingMethodAttributeEnabled() {
return proguardConfiguration.getKeepAttributes().enclosingMethod;
}
@Override
public boolean isKeepInnerClassesAttributeEnabled() {
return proguardConfiguration.getKeepAttributes().innerClasses;
}
@Override
public boolean isKeepRuntimeInvisibleAnnotationsEnabled() {
return proguardConfiguration.getKeepAttributes().runtimeInvisibleAnnotations;
}
@Override
public boolean isKeepRuntimeInvisibleParameterAnnotationsEnabled() {
return proguardConfiguration.getKeepAttributes().runtimeInvisibleParameterAnnotations;
}
@Override
public boolean isKeepRuntimeVisibleAnnotationsEnabled() {
return proguardConfiguration.getKeepAttributes().runtimeVisibleAnnotations;
}
@Override
public boolean isKeepRuntimeVisibleParameterAnnotationsEnabled() {
return proguardConfiguration.getKeepAttributes().runtimeVisibleParameterAnnotations;
}
/**
* If any non-static class merging is enabled, information about types referred to by instanceOf
* and check cast instructions needs to be collected.
*/
public boolean isClassMergingExtensionRequired(Enqueuer.Mode mode) {
if (mode.isInitialTreeShaking()) {
return (horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.INITIAL)
&& !horizontalClassMergerOptions.isRestrictedToSynthetics())
|| enableVerticalClassMerging;
}
if (mode.isFinalTreeShaking()) {
return horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.FINAL)
&& !horizontalClassMergerOptions.isRestrictedToSynthetics();
}
assert false;
return false;
}
@Override
public boolean isAccessModificationEnabled() {
return getProguardConfiguration() != null
&& getProguardConfiguration().isAccessModificationAllowed();
}
@Override
public boolean isMethodStaticizingEnabled() {
return callSiteOptimizationOptions().isMethodStaticizingEnabled();
}
public boolean keepInnerClassStructure() {
return getProguardConfiguration().getKeepAttributes().signature
|| getProguardConfiguration().getKeepAttributes().innerClasses;
}
public boolean canUseInputStackMaps() {
return testing.readInputStackMaps ? testing.readInputStackMaps : isGeneratingClassFiles();
}
public boolean printCfg = false;
public String printCfgFile;
public boolean ignoreMissingClasses = false;
public boolean reportMissingClassesInEnclosingMethodAttribute = false;
public boolean reportMissingClassesInInnerClassAttributes = false;
public boolean disableGenericSignatureValidation = false;
public boolean disableInnerClassSeparatorValidationWhenRepackaging = false;
// EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
public boolean forceProguardCompatibility = false;
public AssertionConfigurationWithDefault assertionsConfiguration = null;
public boolean configurationDebugging = false;
// Don't convert Code objects to IRCode.
public boolean skipIR = false;
public boolean debug = false;
private final CallSiteOptimizationOptions callSiteOptimizationOptions =
new CallSiteOptimizationOptions();
private final CfCodeAnalysisOptions cfCodeAnalysisOptions = new CfCodeAnalysisOptions();
private final ClassInlinerOptions classInlinerOptions = new ClassInlinerOptions();
private final InlinerOptions inlinerOptions = new InlinerOptions();
private final HorizontalClassMergerOptions horizontalClassMergerOptions =
new HorizontalClassMergerOptions();
private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
new OpenClosedInterfacesOptions();
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
private final MappingComposeOptions mappingComposeOptions = new MappingComposeOptions();
private final ArtProfileOptions artProfileOptions = new ArtProfileOptions();
private final StartupOptions startupOptions = new StartupOptions();
private final StartupInstrumentationOptions startupInstrumentationOptions =
new StartupInstrumentationOptions();
public final TestingOptions testing = new TestingOptions();
public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
public boolean minimalMainDex;
/**
* Enable usage of InheritanceClassInDexDistributor for multidex legacy builds. This allows
* distribution of classes to minimize DexOpt LinearAlloc usage by minimizing linking errors
* during DexOpt and controlling the load of classes with linking issues. This has the consequence
* of making minimal main dex not absolutely minimal regarding runtime execution constraints
* because it's adding classes in the main dex to satisfy also DexOpt constraints.
*/
public boolean enableInheritanceClassInDexDistributor = true;
public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
public CallSiteOptimizationOptions callSiteOptimizationOptions() {
return callSiteOptimizationOptions;
}
public ClassInlinerOptions classInlinerOptions() {
return classInlinerOptions;
}
public InlinerOptions inlinerOptions() {
return inlinerOptions;
}
public HorizontalClassMergerOptions horizontalClassMergerOptions() {
return horizontalClassMergerOptions;
}
public ProtoShrinkingOptions protoShrinking() {
return protoShrinking;
}
public KotlinOptimizationOptions kotlinOptimizationOptions() {
return kotlinOptimizationOptions;
}
public ApiModelTestingOptions apiModelingOptions() {
return apiModelTestingOptions;
}
public MappingComposeOptions mappingComposeOptions() {
return mappingComposeOptions;
}
public DesugarSpecificOptions desugarSpecificOptions() {
return desugarSpecificOptions;
}
public CfCodeAnalysisOptions getCfCodeAnalysisOptions() {
return cfCodeAnalysisOptions;
}
public DumpInputFlags getDumpInputFlags() {
return dumpInputFlags;
}
public OpenClosedInterfacesOptions getOpenClosedInterfacesOptions() {
return openClosedInterfacesOptions;
}
public ArtProfileOptions getArtProfileOptions() {
return artProfileOptions;
}
public StartupOptions getStartupOptions() {
return startupOptions;
}
public StartupInstrumentationOptions getStartupInstrumentationOptions() {
return startupInstrumentationOptions;
}
public TestingOptions getTestingOptions() {
return testing;
}
private static Set<String> getExtensiveLoggingFilter() {
String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
if (property != null) {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (String method : property.split(";")) {
builder.add(method);
}
return builder.build();
}
return ImmutableSet.of();
}
private static Set<String> getExtensiveInterfaceMethodMinifierLoggingFilter() {
String property =
System.getProperty("com.android.tools.r8.extensiveInterfaceMethodMinifierLoggingFilter");
if (property != null) {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (String method : property.split(";")) {
builder.add(method);
}
return builder.build();
}
return ImmutableSet.of();
}
public static class InvalidParameterAnnotationInfo {
final DexMethod method;
final int expectedParameterCount;
final int actualParameterCount;
public InvalidParameterAnnotationInfo(
DexMethod method, int expectedParameterCount, int actualParameterCount) {
this.method = method;
this.expectedParameterCount = expectedParameterCount;
this.actualParameterCount = actualParameterCount;
}
}
private static class TypeVersionPair {
final CfVersion version;
final DexType type;
public TypeVersionPair(CfVersion version, DexType type) {
this.version = version;
this.type = type;
}
}
private final Map<Origin, List<TypeVersionPair>> missingEnclosingMembers = new HashMap<>();
private final Map<Origin, List<InvalidParameterAnnotationInfo>> warningInvalidParameterAnnotations
= new HashMap<>();
private final Map<Origin, List<Pair<ProgramMethod, String>>> warningInvalidDebugInfo =
new HashMap<>();
// Don't read code from dex files. Used to extract non-code information from vdex files where
// the code contains unsupported byte codes.
public boolean skipReadingDexCode = false;
// If null, no main-dex list needs to be computed.
// If non null it must be and passed to the consumer.
public StringConsumer mainDexListConsumer = null;
// If null, no proguard map needs to be computed.
// If non null it must be and passed to the consumer.
public StringConsumer proguardMapConsumer = null;
// If null, no usage information needs to be computed.
// If non-null, it must be and is passed to the consumer.
public StringConsumer usageInformationConsumer = null;
public boolean hasUsageInformationConsumer() {
return usageInformationConsumer != null;
}
// If null, no proguard seeds info needs to be computed.
// If non null it must be and passed to the consumer.
public StringConsumer proguardSeedsConsumer = null;
// If null, no configuration information needs to be printed.
// If non-null, configuration must be passed to the consumer.
public StringConsumer configurationConsumer = null;
public void setDesugaredLibrarySpecification(DesugaredLibrarySpecification specification) {
if (specification.isEmpty()) {
return;
}
loadMachineDesugaredLibrarySpecification =
(timing, app) ->
machineDesugaredLibrarySpecification =
specification.toMachineSpecification(app, timing);
}
private ThrowingBiConsumer<Timing, DexApplication, IOException>
loadMachineDesugaredLibrarySpecification = null;
public void loadMachineDesugaredLibrarySpecification(Timing timing, DexApplication app)
throws IOException {
if (loadMachineDesugaredLibrarySpecification == null) {
return;
}
timing.begin("Load machine specification");
loadMachineDesugaredLibrarySpecification.accept(timing, app);
timing.end();
}
// Contains flags describing library desugaring.
public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification =
MachineDesugaredLibrarySpecification.empty();
public TypeRewriter getTypeRewriter() {
return machineDesugaredLibrarySpecification.requiresTypeRewriting()
? new MachineDesugarPrefixRewritingMapper(machineDesugaredLibrarySpecification)
: TypeRewriter.empty();
}
public boolean relocatorCompilation = false;
// If null, no keep rules are recorded.
// If non null it records desugared library APIs used by the program.
public StringConsumer desugaredLibraryKeepRuleConsumer = null;
// If null, no graph information needs to be provided for the keep/inclusion of classes
// in the output. If non-null, each edge pertaining to kept parts of the resulting program
// must be reported to the consumer.
public GraphConsumer keptGraphConsumer = null;
// If null, no graph information needs to be provided for the keep/inclusion of classes
// in the main-dex output. If non-null, each edge pertaining to kept parts in the main-dex output
// of the resulting program must be reported to the consumer.
public GraphConsumer mainDexKeptGraphConsumer = null;
// If null, no desugaring dependencies need to be provided. If non-null, each dependency between
// code objects needed for correct desugaring needs to be provided to the consumer.
public DesugarGraphConsumer desugarGraphConsumer = null;
public Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer = null;
public MapIdProvider mapIdProvider = null;
public SourceFileProvider sourceFileProvider = null;
public static boolean assertionsEnabled() {
boolean assertionsEnabled = false;
assert assertionsEnabled = true; // Intentional side-effect.
return assertionsEnabled;
}
public static void checkAssertionsEnabled() {
if (!assertionsEnabled()) {
throw new Unreachable();
}
}
/** A set of dexitems we have reported missing to dedupe warnings. */
private final Set<DexItem> reportedMissingForDesugaring = Sets.newConcurrentHashSet();
private final AtomicBoolean reportedErrorReadingKotlinMetadataReflectively =
new AtomicBoolean(false);
private final Set<DexItem> invalidLibraryClasses = Sets.newConcurrentHashSet();
public RuntimeException errorMissingNestHost(DexClass clazz) {
throw reporter.fatalError(
new MissingNestHostNestDesugarDiagnostic(
clazz.getOrigin(), Position.UNKNOWN, messageErrorMissingNestHost(clazz)));
}
private static String messageErrorMissingNestHost(DexClass compiledClass) {
String nestHostName = compiledClass.getNestHost().getName();
return "Class "
+ compiledClass.type.getName()
+ " requires its nest host "
+ nestHostName
+ " to be on program or class path.";
}
public RuntimeException errorMissingNestMember(Nest nest) {
throw reporter.fatalError(
new IncompleteNestNestDesugarDiagnosic(
nest.getHostClass().getOrigin(), Position.UNKNOWN, messageErrorIncompleteNest(nest)));
}
private static String messageErrorIncompleteNest(Nest nest) {
List<DexProgramClass> programClassesFromNest = new ArrayList<>();
List<DexClasspathClass> classpathClassesFromNest = new ArrayList<>();
List<DexLibraryClass> libraryClassesFromNest = new ArrayList<>();
nest.getHostClass()
.accept(
programClassesFromNest::add,
classpathClassesFromNest::add,
libraryClassesFromNest::add);
for (DexClass memberClass : nest.getMembers()) {
memberClass.accept(
programClassesFromNest::add, classpathClassesFromNest::add, libraryClassesFromNest::add);
}
StringBuilder stringBuilder =
new StringBuilder("Compilation of classes ")
.append(StringUtils.join(", ", programClassesFromNest, DexClass::getTypeName))
.append(" requires its nest mates ");
if (nest.hasMissingMembers()) {
stringBuilder
.append(StringUtils.join(", ", nest.getMissingMembers(), DexType::getTypeName))
.append(" (unavailable) ");
}
if (!libraryClassesFromNest.isEmpty()) {
stringBuilder
.append(StringUtils.join(", ", libraryClassesFromNest, DexClass::getTypeName))
.append(" (on library path) ");
}
stringBuilder.append("to be on program or class path.");
if (!classpathClassesFromNest.isEmpty()) {
stringBuilder
.append("(Classes ")
.append(StringUtils.join(", ", classpathClassesFromNest, DexClass::getTypeName))
.append(" from the same nest are on class path).");
}
return stringBuilder.toString();
}
public void warningMissingTypeForDesugar(
Origin origin, Position position, DexType missingType, DexMethod context) {
if (reportedMissingForDesugaring.add(missingType)) {
reporter.warning(
new InterfaceDesugarMissingTypeDiagnostic(
origin,
position,
Reference.classFromDescriptor(missingType.toDescriptorString()),
Reference.classFromDescriptor(context.holder.toDescriptorString()),
null));
}
}
public void warningMissingInterfaceForDesugar(
DexClass classToDesugar, DexClass implementing, DexType missing) {
if (reportedMissingForDesugaring.add(missing)) {
reporter.warning(
new InterfaceDesugarMissingTypeDiagnostic(
classToDesugar.getOrigin(),
Position.UNKNOWN,
Reference.classFromDescriptor(missing.toDescriptorString()),
Reference.classFromDescriptor(classToDesugar.getType().toDescriptorString()),
classToDesugar == implementing
? null
: Reference.classFromDescriptor(implementing.getType().toDescriptorString())));
}
}
public void warningReadingKotlinMetadataReflective() {
if (reportedErrorReadingKotlinMetadataReflectively.compareAndSet(false, true)) {
reporter.warning(
new StringDiagnostic(
"Could not read the kotlin metadata message reflectively which indicates the"
+ " compiler running in the context of a Security Manager. Not being able to"
+ " read the kotlin metadata will have a negative effect oncode size"));
}
}
public void warningInvalidLibrarySuperclassForDesugar(
Origin origin,
DexType libraryType,
DexType invalidSuperType,
String message,
Set<DexMethod> retarget) {
if (invalidLibraryClasses.add(invalidSuperType)) {
reporter.warning(
new InvalidLibrarySuperclassDiagnostic(
origin,
Reference.classFromDescriptor(libraryType.toDescriptorString()),
Reference.classFromDescriptor(invalidSuperType.toDescriptorString()),
message,
Lists.newArrayList(
Iterables.transform(retarget, method -> method.asMethodReference()))));
}
}
public void warningMissingEnclosingMember(DexType clazz, Origin origin, CfVersion version) {
TypeVersionPair pair = new TypeVersionPair(version, clazz);
synchronized (missingEnclosingMembers) {
missingEnclosingMembers.computeIfAbsent(origin, k -> new ArrayList<>()).add(pair);
}
}
public void warningInvalidParameterAnnotations(
DexMethod method, Origin origin, int expected, int actual) {
InvalidParameterAnnotationInfo info =
new InvalidParameterAnnotationInfo(method, expected, actual);
synchronized (warningInvalidParameterAnnotations) {
warningInvalidParameterAnnotations.computeIfAbsent(origin, k -> new ArrayList<>()).add(info);
}
}
public void warningInvalidDebugInfo(
ProgramMethod method, Origin origin, InvalidDebugInfoException e) {
if (invalidDebugInfoFatal) {
throw new CompilationError("Fatal warning: Invalid debug info", e);
}
synchronized (warningInvalidDebugInfo) {
warningInvalidDebugInfo.computeIfAbsent(
origin, k -> new ArrayList<>()).add(new Pair<>(method, e.getMessage()));
}
}
public boolean printWarnings() {
boolean printed = false;
boolean printOutdatedToolchain = false;
if (warningInvalidParameterAnnotations.size() > 0) {
// TODO(b/67626202): Add a regression test with a program that hits this issue.
reporter.info(
new StringDiagnostic(
"Invalid parameter counts in MethodParameter attributes. "
+ "This is likely due to Proguard having removed a parameter."));
for (Origin origin : new TreeSet<>(warningInvalidParameterAnnotations.keySet())) {
StringBuilder builder =
new StringBuilder("Methods with invalid MethodParameter attributes:");
for (InvalidParameterAnnotationInfo info : warningInvalidParameterAnnotations.get(origin)) {
builder
.append("\n ")
.append(info.method)
.append(" expected count: ")
.append(info.expectedParameterCount)
.append(" actual count: ")
.append(info.actualParameterCount);
}
reporter.info(new StringDiagnostic(builder.toString(), origin));
}
printed = true;
}
if (warningInvalidDebugInfo.size() > 0) {
int count = 0;
for (List<Pair<ProgramMethod, String>> methods : warningInvalidDebugInfo.values()) {
count += methods.size();
}
reporter.info(
new StringDiagnostic(
"Stripped invalid locals information from "
+ count
+ (count == 1 ? " method." : " methods.")));
for (Origin origin : new TreeSet<>(warningInvalidDebugInfo.keySet())) {
StringBuilder builder = new StringBuilder("Methods with invalid locals information:");
for (Pair<ProgramMethod, String> method : warningInvalidDebugInfo.get(origin)) {
builder.append("\n ").append(method.getFirst().toSourceString());
builder.append("\n ").append(method.getSecond());
}
reporter.info(new StringDiagnostic(builder.toString(), origin));
}
printed = true;
printOutdatedToolchain = true;
}
if (missingEnclosingMembers.size() > 0) {
reporter.info(
new StringDiagnostic(
"InnerClasses attribute has entries missing a corresponding "
+ "EnclosingMethod attribute. "
+ "Such InnerClasses attribute entries are ignored."));
for (Origin origin : new TreeSet<>(missingEnclosingMembers.keySet())) {
StringBuilder builder = new StringBuilder("Classes with missing EnclosingMethod: ");
boolean first = true;
for (TypeVersionPair pair : missingEnclosingMembers.get(origin)) {
if (first) {
first = false;
} else {
builder.append(", ");
}
builder.append(pair.type);
printOutdatedToolchain |= pair.version.isLessThan(CfVersion.V1_5);
}
reporter.info(new StringDiagnostic(builder.toString(), origin));
}
printed = true;
}
if (printOutdatedToolchain) {
reporter.info(
new StringDiagnostic(
"Some warnings are typically a sign of using an outdated Java toolchain."
+ " To fix, recompile the source with an updated toolchain."));
}
return printed;
}
public boolean hasMethodsFilter() {
return methodsFilter.size() > 0;
}
public boolean methodMatchesFilter(DexEncodedMethod method) {
// Not specifying a filter matches all methods.
if (!hasMethodsFilter()) {
return true;
}
// Currently the filter is simple string equality on the qualified name.
String qualifiedName = method.qualifiedName();
return methodsFilter.contains(qualifiedName);
}
public boolean methodMatchesLogArgumentsFilter(DexEncodedMethod method) {
// Not specifying a filter matches no methods.
if (logArgumentsFilter.size() == 0) {
return false;
}
// Currently the filter is simple string equality on the qualified name.
String qualifiedName = method.qualifiedName();
return logArgumentsFilter.contains(qualifiedName);
}
public enum PackageObfuscationMode {
// No package obfuscation.
NONE,
// Strategy based on ordinary package obfuscation when no package-obfuscation mode is specified
// by the users. In practice this falls back to FLATTEN but with keeping package-names.
MINIFICATION,
// Repackaging all classes into the single user-given (or top-level) package.
REPACKAGE,
// Repackaging all packages into the single user-given (or top-level) package.
FLATTEN;
public boolean isNone() {
return this == NONE;
}
public boolean isFlattenPackageHierarchy() {
return this == FLATTEN;
}
public boolean isRepackageClasses() {
return this == REPACKAGE;
}
public boolean isMinification() {
return this == MINIFICATION;
}
public boolean isSome() {
return !isNone();
}
}
public static class OutlineOptions {
public boolean enabled = true;
public int minSize = 3;
public int maxSize = 99;
public int threshold = 20;
public int maxNumberOfInstructionsToBeConsidered = 100;
}
public static class KotlinOptimizationOptions {
public boolean disableKotlinSpecificOptimizations =
System.getProperty("com.android.tools.r8.disableKotlinSpecificOptimizations") != null;
}
// Temporary desugar specific options to make progress on b/147485959
// All options should be including bugs to either fix the underlying issue or extend the api.
public static class DesugarSpecificOptions {
// b/172508621
public boolean sortMethodsOnCfOutput =
System.getProperty("com.android.tools.r8.sortMethodsOnCfWriting") != null;
// Desugaring is not fully idempotent. With this option turned on all desugared input is
// allowed, and if it is detected that the desugared input cannot be reprocessed, that input
// will be passed-through without the problematic rewritings applied.
public boolean allowAllDesugaredInput =
System.getProperty("com.android.tools.r8.allowAllDesugaredInput") != null;
// See b/191469661 for why this is here.
public boolean noCfMarkerForDesugaredCode =
System.getProperty("com.android.tools.r8.noCfMarkerForDesugaredCode") != null;
// See b/182065081 for why this is here.
public boolean lambdaClassFieldsFinal =
System.getProperty("com.android.tools.r8.lambdaClassFieldsNotFinal") == null;
}
public class CallSiteOptimizationOptions {
private boolean enabled = true;
private boolean enableMethodStaticizing = true;
private boolean forceSyntheticsForInstanceInitializers = false;
public void disableOptimization() {
enabled = false;
}
public int getMaxNumberOfInParameters() {
return 10;
}
public boolean isEnabled() {
if (!isOptimizing() || !isShrinking()) {
return false;
}
return enabled;
}
public boolean isForceSyntheticsForInstanceInitializersEnabled() {
return forceSyntheticsForInstanceInitializers;
}
public boolean isMethodStaticizingEnabled() {
return enableMethodStaticizing;
}
public CallSiteOptimizationOptions setEnabled(boolean enabled) {
if (enabled) {
assert isEnabled();
} else {
disableOptimization();
}
return this;
}
public CallSiteOptimizationOptions setForceSyntheticsForInstanceInitializers(
boolean forceSyntheticsForInstanceInitializers) {
this.forceSyntheticsForInstanceInitializers = forceSyntheticsForInstanceInitializers;
return this;
}
public CallSiteOptimizationOptions setEnableMethodStaticizing(boolean enableMethodStaticizing) {
this.enableMethodStaticizing = enableMethodStaticizing;
return this;
}
}
public static class CfCodeAnalysisOptions {
private boolean allowUnreachableCfBlocks = true;
private boolean enableUnverifiableCodeReporting = false;
public boolean isUnverifiableCodeReportingEnabled() {
return enableUnverifiableCodeReporting;
}
public boolean isUnreachableCfBlocksAllowed() {
return allowUnreachableCfBlocks;
}
public CfCodeAnalysisOptions setAllowUnreachableCfBlocks(boolean allowUnreachableCfBlocks) {
this.allowUnreachableCfBlocks = allowUnreachableCfBlocks;
return this;
}
public CfCodeAnalysisOptions setEnableUnverifiableCodeReporting(
boolean enableUnverifiableCodeReporting) {
this.enableUnverifiableCodeReporting = enableUnverifiableCodeReporting;
return this;
}
}
public class ClassInlinerOptions {
public int classInliningInstructionAllowance = -1;
public int getClassInliningInstructionAllowance() {
if (classInliningInstructionAllowance >= 0) {
return classInliningInstructionAllowance;
}
if (isGeneratingClassFiles()) {
return 50;
}
assert isGeneratingDex();
return 65;
}
}
public interface ApplyInliningToInlineePredicate {
boolean test(AppView<?> appView, ProgramMethod method, int inliningDepth);
}
public class InlinerOptions {
public boolean enableInlining =
!parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.disableinlining", false);
// This defines the limit of instructions in the inlinee
public int simpleInliningInstructionLimit =
parseSystemPropertyForDevelopmentOrDefault(
"com.android.tools.r8.inliningInstructionLimit", -1);
public int[] multiCallerInliningInstructionLimits =
new int[] {Integer.MAX_VALUE, 28, 16, 12, 10};
// This defines how many instructions of inlinees we can inlinee overall.
public int inliningInstructionAllowance = 1500;
// Maximum number of distinct values in a method that may be used in a monitor-enter
// instruction.
public int inliningMonitorEnterValuesAllowance = 4;
// Maximum number of control flow resolution blocks that setup the register state before
// the actual catch handler allowed when inlining. Threshold found empirically by testing on
// GMS Core.
public int inliningControlFlowResolutionBlocksThreshold = 15;
public boolean enableInliningOfInvokesWithClassInitializationSideEffects = true;
public boolean enableInliningOfInvokesWithNullableReceivers = true;
public boolean disableInliningOfLibraryMethodOverrides = true;
public ApplyInliningToInlineePredicate applyInliningToInlineePredicateForTesting = null;
public int getSimpleInliningInstructionLimit() {
// If a custom simple inlining instruction limit is set, then use that.
if (simpleInliningInstructionLimit >= 0) {
return simpleInliningInstructionLimit;
}
// Allow 3 instructions when generating to class files.
if (isGeneratingClassFiles()) {
return 3;
}
// Allow the size of the dex code to be up to 5 bytes.
assert isGeneratingDex();
return 5;
}
public boolean shouldApplyInliningToInlinee(
AppView<?> appView, ProgramMethod inlinee, int inliningDepth) {
if (applyInliningToInlineePredicateForTesting != null) {
return applyInliningToInlineePredicateForTesting.test(appView, inlinee, inliningDepth);
}
if (protoShrinking.shouldApplyInliningToInlinee(appView, inlinee, inliningDepth)) {
return true;
}
return false;
}
}
public class HorizontalClassMergerOptions {
// TODO(b/138781768): Set enable to true when this bug is resolved.
private boolean enable =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null;
// TODO(b/205611444): Enable by default.
private boolean enableClassInitializerDeadlockDetection = true;
private boolean enableInterfaceMerging =
System.getProperty("com.android.tools.r8.enableHorizontalInterfaceMerging") != null;
private boolean enableInterfaceMergingInInitial = false;
private boolean enableSameFilePolicy =
System.getProperty("com.android.tools.r8.enableSameFilePolicy") != null;
private boolean enableSyntheticMerging = true;
private boolean ignoreRuntimeTypeChecksForTesting = false;
private boolean restrictToSynthetics = false;
public void disable() {
enable = false;
}
public void disableSyntheticMerging() {
enableSyntheticMerging = false;
}
public void enable() {
enable = true;
}
public void enableIf(boolean enable) {
this.enable = enable;
}
public int getMaxClassGroupSizeInR8() {
return 30;
}
public int getMaxClassGroupSizeInD8() {
return 100;
}
public int getMaxInterfaceGroupSize() {
return 100;
}
public boolean isConstructorMergingEnabled() {
return true;
}
public boolean isClassInitializerDeadlockDetectionEnabled() {
return enableClassInitializerDeadlockDetection;
}
public boolean isEnabled(HorizontalClassMerger.Mode mode) {
if (!enable || debug || intermediate) {
return false;
}
if (mode.isInitial()) {
return inlinerOptions.enableInlining && isShrinking();
}
assert mode.isFinal();
return true;
}
public boolean isIgnoreRuntimeTypeChecksForTestingEnabled() {
return ignoreRuntimeTypeChecksForTesting;
}
public boolean isSameFilePolicyEnabled() {
return enableSameFilePolicy;
}
public boolean isSyntheticMergingEnabled() {
return enableSyntheticMerging;
}
public boolean isInterfaceMergingEnabled(HorizontalClassMerger.Mode mode) {
if (!enableInterfaceMerging) {
return false;
}
if (mode.isInitial()) {
return enableInterfaceMergingInInitial;
}
assert mode.isFinal();
return true;
}
public boolean isRestrictedToSynthetics() {
return restrictToSynthetics || !isOptimizing() || !isShrinking();
}
public void setEnableClassInitializerDeadlockDetection() {
enableClassInitializerDeadlockDetection = true;
}
public void setEnableInterfaceMerging() {
enableInterfaceMerging = true;
}
public void setEnableInterfaceMerging(boolean enableInterfaceMerging) {
this.enableInterfaceMerging = enableInterfaceMerging;
}
public void setEnableInterfaceMergingInInitial() {
enableInterfaceMergingInInitial = true;
}
public void setEnableSameFilePolicy(boolean enableSameFilePolicy) {
this.enableSameFilePolicy = enableSameFilePolicy;
}
public void setIgnoreRuntimeTypeChecksForTesting() {
ignoreRuntimeTypeChecksForTesting = true;
}
public void setRestrictToSynthetics() {
restrictToSynthetics = true;
}
}
public static class OpenClosedInterfacesOptions {
public interface OpenInterfaceWitnessSuppression {
boolean isSuppressed(
AppView<? extends AppInfoWithClassHierarchy> appView,
TypeElement valueType,
DexClass openInterface);
}
// Allow open interfaces by default. This is set to false in testing.
private boolean allowOpenInterfaces = true;
// When open interfaces are not allowed, compilation fails with an assertion error unless each
// open interface witness is expected according to some suppression.
private List<OpenInterfaceWitnessSuppression> suppressions = new ArrayList<>();
public void disallowOpenInterfaces() {
allowOpenInterfaces = false;
}
public OpenClosedInterfacesOptions suppressSingleOpenInterface(ClassReference classReference) {
assert !allowOpenInterfaces;
suppressions.add(
(appView, valueType, openInterface) ->
openInterface.getTypeName().equals(classReference.getTypeName()));
return this;
}
public void suppressAllOpenInterfaces() {
assert !allowOpenInterfaces;
suppressions.add((appView, valueType, openInterface) -> true);
}
public void suppressAllOpenInterfacesDueToMissingClasses() {
assert !allowOpenInterfaces;
suppressions.add(
(appView, valueType, openInterface) -> valueType.isBasedOnMissingClass(appView));
}
public void suppressArrayAssignmentsToJavaLangSerializable() {
assert !allowOpenInterfaces;
suppressions.add(
(appView, valueType, openInterface) ->
valueType.isArrayType()
&& openInterface.getTypeName().equals("java.io.Serializable"));
}
public void suppressZipFileAssignmentsToJavaLangAutoCloseable() {
assert !allowOpenInterfaces;
suppressions.add(
(appView, valueType, openInterface) ->
valueType.isClassType()
&& valueType
.asClassType()
.getClassType()
.getTypeName()
.equals("java.util.zip.ZipFile")
&& openInterface.getTypeName().equals("java.lang.AutoCloseable"));
}
public boolean isOpenInterfacesAllowed() {
return allowOpenInterfaces;
}
public boolean hasSuppressions() {
return !suppressions.isEmpty();
}
public boolean isSuppressed(
AppView<? extends AppInfoWithClassHierarchy> appView,
TypeElement valueType,
DexClass openInterface) {
return allowOpenInterfaces
|| suppressions.stream()
.anyMatch(suppression -> suppression.isSuppressed(appView, valueType, openInterface));
}
}
public static class MappingComposeOptions {
// TODO(b/241763080): Remove when enabled.
public boolean enableExperimentalMappingComposition = false;
// TODO(b/247136434): Disable for internal builds.
public boolean allowNonExistingOriginalRanges = true;
public Consumer<ClassNameMapper> generatedClassNameMapperConsumer = null;
}
public static class ApiModelTestingOptions {
// Flag to specify if we should load the database or not. The api database is used for
// library member rebinding.
public boolean enableLibraryApiModeling =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
// The flag enableApiCallerIdentification controls if we can inline or merge targets with
// different api levels. It is also the flag that specifies if we assign api levels to
// references.
public boolean enableApiCallerIdentification =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public boolean checkAllApiReferencesAreSet =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public boolean enableStubbingOfClasses =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public boolean enableOutliningOfMethods =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public boolean reportUnknownApiReferences =
System.getProperty("com.android.tools.r8.reportUnknownApiReferences") != null;
// TODO(b/232823652): Enable when we can compute the offset correctly.
public boolean useMemoryMappedByteBuffer = false;
// A mapping from references to the api-level introducing them.
public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
public void visitMockedApiLevelsForReferences(
DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) {
if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) {
return;
}
classApiMapping.forEach(
(classReference, apiLevel) -> {
apiLevelConsumer.accept(factory.createType(classReference.getDescriptor()), apiLevel);
});
fieldApiMapping.forEach(
(fieldReference, apiLevel) -> {
apiLevelConsumer.accept(factory.createField(fieldReference), apiLevel);
});
methodApiMapping.forEach(
(methodReference, apiLevel) -> {
apiLevelConsumer.accept(factory.createMethod(methodReference), apiLevel);
});
}
public boolean isApiLibraryModelingEnabled() {
return enableLibraryApiModeling;
}
public boolean isCheckAllApiReferencesAreSet() {
return enableLibraryApiModeling && checkAllApiReferencesAreSet;
}
public boolean isApiCallerIdentificationEnabled() {
return enableLibraryApiModeling && enableApiCallerIdentification;
}
public void disableApiModeling() {
enableLibraryApiModeling = false;
enableApiCallerIdentification = false;
enableOutliningOfMethods = false;
enableStubbingOfClasses = false;
checkAllApiReferencesAreSet = false;
}
/**
* Disable the workarounds for missing APIs. This does not disable the use of the database, just
* the introduction of soft-verification workarounds for potentially missing API references.
*/
public void disableOutliningAndStubbing() {
enableOutliningOfMethods = false;
enableStubbingOfClasses = false;
}
public void disableApiCallerIdentification() {
enableApiCallerIdentification = false;
}
public void disableStubbingOfClasses() {
enableStubbingOfClasses = false;
}
}
public static class ProtoShrinkingOptions {
public boolean enableGeneratedExtensionRegistryShrinking = false;
public boolean enableGeneratedMessageLiteShrinking = false;
public boolean enableGeneratedMessageLiteBuilderShrinking = false;
public boolean traverseOneOfAndRepeatedProtoFields = false;
public boolean enableEnumLiteProtoShrinking = false;
// Breaks the Chrome build if this is not enabled because of MethodToInvoke switchMaps.
// See b/174530756 for more details.
public boolean enableProtoEnumSwitchMapShrinking = true;
public void disable() {
enableGeneratedExtensionRegistryShrinking = false;
enableGeneratedMessageLiteShrinking = false;
enableGeneratedMessageLiteBuilderShrinking = false;
traverseOneOfAndRepeatedProtoFields = false;
enableEnumLiteProtoShrinking = false;
}
public boolean enableRemoveProtoEnumSwitchMap() {
return isProtoShrinkingEnabled() && enableProtoEnumSwitchMapShrinking;
}
public boolean isProtoShrinkingEnabled() {
return enableGeneratedExtensionRegistryShrinking
|| enableGeneratedMessageLiteShrinking
|| enableGeneratedMessageLiteBuilderShrinking
|| enableEnumLiteProtoShrinking;
}
public boolean isEnumLiteProtoShrinkingEnabled() {
return enableEnumLiteProtoShrinking;
}
public boolean shouldApplyInliningToInlinee(
AppView<?> appView, ProgramMethod inlinee, int inliningDepth) {
if (isProtoShrinkingEnabled() && inliningDepth == 1) {
ProtoReferences protoReferences = appView.protoShrinker().getProtoReferences();
return inlinee.getHolderType() == protoReferences.generatedMessageLiteType;
}
return false;
}
}
public static class TestingOptions {
public boolean neverReuseCfLocalRegisters = false;
public boolean roundtripThroughLIR = false;
private boolean hasReadCheckDeterminism = false;
private DeterminismChecker determinismChecker = null;
public boolean usePcEncodingInCfForTesting = false;
public boolean dexContainerExperiment =
System.getProperty("com.android.tools.r8.dexContainerExperiment") != null;
// Testing options to analyse locality of items in DEX files when they are generated.
public boolean calculateItemUseCountInDex = false;
public boolean calculateItemUseCountInDexDumpSingleUseStrings = false;
private DeterminismChecker getDeterminismChecker() {
// Lazily read the env-var so that it can be set after options init.
if (determinismChecker == null && !hasReadCheckDeterminism) {
hasReadCheckDeterminism = true;
String dir = System.getProperty("com.android.tools.r8.checkdeterminism");
if (dir != null) {
setDeterminismChecker(DeterminismChecker.createWithFileBacking(Paths.get(dir)));
}
}
return determinismChecker;
}
public void setDeterminismChecker(DeterminismChecker checker) {
determinismChecker = checker;
}
public void checkDeterminism(AppView<?> appView) {
DeterminismChecker determinismChecker = getDeterminismChecker();
if (determinismChecker != null) {
determinismChecker.check(appView);
}
}
public <E extends Exception> void checkDeterminism(
ThrowingConsumer<DeterminismChecker, E> consumer) {
DeterminismChecker determinismChecker = getDeterminismChecker();
if (determinismChecker != null) {
consumer.acceptWithRuntimeException(determinismChecker);
}
}
public static int NO_LIMIT = -1;
public ArgumentPropagatorEventConsumer argumentPropagatorEventConsumer =
ArgumentPropagatorEventConsumer.emptyConsumer();
public Predicate<DexProgramClass> isEligibleForBridgeHoisting = Predicates.alwaysTrue();
// Force writing the specified bytes as the DEX version content.
public byte[] forceDexVersionBytes = null;
public IROrdering irOrdering =
InternalOptions.assertionsEnabled() && !InternalOptions.DETERMINISTIC_DEBUGGING
? NondeterministicIROrdering.getInstance()
: IdentityIROrdering.getInstance();
public BiFunction<MixedSectionLayoutStrategy, VirtualFile, MixedSectionLayoutStrategy>
mixedSectionLayoutStrategyInspector = (strategy, virtualFile) -> strategy;
public void setMixedSectionLayoutStrategyInspector(
BiFunction<MixedSectionLayoutStrategy, VirtualFile, MixedSectionLayoutStrategy>
mixedSectionLayoutStrategyInspector) {
this.mixedSectionLayoutStrategyInspector = mixedSectionLayoutStrategyInspector;
}
public BiConsumer<AppInfoWithLiveness, Enqueuer.Mode> enqueuerInspector = null;
public Consumer<String> processingContextsConsumer = null;
public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
repackagingConfigurationFactory = DefaultRepackagingConfiguration::new;
public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
ConsumerUtils.emptyBiConsumer();
public Function<List<Policy>, List<Policy>> horizontalClassMergingPolicyRewriter =
Function.identity();
public TriFunction<AppView<?>, Iterable<DexProgramClass>, DexProgramClass, DexProgramClass>
horizontalClassMergingTarget = (appView, candidates, target) -> target;
public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
ConsumerUtils.emptyBiConsumer();
public BiConsumer<DexItemFactory, VerticallyMergedClasses> verticallyMergedClassesConsumer =
ConsumerUtils.emptyBiConsumer();
public Consumer<Deque<ProgramMethodSet>> waveModifier = waves -> {};
public Consumer<DebugRepresentation> debugRepresentationCallback = null;
/**
* If this flag is enabled, we will also compute the set of possible targets for invoke-
* interface and invoke-virtual instructions that target a library method, and add the
* corresponding edges to the call graph.
*
* <p>Setting this flag leads to more call graph edges, which can be good for size (e.g., it
* increases the likelihood that virtual methods have been processed by the time their call
* sites are processed, which allows more inlining).
*
* <p>However, the set of possible targets for such invokes can be very large. As an example,
* consider the instruction {@code invoke-virtual {v0, v1}, `void Object.equals(Object)`}).
* Therefore, tracing such invokes comes at a considerable performance penalty.
*/
public boolean addCallEdgesForLibraryInvokes = false;
public boolean allowCheckDiscardedErrors = false;
public boolean allowClassInliningOfSynthetics = true;
public boolean allowInjectedAnnotationMethods = false;
public boolean allowInliningOfSynthetics = true;
public boolean allowTypeErrors =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
public boolean allowInvokeErrors = false;
public boolean allowUnnecessaryDontWarnWildcards = true;
public boolean allowUnusedDontWarnRules = true;
public boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding = true;
public boolean alwaysUsePessimisticRegisterAllocation = false;
public boolean enableCheckCastAndInstanceOfRemoval = true;
public boolean enableDeadSwitchCaseElimination = true;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableMultiANewArrayDesugaringForClassFiles = false;
public boolean enableRetargetingConstructorBridgeCalls = false;
public boolean enableSwitchToIfRewriting = true;
public boolean enableEnumUnboxingDebugLogs =
System.getProperty("com.android.tools.r8.enableEnumUnboxingDebugLogs") != null;
public boolean forceRedundantConstNumberRemoval = false;
public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false;
public boolean invertConditionals = false;
public boolean placeExceptionalBlocksLast = false;
public boolean forceJumboStringProcessing = false;
public boolean forcePcBasedEncoding = false;
public int pcBasedDebugEncodingOverheadThreshold =
System.getProperty("com.android.tools.r8.pc2pcOverheadThreshold") != null
? Integer.parseInt(System.getProperty("com.android.tools.r8.pc2pcOverheadThreshold"))
: 200000;
public Set<Inliner.Reason> validInliningReasons = null;
public boolean noLocalsTableOnInput = false;
public boolean forceNameReflectionOptimization = false;
public boolean enableNarrowAndWideningingChecksInD8 = false;
public BiConsumer<IRCode, AppView<?>> irModifier = null;
public Consumer<IRCode> inlineeIrModifier = null;
public int basicBlockMuncherIterationLimit = NO_LIMIT;
public boolean dontReportFailingCheckDiscarded = false;
public boolean disableRecordApplicationReaderMap = false;
public PrintStream whyAreYouNotInliningConsumer = System.out;
public boolean trackDesugaredAPIConversions =
System.getProperty("com.android.tools.r8.trackDesugaredAPIConversions") != null;
public boolean enumUnboxingRewriteJavaCGeneratedMethod = false;
// TODO(b/154793333): Enable assertions always when resolved.
public boolean assertConsistentRenamingOfSignature = false;
public boolean allowStaticInterfaceMethodsForPreNApiLevel = false;
public int verificationSizeLimitInBytesOverride = -1;
public boolean forceIRForCfToCfDesugar =
System.getProperty("com.android.tools.r8.forceIRForCfToCfDesugar") != null;
public boolean disableMappingToOriginalProgramVerification = false;
public boolean allowInvalidCfAccessFlags =
System.getProperty("com.android.tools.r8.allowInvalidCfAccessFlags") != null;
public boolean verifyInputs = System.getProperty("com.android.tools.r8.verifyInputs") != null;
// TODO(b/177333791): Set to true
public boolean checkForNotExpandingMainDexTracingResult = false;
public Set<String> allowedUnusedDontWarnPatterns = new HashSet<>();
public boolean enableTestAssertions =
System.getProperty("com.android.tools.r8.enableTestAssertions") != null;
public boolean disableMarkingMethodsFinal =
System.getProperty("com.android.tools.r8.disableMarkingMethodsFinal") != null;
public boolean disableMarkingClassesFinal =
System.getProperty("com.android.tools.r8.disableMarkingClassesFinal") != null;
public boolean testEnableTestAssertions = false;
public boolean keepMetadataInR8IfNotRewritten = true;
// If set, pruned record fields are not used in hashCode/equals/toString and toString prints
// minified field names instead of original field names.
public boolean enableRecordModeling = true;
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
public boolean enableD8ResourcesPassThrough = false;
// TODO(b/144781417): This is disabled by default as some test apps appear to have such classes.
public boolean allowNonAbstractClassesWithAbstractMethods = true;
public boolean verifyKeptGraphInfo = false;
public boolean readInputStackMaps = true;
public boolean disableStackMapVerification = false;
public boolean disableShortenLiveRanges = false;
// Option for testing outlining with interface array arguments, see b/132420510.
public boolean allowOutlinerInterfaceArrayArguments = false;
public int limitNumberOfClassesPerDex = -1;
public MinifierTestingOptions minifier = new MinifierTestingOptions();
// Testing hooks to trigger effects in various compiler places.
public Runnable hookInIrConversion = null;
public static class MinifierTestingOptions {
public Comparator<DexMethod> interfaceMethodOrdering = null;
public Comparator<Wrapper<DexEncodedMethod>> getInterfaceMethodOrderingOrDefault(
Comparator<Wrapper<DexEncodedMethod>> comparator) {
if (interfaceMethodOrdering != null) {
return (a, b) ->
interfaceMethodOrdering.compare(a.get().getReference(), b.get().getReference());
}
return comparator;
}
}
public boolean measureProguardIfRuleEvaluations = false;
public ProguardIfRuleEvaluationData proguardIfRuleEvaluationData =
new ProguardIfRuleEvaluationData();
public static class ProguardIfRuleEvaluationData {
public int numberOfProguardIfRuleClassEvaluations = 0;
public int numberOfProguardIfRuleMemberEvaluations = 0;
}
public Consumer<ProgramMethod> callSiteOptimizationInfoInspector =
ConsumerUtils.emptyConsumer();
public Predicate<DexMethod> cfByteCodePassThrough = null;
public boolean enableExperimentalMapFileVersion = false;
public boolean alwaysGenerateLambdaFactoryMethods = false;
}
public MapVersion getMapFileVersion() {
return testing.enableExperimentalMapFileVersion
? MapVersion.MAP_VERSION_EXPERIMENTAL
: MapVersion.STABLE;
}
@VisibleForTesting
public void disableNameReflectionOptimization() {
// Use this util to disable get*Name() computation if the main intention of tests is checking
// const-class, e.g., canonicalization, or some test classes' only usages are get*Name().
enableNameReflectionOptimization = false;
}
private boolean hasMinApi(AndroidApiLevel level) {
return getMinApiLevel().isGreaterThanOrEqualTo(level);
}
/**
* Predicate to guard on the support of a language feature.
*
* <p>Note that if not desugaring or compiling to DEX, then the output is a mapping of the input
* and thus all parts should be representable (assuming the compiler has support for them).
*/
private boolean hasFeaturePresentFrom(AndroidApiLevel level) {
if (desugarState.isOn() || isGeneratingDex()) {
return level != null && hasMinApi(level);
}
// If not desugaring and not compiling to DEX, then the API level is effectively ignored and
// we assume that everything in the input is supported in the output.
assert minApiLevel.equals(B);
return true;
}
/**
* Predicate to guard against the possible presence of a VM bug.
*
* <p>Note that if the compilation is not desugaring to a min-api or targeting DEX at a min-api,
* then the bug is assumed to be present as the CF output could be futher compiled to any target.
*/
private boolean canHaveBugPresentUntil(AndroidApiLevel level) {
if (desugarState.isOn() || isGeneratingDex()) {
return level == null || !hasMinApi(level);
}
assert minApiLevel.equals(B);
return true;
}
/**
* Allow access modification of synthetic lambda implementation methods in D8 to avoid generating
* an excessive amount of accessibility bridges. In R8, the lambda implementation methods are
* inlined into the synthesized accessibility bridges, thus we don't allow access modification.
*/
public boolean canAccessModifyLambdaImplementationMethods(AppView<?> appView) {
return !appView.enableWholeProgramOptimizations();
}
/**
* Dex2Oat issues a warning for abstract methods on non-abstract classes, so we never allow this.
*
* <p>Note that having an invoke instruction that targets an abstract method on a non-abstract
* class will fail with a verification error on Dalvik. Therefore, this must not be more
* permissive than {@code return minApiLevel >= AndroidApiLevel.L.getLevel()}.
*
* <p>See b/132953944.
*/
@SuppressWarnings("ConstantConditions")
public boolean canUseAbstractMethodOnNonAbstractClass() {
boolean result = false;
assert !(result && canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug());
return result;
}
public boolean canUseConstClassInstructions(CfVersion cfVersion) {
assert isGeneratingClassFiles();
return cfVersion.isGreaterThanOrEqualTo(requiredCfVersionForConstClassInstructions());
}
public CfVersion requiredCfVersionForConstClassInstructions() {
assert isGeneratingClassFiles();
return CfVersion.V1_5;
}
public static AndroidApiLevel invokePolymorphicOnMethodHandleApiLevel() {
return AndroidApiLevel.O;
}
public boolean canUseInvokePolymorphicOnMethodHandle() {
return hasFeaturePresentFrom(invokePolymorphicOnMethodHandleApiLevel());
}
public static AndroidApiLevel invokePolymorphicOnVarHandleApiLevel() {
return AndroidApiLevel.P;
}
public boolean canUseInvokePolymorphicOnVarHandle() {
return hasFeaturePresentFrom(invokePolymorphicOnMethodHandleApiLevel());
}
public static AndroidApiLevel constantMethodHandleApiLevel() {
return AndroidApiLevel.P;
}
public boolean canUseConstantMethodHandle() {
return hasFeaturePresentFrom(constantMethodHandleApiLevel());
}
public static AndroidApiLevel constantMethodTypeApiLevel() {
return AndroidApiLevel.P;
}
public boolean canUseConstantMethodType() {
return hasFeaturePresentFrom(constantMethodTypeApiLevel());
}
public static AndroidApiLevel invokeCustomApiLevel() {
return AndroidApiLevel.O;
}
public boolean canUseInvokeCustom() {
return hasFeaturePresentFrom(invokeCustomApiLevel());
}
public static AndroidApiLevel constantDynamicApiLevel() {
return null;
}
public boolean canUseConstantDynamic() {
return hasFeaturePresentFrom(constantDynamicApiLevel());
}
public static AndroidApiLevel defaultAndStaticInterfaceMethodsApiLevel() {
return AndroidApiLevel.N;
}
public static AndroidApiLevel defaultInterfaceMethodsApiLevel() {
return defaultAndStaticInterfaceMethodsApiLevel();
}
public static AndroidApiLevel staticInterfaceMethodsApiLevel() {
return defaultAndStaticInterfaceMethodsApiLevel();
}
public boolean canUseDefaultAndStaticInterfaceMethods() {
return hasFeaturePresentFrom(defaultInterfaceMethodsApiLevel());
}
public static AndroidApiLevel privateInterfaceMethodsApiLevel() {
return AndroidApiLevel.N;
}
public boolean canUsePrivateInterfaceMethods() {
return hasFeaturePresentFrom(privateInterfaceMethodsApiLevel());
}
public boolean canUseNestBasedAccess() {
return hasFeaturePresentFrom(null) || emitNestAnnotationsInDex;
}
public boolean canUseRecords() {
return hasFeaturePresentFrom(null) || emitRecordAnnotationsInDex;
}
public boolean canUseSealedClasses() {
return hasFeaturePresentFrom(null) || emitPermittedSubclassesAnnotationsInDex;
}
public boolean canLeaveStaticInterfaceMethodInvokes() {
return hasFeaturePresentFrom(AndroidApiLevel.L);
}
public boolean canUseTwrCloseResourceMethod() {
return hasFeaturePresentFrom(AndroidApiLevel.K);
}
public boolean canUseSpacesInSimpleName() {
return itemFactory.getSkipNameValidationForTesting()
|| hasFeaturePresentFrom(AndroidApiLevel.R);
}
public boolean enableBackportedMethodRewriting() {
// Disable rewriting if there are no methods to rewrite or if the API level is higher than
// the highest known API level when the compiler is built. This ensures that when this is used
// by the Android Platform build (which normally use an API level of 10000) there will be
// no rewriting of backported methods. See b/147480264.
return enableBackportMethods
&& desugarState.isOn()
// TODO(b/232073181): This platform check should rather be controlled via the platform flag.
&& getMinApiLevel().isLessThanOrEqualTo(AndroidApiLevel.LATEST);
}
public boolean enableTryWithResourcesDesugaring() {
switch (tryWithResourcesDesugaring) {
case Off:
return false;
case Auto:
return desugarState.isOn() && !canUseTwrCloseResourceMethod();
}
throw new Unreachable();
}
// Debug entries may be dropped only if the source file content allows being omitted from
// stack traces, or if the VM will report the source file even with a null valued debug info.
public boolean allowDiscardingResidualDebugInfo() {
// TODO(b/146565491): We can drop debug info once fixed at a known min-api.
return sourceFileProvider != null && sourceFileProvider.allowDiscardingSourceFile();
}
public boolean canUseDexPc2PcAsDebugInformation() {
return isGeneratingDex() && lineNumberOptimization == LineNumberOptimization.ON;
}
public boolean canUseNativeDexPcInsteadOfDebugInfo() {
return canUseDexPc2PcAsDebugInformation()
&& hasMinApi(AndroidApiLevel.O)
&& allowDiscardingResidualDebugInfo();
}
public boolean isInterfaceMethodDesugaringEnabled() {
// This condition is to filter out tests that never set program consumer.
if (!hasConsumer()) {
return false;
}
return desugarState.isOn()
&& interfaceMethodDesugaring == OffOrAuto.Auto
&& !canUseDefaultAndStaticInterfaceMethods();
}
public boolean isSwitchRewritingEnabled() {
return enableSwitchRewriting && !debug;
}
public boolean isStringSwitchConversionEnabled() {
return enableStringSwitchConversion && !debug;
}
public boolean canUseMultidex() {
assert isGeneratingDex();
return intermediate || hasMinApi(AndroidApiLevel.L);
}
public boolean canUseJavaUtilObjects() {
return hasFeaturePresentFrom(AndroidApiLevel.K);
}
public boolean canUseJavaUtilObjectsIsNull() {
return hasFeaturePresentFrom(AndroidApiLevel.N);
}
public boolean canUseSuppressedExceptions() {
// TODO(b/214239152): Suppressed exceptions are @hide from at least 4.0.1 / Android I / API 14.
return hasFeaturePresentFrom(AndroidApiLevel.K);
}
public boolean canUseAssertionErrorTwoArgumentConstructor() {
return hasFeaturePresentFrom(AndroidApiLevel.K);
}
public CfVersion classFileVersionAfterDesugaring(CfVersion version) {
assert isGeneratingClassFiles();
if (!isDesugaring()) {
return version;
}
CfVersion maxVersionAfterDesugar =
canUseDefaultAndStaticInterfaceMethods() ? CfVersion.V1_8 : CfVersion.V1_7;
return Ordered.min(maxVersionAfterDesugar, version);
}
// The Apache Harmony-based AssertionError constructor which takes an Object on API 15 and older
// calls the Error supertype constructor with null as the exception cause. This prevents
// subsequent calls to initCause() because its implementation checks that cause==this before
// allowing a cause to be set.
//
// https://android.googlesource.com/platform/libcore/+/refs/heads/ics-mr1/luni/src/main/java/java/lang/AssertionError.java#56
public boolean canInitCauseAfterAssertionErrorObjectConstructor() {
return hasFeaturePresentFrom(AndroidApiLevel.J);
}
// Dalvik x86-atom backend had a bug that made it crash on filled-new-array instructions for
// arrays of objects. This is unfortunate, since this never hits arm devices, but we have
// to disallow filled-new-array of objects for dalvik until kitkat. The buggy code was
// removed during the jelly-bean release cycle and is not there from kitkat.
//
// Buggy code that accidentally call code that only works on primitives arrays.
//
// https://android.googlesource.com/platform/dalvik/+/ics-mr0/vm/mterp/out/InterpAsm-x86-atom.S#25106
public boolean canUseFilledNewArrayOfObjects() {
assert isGeneratingDex();
return hasFeaturePresentFrom(AndroidApiLevel.K);
}
// Art had a bug (b/68761724) for Android N and O in the arm32 interpreter
// where an aget-wide instruction using the same register for the array
// and the first register of the result could lead to the wrong exception
// being thrown on out of bounds.
public boolean canUseSameArrayAndResultRegisterInArrayGetWide() {
return hasFeaturePresentFrom(AndroidApiLevel.P);
}
// Some Lollipop versions of Art found in the wild perform invalid bounds
// check elimination. There is a fast path of loops and a slow path.
// The bailout to the slow path is performed too early and therefore
// the array-index variable might not be defined in the slow path code leading
// to use of undefined registers as indices into arrays. The result
// is ArrayIndexOutOfBounds exceptions.
//
// In an attempt to help these Art VMs, all single-width constants are initialized and not moved.
//
// There is no guarantee that this works, but it does make the problem
// disappear on the one known instance of this problem.
//
// See b/69364976 and b/77996377.
public boolean canHaveBoundsCheckEliminationBug() {
return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// MediaTek JIT compilers for KitKat phones did not implement the not
// instruction as it was not generated by DX. Therefore, apps containing
// not instructions would crash if the code was JIT compiled. Therefore,
// we can only use not instructions if we are targeting Art-based
// phones.
public boolean canUseNotInstruction() {
return hasFeaturePresentFrom(AndroidApiLevel.L);
}
// Art before M has a verifier bug where the type of the contents of the receiver register is
// assumed to not change. If the receiver register is reused for something else the verifier
// will fail and the code will not run.
public boolean canHaveThisTypeVerifierBug() {
return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Art crashes if we do dead reference elimination of the receiver in release mode and Art
// is asked for the |this| object over a JDWP connection at a point where the receiver
// register has been clobbered.
//
// See b/116683601 and b/116837585.
public boolean canHaveThisJitCodeDebuggingBug() {
return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
// the first part of the result long before reading the second part of the input longs.
public boolean canHaveOverlappingLongRegisterBug() {
return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Some dalvik versions found in the wild perform invalid JIT compilation of cmp-long
// instructions where the result register overlaps with the input registers.
// See b/74084493.
//
// The same dalvik versions also have a bug where the JIT compilation of code such as:
//
// void method(long l) {
// if (l < 0) throw new RuntimeException("less than");
// if (l == 0) throw new RuntimeException("equal");
// }
//
// Will enter the case for l==0 even when l is non-zero. The code generated for this is of
// the form:
//
// 0: 0x00: ConstWide16 v0, 0x0000000000000000 (0)
// 1: 0x02: CmpLong v2, v4, v0
// 2: 0x04: IfLtz v2, 0x0c (+8)
// 3: 0x06: IfNez v2, 0x0a (+4)
//
// However, the jit apparently clobbers the input register in the IfLtz instruction. Therefore,
// for dalvik VMs we have to instead generate the following code:
//
// 0: 0x00: ConstWide16 v0, 0x0000000000000000 (0)
// 1: 0x02: CmpLong v2, v4, v0
// 2: 0x04: IfLtz v2, 0x0e (+10)
// 3: 0x06: CmpLong v2, v4, v0
// 4: 0x08: IfNez v2, 0x0c (+4)
//
// See b/75408029.
public boolean canHaveCmpLongBug() {
return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Some Lollipop VMs crash if there is a const instruction between a cmp and an if instruction.
//
// Crashing code:
//
// :goto_0
// cmpg-float v0, p0, p0
// const/4 v1, 0
// if-gez v0, :cond_0
// add-float/2addr p0, v1
// goto :goto_0
// :cond_0
// return p0
//
// Working code:
// :goto_0
// const/4 v1, 0
// cmpg-float v0, p0, p0
// if-gez v0, :cond_0
// add-float/2addr p0, v1
// goto :goto_0
// :cond_0
// return p0
//
// See b/115552239.
public boolean canHaveCmpIfFloatBug() {
return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Some Lollipop VMs incorrectly optimize code with mul2addr instructions. In particular,
// the following hash code method produces wrong results after optimizations:
//
// 0: 0x00: IgetObject v0, v3, Field java.lang.Class MultiClassKey.first
// 1: 0x02: InvokeVirtual { v0 } Ljava/lang/Object;->hashCode()I
// 2: 0x05: MoveResult v0
// 3: 0x06: Const16 v1, 0x001f (31)
// 4: 0x08: MulInt2Addr v1, v0
// 5: 0x09: IgetObject v2, v3, Field java.lang.Class MultiClassKey.second
// 6: 0x0b: InvokeVirtual { v2 } Ljava/lang/Object;->hashCode()I
// 7: 0x0e: MoveResult v2
// 8: 0x0f: AddInt2Addr v1, v2
// 9: 0x10: Return v1
//
// It seems that the issue is the MulInt2Addr instructions. Avoiding that, the VM computes
// hash codes correctly also after optimizations.
//
// This issue has only been observed on a Verizon Ellipsis 8 tablet. See b/76115465.
public boolean canHaveMul2AddrBug() {
return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Some Marshmallow VMs create an incorrect doubly-linked list of instructions. When the VM
// attempts to create a fixup for a Cortex 53 long add/sub issue, it may diverge due to the cyclic
// list.
//
// See b/77842465.
public boolean canHaveDex2OatLinkedListBug() {
return canHaveBugPresentUntil(AndroidApiLevel.N);
}
// dex2oat on Marshmallow VMs does aggressive inlining which can eat up all the memory on
// devices for self-recursive methods.
//
// See b/111960171
public boolean canHaveDex2OatInliningIssue() {
return canHaveBugPresentUntil(AndroidApiLevel.N);
}
// Art 7.0.0 and later Art JIT may perform an invalid optimization if a string new-instance does
// not flow directly to the init call.
//
// See b/78493232 and b/80118070.
public boolean canHaveArtStringNewInitBug() {
return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// Dalvik tracing JIT may perform invalid optimizations when int/float values are converted to
// double and used in arithmetic operations.
//
// See b/77496850.
public boolean canHaveNumberConversionRegisterAllocationBug() {
return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Some Lollipop mediatek VMs have a peculiar bug where the inliner crashes if there is a
// simple constructor that just forwards its arguments to the super constructor. Strangely,
// this happens only for specific signatures: so far the only reproduction we have is for
// a constructor accepting two doubles and one object.
//
// To workaround this we insert a materializing const instruction before the super init
// call. Having a temporary register seems to disable the buggy optimizations.
//
// See b/68378480.
public boolean canHaveForwardingInitInliningBug() {
return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Some Lollipop x86_64 VMs have a bug causing a segfault if an exception handler directly targets
// a conditional-loop header. This cannot happen for debug builds as the existence of a
// move-exception instruction will ensure a non-direct target.
//
// To workaround this in release builds, we insert a materializing nop instruction in the
// exception handler forcing it not directly target any loop header.
//
// See b/111337896.
public boolean canHaveExceptionTargetingLoopHeaderBug() {
assert isGeneratingDex();
return !debug && canHaveBugPresentUntil(AndroidApiLevel.M);
}
// The Dalvik tracing JIT can trace past the end of the instruction stream and end up
// parsing non-code bytes as code (typically leading to a crash). See b/117907456.
//
// In order to workaround this we insert a goto past the throw, and another goto after the throw
// jumping back to the throw.
// We used to insert a empty loop at the end, however, mediatek has an optimizer
// on lollipop devices that cannot deal with an unreachable infinite loop, so we
// couldn't do that. See b/119895393.
// We also could not insert any dead code (e.g. a return) because that would make mediatek
// dominator calculations on 7.0.0 crash. See b/128926846.
public boolean canHaveTracingPastInstructionsStreamBug() {
return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// The art verifier incorrectly propagates type information for the following pattern:
//
// move vA, vB
// instance-of vB, vA, Type
// if-eqz/nez vB
//
// In that case it will assume that vB has object type after the if. Therefore, if the
// result of the instance-of operation is reused in a boolean context the verifier will
// fail with a type conflict.
//
// In order to make sure that cannot happen, we insert a nop between the move and
// the instance-of instruction so that this pattern in the art verifier does not
// match.
//
// move vA, vB
// nop
// instance-of vB, vA, Type
// if-eqz/nez vB
//
// This happens rarely, but it can happen in debug mode where the move
// put a value into a new register which has associated locals information.
//
// Fixed in Android Q, see b/120985556.
public boolean canHaveArtInstanceOfVerifierBug() {
return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// Some Art Lollipop version do not deal correctly with long-to-int conversions.
//
// In particular, the following code performs an out of bounds array access when the
// long loaded from the long array is very large (has non-zero values in the upper 32 bits).
//
// aget-wide v9, v3, v1
// long-to-int v9, v9
// aget-wide v10, v3, v9
//
// The issue seems to be that the higher bits of the 64-bit register holding the long
// are not cleared and the integer is therefore a 64-bit integer that is not truncated
// to 32 bits.
//
// As a workaround, we do not allow long-to-int to have the same source and target register
// for min-apis where lollipop devices could be targeted.
//
// See b/80262475.
public boolean canHaveLongToIntBug() {
// We have only seen this happening on Lollipop arm64 backends. We have tested on
// Marshmallow and Nougat arm64 devices and they do not have the bug.
return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// The Art VM for Android N through P has a bug in the JIT that means that if the same
// exception block with a move-exception instruction is targeted with more than one type
// of exception the JIT will incorrectly assume that the exception object has one of these
// types and will optimize based on that one type instead of taking all the types into account.
//
// In order to workaround that, we always generate distinct move-exception instructions for
// distinct dex types.
//
// See b/120164595.
public boolean canHaveExceptionTypeBug() {
return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// Art 4.0.4 fails with a verification error when a null-literal is being passed directly to an
// aget instruction. We therefore need to be careful when performing trivial check-cast
// elimination of check-cast instructions where the value being cast is the constant null.
// See b/123269162.
public boolean canHaveArtCheckCastVerifierBug() {
return canHaveBugPresentUntil(AndroidApiLevel.J);
}
// The verifier will merge A[] and B[] to Object[], even when both A and B implement an interface
// I, i.e., the join should have been I[]. This can lead to verification errors when the value is
// used as an I[].
//
// See b/69826014.
public boolean canHaveIncorrectJoinForArrayOfInterfacesBug() {
return true;
}
// The dalvik verifier will crash the program if there is a try catch block with an exception
// type that does not exist.
// We don't do anything special about this, except that we don't inline methods that have a
// catch handler with the ReflectiveOperationException type, i.e., if the program did not crash
// in the non R8 case it should not in the R8 case.
// Currently we handle only the ReflectiveOperationException, but there could be other exceptions.
// We do this for all pre art version, in case we add more Exception types later on. The
// problem is there for all dalvik vms, but the exception was added in api level 19
// so we don't see it there.
//
// See b/131349148
public boolean canHaveDalvikCatchHandlerVerificationBug() {
return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Having an invoke instruction that targets an abstract method on a non-abstract class will fail
// with a verification error.
//
// See b/132953944.
public boolean canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() {
return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// On dalvik we see issues when using an int value in places where a boolean, byte, char, or short
// is expected.
//
// For example, if we inline the following method into the call site:
// public int value;
// public boolean getValue() {
// return value;
// }
//
// See also b/134304597 and b/124152497.
public boolean canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug() {
return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// The standard library prior to API 19 did not contain a ZipFile that implemented Closable.
//
// See b/177532008.
public boolean canHaveZipFileWithMissingCloseableBug() {
return canHaveBugPresentUntil(AndroidApiLevel.K);
}
// Some versions of Dalvik had a bug where a switch with a MAX_INT key would still go to
// the default case when switching on the value MAX_INT.
//
// See b/177790310.
public boolean canHaveSwitchMaxIntBug() {
return canHaveBugPresentUntil(AndroidApiLevel.K);
}
// On Dalvik the methods Integer.parseInt and Long.parseLong does not support strings with a '+'
// prefix
//
// See b/182137865.
public boolean canParseNumbersWithPlusPrefix() {
return hasFeaturePresentFrom(AndroidApiLevel.L);
}
// Lollipop and Marshmallow devices do not correctly handle invoke-super when the static holder
// is higher up in the hierarchy than the method that the invoke-super should resolve to.
//
// See b/215573892.
public boolean canHaveSuperInvokeBug() {
return canHaveBugPresentUntil(AndroidApiLevel.N);
}
// Some Dalvik and Art MVs does not support interface invokes to Object
// members not explicitly defined on the symbolic reference of the
// interface invoke. In these cases rewrite to a virtual invoke with
// the symbolic reference java.lang.Object.
//
// The support was added in Android O, however at least for j.l.CharSequence.equals the handling
// in Art was incorrect (b/231450655).
//
// javac started generating code like this with the fix for JDK-8272564, which will be part of
// JDK 18.
//
// See b/218298666.
public boolean canHaveInvokeInterfaceToObjectMethodBug() {
return canHaveBugPresentUntil(AndroidApiLevel.P);
}
// Until we fully drop support for API levels < 16, we have to emit an empty annotation set to
// work around a DALVIK bug. See b/36951668.
public boolean canHaveDalvikEmptyAnnotationSetBug() {
return canHaveBugPresentUntil(AndroidApiLevel.J_MR1);
}
public boolean canHaveNonReboundConstructorInvoke() {
// TODO(b/246679983): Turned off while diagnosing b/246679983.
return false && isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
}