Merge commit 'a736e22be57f144c823cc33d03f895d081f7b795' into dev-release
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index 107efb4..61caf95 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -104,7 +104,7 @@
InternalOptions getInternalOptions() {
InternalOptions options = new InternalOptions(factory, getReporter());
- options.minApiLevel = AndroidApiLevel.getAndroidApiLevel(minApiLevel);
+ options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
options.desugaredLibraryConfiguration = desugaredLibraryConfiguration;
return options;
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 9e5376f..4af2547 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -678,7 +678,7 @@
reporter.error(builder.toString());
}
if (getMinApiLevel() > AndroidApiLevel.LATEST.getLevel()) {
- if (getMinApiLevel() != AndroidApiLevel.magicApiLevelUsedByAndroidPlatformBuild) {
+ if (getMinApiLevel() != AndroidApiLevel.ANDROID_PLATFORM.getLevel()) {
reporter.warning(
"An API level of "
+ getMinApiLevel()
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 1e6d847..8d0f357 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -465,7 +465,7 @@
internal.mainDexListConsumer = getMainDexListConsumer();
internal.minimalMainDex = internal.debug || minimalMainDex;
internal.enableMainDexListCheck = enableMainDexListCheck;
- internal.minApiLevel = AndroidApiLevel.getAndroidApiLevel(getMinApiLevel());
+ internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(getMinApiLevel()));
internal.intermediate = intermediate;
internal.retainCompileTimeAnnotations = intermediate;
internal.desugarGraphConsumer = desugarGraphConsumer;
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index b21bb03..320ad14 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -101,7 +101,7 @@
private static Collection<Marker> extractMarker(AndroidApp app) throws IOException {
InternalOptions options = new InternalOptions();
options.skipReadingDexCode = true;
- options.minApiLevel = AndroidApiLevel.P;
+ options.setMinApiLevel(AndroidApiLevel.P);
DexApplication dexApp = new ApplicationReader(app, options, new Timing("ExtractMarker")).read();
return dexApp.dexItemFactory.extractMarkers();
}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 745d94d..51ea781 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -164,7 +164,7 @@
internal.debug = getMode() == CompilationMode.DEBUG;
assert internal.mainDexListConsumer == null;
assert !internal.minimalMainDex;
- internal.minApiLevel = AndroidApiLevel.getAndroidApiLevel(getMinApiLevel());
+ internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(getMinApiLevel()));
assert !internal.intermediate;
assert internal.retainCompileTimeAnnotations;
internal.programConsumer = getProgramConsumer();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a0ac288..1483137 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -354,7 +354,7 @@
options.itemFactory, options.getProguardConfiguration().getRules())) {
synthesizedProguardRules.add(
ProguardConfigurationUtils.buildAssumeNoSideEffectsRuleForApiLevel(
- options.itemFactory, options.minApiLevel));
+ options.itemFactory, options.getMinApiLevel()));
}
}
SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
@@ -883,20 +883,16 @@
return true;
}
// This will return false if we find anything in the library which is not modeled.
- appView
- .appInfo()
- .classes()
- .forEach(
- clazz -> {
- clazz.forEachProgramMember(
- member -> {
- assert member.getDefinition().getApiLevel() != AndroidApiLevel.NOT_SET
- : "Every member should have been analyzed";
- assert appView.options().apiModelingOptions().enableApiCallerIdentification
- || member.getDefinition().getApiLevel() == AndroidApiLevel.UNKNOWN
- : "Every member should have level UNKNOWN";
- });
- });
+ for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+ clazz.forEachProgramMember(
+ member -> {
+ assert member.getDefinition().getApiLevel() != AndroidApiLevel.NOT_SET
+ : "Every member should have been analyzed";
+ assert appView.options().apiModelingOptions().enableApiCallerIdentification
+ || member.getDefinition().getApiLevel() == AndroidApiLevel.UNKNOWN
+ : "Every member should have level UNKNOWN";
+ });
+ }
return true;
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index ecc0eff..4923f93 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -833,7 +833,7 @@
InternalOptions internal = new InternalOptions(getMode(), proguardConfiguration, getReporter());
assert !internal.testing.allowOutlinerInterfaceArrayArguments; // Only allow in tests.
internal.programConsumer = getProgramConsumer();
- internal.minApiLevel = AndroidApiLevel.getAndroidApiLevel(getMinApiLevel());
+ internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(getMinApiLevel()));
internal.desugarState = getDesugarState();
assert internal.isShrinking() == getEnableTreeShaking();
assert internal.isMinifying() == getEnableMinification();
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
index 406ed61..34a2d65 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -50,7 +50,7 @@
public DefaultAndroidApiLevelCompute(AppView<?> appView) {
this.cache = AndroidApiReferenceLevelCache.create(appView);
- this.minApiLevel = appView.options().minApiLevel;
+ this.minApiLevel = appView.options().getMinApiLevel();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index 9a4afa3..f4049a0 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -66,34 +66,34 @@
if (contextType.isArrayType()) {
if (reference.isDexMethod()
&& reference.asDexMethod().match(appView.dexItemFactory().objectMembers.clone)) {
- return appView.options().minApiLevel;
+ return appView.options().getMinApiLevel();
}
return lookup(contextType.toBaseType(appView.dexItemFactory()));
}
if (contextType.isPrimitiveType() || contextType.isVoidType()) {
- return appView.options().minApiLevel;
+ return appView.options().getMinApiLevel();
}
DexClass clazz = appView.definitionFor(contextType);
if (clazz == null) {
return AndroidApiLevel.UNKNOWN;
}
if (!clazz.isLibraryClass()) {
- return appView.options().minApiLevel;
+ return appView.options().getMinApiLevel();
}
if (isReferenceToJavaLangObject(reference)) {
- return appView.options().minApiLevel;
+ return appView.options().getMinApiLevel();
}
if (desugaredLibraryConfiguration.isSupported(reference, appView)) {
// If we end up desugaring the reference, the library classes is bridged by j$ which is part
// of the program.
- return appView.options().minApiLevel;
+ return appView.options().getMinApiLevel();
}
return reference
.apply(
androidApiLevelDatabase::getTypeApiLevel,
androidApiLevelDatabase::getFieldApiLevel,
androidApiLevelDatabase::getMethodApiLevel)
- .max(appView.options().minApiLevel);
+ .max(appView.options().getMinApiLevel());
}
private boolean isReferenceToJavaLangObject(DexReference reference) {
diff --git a/src/main/java/com/android/tools/r8/androidapi/AvailableApiExceptions.java b/src/main/java/com/android/tools/r8/androidapi/AvailableApiExceptions.java
index 606d653..0525b8f 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AvailableApiExceptions.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AvailableApiExceptions.java
@@ -26,8 +26,8 @@
private final Set<DexType> exceptions;
public AvailableApiExceptions(InternalOptions options) {
- assert options.minApiLevel.isLessThan(AndroidApiLevel.L);
- exceptions = build(options.itemFactory, options.minApiLevel);
+ assert options.getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ exceptions = build(options.itemFactory, options.getMinApiLevel());
}
public boolean canCauseVerificationError(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 07e0617..f00f7fb 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -268,7 +268,7 @@
return true;
}
AndroidApiLevel nativeMultiDex = AndroidApiLevel.L;
- if (options.minApiLevel.isLessThan(nativeMultiDex)) {
+ if (options.getMinApiLevel().isLessThan(nativeMultiDex)) {
return true;
}
assert options.mainDexKeepRules.isEmpty();
@@ -280,11 +280,15 @@
private AndroidApiLevel validateOrComputeMinApiLevel(
AndroidApiLevel computedMinApiLevel, DexReader dexReader) {
DexVersion version = dexReader.getDexVersion();
- if (options.minApiLevel == AndroidApiLevel.getDefault()) {
+ if (options.getMinApiLevel() == AndroidApiLevel.getDefault()) {
computedMinApiLevel = computedMinApiLevel.max(AndroidApiLevel.getMinAndroidApiLevel(version));
- } else if (!version.matchesApiLevel(options.minApiLevel)) {
- throw new CompilationError("Dex file with version '" + version.getIntValue() +
- "' cannot be used with min sdk level '" + options.minApiLevel + "'.");
+ } else if (!version.matchesApiLevel(options.getMinApiLevel())) {
+ throw new CompilationError(
+ "Dex file with version '"
+ + version.getIntValue()
+ + "' cannot be used with min sdk level '"
+ + options.getMinApiLevel()
+ + "'.");
}
return computedMinApiLevel;
}
@@ -353,7 +357,7 @@
}
hasReadProgramResourceFromDex = true;
List<DexParser<DexProgramClass>> dexParsers = new ArrayList<>(dexSources.size());
- AndroidApiLevel computedMinApiLevel = options.minApiLevel;
+ AndroidApiLevel computedMinApiLevel = options.getMinApiLevel();
for (ProgramResource input : dexSources) {
DexReader dexReader = new DexReader(input);
if (options.passthroughDexCode) {
@@ -362,7 +366,7 @@
dexParsers.add(new DexParser<>(dexReader, PROGRAM, options));
}
- options.minApiLevel = computedMinApiLevel;
+ options.setMinApiLevel(computedMinApiLevel);
for (DexParser<DexProgramClass> dexParser : dexParsers) {
dexParser.populateIndexTables();
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 0601b5c..43a9a57 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -302,7 +302,7 @@
private void checkName(DexString name) {
if (!options.itemFactory.getSkipNameValidationForTesting()
- && !name.isValidSimpleName(options.minApiLevel)) {
+ && !name.isValidSimpleName(options.getMinApiLevel())) {
throw new CompilationError("Space characters in SimpleName '"
+ name.toASCIIString()
+ "' are not allowed prior to DEX version 040");
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 079d0d6..c937839 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -317,7 +317,7 @@
return true;
}
- AndroidApiLevel apiLevel = options.minApiLevel;
+ AndroidApiLevel apiLevel = options.getMinApiLevel();
for (DexField field : mapping.getFields()) {
assert field.name.isValidSimpleName(apiLevel);
}
@@ -811,7 +811,7 @@
dest.putBytes(
options.testing.forceDexVersionBytes != null
? options.testing.forceDexVersionBytes
- : DexVersion.getDexVersion(options.minApiLevel).getBytes());
+ : DexVersion.getDexVersion(options.getMinApiLevel()).getBytes());
dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
// Leave out checksum and signature for now.
dest.moveTo(Constants.FILE_SIZE_OFFSET);
@@ -1109,7 +1109,7 @@
}
private MixedSectionOffsets(InternalOptions options, MethodToCodeObjectMapping codeMapping) {
- this.minApiLevel = options.minApiLevel;
+ this.minApiLevel = options.getMinApiLevel();
this.codeMapping = codeMapping;
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index b59c44f..6d3e71c 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -544,7 +544,7 @@
private void checkName(String name) {
if (!application.getFactory().getSkipNameValidationForTesting()
- && !DexString.isValidSimpleName(application.options.minApiLevel, name)) {
+ && !DexString.isValidSimpleName(application.options.getMinApiLevel(), name)) {
throw new CompilationError("Space characters in SimpleName '"
+ name + "' are not allowed prior to DEX version 040");
}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index cf0d761..cce1760 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -15,10 +15,13 @@
import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
public class LazyLoadedDexApplication extends DexApplication {
@@ -130,13 +133,13 @@
// on the configured lookup order.
Map<DexType, DexClass> prioritizedClasses = new IdentityHashMap<>(expectedMaxSize);
if (options.lookupLibraryBeforeProgram) {
- libraryClasses = fillPrioritizedClasses(allLibraryClasses, prioritizedClasses);
- programClasses = fillPrioritizedClasses(allProgramClasses, prioritizedClasses);
- classpathClasses = fillPrioritizedClasses(allClasspathClasses, prioritizedClasses);
+ libraryClasses = fillPrioritizedClasses(allLibraryClasses, prioritizedClasses, options);
+ programClasses = fillPrioritizedClasses(allProgramClasses, prioritizedClasses, options);
+ classpathClasses = fillPrioritizedClasses(allClasspathClasses, prioritizedClasses, options);
} else {
- programClasses = fillPrioritizedClasses(allProgramClasses, prioritizedClasses);
- classpathClasses = fillPrioritizedClasses(allClasspathClasses, prioritizedClasses);
- libraryClasses = fillPrioritizedClasses(allLibraryClasses, prioritizedClasses);
+ programClasses = fillPrioritizedClasses(allProgramClasses, prioritizedClasses, options);
+ classpathClasses = fillPrioritizedClasses(allClasspathClasses, prioritizedClasses, options);
+ libraryClasses = fillPrioritizedClasses(allLibraryClasses, prioritizedClasses, options);
}
allClasses = Collections.unmodifiableMap(prioritizedClasses);
@@ -163,22 +166,49 @@
}
private static <T extends DexClass> ImmutableList<T> fillPrioritizedClasses(
- Map<DexType, T> classCollection, Map<DexType, DexClass> prioritizedClasses) {
+ Map<DexType, T> classCollection,
+ Map<DexType, DexClass> prioritizedClasses,
+ InternalOptions options) {
if (classCollection != null) {
+ Set<DexType> javaLibraryOverride = Sets.newIdentityHashSet();
ImmutableList.Builder<T> builder = ImmutableList.builder();
classCollection.forEach(
(type, clazz) -> {
- if (!prioritizedClasses.containsKey(type)) {
+ DexClass other = prioritizedClasses.get(type);
+ if (other == null) {
prioritizedClasses.put(type, clazz);
builder.add(clazz);
+ } else if (type.getPackageName().startsWith("java.")
+ && (clazz.isLibraryClass() || other.isLibraryClass())) {
+ javaLibraryOverride.add(type);
}
});
+ if (!javaLibraryOverride.isEmpty()) {
+ warnJavaLibraryOverride(options, javaLibraryOverride);
+ }
return builder.build();
} else {
return ImmutableList.of();
}
}
+ private static void warnJavaLibraryOverride(
+ InternalOptions options, Set<DexType> javaLibraryOverride) {
+ String joined =
+ javaLibraryOverride.stream()
+ .sorted()
+ .map(DexType::toString)
+ .collect(Collectors.joining(", "));
+ String message =
+ "The following library types, prefixed by java.,"
+ + " are present both as library and non library classes: "
+ + joined
+ + ". "
+ + (options.lookupLibraryBeforeProgram ? "Non library" : "Library")
+ + " classes will be ignored.";
+ options.reporter.warning(message);
+ }
+
/**
* Force load all classes and return type -> class map containing all the classes.
*/
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index 7c52c77..7f4b9f9 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -23,7 +23,7 @@
public ApiModelAnalysis(AppView<?> appView, AndroidApiLevelCompute apiCompute) {
this.appView = appView;
- this.minApiLevel = appView.options().minApiLevel;
+ this.minApiLevel = appView.options().getMinApiLevel();
this.apiCompute = apiCompute;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
index 4191a9c..acc4577 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
@@ -46,7 +46,7 @@
@Override
public boolean equals(Object o) {
- if (getClass() != o.getClass()) {
+ if (o == null || getClass() != o.getClass()) {
return false;
}
SingleStatefulFieldValue singleFieldValue = (SingleStatefulFieldValue) o;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index ce9faef..2b72d1e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -76,7 +76,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.MemberValuePropagation;
import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
-import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
+import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
@@ -1344,9 +1344,9 @@
codeRewriter.redundantConstNumberRemoval(code);
timing.end();
}
- if (RedundantFieldLoadElimination.shouldRun(appView, code)) {
+ if (RedundantFieldLoadAndStoreElimination.shouldRun(appView, code)) {
timing.begin("Remove field loads");
- new RedundantFieldLoadElimination(appView, code).run();
+ new RedundantFieldLoadAndStoreElimination(appView, code).run();
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 2e9fcab..c516da4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -169,19 +169,19 @@
DexItemFactory factory = options.itemFactory;
- if (options.minApiLevel.isLessThan(AndroidApiLevel.K)) {
+ if (options.getMinApiLevel().isLessThan(AndroidApiLevel.K)) {
initializeAndroidKMethodProviders(factory);
}
- if (options.minApiLevel.isLessThan(AndroidApiLevel.N)) {
+ if (options.getMinApiLevel().isLessThan(AndroidApiLevel.N)) {
initializeAndroidNMethodProviders(factory);
}
- if (options.minApiLevel.isLessThan(AndroidApiLevel.O)) {
+ if (options.getMinApiLevel().isLessThan(AndroidApiLevel.O)) {
initializeAndroidOMethodProviders(factory);
}
- if (options.minApiLevel.isLessThan(AndroidApiLevel.R)) {
+ if (options.getMinApiLevel().isLessThan(AndroidApiLevel.R)) {
initializeAndroidRMethodProviders(factory);
}
- if (options.minApiLevel.isLessThan(AndroidApiLevel.S)) {
+ if (options.getMinApiLevel().isLessThan(AndroidApiLevel.S)) {
initializeAndroidSMethodProviders(factory);
}
@@ -190,13 +190,13 @@
// libraries or natively. If Optional/Stream class is not present, we do not desugar to
// avoid confusion in error messages.
if (appView.rewritePrefix.hasRewrittenType(factory.optionalType, appView)
- || options.minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
+ || options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
initializeJava9OptionalMethodProviders(factory);
initializeJava10OptionalMethodProviders(factory);
initializeJava11OptionalMethodProviders(factory);
}
if (appView.rewritePrefix.hasRewrittenType(factory.streamType, appView)
- || options.minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
+ || options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
initializeStreamMethodProviders(factory);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
similarity index 80%
rename from src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
rename to src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 290f25b..117ecfc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -1,9 +1,10 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import static com.android.tools.r8.utils.PredicateUtils.not;
import com.android.tools.r8.errors.Unreachable;
@@ -38,6 +39,7 @@
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -52,7 +54,7 @@
* <p>Simple algorithm that goes through all blocks in one pass in topological order and propagates
* active field sets across control-flow edges where the target has only one predecessor.
*/
-public class RedundantFieldLoadElimination {
+public class RedundantFieldLoadAndStoreElimination {
private static final int MAX_CAPACITY = 10000;
private static final int MIN_CAPACITY_PER_BLOCK = 50;
@@ -72,7 +74,9 @@
// elimination.
private BlockState activeState;
- public RedundantFieldLoadElimination(AppView<?> appView, IRCode code) {
+ private final Map<BasicBlock, Set<Instruction>> instructionsToRemove = new IdentityHashMap<>();
+
+ public RedundantFieldLoadAndStoreElimination(AppView<?> appView, IRCode code) {
this.appView = appView;
this.method = code.context();
this.code = code;
@@ -81,7 +85,7 @@
public static boolean shouldRun(AppView<?> appView, IRCode code) {
return appView.options().enableRedundantFieldLoadElimination
- && (code.metadata().mayHaveFieldGet() || code.metadata().mayHaveInitClass());
+ && (code.metadata().mayHaveFieldInstruction() || code.metadata().mayHaveInitClass());
}
private interface FieldValue {
@@ -219,48 +223,14 @@
fieldAndObject, new ExistingValue(instanceGet.value()));
}
} else if (instruction.isInstancePut()) {
- InstancePut instancePut = instruction.asInstancePut();
- // An instance-put instruction can potentially write the given field on all objects
- // because of aliases.
- killNonFinalActiveFields(instancePut);
- // ... but at least we know the field value for this particular object.
- Value object = instancePut.object().getAliasedValue();
- FieldAndObject fieldAndObject = new FieldAndObject(reference, object);
- ExistingValue value = new ExistingValue(instancePut.value());
- if (isFinal(field)) {
- assert !field.getDefinition().isFinal()
- || method.getDefinition().isInstanceInitializer()
- || verifyWasInstanceInitializer();
- activeState.putFinalInstanceField(fieldAndObject, value);
- } else {
- activeState.putNonFinalInstanceField(fieldAndObject, value);
- }
+ handleInstancePut(instruction.asInstancePut(), field);
} else if (instruction.isStaticGet()) {
handleStaticGet(it, instruction.asStaticGet(), field);
} else if (instruction.isStaticPut()) {
- StaticPut staticPut = instruction.asStaticPut();
- // A field put on a different class can cause <clinit> to run and change static
- // field values.
- killNonFinalActiveFields(staticPut);
- ExistingValue value = new ExistingValue(staticPut.value());
- if (isFinal(field)) {
- assert appView.checkForTesting(
- () ->
- !field.getDefinition().isFinal()
- || method.getDefinition().isClassInitializer());
- activeState.putFinalStaticField(reference, value);
- } else {
- activeState.putNonFinalStaticField(reference, value);
- }
+ handleStaticPut(instruction.asStaticPut(), field);
}
} else if (instruction.isInitClass()) {
- InitClass initClass = instruction.asInitClass();
- assert !initClass.outValue().hasAnyUsers();
- DexType clazz = initClass.getClassValue();
- if (activeState.isClassInitialized(clazz)) {
- it.removeOrReplaceByDebugLocalRead();
- }
- activeState.markClassAsInitialized(clazz);
+ handleInitClass(it, instruction.asInitClass());
} else if (instruction.isMonitor()) {
if (instruction.asMonitor().isEnter()) {
killAllNonFinalActiveFields();
@@ -322,12 +292,33 @@
assert end != null;
activeStates.recordActiveStateOnBlockExit(end, activeState);
}
+ processInstructionsToRemove();
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
}
assert code.isConsistentSSA();
}
+ private void processInstructionsToRemove() {
+ instructionsToRemove.forEach(
+ (block, instructionsToRemoveInBlock) -> {
+ assert instructionsToRemoveInBlock.stream()
+ .allMatch(instruction -> instruction.getBlock() == block);
+ InstructionListIterator instructionIterator = block.listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ assert !instruction.isJumpInstruction();
+ if (instructionsToRemoveInBlock.contains(instruction)) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ instructionsToRemoveInBlock.remove(instruction);
+ if (instructionsToRemoveInBlock.isEmpty()) {
+ return;
+ }
+ }
+ }
+ });
+ }
+
private boolean verifyWasInstanceInitializer() {
VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
assert verticallyMergedClasses != null;
@@ -394,11 +385,48 @@
});
}
+ private void handleInitClass(InstructionListIterator instructionIterator, InitClass initClass) {
+ assert !initClass.outValue().hasAnyUsers();
+ killNonFinalActiveFields(initClass);
+ DexType clazz = initClass.getClassValue();
+ if (!activeState.markClassAsInitialized(clazz)) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+
+ private void handleInstancePut(InstancePut instancePut, DexClassAndField field) {
+ // An instance-put instruction can potentially write the given field on all objects
+ // because of aliases.
+ activeState.removeNonFinalInstanceFields(field.getReference());
+ // ... but at least we know the field value for this particular object.
+ Value object = instancePut.object().getAliasedValue();
+ FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
+ ExistingValue value = new ExistingValue(instancePut.value());
+ if (isFinal(field)) {
+ assert !field.getDefinition().isFinal()
+ || method.getDefinition().isInstanceInitializer()
+ || verifyWasInstanceInitializer();
+ activeState.putFinalInstanceField(fieldAndObject, value);
+ } else {
+ activeState.putNonFinalInstanceField(fieldAndObject, value);
+
+ InstancePut mostRecentInstanceFieldWrite =
+ activeState.putMostRecentInstanceFieldWrite(fieldAndObject, instancePut);
+ if (mostRecentInstanceFieldWrite != null) {
+ instructionsToRemove
+ .computeIfAbsent(
+ mostRecentInstanceFieldWrite.getBlock(), ignoreKey(Sets::newIdentityHashSet))
+ .add(mostRecentInstanceFieldWrite);
+ }
+ }
+ }
+
private void handleStaticGet(
InstructionListIterator instructionIterator, StaticGet staticGet, DexClassAndField field) {
if (staticGet.outValue().hasLocalInfo()) {
return;
}
+
FieldValue replacement = activeState.getStaticFieldValue(field.getReference());
if (replacement != null) {
replacement.eliminateRedundantRead(instructionIterator, staticGet);
@@ -406,9 +434,7 @@
}
// A field get on a different class can cause <clinit> to run and change static field values.
- if (staticGet.instructionMayHaveSideEffects(appView, method)) {
- killNonFinalActiveFields(staticGet);
- }
+ killNonFinalActiveFields(staticGet);
FieldValue value = new ExistingValue(staticGet.value());
if (isFinal(field)) {
@@ -426,6 +452,28 @@
}
}
+ private void handleStaticPut(StaticPut staticPut, DexClassAndField field) {
+ // A field put on a different class can cause <clinit> to run and change static field values.
+ killNonFinalActiveFields(staticPut);
+ ExistingValue value = new ExistingValue(staticPut.value());
+ if (isFinal(field)) {
+ assert appView.checkForTesting(
+ () -> !field.getDefinition().isFinal() || method.getDefinition().isClassInitializer());
+ activeState.putFinalStaticField(field.getReference(), value);
+ } else {
+ activeState.putNonFinalStaticField(field.getReference(), value);
+
+ StaticPut mostRecentStaticFieldWrite =
+ activeState.putMostRecentStaticFieldWrite(field.getReference(), staticPut);
+ if (mostRecentStaticFieldWrite != null) {
+ instructionsToRemove
+ .computeIfAbsent(
+ mostRecentStaticFieldWrite.getBlock(), ignoreKey(Sets::newIdentityHashSet))
+ .add(mostRecentStaticFieldWrite);
+ }
+ }
+ }
+
private void applyObjectState(Value value, ObjectState objectState) {
objectState.forEachAbstractFieldValue(
(field, fieldValue) -> {
@@ -443,27 +491,26 @@
private void killAllNonFinalActiveFields() {
activeState.clearNonFinalInstanceFields();
activeState.clearNonFinalStaticFields();
+ activeState.clearMostRecentFieldWrites();
}
- private void killNonFinalActiveFields(FieldInstruction instruction) {
- DexField field = instruction.getField();
- if (instruction.isInstancePut()) {
- // Remove all the field/object pairs that refer to this field to make sure
- // that we are conservative.
- activeState.removeNonFinalInstanceFields(field);
- } else if (instruction.isStaticPut()) {
- if (field.holder != code.method().getHolderType()) {
+ private void killNonFinalActiveFields(Instruction instruction) {
+ assert instruction.isInitClass() || instruction.isStaticFieldInstruction();
+ if (instruction.isStaticPut()) {
+ if (instruction.instructionMayTriggerMethodInvocation(appView, method)) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
activeState.clearNonFinalStaticFields();
+ activeState.clearMostRecentFieldWrites();
} else {
- activeState.removeNonFinalStaticField(field);
+ activeState.removeNonFinalStaticField(instruction.asStaticPut().getField());
}
- } else if (instruction.isStaticGet()) {
- if (field.holder != code.method().getHolderType()) {
+ } else if (instruction.isInitClass() || instruction.isStaticGet()) {
+ if (instruction.instructionMayTriggerMethodInvocation(appView, method)) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
activeState.clearNonFinalStaticFields();
+ activeState.clearMostRecentFieldWrites();
}
} else if (instruction.isInstanceGet()) {
throw new Unreachable();
@@ -564,6 +611,9 @@
if (state.isEmpty()) {
return;
}
+ if (!block.hasUniqueSuccessorWithUniquePredecessor()) {
+ state.clearMostRecentFieldWrites();
+ }
ensureCapacity(state);
activeStateAtExit.put(block, state);
capacity -= state.size();
@@ -602,6 +652,10 @@
private LinkedHashMap<DexField, FieldValue> nonFinalStaticFieldValues;
+ private LinkedHashMap<FieldAndObject, InstancePut> mostRecentInstanceFieldWrites;
+
+ private LinkedHashMap<DexField, StaticPut> mostRecentStaticFieldWrites;
+
private final int maxCapacity;
public BlockState(int maxCapacity) {
@@ -632,9 +686,32 @@
nonFinalStaticFieldValues = new LinkedHashMap<>();
nonFinalStaticFieldValues.putAll(state.nonFinalStaticFieldValues);
}
+ if (state.mostRecentInstanceFieldWrites != null
+ && !state.mostRecentInstanceFieldWrites.isEmpty()) {
+ mostRecentInstanceFieldWrites = new LinkedHashMap<>();
+ mostRecentInstanceFieldWrites.putAll(state.mostRecentInstanceFieldWrites);
+ }
+ if (state.mostRecentStaticFieldWrites != null
+ && !state.mostRecentStaticFieldWrites.isEmpty()) {
+ mostRecentStaticFieldWrites = new LinkedHashMap<>();
+ mostRecentStaticFieldWrites.putAll(state.mostRecentStaticFieldWrites);
+ }
}
}
+ public void clearMostRecentFieldWrites() {
+ clearMostRecentInstanceFieldWrites();
+ clearMostRecentStaticFieldWrites();
+ }
+
+ public void clearMostRecentInstanceFieldWrites() {
+ mostRecentInstanceFieldWrites = null;
+ }
+
+ public void clearMostRecentStaticFieldWrites() {
+ mostRecentStaticFieldWrites = null;
+ }
+
public void clearNonFinalInstanceFields() {
nonFinalInstanceFieldValues = null;
}
@@ -695,6 +772,8 @@
} else {
nonFinalStaticFieldValues = null;
}
+ assert mostRecentInstanceFieldWrites == null;
+ assert mostRecentStaticFieldWrites == null;
}
private static <K> void intersectFieldValues(
@@ -747,12 +826,12 @@
}
}
- public void markClassAsInitialized(DexType clazz) {
+ public boolean markClassAsInitialized(DexType clazz) {
ensureCapacityForNewElement();
if (initializedClasses == null) {
initializedClasses = new LinkedHashSet<>();
}
- initializedClasses.add(clazz);
+ return initializedClasses.add(clazz);
}
public void reduceSize(int numberOfItemsToRemove) {
@@ -763,6 +842,8 @@
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalStaticFieldValues);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, finalInstanceFieldValues);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, finalStaticFieldValues);
+ numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, mostRecentInstanceFieldWrites);
+ numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, mostRecentStaticFieldWrites);
assert numberOfItemsToRemove == 0;
}
@@ -786,6 +867,7 @@
public void removeInstanceField(FieldAndObject field) {
removeFinalInstanceField(field);
removeNonFinalInstanceField(field);
+ removeMostRecentInstanceFieldWrite(field);
}
public void removeFinalInstanceField(FieldAndObject field) {
@@ -809,6 +891,7 @@
public void removeStaticField(DexField field) {
removeFinalStaticField(field);
removeNonFinalStaticField(field);
+ removeMostRecentStaticFieldWrite(field);
}
public void removeFinalStaticField(DexField field) {
@@ -823,6 +906,18 @@
}
}
+ public void removeMostRecentInstanceFieldWrite(FieldAndObject field) {
+ if (mostRecentInstanceFieldWrites != null) {
+ mostRecentInstanceFieldWrites.remove(field);
+ }
+ }
+
+ public void removeMostRecentStaticFieldWrite(DexField field) {
+ if (mostRecentStaticFieldWrites != null) {
+ mostRecentStaticFieldWrites.remove(field);
+ }
+ }
+
public void putFinalInstanceField(FieldAndObject field, FieldValue value) {
ensureCapacityForNewElement();
if (finalInstanceFieldValues == null) {
@@ -839,6 +934,23 @@
finalStaticFieldValues.put(field, value);
}
+ public InstancePut putMostRecentInstanceFieldWrite(
+ FieldAndObject field, InstancePut instancePut) {
+ ensureCapacityForNewElement();
+ if (mostRecentInstanceFieldWrites == null) {
+ mostRecentInstanceFieldWrites = new LinkedHashMap<>();
+ }
+ return mostRecentInstanceFieldWrites.put(field, instancePut);
+ }
+
+ public StaticPut putMostRecentStaticFieldWrite(DexField field, StaticPut staticPut) {
+ ensureCapacityForNewElement();
+ if (mostRecentStaticFieldWrites == null) {
+ mostRecentStaticFieldWrites = new LinkedHashMap<>();
+ }
+ return mostRecentStaticFieldWrites.put(field, staticPut);
+ }
+
public void putNonFinalInstanceField(FieldAndObject field, FieldValue value) {
ensureCapacityForNewElement();
assert finalInstanceFieldValues == null || !finalInstanceFieldValues.containsKey(field);
@@ -862,7 +974,9 @@
+ size(finalStaticFieldValues)
+ size(initializedClasses)
+ size(nonFinalInstanceFieldValues)
- + size(nonFinalStaticFieldValues);
+ + size(nonFinalStaticFieldValues)
+ + size(mostRecentInstanceFieldWrites)
+ + size(mostRecentStaticFieldWrites);
}
private static int size(Set<?> set) {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 524790c..aa91add 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -109,7 +109,7 @@
+ Version.LABEL
+ "\n");
if (options.isGeneratingDex()) {
- builder.append("# " + MARKER_KEY_MIN_API + ": " + options.minApiLevel.getLevel() + "\n");
+ builder.append("# " + MARKER_KEY_MIN_API + ": " + options.getMinApiLevel().getLevel() + "\n");
}
if (Version.isDevelopmentVersion()) {
builder.append(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 72b0295..a198bb7 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
+import static com.android.tools.r8.utils.AndroidApiLevelUtils.isApiSafeForMemberRebinding;
+
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -25,7 +27,6 @@
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BiForEachable;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
@@ -126,7 +127,8 @@
return resolvedMethod.isLibraryMethod()
&& isAccessibleInAllContexts(resolvedMethod, resolutionResult, contexts)
&& !isInvokeSuperToInterfaceMethod(resolvedMethod, invokeType)
- && isPresentSinceMinApi(resolvedMethod.asLibraryMethod());
+ && isApiSafeForMemberRebinding(
+ resolvedMethod.asLibraryMethod(), androidApiLevelCompute, options);
}
private boolean isAccessibleInAllContexts(
@@ -145,12 +147,6 @@
return method.getHolder().isInterface() && invokeType.isSuper();
}
- private boolean isPresentSinceMinApi(LibraryMethod method) {
- AndroidApiLevel apiLevel =
- androidApiLevelCompute.computeApiLevelForLibraryReference(method.getReference());
- return apiLevel != AndroidApiLevel.UNKNOWN && apiLevel.isLessThanOrEqualTo(options.minApiLevel);
- }
-
public static DexField validMemberRebindingTargetFor(
DexDefinitionSupplier definitions, DexClassAndField field, DexField original) {
if (field.isProgramField()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index d0bf27c..941bcff 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -40,7 +40,7 @@
this.appView = appView;
this.enqueuer = enqueuer;
this.computeApiLevel = computeApiLevel;
- this.maxApiReferenceLevel = appView.options().minApiLevel;
+ this.maxApiReferenceLevel = appView.options().getMinApiLevel();
}
public DexProgramClass getContextHolder() {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index e1a79db..6b82664 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -43,16 +43,15 @@
R(30),
S(31),
Sv2(32),
- UNKNOWN(10000),
- NOT_SET(10001);
+ ANDROID_PLATFORM(10000),
+ UNKNOWN(10001),
+ NOT_SET(10002);
// When updating LATEST and a new version goes stable, add a new api-versions.xml to third_party
// and update the version and generated jar in AndroidApiDatabaseBuilderGeneratorTest.
// TODO(b/204738868): Update API database for Sv2.
public static final AndroidApiLevel LATEST = Sv2;
- public static final int magicApiLevelUsedByAndroidPlatformBuild = 10000;
-
private final int level;
AndroidApiLevel(int level) {
@@ -82,7 +81,7 @@
public static AndroidApiLevel minApiLevelIfEnabledOrUnknown(AppView<?> appView) {
InternalOptions options = appView.options();
return options.apiModelingOptions().enableApiCallerIdentification
- ? options.minApiLevel
+ ? options.getMinApiLevel()
: UNKNOWN;
}
@@ -107,7 +106,7 @@
public static AndroidApiLevel getAndroidApiLevel(int apiLevel) {
assert apiLevel > 0;
- assert UNKNOWN.isGreaterThan(LATEST);
+ assert ANDROID_PLATFORM.isGreaterThan(LATEST);
switch (apiLevel) {
case 1:
return B;
@@ -173,10 +172,12 @@
return S;
case 32:
return Sv2;
+ case 10000:
+ return ANDROID_PLATFORM;
default:
// This has to be updated when we add new api levels.
assert Sv2 == LATEST;
- return UNKNOWN;
+ return LATEST;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 73ee4de..c4a7396 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.ProgramMethod;
public class AndroidApiLevelUtils {
@@ -13,6 +15,10 @@
if (!options.apiModelingOptions().enableApiCallerIdentification) {
return true;
}
+ if (options.isAndroidPlatform()) {
+ // Don't disable inlining in the Android platform based on the Api database.
+ return true;
+ }
if (caller.getHolderType() == inlinee.getHolderType()) {
return true;
}
@@ -21,4 +27,17 @@
.getApiLevel()
.isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode());
}
+
+ public static boolean isApiSafeForMemberRebinding(
+ LibraryMethod method,
+ AndroidApiLevelCompute androidApiLevelCompute,
+ InternalOptions options) {
+ AndroidApiLevel apiLevel =
+ androidApiLevelCompute.computeApiLevelForLibraryReference(method.getReference());
+ if (apiLevel == AndroidApiLevel.UNKNOWN) {
+ return false;
+ }
+ assert options.apiModelingOptions().enableApiCallerIdentification;
+ return apiLevel.isLessThanOrEqualTo(options.getMinApiLevel());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index 24d374e..a807bba 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -38,9 +38,9 @@
public static DexVersion getDexVersion(AndroidApiLevel androidApiLevel) {
switch (androidApiLevel) {
- // UNKNOWN is an unknown higher api version we therefore choose the highest known
+ // ANDROID_PLATFORM is an unknown higher api version we therefore choose the highest known
// version.
- case UNKNOWN:
+ case ANDROID_PLATFORM:
case Sv2:
case S:
case R:
@@ -77,7 +77,8 @@
case L_MR1:
case M:
return DexVersion.V35;
- default :
+ case UNKNOWN:
+ default:
throw new Unreachable("Unsupported api level " + androidApiLevel);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index acdf404..c012686 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -377,7 +377,7 @@
// 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(minApiLevel.getLevel());
+ marker.setMinApi(getMinApiLevel().getLevel());
}
if (desugaredLibraryConfiguration.getIdentifier() != null) {
marker.setDesugaredLibraryIdentifiers(desugaredLibraryConfiguration.getIdentifier());
@@ -422,6 +422,10 @@
throw new UnsupportedOperationException("Cannot find internal output mode.");
}
+ public boolean isAndroidPlatform() {
+ return minApiLevel == AndroidApiLevel.ANDROID_PLATFORM;
+ }
+
public boolean isDesugaredLibraryCompilation() {
return desugaredLibraryConfiguration.isLibraryCompilation();
}
@@ -512,7 +516,7 @@
getExtensiveInterfaceMethodMinifierLoggingFilter();
public List<String> methodsFilter = ImmutableList.of();
- public AndroidApiLevel minApiLevel = AndroidApiLevel.getDefault();
+ 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;
@@ -553,6 +557,16 @@
private final boolean enableTreeShaking;
private final boolean enableMinification;
+ public AndroidApiLevel getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ public void setMinApiLevel(AndroidApiLevel minApiLevel) {
+ assert minApiLevel != null;
+ assert minApiLevel.isLessThan(AndroidApiLevel.UNKNOWN);
+ this.minApiLevel = minApiLevel;
+ }
+
public boolean isOptimizing() {
return hasProguardConfiguration() && getProguardConfiguration().isOptimizing();
}
@@ -1742,7 +1756,7 @@
}
private boolean hasMinApi(AndroidApiLevel level) {
- return minApiLevel.isGreaterThanOrEqualTo(level);
+ return getMinApiLevel().isGreaterThanOrEqualTo(level);
}
/**
@@ -1825,7 +1839,7 @@
// 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 desugarState.isOn() && minApiLevel.isLessThanOrEqualTo(AndroidApiLevel.LATEST);
+ return desugarState.isOn() && getMinApiLevel().isLessThanOrEqualTo(AndroidApiLevel.LATEST);
}
public boolean enableTryWithResourcesDesugaring() {
@@ -1927,7 +1941,7 @@
// being thrown on out of bounds.
public boolean canUseSameArrayAndResultRegisterInArrayGetWide() {
assert isGeneratingDex();
- return minApiLevel.isGreaterThan(AndroidApiLevel.O_MR1);
+ return getMinApiLevel().isGreaterThan(AndroidApiLevel.O_MR1);
}
// Some Lollipop versions of Art found in the wild perform invalid bounds
@@ -1944,7 +1958,7 @@
//
// See b/69364976 and b/77996377.
public boolean canHaveBoundsCheckEliminationBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.M);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
}
// MediaTek JIT compilers for KitKat phones did not implement the not
@@ -1960,7 +1974,7 @@
// 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 isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.M);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
}
// Art crashes if we do dead reference elimination of the receiver in release mode and Art
@@ -1969,13 +1983,13 @@
//
// See b/116683601 and b/116837585.
public boolean canHaveThisJitCodeDebuggingBug() {
- return minApiLevel.isLessThan(AndroidApiLevel.Q);
+ return getMinApiLevel().isLessThan(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 isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.L);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
}
// Some dalvik versions found in the wild perform invalid JIT compilation of cmp-long
@@ -2008,7 +2022,7 @@
//
// See b/75408029.
public boolean canHaveCmpLongBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.L);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
}
// Some Lollipop VMs crash if there is a const instruction between a cmp and an if instruction.
@@ -2036,7 +2050,7 @@
//
// See b/115552239.
public boolean canHaveCmpIfFloatBug() {
- return minApiLevel.isLessThan(AndroidApiLevel.M);
+ return getMinApiLevel().isLessThan(AndroidApiLevel.M);
}
// Some Lollipop VMs incorrectly optimize code with mul2addr instructions. In particular,
@@ -2058,7 +2072,7 @@
//
// This issue has only been observed on a Verizon Ellipsis 8 tablet. See b/76115465.
public boolean canHaveMul2AddrBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.M);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
}
// Some Marshmallow VMs create an incorrect doubly-linked list of instructions. When the VM
@@ -2067,7 +2081,7 @@
//
// See b/77842465.
public boolean canHaveDex2OatLinkedListBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.N);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.N);
}
// dex2oat on Marshmallow VMs does aggressive inlining which can eat up all the memory on
@@ -2075,7 +2089,7 @@
//
// See b/111960171
public boolean canHaveDex2OatInliningIssue() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.N);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.N);
}
// Art 7.0.0 and later Art JIT may perform an invalid optimization if a string new-instance does
@@ -2083,7 +2097,7 @@
//
// See b/78493232 and b/80118070.
public boolean canHaveArtStringNewInitBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.Q);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.Q);
}
// Dalvik tracing JIT may perform invalid optimizations when int/float values are converted to
@@ -2091,7 +2105,7 @@
//
// See b/77496850.
public boolean canHaveNumberConversionRegisterAllocationBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.L);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
}
// Some Lollipop mediatek VMs have a peculiar bug where the inliner crashes if there is a
@@ -2104,7 +2118,7 @@
//
// See b/68378480.
public boolean canHaveForwardingInitInliningBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.M);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
}
// Some Lollipop x86_64 VMs have a bug causing a segfault if an exception handler directly targets
@@ -2116,7 +2130,7 @@
//
// See b/111337896.
public boolean canHaveExceptionTargetingLoopHeaderBug() {
- return isGeneratingDex() && !debug && minApiLevel.isLessThan(AndroidApiLevel.M);
+ return isGeneratingDex() && !debug && getMinApiLevel().isLessThan(AndroidApiLevel.M);
}
// The Dalvik tracing JIT can trace past the end of the instruction stream and end up
@@ -2131,7 +2145,7 @@
// 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 minApiLevel.isLessThan(AndroidApiLevel.L);
+ return getMinApiLevel().isLessThan(AndroidApiLevel.L);
}
// The art verifier incorrectly propagates type information for the following pattern:
@@ -2159,7 +2173,7 @@
// Fixed in Android Q, see b/120985556.
public boolean canHaveArtInstanceOfVerifierBug() {
assert isGeneratingDex();
- return minApiLevel.isLessThan(AndroidApiLevel.Q);
+ return getMinApiLevel().isLessThan(AndroidApiLevel.Q);
}
// Some Art Lollipop version do not deal correctly with long-to-int conversions.
@@ -2182,7 +2196,7 @@
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 minApiLevel.isLessThan(AndroidApiLevel.M);
+ return getMinApiLevel().isLessThan(AndroidApiLevel.M);
}
// The Art VM for Android N through P has a bug in the JIT that means that if the same
@@ -2195,7 +2209,7 @@
//
// See b/120164595.
public boolean canHaveExceptionTypeBug() {
- return minApiLevel.isLessThan(AndroidApiLevel.Q);
+ return getMinApiLevel().isLessThan(AndroidApiLevel.Q);
}
// Art 4.0.4 fails with a verification error when a null-literal is being passed directly to an
@@ -2203,7 +2217,7 @@
// elimination of check-cast instructions where the value being cast is the constant null.
// See b/123269162.
public boolean canHaveArtCheckCastVerifierBug() {
- return minApiLevel.isLessThan(AndroidApiLevel.J);
+ return getMinApiLevel().isLessThan(AndroidApiLevel.J);
}
// The verifier will merge A[] and B[] to Object[], even when both A and B implement an interface
@@ -2227,7 +2241,7 @@
//
// See b/131349148
public boolean canHaveDalvikCatchHandlerVerificationBug() {
- return isGeneratingClassFiles() || minApiLevel.isLessThan(AndroidApiLevel.L);
+ return isGeneratingClassFiles() || getMinApiLevel().isLessThan(AndroidApiLevel.L);
}
// Having an invoke instruction that targets an abstract method on a non-abstract class will fail
@@ -2235,7 +2249,7 @@
//
// See b/132953944.
public boolean canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.L);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
}
// On dalvik we see issues when using an int value in places where a boolean, byte, char, or short
@@ -2249,14 +2263,14 @@
//
// See also b/134304597 and b/124152497.
public boolean canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug() {
- return isGeneratingClassFiles() || minApiLevel.isLessThan(AndroidApiLevel.L);
+ return isGeneratingClassFiles() || getMinApiLevel().isLessThan(AndroidApiLevel.L);
}
// The standard library prior to API 19 did not contain a ZipFile that implemented Closable.
//
// See b/177532008.
public boolean canHaveZipFileWithMissingCloseableBug() {
- return isGeneratingClassFiles() || minApiLevel.isLessThan(AndroidApiLevel.K);
+ return isGeneratingClassFiles() || getMinApiLevel().isLessThan(AndroidApiLevel.K);
}
// Some versions of Dalvik had a bug where a switch with a MAX_INT key would still go to
@@ -2264,7 +2278,7 @@
//
// See b/177790310.
public boolean canHaveSwitchMaxIntBug() {
- return isGeneratingDex() && minApiLevel.isLessThan(AndroidApiLevel.K);
+ return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.K);
}
// On Dalvik the methods Integer.parseInt and Long.parseLong does not support strings with a '+'
@@ -2272,6 +2286,6 @@
//
// See b/182137865.
public boolean canParseNumbersWithPlusPrefix() {
- return minApiLevel.isGreaterThan(AndroidApiLevel.K);
+ return getMinApiLevel().isGreaterThan(AndroidApiLevel.K);
}
}
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index a3d5cf6..2e7cd20 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -29,7 +29,7 @@
protected void ensureSameOutput(String main, AndroidApiLevel apiLevel,
List<String> args, byte[]... classes) throws Exception {
AndroidApp app = buildAndroidApp(classes);
- Consumer<InternalOptions> setMinApiLevel = o -> o.minApiLevel = apiLevel;
+ Consumer<InternalOptions> setMinApiLevel = o -> o.setMinApiLevel(apiLevel);
ProcessResult javaResult = runOnJavaRaw(main, Arrays.asList(classes), args);
Consumer<ArtCommandBuilder> cmdBuilder = builder -> {
for (String arg : args) {
diff --git a/src/test/java/com/android/tools/r8/TestAppViewBuilder.java b/src/test/java/com/android/tools/r8/TestAppViewBuilder.java
index d5ef230..e8e75f5 100644
--- a/src/test/java/com/android/tools/r8/TestAppViewBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestAppViewBuilder.java
@@ -92,7 +92,7 @@
}
public TestAppViewBuilder setMinApi(AndroidApiLevel minApi) {
- optionModifications.add(options -> options.minApiLevel = minApi);
+ optionModifications.add(options -> options.setMinApiLevel(minApi));
return this;
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 11dfa2d..033e512 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -861,6 +861,7 @@
case J_MR1:
case J_MR2:
case K_WATCH:
+ case ANDROID_PLATFORM:
case UNKNOWN:
case NOT_SET:
return false;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingClassInitializedBySuperTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingClassInitializedBySuperTest.java
new file mode 100644
index 0000000..aa46eb2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingClassInitializedBySuperTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2021, 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.classmerging.horizontal;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClinitDeadlockAfterMergingClassInitializedBySuperTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().setEnableClassInitializerDeadlockDetection())
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ }
+
+ static class Main {
+
+ // @Keep
+ public static void thread0() {
+ // This will take the lock for A and then wait to take the lock for B (BC if B and C are
+ // merged).
+ System.out.println(A.b);
+ }
+
+ // @Keep
+ public static void thread1() {
+ // This will take the lock for C (BC if B and C are merged) and then wait to the the lock for
+ // its superclass A.
+ System.out.println(new C());
+ }
+ }
+
+ static class A {
+
+ static B b = new B();
+ }
+
+ static class B extends A {}
+
+ static class C extends A {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingMutuallyDependentClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingMutuallyDependentClassesTest.java
new file mode 100644
index 0000000..ae35061
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingMutuallyDependentClassesTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2021, 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.classmerging.horizontal;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClinitDeadlockAfterMergingMutuallyDependentClassesTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().setEnableClassInitializerDeadlockDetection())
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ }
+
+ static class Main {
+
+ // @Keep
+ public static void thread0() {
+ // This will take the lock for A and then wait to take the lock for C (BC if B and C are
+ // merged).
+ System.out.println(A.c);
+ }
+
+ // @Keep
+ public static void thread1() {
+ // This will take the lock for B (BC if B and C are merged) and then wait to take the lock for
+ // A.
+ System.out.println(new B());
+ System.out.println(B.a);
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {
+
+ static C c = new C();
+ }
+
+ static class B {
+
+ static A a = new A();
+ }
+
+ static class C {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest.java
new file mode 100644
index 0000000..2ea472e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2021, 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest.Host.Companion.HostA;
+import com.android.tools.r8.classmerging.horizontal.ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest.Host.Companion.HostB;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClinitDeadlockAfterMergingSingletonClassesInstantiatedByCompanionTest
+ extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public int thread;
+
+ @Parameters(name = "{0}, thread: {1}")
+ public static List<Object[]> parameters() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), ImmutableList.of(1, 2, 3));
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void thread0();",
+ " public static void thread" + thread + "();",
+ "}")
+ // TODO(b/205611444): HostA and HostB should be merged when thread is 1.
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().setEnableClassInitializerDeadlockDetection())
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ }
+
+ static class Main {
+
+ // @Keep
+ public static void thread0() {
+ // Takes the lock for Host, then Companion, and then waits to take the lock for HostA.
+ System.out.println(Host.companion.a);
+ System.out.println(Host.companion.b);
+ }
+
+ // @Keep
+ public static void thread1() {
+ // Ditto. In this case there is no risk of a deadlock, since one of thread0 and thread1 will
+ // take the lock for Host and the other thread will then wait on the lock for Host to be
+ // released.
+ System.out.println(Host.companion.a);
+ System.out.println(Host.companion.b);
+ }
+
+ // @Keep
+ public static void thread2() {
+ // Takes the lock for Companion, then HostA, and then waits to take the lock for its
+ // superclass Host.
+ System.out.println(new Host.Companion());
+ }
+
+ // @Keep
+ public static void thread3() {
+ // Takes the lock for HostA, and then waits to take the lock for its superclass Host.
+ HostA.init();
+ }
+ }
+
+ static class Host {
+
+ static Companion companion = new Companion();
+
+ static class Companion {
+
+ Host a = new HostA();
+
+ Host b = new HostB();
+
+ static class HostA extends Host {
+
+ @NeverInline
+ static void init() {
+ System.out.println("HostA.init");
+ }
+ }
+
+ static class HostB extends Host {}
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThisRunner.java b/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThisRunner.java
index 2363533..53d4d26 100644
--- a/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThisRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThisRunner.java
@@ -38,7 +38,7 @@
// Api level M so that the workarounds for Lollipop verifier doesn't
// block the receiver register. We want to check b/116683601 which
// happens on at least 7.0.0.
- options.minApiLevel = AndroidApiLevel.M;
+ options.setMinApiLevel(AndroidApiLevel.M);
});
return ImmutableList.of(new Object[]{"CF", cf}, new Object[]{"D8", d8});
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
index 67db3d7..ea0f614 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
@@ -79,17 +79,14 @@
.assertOnlyWarnings()
.assertWarningMessageThatMatches(containsString("is not supported by this compiler"))
.run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
- .assertFailureWithErrorThatMatches(
- containsString(
- "java.lang.NoSuchMethodError: No static method"
- + " parseInt(Ljava/lang/CharSequence;III)I"));
+ .assertSuccessWithOutputLines("4");
}
@Test
public void noWarningForPlatformBuild() throws Exception {
testForD8()
.addProgramClassFileData(transformTestMathMultiplyExactLongInt())
- .setMinApi(AndroidApiLevel.magicApiLevelUsedByAndroidPlatformBuild)
+ .setMinApi(AndroidApiLevel.ANDROID_PLATFORM)
.run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
.assertFailureWithErrorThatMatches(
containsString(
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index 5cfc6cd..bd338b6 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -39,7 +39,7 @@
// android.jar for that level.
CodeInspector inspector = new CodeInspector(ToolHelper.getAndroidJar(apiLevel));
InternalOptions options = new InternalOptions();
- options.minApiLevel = apiLevel;
+ options.setMinApiLevel(apiLevel);
List<DexMethod> backportedMethods =
BackportedMethodRewriter.generateListOfBackportedMethods(
AndroidApp.builder().build(), options, ThreadUtils.getExecutorService(options));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index 905529a..f84c38c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -126,7 +126,7 @@
.parse(StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()));
for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
- if (apiLevel == AndroidApiLevel.UNKNOWN || apiLevel == AndroidApiLevel.NOT_SET) {
+ if (apiLevel.isGreaterThan(AndroidApiLevel.LATEST)) {
continue;
}
Path compileApiLevelDirectory = directory.resolve("compile_api_level_" + apiLevel.getLevel());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DurationJDK11Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DurationJDK11Test.java
new file mode 100644
index 0000000..7f76531
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DurationJDK11Test.java
@@ -0,0 +1,136 @@
+// Copyright (c) 2021, 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.desugar.desugaredlibrary.jdk11;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class DurationJDK11Test extends DesugaredLibraryTestBase {
+
+ private static final String EXPECTED_RESULT = StringUtils.lines("7");
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public DurationJDK11Test(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
+ Assume.assumeFalse(
+ "TODO(b/206068300)",
+ parameters.getApiLevel().getLevel() >= AndroidApiLevel.O.getLevel()
+ && parameters.getApiLevel().getLevel() < AndroidApiLevel.S.getLevel());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramClassFileData(getProgramClassFileData())
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
+ Assume.assumeFalse(
+ "TODO(b/206068300)",
+ parameters.getApiLevel().getLevel() >= AndroidApiLevel.O.getLevel()
+ && parameters.getApiLevel().getLevel() < AndroidApiLevel.S.getLevel());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(Backend.DEX)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramClassFileData(getProgramClassFileData())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ private Collection<byte[]> getProgramClassFileData() throws IOException {
+ return ImmutableList.of(
+ transformer(TestClass.class)
+ .addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitMethodInsn(
+ int opcode,
+ String owner,
+ String name,
+ String descriptor,
+ boolean isInterface) {
+ if (opcode == Opcodes.INVOKESTATIC && name.equals("toHoursPart")) {
+ super.visitMethodInsn(
+ Opcodes.INVOKEVIRTUAL,
+ "java/time/Duration",
+ name,
+ withoutFirstObjectArg(descriptor),
+ isInterface);
+ return;
+ }
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform());
+ }
+
+ private String withoutFirstObjectArg(String descriptor) {
+ int i = descriptor.indexOf(";");
+ return "(" + descriptor.substring(i + 1);
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ Duration duration = Duration.ofHours(31);
+ System.out.println(toHoursPart(duration));
+ }
+
+ // Replaced in the transformer by JDK 11 Duration#toHoursPart.
+ private static int toHoursPart(Duration duration) {
+ return -1;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/TrivialArrayCheckCastTest.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/TrivialArrayCheckCastTest.java
index b4d7969..c885a62 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/TrivialArrayCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/TrivialArrayCheckCastTest.java
@@ -27,7 +27,7 @@
testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
InternalOptions options = new InternalOptions();
- options.minApiLevel = AndroidApiLevel.I_MR1;
+ options.setMinApiLevel(AndroidApiLevel.I_MR1);
assert options.canHaveArtCheckCastVerifierBug();
testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ZipFileInstanceOfAutoCloseableTest.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ZipFileInstanceOfAutoCloseableTest.java
index 14d84e7..ea167e3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ZipFileInstanceOfAutoCloseableTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ZipFileInstanceOfAutoCloseableTest.java
@@ -103,7 +103,7 @@
assumeTrue(parameters.isDexRuntime());
// Set the min API and create the raw app.
InternalOptions options = new InternalOptions();
- options.minApiLevel = parameters.getApiLevel();
+ options.setMinApiLevel(parameters.getApiLevel());
DirectMappedDexApplication application =
new ApplicationReader(
AndroidApp.builder()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/EscapeBeforeStaticPutTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/EscapeBeforeStaticPutTest.java
index cde325b..d8cf4f3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/EscapeBeforeStaticPutTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/EscapeBeforeStaticPutTest.java
@@ -19,7 +19,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public EscapeBeforeStaticPutTest(TestParameters parameters) {
@@ -32,7 +32,7 @@
.addInnerClasses(EscapeBeforeStaticPutTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines("Hello world!");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantStaticFieldStoreBeforeStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantStaticFieldStoreBeforeStoreTest.java
new file mode 100644
index 0000000..d885356
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantStaticFieldStoreBeforeStoreTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.redundantfieldloadelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RedundantStaticFieldStoreBeforeStoreTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertEquals(
+ 1,
+ mainMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticPut)
+ .count());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("2");
+ }
+
+ static class Main {
+
+ static int f;
+
+ public static void main(String[] args) {
+ f = 1;
+ f = 2;
+ print();
+ }
+
+ @NeverInline
+ private static void print() {
+ System.out.println(f);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index fd893ea..5db275b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -124,7 +124,7 @@
ClassBuilder superClass = builder.addClass("SuperClass");
superClass.addDefaultConstructor();
- superClass.addStaticFinalField("aField", "I", "42");
+ superClass.addStaticField("aField", "I", "42");
ClassBuilder subClass = builder.addClass("SubClass", "SuperClass");
subClass.addDefaultConstructor();
diff --git a/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
index f0d186f..594bdbe 100644
--- a/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
@@ -113,13 +113,15 @@
runOnArtD8(
jasminBuilder,
mainClassName,
- o -> o.minApiLevel = ToolHelper.getMinApiLevelForDexVm());
+ o -> o.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm()));
assertEquals(expectedResult, artResult);
} else {
// Make sure the compiler fails.
try {
runOnArtD8(
- jasminBuilder, mainClassName, o -> o.minApiLevel = ToolHelper.getMinApiLevelForDexVm());
+ jasminBuilder,
+ mainClassName,
+ o -> o.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm()));
fail("D8 should have rejected this case.");
} catch (CompilationFailedException t) {
assertTrue(t.getCause().getMessage().contains(expectedNameInFailingD8Message));
@@ -132,7 +134,7 @@
mainClassName,
options -> {
options.itemFactory.setSkipNameValidationForTesting(true);
- options.minApiLevel = ToolHelper.getMinApiLevelForDexVm();
+ options.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm());
});
fail("Art should have failed.");
} catch (AssertionError e) {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 441b85f..9347e50 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -814,7 +814,7 @@
Timing timing = Timing.empty();
InternalOptions options =
new InternalOptions(new DexItemFactory(), new Reporter(diagnosticsHandler));
- options.minApiLevel = AndroidApiLevel.getAndroidApiLevel(minApi);
+ options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApi));
options.intermediate = intermediate;
DexItemFactory factory = options.itemFactory;
AppView<?> appView = AppView.createForR8(DexApplication.builder(options, timing).build());
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
index 7ed73c6..1a7919f 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming.applymapping.sourcelibrary;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -142,8 +143,7 @@
ClassSubject sub = inspector.clazz(ConcreteChecker.class);
assertThat(sub, isPresent());
FieldSubject q = sub.field("java.lang.String", "tag");
- assertThat(q, isPresentAndRenamed());
- assertEquals("q", q.getFinalName());
+ assertThat(q, isAbsent());
MethodSubject y = sub.method("void", "check", ImmutableList.of());
assertThat(y, isPresentAndRenamed());
assertEquals("y", y.getFinalName());
diff --git a/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java b/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java
index bb1fed9..aeb2ba4 100644
--- a/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java
@@ -24,7 +24,7 @@
.addProguardConfiguration(
ImmutableList.of("-keepattributes *Annotation*"), Origin.unknown())
.build(),
- options -> options.minApiLevel = ToolHelper.getMinApiLevelForDexVm());
+ options -> options.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm()));
String result = runOnArt(androidApp, ClassWithAnnotations.class);
Assert.assertEquals("@" + AnAnnotation.class.getCanonicalName() + "()", result);
}
diff --git a/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java b/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java
index 03efc25..7fb7e88 100644
--- a/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java
+++ b/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java
@@ -456,10 +456,10 @@
throws Exception {
AndroidApp app = readClasses(Path.class, Log.class, testClass);
if (compiler == Tool.D8) {
- app = compileWithD8(app, o -> o.minApiLevel = apiLevel);
+ app = compileWithD8(app, o -> o.setMinApiLevel(apiLevel));
} else {
assert compiler == Tool.R8;
- app = compileWithR8(app, "-keep class * { *; }", o -> o.minApiLevel = apiLevel);
+ app = compileWithR8(app, "-keep class * { *; }", o -> o.setMinApiLevel(apiLevel));
}
checkPathParserMethods(app, testClass, a, b);
}
@@ -479,10 +479,10 @@
throws Exception {
AndroidApp app = readClasses(testClass);
if (compiler == Tool.D8) {
- app = compileWithD8(app, o -> o.minApiLevel = apiLevel);
+ app = compileWithD8(app, o -> o.setMinApiLevel(apiLevel));
} else {
assert compiler == Tool.R8;
- app = compileWithR8(app, "-keep class * { *; }", o -> o.minApiLevel = apiLevel);
+ app = compileWithR8(app, "-keep class * { *; }", o -> o.setMinApiLevel(apiLevel));
}
CodeInspector inspector = new CodeInspector(app);
DexItemFactory factory = inspector.getFactory();
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
index d408a4f..d19d8b8 100644
--- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
@@ -74,8 +74,7 @@
mainMethod
.streamInstructions()
.noneMatch(i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
- // TODO(b/138913138): not trivial; assigned only once in <init>
- assertFalse(
+ assertTrue(
mainMethod
.streamInstructions()
.noneMatch(i -> i.isConstString("Dead code: 3", JumboStringMode.ALLOW)));
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index d698e0b..84099dc 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -108,7 +108,7 @@
SingleFileConsumer consumer = new SingleFileConsumer();
AndroidApp app = AndroidApp.builder().addDexProgramData(data, Origin.unknown()).build();
InternalOptions options = new InternalOptions();
- options.minApiLevel = AndroidApiLevel.getAndroidApiLevel(apiLevel);
+ options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(apiLevel));
options.programConsumer = consumer;
ExecutorService executor = ThreadUtils.getExecutorService(1);
try {
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 88c758d..f71a314 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -1107,7 +1107,7 @@
quiet=options.quiet)
elif options.version == 'main':
if not (options.no_build or options.golem):
- gradle.RunGradle(['r8', '-Pno_internal'])
+ gradle.RunGradle(['R8Retrace', 'r8', '-Pno_internal'])
build_r8lib = False
for shrinker in options.shrinker:
if is_minified_r8(shrinker):