Merge commit '7c1e8bbef02070ca0ce6b9afd70b02d157fcf201' into dev-release
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 0744c8f..fb03dbb 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -83,6 +83,27 @@
}
},
{
+ "api_level_below_or_equal": 30,
+ "rewrite_prefix": {
+ "j$.time.": "java.time.",
+ "java.time.": "j$.time.",
+ "java.util.Desugar": "j$.util.Desugar"
+ },
+ "retarget_lib_member": {
+ "java.util.Date#toInstant": "java.util.DesugarDate",
+ "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
+ "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
+ },
+ "custom_conversion": {
+ "java.time.ZonedDateTime": "java.time.TimeConversions",
+ "java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.Duration": "java.time.TimeConversions",
+ "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Instant": "java.time.TimeConversions"
+ }
+ },
+ {
"api_level_below_or_equal": 29,
"rewrite_prefix": {
"java.util.concurrent.Flow": "j$.util.concurrent.Flow"
@@ -91,10 +112,7 @@
{
"api_level_below_or_equal": 25,
"rewrite_prefix": {
- "j$.time.": "java.time.",
- "java.time.": "j$.time.",
"java.io.DesugarFile" : "j$.io.DesugarFile",
- "java.util.Desugar": "j$.util.Desugar",
"sun.misc.Desugar": "j$.sun.misc.Desugar",
"jdk.internal.": "j$.jdk.internal.",
"java.nio.Desugar": "j$.nio.Desugar",
@@ -117,18 +135,7 @@
"java.lang.DesugarMath": "java.lang.Math"
},
"retarget_lib_member": {
- "java.io.File#toPath": "java.io.DesugarFile",
- "java.util.Date#toInstant": "java.util.DesugarDate",
- "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
- "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
- },
- "custom_conversion": {
- "java.time.ZonedDateTime": "java.time.TimeConversions",
- "java.time.LocalDate": "java.time.TimeConversions",
- "java.time.Duration": "java.time.TimeConversions",
- "java.time.ZoneId": "java.time.TimeConversions",
- "java.time.MonthDay": "java.time.TimeConversions",
- "java.time.Instant": "java.time.TimeConversions"
+ "java.io.File#toPath": "java.io.DesugarFile"
}
},
{
@@ -211,7 +218,7 @@
}
},
{
- "api_level_below_or_equal": 25,
+ "api_level_below_or_equal": 30,
"rewrite_prefix": {
"java.time.": "j$.time.",
"java.util.Desugar": "j$.util.Desugar"
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
index 863111d..fac2ec0 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -83,18 +83,36 @@
}
},
{
+ "api_level_below_or_equal": 30,
+ "rewrite_prefix": {
+ "j$.time.": "java.time.",
+ "java.time.": "j$.time.",
+ "java.util.Desugar": "j$.util.Desugar"
+ },
+ "retarget_lib_member": {
+ "java.util.Date#toInstant": "java.util.DesugarDate",
+ "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
+ "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
+ },
+ "custom_conversion": {
+ "java.time.ZonedDateTime": "java.time.TimeConversions",
+ "java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.Duration": "java.time.TimeConversions",
+ "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Instant": "java.time.TimeConversions"
+ }
+ },
+ {
"api_level_below_or_equal": 29,
"rewrite_prefix": {
"java.util.concurrent.Flow": "j$.util.concurrent.Flow"
}
},
{
- "api_level_below_or_equal": 25,
+ "api_level_below_or_equal": 30,
"rewrite_prefix": {
- "j$.time.": "java.time.",
- "java.time.": "j$.time.",
"java.io.DesugarFile" : "j$.io.DesugarFile",
- "java.util.Desugar": "j$.util.Desugar",
"sun.misc.Desugar": "j$.sun.misc.Desugar",
"jdk.internal.": "j$.jdk.internal.",
"java.nio.Desugar": "j$.nio.Desugar",
@@ -117,18 +135,7 @@
"java.lang.DesugarMath": "java.lang.Math"
},
"retarget_lib_member": {
- "java.io.File#toPath": "java.io.DesugarFile",
- "java.util.Date#toInstant": "java.util.DesugarDate",
- "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
- "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
- },
- "custom_conversion": {
- "java.time.ZonedDateTime": "java.time.TimeConversions",
- "java.time.LocalDate": "java.time.TimeConversions",
- "java.time.Duration": "java.time.TimeConversions",
- "java.time.ZoneId": "java.time.TimeConversions",
- "java.time.MonthDay": "java.time.TimeConversions",
- "java.time.Instant": "java.time.TimeConversions"
+ "java.io.File#toPath": "java.io.DesugarFile"
}
},
{
@@ -208,7 +215,14 @@
],
"program_flags": [
{
- "api_level_below_or_equal": 25,
+ "api_level_below_or_equal": 10000,
+ "rewrite_prefix": {
+ "java.net.URLDecoder": "j$.net.URLDecoder",
+ "java.net.URLEncoder": "j$.net.URLEncoder"
+ }
+ },
+ {
+ "api_level_below_or_equal": 30,
"rewrite_prefix": {
"java.time.": "j$.time.",
"java.util.Desugar": "j$.util.Desugar"
@@ -293,6 +307,15 @@
"java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
"java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
}
+ },
+ {
+ "api_level_below_or_equal": 18,
+ "rewrite_prefix": {
+ "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
+ },
+ "retarget_lib_member": {
+ "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
+ }
}
],
"shrinker_config": [
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 72c0cbb..722ae3e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -104,7 +104,6 @@
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
import com.android.tools.r8.synthesis.SyntheticFinalization;
import com.android.tools.r8.synthesis.SyntheticItems;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.CollectionUtils;
@@ -880,10 +879,10 @@
for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
clazz.forEachProgramMember(
member -> {
- assert member.getDefinition().getApiLevel() != AndroidApiLevel.NOT_SET
+ assert !member.getDefinition().getApiLevel().isNotSetApiLevel()
: "Every member should have been analyzed";
assert appView.options().apiModelingOptions().enableApiCallerIdentification
- || member.getDefinition().getApiLevel() == AndroidApiLevel.UNKNOWN
+ || member.getDefinition().getApiLevel().isUnknownApiLevel()
: "Every member should have level UNKNOWN";
});
}
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 34a2d65..48d4d4f 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -4,67 +4,108 @@
package com.android.tools.r8.androidapi;
-import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel.KnownApiLevel;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
-public interface AndroidApiLevelCompute {
+public abstract class AndroidApiLevelCompute {
- AndroidApiLevel computeApiLevelForLibraryReference(DexReference reference);
+ private final KnownApiLevel[] knownApiLevelCache;
- AndroidApiLevel computeApiLevelForDefinition(Iterable<DexType> types);
-
- default AndroidApiLevel computeApiLevelForDefinition(
- DexMember<?, ?> reference, DexItemFactory factory) {
- return computeApiLevelForDefinition(reference.getReferencedBaseTypes(factory));
+ public AndroidApiLevelCompute() {
+ knownApiLevelCache = new KnownApiLevel[AndroidApiLevel.LATEST.getLevel() + 1];
+ for (AndroidApiLevel value : AndroidApiLevel.values()) {
+ if (value != AndroidApiLevel.ANDROID_PLATFORM) {
+ knownApiLevelCache[value.getLevel()] = new KnownApiLevel(value);
+ }
+ }
}
- static AndroidApiLevelCompute create(AppView<?> appView) {
+ public KnownApiLevel of(AndroidApiLevel apiLevel) {
+ if (apiLevel == AndroidApiLevel.ANDROID_PLATFORM) {
+ return ComputedApiLevel.platform();
+ }
+ return knownApiLevelCache[apiLevel.getLevel()];
+ }
+
+ public abstract ComputedApiLevel computeApiLevelForLibraryReference(
+ DexReference reference, ComputedApiLevel unknownValue);
+
+ public abstract ComputedApiLevel computeApiLevelForDefinition(
+ Iterable<DexType> types, ComputedApiLevel unknownValue);
+
+ public ComputedApiLevel computeApiLevelForDefinition(
+ DexMember<?, ?> reference, DexItemFactory factory, ComputedApiLevel unknownValue) {
+ return computeApiLevelForDefinition(reference.getReferencedBaseTypes(factory), unknownValue);
+ }
+
+ public static AndroidApiLevelCompute create(AppView<?> appView) {
return appView.options().apiModelingOptions().enableApiCallerIdentification
? new DefaultAndroidApiLevelCompute(appView)
: new NoAndroidApiLevelCompute();
}
- class NoAndroidApiLevelCompute implements AndroidApiLevelCompute {
-
- @Override
- public AndroidApiLevel computeApiLevelForDefinition(Iterable<DexType> types) {
- return UNKNOWN;
- }
-
- @Override
- public AndroidApiLevel computeApiLevelForLibraryReference(DexReference reference) {
- return UNKNOWN;
+ public static ComputedApiLevel computeInitialMinApiLevel(InternalOptions options) {
+ if (options.apiModelingOptions().enableApiCallerIdentification) {
+ return options.getMinApiLevel() == AndroidApiLevel.ANDROID_PLATFORM
+ ? ComputedApiLevel.platform()
+ : new KnownApiLevel(options.getMinApiLevel());
+ } else {
+ return ComputedApiLevel.unknown();
}
}
- class DefaultAndroidApiLevelCompute implements AndroidApiLevelCompute {
+ public ComputedApiLevel getPlatformApiLevelOrUnknown(AppView<?> appView) {
+ if (appView.options().getMinApiLevel() == AndroidApiLevel.ANDROID_PLATFORM) {
+ return ComputedApiLevel.platform();
+ }
+ return ComputedApiLevel.unknown();
+ }
- private final AndroidApiReferenceLevelCache cache;
- private final AndroidApiLevel minApiLevel;
+ public static class NoAndroidApiLevelCompute extends AndroidApiLevelCompute {
- public DefaultAndroidApiLevelCompute(AppView<?> appView) {
- this.cache = AndroidApiReferenceLevelCache.create(appView);
- this.minApiLevel = appView.options().getMinApiLevel();
+ @Override
+ public ComputedApiLevel computeApiLevelForDefinition(
+ Iterable<DexType> types, ComputedApiLevel unknownValue) {
+ return unknownValue;
}
@Override
- public AndroidApiLevel computeApiLevelForDefinition(Iterable<DexType> types) {
- AndroidApiLevel computedLevel = minApiLevel;
+ public ComputedApiLevel computeApiLevelForLibraryReference(
+ DexReference reference, ComputedApiLevel unknownValue) {
+ return unknownValue;
+ }
+ }
+
+ public static class DefaultAndroidApiLevelCompute extends AndroidApiLevelCompute {
+
+ private final AndroidApiReferenceLevelCache cache;
+ private final ComputedApiLevel minApiLevel;
+
+ public DefaultAndroidApiLevelCompute(AppView<?> appView) {
+ this.cache = AndroidApiReferenceLevelCache.create(appView, this);
+ this.minApiLevel = of(appView.options().getMinApiLevel());
+ }
+
+ @Override
+ public ComputedApiLevel computeApiLevelForDefinition(
+ Iterable<DexType> types, ComputedApiLevel unknownValue) {
+ ComputedApiLevel computedLevel = minApiLevel;
for (DexType type : types) {
- computedLevel = cache.lookupMax(type, computedLevel);
+ computedLevel = cache.lookupMax(type, computedLevel, unknownValue);
}
return computedLevel;
}
@Override
- public AndroidApiLevel computeApiLevelForLibraryReference(DexReference reference) {
- return cache.lookup(reference);
+ public ComputedApiLevel computeApiLevelForLibraryReference(
+ DexReference reference, ComputedApiLevel unknownValue) {
+ return cache.lookup(reference, unknownValue);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
index 551085a..bedc254 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -4,12 +4,10 @@
package com.android.tools.r8.androidapi;
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
-import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
+import static com.android.tools.r8.utils.AndroidApiLevel.ANDROID_PLATFORM;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
@@ -41,25 +39,23 @@
new Int2ReferenceOpenHashMap<AndroidApiLevel>();
private final Map<String, AndroidApiLevel> ambiguousHashesWithApiLevel = new HashMap<>();
private final Map<DexReference, AndroidApiLevel> ambiguousCache = new IdentityHashMap<>();
- private final DexItemFactory factory;
public AndroidApiLevelHashingDatabaseImpl(
- DexItemFactory factory, List<AndroidApiForHashingClass> predefinedApiTypeLookup) {
- this.factory = factory;
+ List<AndroidApiForHashingClass> predefinedApiTypeLookup) {
loadData();
predefinedApiTypeLookup.forEach(
apiClass -> {
DexType type = apiClass.getType();
- lookupNonAmbiguousCache.put(type.hashCode(), NOT_SET);
+ lookupNonAmbiguousCache.put(type.hashCode(), null);
ambiguousCache.put(type, apiClass.getApiLevel());
apiClass.visitMethodsWithApiLevels(
(method, apiLevel) -> {
- lookupNonAmbiguousCache.put(method.hashCode(), NOT_SET);
+ lookupNonAmbiguousCache.put(method.hashCode(), null);
ambiguousCache.put(method, apiLevel);
});
apiClass.visitFieldsWithApiLevels(
(field, apiLevel) -> {
- lookupNonAmbiguousCache.put(field.hashCode(), NOT_SET);
+ lookupNonAmbiguousCache.put(field.hashCode(), null);
ambiguousCache.put(field, apiLevel);
});
});
@@ -96,7 +92,7 @@
for (int i = 0; i < hashIndices.length; i++) {
byte apiLevel = apiLevels[i];
lookupNonAmbiguousCache.put(
- hashIndices[i], apiLevel == -1 ? NOT_SET : AndroidApiLevel.getAndroidApiLevel(apiLevel));
+ hashIndices[i], apiLevel == -1 ? null : AndroidApiLevel.getAndroidApiLevel(apiLevel));
}
ambiguous.forEach(this::parseAmbiguous);
}
@@ -130,9 +126,12 @@
}
private AndroidApiLevel lookupApiLevel(DexReference reference) {
- AndroidApiLevel result = lookupNonAmbiguousCache.getOrDefault(reference.hashCode(), UNKNOWN);
- if (result != NOT_SET) {
- return result;
+ // We use Android platform to track if an element is unknown since no occurrences of that api
+ // level exists in the database.
+ AndroidApiLevel result =
+ lookupNonAmbiguousCache.getOrDefault(reference.hashCode(), ANDROID_PLATFORM);
+ if (result != null) {
+ return result == ANDROID_PLATFORM ? null : result;
}
return ambiguousCache.computeIfAbsent(
reference,
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 2eed210..4e17f60 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
@@ -16,85 +17,89 @@
public class AndroidApiReferenceLevelCache {
private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+ private final AndroidApiLevelCompute apiLevelCompute;
private final AndroidApiLevelDatabase androidApiLevelDatabase;
private final AppView<?> appView;
-
- private AndroidApiReferenceLevelCache(AppView<?> appView) {
- this(appView, ImmutableList.of());
- }
+ private final DexItemFactory factory;
private AndroidApiReferenceLevelCache(
- AppView<?> appView, List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
+ AppView<?> appView,
+ AndroidApiLevelCompute apiLevelCompute,
+ List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
this.appView = appView;
+ this.apiLevelCompute = apiLevelCompute;
+ factory = appView.dexItemFactory();
androidApiLevelDatabase =
- new AndroidApiLevelHashingDatabaseImpl(
- appView.dexItemFactory(), predefinedApiTypeLookupForHashing);
+ new AndroidApiLevelHashingDatabaseImpl(predefinedApiTypeLookupForHashing);
desugaredLibraryConfiguration = appView.options().desugaredLibraryConfiguration;
}
- public static AndroidApiReferenceLevelCache create(AppView<?> appView) {
- if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
- // If enableApiCallerIdentification is not enabled then override lookup to always return
- // AndroidApiLevel.B.
- return new AndroidApiReferenceLevelCache(appView) {
- @Override
- public AndroidApiLevel lookup(DexReference reference) {
- return AndroidApiLevel.B;
- }
- };
- }
+ public static AndroidApiReferenceLevelCache create(
+ AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+ assert appView.options().apiModelingOptions().enableApiCallerIdentification;
ImmutableList.Builder<AndroidApiForHashingClass> builder = ImmutableList.builder();
appView
.options()
.apiModelingOptions()
.visitMockedApiLevelsForReferences(appView.dexItemFactory(), builder::add);
- return new AndroidApiReferenceLevelCache(appView, builder.build());
+ return new AndroidApiReferenceLevelCache(appView, apiLevelCompute, builder.build());
}
- public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
- return lookup(reference).max(minApiLevel);
+ public ComputedApiLevel lookupMax(
+ DexReference reference, ComputedApiLevel minApiLevel, ComputedApiLevel unknownValue) {
+ assert !minApiLevel.isNotSetApiLevel();
+ return lookup(reference, unknownValue).max(minApiLevel);
}
- public AndroidApiLevel lookup(DexReference reference) {
+ public ComputedApiLevel lookup(DexReference reference, ComputedApiLevel unknownValue) {
DexType contextType = reference.getContextType();
if (contextType.isArrayType()) {
- if (reference.isDexMethod()
- && reference.asDexMethod().match(appView.dexItemFactory().objectMembers.clone)) {
- return appView.options().getMinApiLevel();
+ if (reference.isDexMethod() && reference.asDexMethod().match(factory.objectMembers.clone)) {
+ return appView.computedMinApiLevel();
}
- return lookup(contextType.toBaseType(appView.dexItemFactory()));
+ return lookup(contextType.toBaseType(factory), unknownValue);
}
if (contextType.isPrimitiveType() || contextType.isVoidType()) {
- return appView.options().getMinApiLevel();
+ return appView.computedMinApiLevel();
}
DexClass clazz = appView.definitionFor(contextType);
if (clazz == null) {
- return AndroidApiLevel.UNKNOWN;
+ return unknownValue;
}
if (!clazz.isLibraryClass()) {
- return appView.options().getMinApiLevel();
+ return appView.computedMinApiLevel();
}
- if (isReferenceToJavaLangObject(reference)) {
- return appView.options().getMinApiLevel();
+ if (reference.getContextType() == factory.objectType) {
+ return appView.computedMinApiLevel();
}
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().getMinApiLevel();
+ return appView.computedMinApiLevel();
}
- return reference
- .apply(
+ if (reference.isDexMethod()
+ && !reference.asDexMethod().isInstanceInitializer(factory)
+ && factory.objectMembers.isObjectMember(reference.asDexMethod())) {
+ // If we can lookup the method it was introduced/overwritten later. Take for example
+ // a default toString that was not available before some api level. If unknown we default
+ // back to the static holder.
+ AndroidApiLevel methodApiLevel =
+ androidApiLevelDatabase.getMethodApiLevel(reference.asDexMethod());
+ if (methodApiLevel != null) {
+ return apiLevelCompute.of(methodApiLevel);
+ }
+ AndroidApiLevel typeApiLevel =
+ androidApiLevelDatabase.getTypeApiLevel(reference.getContextType());
+ // TODO(b/207452750): Investigate if we can return minApi here.
+ return typeApiLevel == null ? ComputedApiLevel.unknown() : apiLevelCompute.of(typeApiLevel);
+ }
+ AndroidApiLevel foundApiLevel =
+ reference.apply(
androidApiLevelDatabase::getTypeApiLevel,
androidApiLevelDatabase::getFieldApiLevel,
- androidApiLevelDatabase::getMethodApiLevel)
- .max(appView.options().getMinApiLevel());
- }
-
- private boolean isReferenceToJavaLangObject(DexReference reference) {
- if (reference.getContextType() == appView.dexItemFactory().objectType) {
- return true;
- }
- return reference.isDexMethod()
- && appView.dexItemFactory().objectMembers.isObjectMember(reference.asDexMethod());
+ androidApiLevelDatabase::getMethodApiLevel);
+ return (foundApiLevel == null)
+ ? unknownValue
+ : apiLevelCompute.of(foundApiLevel.max(appView.options().getMinApiLevel()));
}
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
new file mode 100644
index 0000000..2f5ab5e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
@@ -0,0 +1,165 @@
+// 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.androidapi;
+
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.structural.Equatable;
+import java.util.Objects;
+
+/**
+ * The ComputedApiLevel encodes a lattice over AndroidApiLevel with a bottom (NotSet) and a top
+ * (Unknown).
+ */
+public interface ComputedApiLevel extends Equatable<ComputedApiLevel> {
+
+ static NotSetApiLevel notSet() {
+ return NotSetApiLevel.INSTANCE;
+ }
+
+ static UnknownApiLevel unknown() {
+ return UnknownApiLevel.INSTANCE;
+ }
+
+ static KnownApiLevel platform() {
+ return KnownApiLevel.PLATFORM_INSTANCE;
+ }
+
+ default boolean isNotSetApiLevel() {
+ return false;
+ }
+
+ default boolean isUnknownApiLevel() {
+ return false;
+ }
+
+ default ComputedApiLevel max(ComputedApiLevel other) {
+ return isGreaterThanOrEqualTo(other) ? this : other;
+ }
+
+ default boolean isGreaterThanOrEqualTo(ComputedApiLevel other) {
+ assert !isNotSetApiLevel() && !other.isNotSetApiLevel()
+ : "Cannot compute relationship for not set";
+ if (isUnknownApiLevel()) {
+ return true;
+ }
+ if (other.isUnknownApiLevel()) {
+ return false;
+ }
+ assert isKnownApiLevel() && other.isKnownApiLevel();
+ return asKnownApiLevel()
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(other.asKnownApiLevel().getApiLevel());
+ }
+
+ default boolean isKnownApiLevel() {
+ return false;
+ }
+
+ default KnownApiLevel asKnownApiLevel() {
+ return null;
+ }
+
+ @Override
+ default boolean isEqualTo(ComputedApiLevel other) {
+ return this.equals(other);
+ }
+
+ class NotSetApiLevel implements ComputedApiLevel {
+
+ private static final NotSetApiLevel INSTANCE = new NotSetApiLevel();
+
+ private NotSetApiLevel() {}
+
+ @Override
+ public boolean isNotSetApiLevel() {
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+ }
+
+ class UnknownApiLevel implements ComputedApiLevel {
+
+ private static final UnknownApiLevel INSTANCE = new UnknownApiLevel();
+
+ private UnknownApiLevel() {}
+
+ @Override
+ public boolean isUnknownApiLevel() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "UNKNOWN";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+ }
+
+ class KnownApiLevel implements ComputedApiLevel {
+
+ private static final KnownApiLevel PLATFORM_INSTANCE =
+ new KnownApiLevel(AndroidApiLevel.ANDROID_PLATFORM);
+
+ private final AndroidApiLevel apiLevel;
+
+ KnownApiLevel(AndroidApiLevel apiLevel) {
+ this.apiLevel = apiLevel;
+ }
+
+ public AndroidApiLevel getApiLevel() {
+ return apiLevel;
+ }
+
+ @Override
+ public boolean isKnownApiLevel() {
+ return true;
+ }
+
+ @Override
+ public KnownApiLevel asKnownApiLevel() {
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return apiLevel.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof KnownApiLevel)) {
+ return false;
+ }
+ KnownApiLevel that = (KnownApiLevel) o;
+ return apiLevel == that.apiLevel;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(apiLevel);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 32e1f64..e21fbf4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.contexts.CompilationContext;
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
@@ -116,6 +118,8 @@
private final Thread mainThread = Thread.currentThread();
+ private final ComputedApiLevel computedMinApiLevel;
+
private AppView(
T appInfo,
WholeProgramOptimizations wholeProgramOptimizations,
@@ -137,6 +141,8 @@
this.libraryMethodSideEffectModelCollection = new LibraryMethodSideEffectModelCollection(this);
this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
this.protoShrinker = ProtoShrinker.create(withLiveness());
+
+ this.computedMinApiLevel = AndroidApiLevelCompute.computeInitialMinApiLevel(appInfo.options());
}
public boolean verifyMainThread() {
@@ -827,4 +833,8 @@
public boolean checkForTesting(Supplier<Boolean> test) {
return testing().enableTestAssertions ? test.get() : true;
}
+
+ public ComputedApiLevel computedMinApiLevel() {
+ return computedMinApiLevel;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 56a302d..44a5132 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -21,7 +22,6 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
@@ -59,7 +59,7 @@
FieldTypeSignature genericSignature,
DexAnnotationSet annotations,
DexValue staticValue,
- AndroidApiLevel apiLevel,
+ ComputedApiLevel apiLevel,
boolean deprecated,
boolean d8R8Synthesized) {
super(field, annotations, d8R8Synthesized, apiLevel);
@@ -103,7 +103,7 @@
}
@Override
- public AndroidApiLevel getApiLevel() {
+ public ComputedApiLevel getApiLevel() {
return getApiLevelForDefinition();
}
@@ -365,7 +365,7 @@
private FieldAccessFlags accessFlags;
private FieldTypeSignature genericSignature = FieldTypeSignature.noSignature();
private DexValue staticValue = null;
- private AndroidApiLevel apiLevel = AndroidApiLevel.NOT_SET;
+ private ComputedApiLevel apiLevel = ComputedApiLevel.notSet();
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
private boolean deprecated;
private final boolean d8R8Synthesized;
@@ -438,7 +438,7 @@
return this;
}
- public Builder setApiLevel(AndroidApiLevel apiLevel) {
+ public Builder setApiLevel(ComputedApiLevel apiLevel) {
this.apiLevel = apiLevel;
return this;
}
@@ -474,7 +474,7 @@
assert accessFlags != null;
assert genericSignature != null;
assert annotations != null;
- assert !checkAndroidApiLevel || apiLevel != AndroidApiLevel.NOT_SET;
+ assert !checkAndroidApiLevel || !apiLevel.isNotSetApiLevel();
DexEncodedField dexEncodedField =
new DexEncodedField(
field,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index a5ea49e..7f42e49 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -3,11 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
-import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -23,7 +21,7 @@
private final boolean d8R8Synthesized;
/** apiLevelForDefinition describes the api level needed for knowing all types */
- private AndroidApiLevel apiLevelForDefinition;
+ private ComputedApiLevel apiLevelForDefinition;
private final R reference;
@@ -31,7 +29,7 @@
R reference,
DexAnnotationSet annotations,
boolean d8R8Synthesized,
- AndroidApiLevel apiLevelForDefinition) {
+ ComputedApiLevel apiLevelForDefinition) {
super(annotations);
this.reference = reference;
this.d8R8Synthesized = d8R8Synthesized;
@@ -95,18 +93,18 @@
public abstract MemberOptimizationInfo<?> getOptimizationInfo();
- public abstract AndroidApiLevel getApiLevel();
+ public abstract ComputedApiLevel getApiLevel();
- public AndroidApiLevel getApiLevelForDefinition() {
+ public ComputedApiLevel getApiLevelForDefinition() {
return apiLevelForDefinition;
}
- public void setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
+ public void setApiLevelForDefinition(ComputedApiLevel apiLevelForDefinition) {
this.apiLevelForDefinition = apiLevelForDefinition;
}
public boolean hasComputedApiReferenceLevel() {
- return getApiLevel() != NOT_SET;
+ return !getApiLevel().isNotSetApiLevel();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index ccf7d83..a8709ec 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -11,10 +11,10 @@
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
import static java.util.Objects.requireNonNull;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
@@ -71,7 +71,6 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -148,8 +147,8 @@
ParameterAnnotationsList.empty(),
null,
false,
- NOT_SET,
- NOT_SET,
+ ComputedApiLevel.notSet(),
+ ComputedApiLevel.notSet(),
null,
CallSiteOptimizationInfo.top(),
DefaultMethodOptimizationInfo.getInstance(),
@@ -168,7 +167,7 @@
private CallSiteOptimizationInfo callSiteOptimizationInfo;
private CfVersion classFileVersion;
/** The apiLevelForCode describes the api level needed for knowing all references in the code */
- private AndroidApiLevel apiLevelForCode;
+ private ComputedApiLevel apiLevelForCode;
private KotlinMethodLevelInfo kotlinMemberInfo = getNoKotlinInfo();
/** Generic signature information if the attribute is present in the input */
@@ -242,8 +241,8 @@
ParameterAnnotationsList parameterAnnotationsList,
Code code,
boolean d8R8Synthesized,
- AndroidApiLevel apiLevelForDefinition,
- AndroidApiLevel apiLevelForCode,
+ ComputedApiLevel apiLevelForDefinition,
+ ComputedApiLevel apiLevelForCode,
CfVersion classFileVersion,
CallSiteOptimizationInfo callSiteOptimizationInfo,
MethodOptimizationInfo optimizationInfo,
@@ -1232,7 +1231,7 @@
.fixupOptimizationInfo(appView, prototypeChanges.createMethodOptimizationInfoFixer())
.setGenericSignature(MethodTypeSignature.noSignature());
DexEncodedMethod method = builder.build();
- method.copyMetadata(this);
+ method.copyMetadata(appView, this);
setObsolete();
return method;
}
@@ -1268,23 +1267,23 @@
return optimizationInfo;
}
- public AndroidApiLevel getApiLevelForCode() {
+ public ComputedApiLevel getApiLevelForCode() {
return apiLevelForCode;
}
- public void clearApiLevelForCode(AppView<?> appView) {
- this.apiLevelForCode = AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView);
+ public void clearApiLevelForCode() {
+ this.apiLevelForCode = ComputedApiLevel.notSet();
}
- public void setApiLevelForCode(AndroidApiLevel apiLevel) {
+ public void setApiLevelForCode(ComputedApiLevel apiLevel) {
assert apiLevel != null;
this.apiLevelForCode = apiLevel;
}
@Override
- public AndroidApiLevel getApiLevel() {
- return (shouldNotHaveCode() ? AndroidApiLevel.B : getApiLevelForCode())
- .max(getApiLevelForDefinition());
+ public ComputedApiLevel getApiLevel() {
+ ComputedApiLevel apiLevelForDefinition = getApiLevelForDefinition();
+ return shouldNotHaveCode() ? apiLevelForDefinition : apiLevelForDefinition.max(apiLevelForCode);
}
public synchronized MutableMethodOptimizationInfo getMutableOptimizationInfo() {
@@ -1320,12 +1319,14 @@
this.callSiteOptimizationInfo = callSiteOptimizationInfo;
}
- public void copyMetadata(DexEncodedMethod from) {
+ public void copyMetadata(AppView<?> appView, DexEncodedMethod from) {
checkIfObsolete();
if (from.hasClassFileVersion()) {
upgradeClassFileVersion(from.getClassFileVersion());
}
- apiLevelForCode = getApiLevelForCode().max(from.getApiLevelForCode());
+ if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
+ apiLevelForCode = getApiLevelForCode().max(from.getApiLevelForCode());
+ }
}
public MethodTypeSignature getGenericSignature() {
@@ -1374,8 +1375,8 @@
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
private KotlinMethodLevelInfo kotlinInfo = getNoKotlinInfo();
private CfVersion classFileVersion = null;
- private AndroidApiLevel apiLevelForDefinition = NOT_SET;
- private AndroidApiLevel apiLevelForCode = NOT_SET;
+ private ComputedApiLevel apiLevelForDefinition = ComputedApiLevel.notSet();
+ private ComputedApiLevel apiLevelForCode = ComputedApiLevel.notSet();
private final boolean d8R8Synthesized;
private boolean deprecated = false;
@@ -1611,12 +1612,12 @@
return this;
}
- public Builder setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
+ public Builder setApiLevelForDefinition(ComputedApiLevel apiLevelForDefinition) {
this.apiLevelForDefinition = apiLevelForDefinition;
return this;
}
- public Builder setApiLevelForCode(AndroidApiLevel apiLevelForCode) {
+ public Builder setApiLevelForCode(ComputedApiLevel apiLevelForCode) {
this.apiLevelForCode = apiLevelForCode;
return this;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index e297bd4..900e79c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -4,11 +4,12 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
@@ -21,7 +22,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticMarker;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.structural.Ordered;
import com.android.tools.r8.utils.structural.StructuralItem;
@@ -34,7 +34,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -823,26 +822,19 @@
return checksumSupplier;
}
- public AndroidApiLevel getApiReferenceLevel(
- AppView<?> appView,
- BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelLookup) {
+ public ComputedApiLevel getApiReferenceLevel(
+ AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
// The api level of a class is the max level of it's members, super class and interfaces.
- AndroidApiLevel computedApiLevel = minApiLevelIfEnabledOrUnknown(appView);
- for (DexType superType : allImmediateSupertypes()) {
- computedApiLevel = apiLevelLookup.apply(superType, computedApiLevel);
- if (computedApiLevel == AndroidApiLevel.UNKNOWN) {
- return AndroidApiLevel.UNKNOWN;
- }
- }
- return computedApiLevel.max(getMembersApiReferenceLevel(appView));
+ return getMembersApiReferenceLevel(
+ apiLevelCompute.computeApiLevelForDefinition(
+ allImmediateSupertypes(), apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
}
- public AndroidApiLevel getMembersApiReferenceLevel(AppView<?> appView) {
- AndroidApiLevel memberLevel = minApiLevelIfEnabledOrUnknown(appView);
+ public ComputedApiLevel getMembersApiReferenceLevel(ComputedApiLevel memberLevel) {
for (DexEncodedMember<?, ?> member : members()) {
memberLevel = memberLevel.max(member.getApiLevel());
- if (memberLevel == AndroidApiLevel.UNKNOWN) {
- return AndroidApiLevel.UNKNOWN;
+ if (memberLevel.isUnknownApiLevel()) {
+ return memberLevel;
}
}
return memberLevel;
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 844b45e..cf866d4 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
/** Type representing a method definition in the programs compilation unit and its holder. */
public final class ProgramMethod extends DexClassAndMethod
@@ -95,7 +94,7 @@
accessFlags.demoteFromStrict();
accessFlags.demoteFromSynchronized();
accessFlags.promoteToAbstract();
- getDefinition().clearApiLevelForCode(appView);
+ getDefinition().clearApiLevelForCode();
getDefinition().unsetCode();
getSimpleFeedback().unsetOptimizationInfoForAbstractMethod(this);
}
@@ -105,7 +104,7 @@
public void convertToThrowNullMethod(AppView<?> appView) {
MethodAccessFlags accessFlags = getAccessFlags();
accessFlags.demoteFromAbstract();
- getDefinition().setApiLevelForCode(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView));
+ getDefinition().setApiLevelForCode(appView.computedMinApiLevel());
getDefinition().setCode(ThrowNullCode.get(), appView);
getSimpleFeedback().markProcessed(getDefinition(), ConstraintWithTarget.ALWAYS);
getSimpleFeedback().unsetOptimizationInfoForThrowNullMethod(this);
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 7f4b9f9..45e2065 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
@@ -5,6 +5,7 @@
package com.android.tools.r8.graph.analysis;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -13,18 +14,17 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
-import com.android.tools.r8.utils.AndroidApiLevel;
public class ApiModelAnalysis extends EnqueuerAnalysis {
private final AppView<?> appView;
- private final AndroidApiLevel minApiLevel;
private final AndroidApiLevelCompute apiCompute;
+ private final ComputedApiLevel minApiLevel;
public ApiModelAnalysis(AppView<?> appView, AndroidApiLevelCompute apiCompute) {
this.appView = appView;
- this.minApiLevel = appView.options().getMinApiLevel();
this.apiCompute = apiCompute;
+ this.minApiLevel = appView.computedMinApiLevel();
}
@Override
@@ -73,7 +73,7 @@
@Override
public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {
// We may not trace into failed resolution targets.
- method.setApiLevelForCode(AndroidApiLevel.UNKNOWN);
+ method.setApiLevelForCode(ComputedApiLevel.unknown());
}
private void computeAndSetApiLevelForDefinition(DexClassAndMember<?, ?> member) {
@@ -81,6 +81,8 @@
.getDefinition()
.setApiLevelForDefinition(
apiCompute.computeApiLevelForDefinition(
- member.getReference(), appView.dexItemFactory()));
+ member.getReference(),
+ appView.dexItemFactory(),
+ apiCompute.getPlatformApiLevelOrUnknown(appView)));
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
new file mode 100644
index 0000000..eee1f34
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
@@ -0,0 +1,24 @@
+// 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.graph.analysis;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface EnqueuerFieldAccessAnalysis {
+
+ void traceInstanceFieldRead(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+
+ void traceInstanceFieldWrite(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+
+ void traceStaticFieldRead(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+
+ void traceStaticFieldWrite(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
new file mode 100644
index 0000000..1243e98
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
@@ -0,0 +1,122 @@
+// 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.graph.analysis;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.KeepInfo.Joiner;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+
+/**
+ * In Dalvik it is a verification error to read and use a field of type Missing[].
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * Consumer<?>[] consumer = this.field;
+ * acceptConsumer(consumer); // acceptConsumer(Consumer[])
+ * </pre>
+ *
+ * <p>To avoid that the compiler moves such code into other contexts (e.g., as a result of inlining
+ * or class merging), and thereby causes new classes to no longer verify on Dalvik, we soft-pin
+ * methods that reads such fields.
+ */
+public class GetArrayOfMissingTypeVerifyErrorWorkaround implements EnqueuerFieldAccessAnalysis {
+
+ private final DexItemFactory dexItemFactory;
+ private final Enqueuer enqueuer;
+ private final Set<DexType> knownToBePresentOnDalvik;
+
+ public GetArrayOfMissingTypeVerifyErrorWorkaround(
+ AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
+ this.dexItemFactory = appView.dexItemFactory();
+ this.enqueuer = enqueuer;
+ this.knownToBePresentOnDalvik =
+ ImmutableSet.<DexType>builder()
+ .add(dexItemFactory.boxedBooleanType)
+ .add(dexItemFactory.boxedByteType)
+ .add(dexItemFactory.boxedCharType)
+ .add(dexItemFactory.boxedDoubleType)
+ .add(dexItemFactory.boxedFloatType)
+ .add(dexItemFactory.boxedIntType)
+ .add(dexItemFactory.boxedLongType)
+ .add(dexItemFactory.boxedShortType)
+ .add(dexItemFactory.classType)
+ .add(dexItemFactory.objectType)
+ .add(dexItemFactory.enumType)
+ .add(dexItemFactory.stringType)
+ .build();
+ }
+
+ public static void register(
+ AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
+ if (!isNoop(appView)) {
+ enqueuer.registerFieldAccessAnalysis(
+ new GetArrayOfMissingTypeVerifyErrorWorkaround(appView, enqueuer));
+ }
+ }
+
+ private static boolean isNoop(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ InternalOptions options = appView.options();
+ return options.isGeneratingDex()
+ && options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ }
+
+ @Override
+ public void traceInstanceFieldRead(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+ if (isUnsafeToUseFieldOnDalvik(field, context)) {
+ enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
+ }
+ }
+
+ @Override
+ public void traceStaticFieldRead(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+ if (isUnsafeToUseFieldOnDalvik(field, context)) {
+ enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
+ }
+ }
+
+ private boolean isUnsafeToUseFieldOnDalvik(DexField field, ProgramMethod context) {
+ DexType fieldType = field.getType();
+ if (!fieldType.isArrayType()) {
+ return false;
+ }
+ DexType baseType = fieldType.toBaseType(dexItemFactory);
+ if (!baseType.isClassType()) {
+ return false;
+ }
+ if (knownToBePresentOnDalvik.contains(baseType)) {
+ return false;
+ }
+ // TODO(b/206891715): Use the API database to determine if the given type is introduced in API
+ // level L or later.
+ DexClass baseClass = enqueuer.definitionFor(baseType, context);
+ return baseClass != null && baseClass.isLibraryClass();
+ }
+
+ @Override
+ public void traceInstanceFieldWrite(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void traceStaticFieldWrite(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+ // Intentionally empty.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index b1055ef..c76f43f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.horizontalclassmerging;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import static com.google.common.base.Predicates.not;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -29,7 +29,6 @@
import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -125,7 +124,7 @@
newMethodReference.withName("$r8$clinit$synthetic", dexItemFactory);
lensBuilder.recordNewMethodSignature(syntheticMethodReference, newMethodReference, true);
- AndroidApiLevel apiReferenceLevel = classInitializerMerger.getApiReferenceLevel(appView);
+ ComputedApiLevel apiReferenceLevel = classInitializerMerger.getApiReferenceLevel(appView);
DexEncodedMethod definition =
DexEncodedMethod.syntheticBuilder()
.setMethod(newMethodReference)
@@ -215,7 +214,7 @@
DexEncodedField.syntheticBuilder()
.setField(group.getClassIdField())
.setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
- .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevel(appView.computedMinApiLevel())
.build();
// For the $r8$classId synthesized fields, we try to over-approximate the set of values it may
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index fbbe362..5e76e2d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.CheckSyntheticClasses;
import com.android.tools.r8.horizontalclassmerging.policies.FinalizeMergeGroup;
import com.android.tools.r8.horizontalclassmerging.policies.LimitClassGroups;
+import com.android.tools.r8.horizontalclassmerging.policies.LimitInterfaceGroups;
import com.android.tools.r8.horizontalclassmerging.policies.MinimizeInstanceFieldCasts;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotationClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoCheckDiscard;
@@ -229,6 +230,7 @@
builder.add(
new NoDefaultInterfaceMethodMerging(appView, mode),
new NoDefaultInterfaceMethodCollisions(appView, mode),
+ new LimitInterfaceGroups(appView),
new OnlyDirectlyConnectedOrUnrelatedInterfaces(appView, mode));
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
index 085276f..b1219c4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.horizontalclassmerging.code;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import static java.lang.Integer.max;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.cf.code.CfInstruction;
@@ -36,7 +36,6 @@
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.CfVersionUtils;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
@@ -101,11 +100,11 @@
return null;
}
- public AndroidApiLevel getApiReferenceLevel(AppView<?> appView) {
+ public ComputedApiLevel getApiReferenceLevel(AppView<?> appView) {
assert !classInitializers.isEmpty();
return ListUtils.fold(
classInitializers,
- minApiLevelIfEnabledOrUnknown(appView),
+ appView.computedMinApiLevel(),
(accApiLevel, method) -> accApiLevel.max(method.getDefinition().getApiLevel()));
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
index 210f21d..2752544 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
@@ -18,7 +18,7 @@
private final int maxGroupSize;
public LimitClassGroups(AppView<? extends AppInfoWithClassHierarchy> appView) {
- maxGroupSize = appView.options().horizontalClassMergerOptions().getMaxGroupSize();
+ maxGroupSize = appView.options().horizontalClassMergerOptions().getMaxClassGroupSize();
assert maxGroupSize >= 2;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitInterfaceGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitInterfaceGroups.java
new file mode 100644
index 0000000..adee1f4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitInterfaceGroups.java
@@ -0,0 +1,63 @@
+// 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class LimitInterfaceGroups extends MultiClassPolicy {
+
+ private final int maxGroupSize;
+
+ public LimitInterfaceGroups(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ maxGroupSize = appView.options().horizontalClassMergerOptions().getMaxInterfaceGroupSize();
+ assert maxGroupSize >= 0;
+ }
+
+ @Override
+ public Collection<MergeGroup> apply(MergeGroup group) {
+ if (group.isClassGroup()) {
+ return Collections.singletonList(group);
+ }
+ // Mapping from new merge groups to their size.
+ Map<MergeGroup, Integer> newGroups = new LinkedHashMap<>();
+ for (DexProgramClass clazz : group) {
+ processClass(clazz, newGroups);
+ }
+ return removeTrivialGroups(newGroups.keySet());
+ }
+
+ private void processClass(DexProgramClass clazz, Map<MergeGroup, Integer> newGroups) {
+ int increment = clazz.getMethodCollection().size();
+
+ // Find an existing group.
+ for (Entry<MergeGroup, Integer> entry : newGroups.entrySet()) {
+ MergeGroup candidateGroup = entry.getKey();
+ int candidateGroupSize = entry.getValue();
+ int newCandidateGroupSize = candidateGroupSize + increment;
+ if (newCandidateGroupSize <= maxGroupSize) {
+ candidateGroup.add(clazz);
+ entry.setValue(newCandidateGroupSize);
+ return;
+ }
+ }
+
+ // Failed to find an existing group.
+ newGroups.put(new MergeGroup(clazz), increment);
+ }
+
+ @Override
+ public String getName() {
+ return "LimitInterfaceGroups";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
index 3b7e1e0..1f953f4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -4,21 +4,21 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-import com.android.tools.r8.utils.AndroidApiLevel;
-public class NoDifferentApiReferenceLevel extends MultiClassSameReferencePolicy<AndroidApiLevel> {
+public class NoDifferentApiReferenceLevel extends MultiClassSameReferencePolicy<ComputedApiLevel> {
- private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
+ private final AndroidApiLevelCompute apiLevelCompute;
private final AppView<?> appView;
// TODO(b/188388130): Remove when stabilized.
private final boolean enableApiCallerIdentification;
public NoDifferentApiReferenceLevel(AppView<?> appView) {
- apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
+ apiLevelCompute = AndroidApiLevelCompute.create(appView);
this.appView = appView;
enableApiCallerIdentification =
appView.options().apiModelingOptions().enableApiCallerIdentification;
@@ -35,8 +35,8 @@
}
@Override
- public AndroidApiLevel getMergeKey(DexProgramClass clazz) {
+ public ComputedApiLevel getMergeKey(DexProgramClass clazz) {
assert enableApiCallerIdentification;
- return clazz.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
+ return clazz.getApiReferenceLevel(appView, apiLevelCompute);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
index 6704f91..1515932 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
@@ -119,7 +119,7 @@
* </pre>
*/
public boolean hasHazzerBitField(ProtoMessageInfo protoMessageInfo) {
- return protoMessageInfo.isProto2() && type.isSingular();
+ return type.hasAuxData(protoMessageInfo.isProto2());
}
public ProgramField getHazzerBitField(
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 2b72d1e..37ce381 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
@@ -205,7 +205,6 @@
.map(prefix -> "L" + DescriptorUtils.getPackageBinaryNameFromJavaType(prefix))
.map(options.itemFactory::createString)
.collect(Collectors.toList());
- AndroidApiLevelCompute apiLevelCompute = AndroidApiLevelCompute.create(appView);
if (options.isDesugaredLibraryCompilation()) {
// Specific L8 Settings, performs all desugaring including L8 specific desugaring.
//
@@ -223,8 +222,7 @@
// - nest based access desugaring,
// - invoke-special desugaring.
assert options.desugarState.isOn();
- this.instructionDesugaring =
- CfInstructionDesugaringCollection.create(appView, apiLevelCompute);
+ this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
this.covariantReturnTypeAnnotationTransformer = null;
this.dynamicTypeOptimization = null;
this.classInliner = null;
@@ -249,7 +247,7 @@
this.instructionDesugaring =
appView.enableWholeProgramOptimizations()
? CfInstructionDesugaringCollection.empty()
- : CfInstructionDesugaringCollection.create(appView, apiLevelCompute);
+ : CfInstructionDesugaringCollection.create(appView);
this.covariantReturnTypeAnnotationTransformer =
options.processCovariantReturnTypeAnnotations
? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
@@ -288,7 +286,8 @@
this.typeChecker = new TypeChecker(appViewWithLiveness, VerifyTypesHelper.create(appView));
this.serviceLoaderRewriter =
options.enableServiceLoaderRewriting
- ? new ServiceLoaderRewriter(appViewWithLiveness, apiLevelCompute)
+ ? new ServiceLoaderRewriter(
+ appViewWithLiveness, AndroidApiLevelCompute.create(appView))
: null;
this.enumValueOptimizer =
options.enableEnumValueOptimization ? new EnumValueOptimizer(appViewWithLiveness) : null;
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 c516da4..ed1260e 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
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
@@ -1470,8 +1468,7 @@
appView,
builder ->
builder
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .disableAndroidApiLevelCheck()
.setProto(getProto(appView.dexItemFactory()))
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(methodSig -> generateTemplateMethod(appView.options(), methodSig)));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
index 3f60ffe..8b8b2e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -31,6 +31,11 @@
}
@Override
+ public void acceptEnumConversionProgramClass(DexProgramClass clazz) {
+ synthesizedClasses.add(clazz);
+ }
+
+ @Override
public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
synthesizedClasses.add(clazz);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 8328e9b..b34a515 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
@@ -28,19 +27,16 @@
*/
public abstract class CfInstructionDesugaringCollection {
- public static CfInstructionDesugaringCollection create(
- AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+ public static CfInstructionDesugaringCollection create(AppView<?> appView) {
if (appView.options().desugarState.isOn()) {
- return new NonEmptyCfInstructionDesugaringCollection(appView, apiLevelCompute);
+ return new NonEmptyCfInstructionDesugaringCollection(appView);
}
// TODO(b/145775365): invoke-special desugaring is mandatory, since we currently can't map
// invoke-special instructions that require desugaring into IR.
if (appView.options().isGeneratingClassFiles()) {
- return NonEmptyCfInstructionDesugaringCollection.createForCfToCfNonDesugar(
- appView, apiLevelCompute);
+ return NonEmptyCfInstructionDesugaringCollection.createForCfToCfNonDesugar(appView);
}
- return NonEmptyCfInstructionDesugaringCollection.createForCfToDexNonDesugar(
- appView, apiLevelCompute);
+ return NonEmptyCfInstructionDesugaringCollection.createForCfToDexNonDesugar(appView);
}
public static CfInstructionDesugaringCollection empty() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 28334b1..42212fd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -181,6 +181,11 @@
}
@Override
+ public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
+
+ @Override
public void acceptAPIConversion(ProgramMethod method) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@@ -336,6 +341,11 @@
}
@Override
+ public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
+
+ @Override
public void acceptAPIConversion(ProgramMethod method) {
// Intentionally empty. The method will be hit by tracing if required.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 31b8c3c..496cef9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -108,6 +108,11 @@
public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
// Intentionally empty.
}
+
+ @Override
+ public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
}
public static class R8PostProcessingDesugaringEventConsumer
@@ -169,5 +174,10 @@
public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
additions.addLiveClasspathClass(clazz);
}
+
+ @Override
+ public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index cd17565..a014283 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -645,7 +645,7 @@
.setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
.setApiLevelForCode(encodedMethod.getApiLevelForCode())
.build();
- newMethod.copyMetadata(encodedMethod);
+ newMethod.copyMetadata(appView, encodedMethod);
forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
encodedMethod.getReference(), callTarget);
@@ -736,7 +736,7 @@
.setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
.setApiLevelForCode(encodedMethod.getApiLevelForCode())
.build();
- newMethod.copyMetadata(encodedMethod);
+ newMethod.copyMetadata(appView, encodedMethod);
forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
encodedMethod.getReference(), callTarget);
return newMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 66d3a56..bc5c0fa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.Unreachable;
@@ -52,12 +51,9 @@
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
- private final AndroidApiLevelCompute apiLevelCompute;
- NonEmptyCfInstructionDesugaringCollection(
- AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+ NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
this.appView = appView;
- this.apiLevelCompute = apiLevelCompute;
AlwaysThrowingInstructionDesugaring alwaysThrowingInstructionDesugaring =
appView.enableWholeProgramOptimizations()
? new AlwaysThrowingInstructionDesugaring(appView.withClassHierarchy())
@@ -131,24 +127,22 @@
}
}
- static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(
- AppView<?> appView, AndroidApiLevelCompute computeApiLevel) {
+ static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(AppView<?> appView) {
assert appView.options().desugarState.isOff();
assert appView.options().isGeneratingClassFiles();
NonEmptyCfInstructionDesugaringCollection desugaringCollection =
- new NonEmptyCfInstructionDesugaringCollection(appView, computeApiLevel);
+ new NonEmptyCfInstructionDesugaringCollection(appView);
// TODO(b/145775365): special constructor for cf-to-cf compilations with desugaring disabled.
// This should be removed once we can represent invoke-special instructions in the IR.
desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
return desugaringCollection;
}
- static NonEmptyCfInstructionDesugaringCollection createForCfToDexNonDesugar(
- AppView<?> appView, AndroidApiLevelCompute computeApiLevel) {
+ static NonEmptyCfInstructionDesugaringCollection createForCfToDexNonDesugar(AppView<?> appView) {
assert appView.options().desugarState.isOff();
assert appView.options().isGeneratingDex();
NonEmptyCfInstructionDesugaringCollection desugaringCollection =
- new NonEmptyCfInstructionDesugaringCollection(appView, computeApiLevel);
+ new NonEmptyCfInstructionDesugaringCollection(appView);
desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
desugaringCollection.desugarings.add(new InvokeToPrivateRewriter());
return desugaringCollection;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index 3f9e078..fb848df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.CACHE_CONSTANT;
import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.THROW_ICCE;
import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.THROW_NSME;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import com.android.tools.r8.cf.code.CfCheckCast;
@@ -56,7 +55,6 @@
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import com.google.common.collect.ImmutableList;
@@ -194,12 +192,12 @@
DexEncodedField.syntheticBuilder()
.setField(this.initializedValueField)
.setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
- .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+ .disableAndroidApiLevelCheck()
.build(),
DexEncodedField.syntheticBuilder()
.setField(this.constantValueField)
.setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
- .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+ .disableAndroidApiLevelCheck()
.build()));
}
@@ -210,8 +208,7 @@
.setMethod(getConstMethod)
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(generateGetterCode(builder))
- .setApiLevelForDefinition(AndroidApiLevel.S)
- .setApiLevelForCode(AndroidApiLevel.S)
+ .disableAndroidApiLevelCheck()
.build()));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryEnumConversionSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryEnumConversionSynthesizer.java
new file mode 100644
index 0000000..c97d2ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryEnumConversionSynthesizer.java
@@ -0,0 +1,197 @@
+// 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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.vivifiedTypeFor;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer;
+import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.EnumArrayConversionCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.EnumConversionCfCodeProvider;
+import com.android.tools.r8.synthesis.SyntheticClasspathClassBuilder;
+import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
+import com.android.tools.r8.synthesis.SyntheticMethodBuilder.SyntheticCodeGenerator;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
+import com.google.common.collect.Iterables;
+
+public class DesugaredLibraryEnumConversionSynthesizer {
+
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
+
+ public DesugaredLibraryEnumConversionSynthesizer(AppView<?> appView) {
+ this.appView = appView;
+ this.factory = appView.dexItemFactory();
+ }
+
+ private void buildEnumConvert(
+ SyntheticMethodBuilder builder,
+ DexType src,
+ DexType dest,
+ SyntheticCodeGenerator codeGenerator) {
+ builder
+ .setName(factory.convertMethodName)
+ .setProto(factory.createProto(dest, src))
+ .setAccessFlags(
+ MethodAccessFlags.fromCfAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, false))
+ // Will be traced by the enqueuer.
+ .disableAndroidApiLevelCheck()
+ .setCode(codeGenerator);
+ }
+
+ private void buildEnumMethodsWithCode(
+ SyntheticProgramClassBuilder builder,
+ Iterable<DexEncodedField> enumFields,
+ DexType enumType,
+ DexType convertType) {
+ DexType enumArray = factory.createArrayType(1, enumType);
+ DexType convertArray = factory.createArrayType(1, convertType);
+ builder
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(
+ methodBuilder,
+ enumType,
+ convertType,
+ codeSynthesizor ->
+ new EnumConversionCfCodeProvider(
+ appView,
+ codeSynthesizor.getHolderType(),
+ enumFields,
+ enumType,
+ convertType)
+ .generateCfCode()))
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(
+ methodBuilder,
+ convertType,
+ enumType,
+ codeSynthesizor ->
+ new EnumConversionCfCodeProvider(
+ appView,
+ codeSynthesizor.getHolderType(),
+ enumFields,
+ convertType,
+ enumType)
+ .generateCfCode()))
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(
+ methodBuilder,
+ enumArray,
+ convertArray,
+ codeSynthesizor ->
+ new EnumArrayConversionCfCodeProvider(
+ appView, codeSynthesizor.getHolderType(), enumType, convertType)
+ .generateCfCode()))
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(
+ methodBuilder,
+ convertArray,
+ enumArray,
+ codeSynthesizor ->
+ new EnumArrayConversionCfCodeProvider(
+ appView, codeSynthesizor.getHolderType(), convertType, enumType)
+ .generateCfCode()));
+ }
+
+ private void buildEnumMethodsWithoutCode(
+ SyntheticClasspathClassBuilder builder, DexType enumType, DexType convertType) {
+ DexType enumArray = factory.createArrayType(1, enumType);
+ DexType convertArray = factory.createArrayType(1, convertType);
+ builder
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(methodBuilder, enumType, convertType, ignored -> null))
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(methodBuilder, convertType, enumType, ignored -> null))
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(methodBuilder, enumArray, convertArray, ignored -> null))
+ .addMethod(
+ methodBuilder ->
+ buildEnumConvert(methodBuilder, convertArray, enumArray, ignored -> null));
+ }
+
+ DexMethod ensureEnumConversionMethod(
+ DexClass clazz,
+ DexType srcType,
+ DexType destType,
+ DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
+ DexClass enumConversion = ensureEnumConversionClass(clazz, eventConsumer);
+ DexMethod method =
+ factory.createMethod(
+ enumConversion.type, factory.createProto(destType, srcType), factory.convertMethodName);
+ assert enumConversion.lookupDirectMethod(method) != null;
+ return method;
+ }
+
+ DexMethod getExistingProgramEnumConversionMethod(
+ DexClass clazz, DexType srcType, DexType destType) {
+ DexProgramClass enumConversion =
+ appView
+ .getSyntheticItems()
+ .getExistingFixedClass(SyntheticKind.ENUM_CONVERSION, clazz, appView);
+ DexMethod method =
+ factory.createMethod(
+ enumConversion.type, factory.createProto(destType, srcType), factory.convertMethodName);
+ assert enumConversion.lookupProgramMethod(method) != null;
+ return method;
+ }
+
+ DexProgramClass ensureProgramEnumConversionClass(
+ DexClass context, CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+ assert eventConsumer != null;
+ assert context.isProgramClass();
+ DexType type = context.type;
+ DexType vivifiedType = vivifiedTypeFor(context.type, appView);
+ assert appView.options().isDesugaredLibraryCompilation();
+ DexProgramClass programContext = context.asProgramClass();
+ Iterable<DexEncodedField> enumFields =
+ Iterables.filter(programContext.staticFields(), DexEncodedField::isEnum);
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClass(
+ SyntheticKind.ENUM_CONVERSION,
+ programContext,
+ appView,
+ builder -> buildEnumMethodsWithCode(builder, enumFields, type, vivifiedType),
+ eventConsumer::acceptWrapperProgramClass);
+ }
+
+ private DexClass ensureEnumConversionClass(
+ DexClass context, DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
+ assert eventConsumer != null;
+ if (context.isProgramClass()) {
+ return appView
+ .getSyntheticItems()
+ .getExistingFixedClass(SyntheticKind.ENUM_CONVERSION, context, appView);
+ }
+ DexType type = context.type;
+ DexType vivifiedType = vivifiedTypeFor(context.type, appView);
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClass(
+ SyntheticKind.ENUM_CONVERSION,
+ context.asClasspathOrLibraryClass(),
+ appView,
+ builder -> buildEnumMethodsWithoutCode(builder, type, vivifiedType),
+ eventConsumer::acceptWrapperClasspathClass);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
index 6aed582..14240a6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
@@ -4,8 +4,7 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary;
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -101,10 +100,12 @@
private final DexItemFactory factory;
private final ConcurrentHashMap<DexType, List<DexEncodedMethod>> allImplementedMethodsCache =
new ConcurrentHashMap<>();
+ private final DesugaredLibraryEnumConversionSynthesizer enumConverter;
public DesugaredLibraryWrapperSynthesizer(AppView<?> appView) {
this.appView = appView;
this.factory = appView.dexItemFactory();
+ this.enumConverter = new DesugaredLibraryEnumConversionSynthesizer(appView);
}
public boolean isSyntheticWrapper(DexType type) {
@@ -117,6 +118,9 @@
}
public boolean shouldConvert(DexType type, DexMethod method, ProgramMethod context) {
+ if (type.isArrayType()) {
+ return shouldConvert(type.toBaseType(appView.dexItemFactory()), method, context);
+ }
if (!appView.rewritePrefix.hasRewrittenType(type, appView)) {
return false;
}
@@ -136,8 +140,11 @@
if (customConversion != null) {
return customConversion;
}
- assert canGenerateWrapper(type) : type;
DexClass clazz = getValidClassToWrap(type);
+ if (clazz.isEnum()) {
+ return enumConverter.ensureEnumConversionMethod(clazz, srcType, destType, eventConsumer);
+ }
+ assert canGenerateWrapper(type) : type;
WrapperConversions wrapperConversions = ensureWrappers(clazz, eventConsumer);
DexMethod conversion =
type == srcType
@@ -154,8 +161,11 @@
if (customConversion != null) {
return customConversion;
}
- WrapperConversions wrapperConversions =
- getExistingProgramWrapperConversions(getValidClassToWrap(type));
+ DexClass clazz = getValidClassToWrap(type);
+ if (clazz.isEnum()) {
+ return enumConverter.getExistingProgramEnumConversionMethod(clazz, srcType, destType);
+ }
+ WrapperConversions wrapperConversions = getExistingProgramWrapperConversions(clazz);
DexMethod conversion =
type == srcType
? wrapperConversions.getConversion()
@@ -209,11 +219,14 @@
}
private DexClass getValidClassToWrap(DexType type) {
+ if (type.isArrayType()) {
+ return getValidClassToWrap(type.toBaseType(factory));
+ }
DexClass dexClass = appView.definitionFor(type);
// The dexClass should be a library class, so it cannot be null.
assert dexClass != null;
assert dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation();
- assert !dexClass.accessFlags.isFinal();
+ assert !dexClass.accessFlags.isFinal() || dexClass.isEnum();
return dexClass;
}
@@ -581,7 +594,8 @@
.setAccessFlags(newFlags)
.setCode(code)
.setApiLevelForDefinition(template.getApiLevelForDefinition())
- .setApiLevelForCode(code == null ? NOT_SET : template.getApiLevelForCode())
+ .setApiLevelForCode(
+ code == null ? ComputedApiLevel.notSet() : template.getApiLevelForCode())
.build();
}
@@ -660,8 +674,12 @@
// In broken set-ups we can end up having a json files containing wrappers of non desugared
// classes. Such wrappers are not required since the class won't be rewritten.
if (validClassToWrap.isProgramClass()) {
- validClassesToWrap.add(validClassToWrap.asProgramClass());
- ensureProgramWrappersWithoutVirtualMethods(validClassToWrap, eventConsumer);
+ if (validClassToWrap.isEnum()) {
+ enumConverter.ensureProgramEnumConversionClass(validClassToWrap, eventConsumer);
+ } else {
+ validClassesToWrap.add(validClassToWrap.asProgramClass());
+ ensureProgramWrappersWithoutVirtualMethods(validClassToWrap, eventConsumer);
+ }
}
}
for (DexProgramClass validClassToWrap : validClassesToWrap) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
index 212ad66..511e0d2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
@@ -13,11 +13,15 @@
interface DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer {
void acceptWrapperProgramClass(DexProgramClass clazz);
+
+ void acceptEnumConversionProgramClass(DexProgramClass clazz);
}
interface DesugaredLibraryClasspathWrapperSynthesizeEventConsumer {
void acceptWrapperClasspathClass(DexClasspathClass clazz);
+
+ void acceptEnumConversionClasspathClass(DexClasspathClass clazz);
}
interface DesugaredLibraryAPIConverterEventConsumer
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index e1462ea..36519f8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar.itf;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfStackInstruction;
@@ -802,8 +800,7 @@
.setAccessFlags(accessFlags)
.setCode(
createExceptionThrowingCfCode(newMethod, accessFlags, errorType, dexItemFactory))
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .disableAndroidApiLevelCheck()
.build();
addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 674493f..510331b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -1088,7 +1088,7 @@
context.getDefinition().accessFlags.demoteFromSynthetic();
}
- context.getDefinition().copyMetadata(singleTargetMethod);
+ context.getDefinition().copyMetadata(appView, singleTargetMethod);
if (inlineeMayHaveInvokeMethod && options.applyInliningToInlinee) {
if (inlineeStack.size() + 1 > options.applyInliningToInlineeMaxDepth
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
index d73ac60..7ef7216 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.dex.Constants;
@@ -68,7 +69,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.ListUtils;
@@ -1527,8 +1527,8 @@
.setProto(outline.buildProto())
// It is OK to set the api level to UNKNOWN since we are not interested in
// inlining the outlines anyway.
- .setApiLevelForDefinition(AndroidApiLevel.UNKNOWN)
- .setApiLevelForCode(AndroidApiLevel.UNKNOWN)
+ .setApiLevelForDefinition(ComputedApiLevel.unknown())
+ .setApiLevelForCode(ComputedApiLevel.unknown())
.setCode(m -> new OutlineCode(outline));
if (appView.options().isGeneratingClassFiles()) {
builder.setClassFileVersion(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 117ecfc..afb7003 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -63,6 +63,7 @@
private final ProgramMethod method;
private final IRCode code;
private final int maxCapacityPerBlock;
+ private final boolean release;
// Values that may require type propagation.
private final Set<Value> affectedValues = Sets.newIdentityHashSet();
@@ -81,6 +82,7 @@
this.method = code.context();
this.code = code;
this.maxCapacityPerBlock = Math.max(MIN_CAPACITY_PER_BLOCK, MAX_CAPACITY / code.blocks.size());
+ this.release = !appView.options().debug;
}
public static boolean shouldRun(AppView<?> appView, IRCode code) {
@@ -209,19 +211,7 @@
}
if (instruction.isInstanceGet()) {
- InstanceGet instanceGet = instruction.asInstanceGet();
- if (instanceGet.outValue().hasLocalInfo()) {
- continue;
- }
- Value object = instanceGet.object().getAliasedValue();
- FieldAndObject fieldAndObject = new FieldAndObject(reference, object);
- FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
- if (replacement != null) {
- replacement.eliminateRedundantRead(it, instanceGet);
- } else {
- activeState.putNonFinalInstanceField(
- fieldAndObject, new ExistingValue(instanceGet.value()));
- }
+ handleInstanceGet(it, instruction.asInstanceGet(), field);
} else if (instruction.isInstancePut()) {
handleInstancePut(instruction.asInstancePut(), field);
} else if (instruction.isStaticGet()) {
@@ -249,6 +239,11 @@
// field values to change. In that case, it must be handled above.
assert !instruction.instructionMayTriggerMethodInvocation(appView, method);
+ // Clear the field writes.
+ if (instruction.instructionInstanceCanThrow(appView, method)) {
+ activeState.clearMostRecentFieldWrites();
+ }
+
// If this assertion fails for a new instruction we need to determine if that
// instruction has side-effects that can change the value of fields. If so, it must be
// handled above. If not, it can be safely added to the assert.
@@ -387,18 +382,62 @@
private void handleInitClass(InstructionListIterator instructionIterator, InitClass initClass) {
assert !initClass.outValue().hasAnyUsers();
+
killNonFinalActiveFields(initClass);
+
+ // If the instruction can throw, we can't use any previous field stores for store-after-store
+ // elimination.
+ if (initClass.instructionInstanceCanThrow(appView, method)) {
+ activeState.clearMostRecentFieldWrites();
+ }
+
DexType clazz = initClass.getClassValue();
if (!activeState.markClassAsInitialized(clazz)) {
instructionIterator.removeOrReplaceByDebugLocalRead();
}
}
+ private void handleInstanceGet(
+ InstructionListIterator it, InstanceGet instanceGet, DexClassAndField field) {
+ if (instanceGet.outValue().hasLocalInfo()) {
+ clearMostRecentInstanceFieldWrite(instanceGet, field);
+ return;
+ }
+
+ Value object = instanceGet.object().getAliasedValue();
+ FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
+ FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
+ if (replacement != null) {
+ replacement.eliminateRedundantRead(it, instanceGet);
+ return;
+ }
+
+ activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(instanceGet.value()));
+ clearMostRecentInstanceFieldWrite(instanceGet, field);
+ }
+
+ private void clearMostRecentInstanceFieldWrite(InstanceGet instanceGet, DexClassAndField field) {
+ // If the instruction can throw, we need to clear all most-recent-writes, since subsequent field
+ // writes (if any) are not guaranteed to be executed.
+ if (instanceGet.instructionInstanceCanThrow(appView, method)) {
+ activeState.clearMostRecentFieldWrites();
+ } else {
+ activeState.clearMostRecentInstanceFieldWrite(field.getReference());
+ }
+ }
+
private void handleInstancePut(InstancePut instancePut, DexClassAndField field) {
- // An instance-put instruction can potentially write the given field on all objects
- // because of aliases.
+ // 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.
+
+ // If the instruction can throw, we can't use any previous field stores for store-after-store
+ // elimination.
+ if (instancePut.instructionInstanceCanThrow(appView, method)) {
+ activeState.clearMostRecentFieldWrites();
+ }
+
+ // Update the value of the field to allow redundant load elimination.
Value object = instancePut.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
ExistingValue value = new ExistingValue(instancePut.value());
@@ -410,13 +449,16 @@
} else {
activeState.putNonFinalInstanceField(fieldAndObject, value);
- InstancePut mostRecentInstanceFieldWrite =
- activeState.putMostRecentInstanceFieldWrite(fieldAndObject, instancePut);
- if (mostRecentInstanceFieldWrite != null) {
- instructionsToRemove
- .computeIfAbsent(
- mostRecentInstanceFieldWrite.getBlock(), ignoreKey(Sets::newIdentityHashSet))
- .add(mostRecentInstanceFieldWrite);
+ // Record that this field is now most recently written by the current instruction.
+ if (release) {
+ InstancePut mostRecentInstanceFieldWrite =
+ activeState.putMostRecentInstanceFieldWrite(fieldAndObject, instancePut);
+ if (mostRecentInstanceFieldWrite != null) {
+ instructionsToRemove
+ .computeIfAbsent(
+ mostRecentInstanceFieldWrite.getBlock(), ignoreKey(Sets::newIdentityHashSet))
+ .add(mostRecentInstanceFieldWrite);
+ }
}
}
}
@@ -424,6 +466,8 @@
private void handleStaticGet(
InstructionListIterator instructionIterator, StaticGet staticGet, DexClassAndField field) {
if (staticGet.outValue().hasLocalInfo()) {
+ killNonFinalActiveFields(staticGet);
+ clearMostRecentStaticFieldWrite(staticGet, field);
return;
}
@@ -435,6 +479,7 @@
// A field get on a different class can cause <clinit> to run and change static field values.
killNonFinalActiveFields(staticGet);
+ clearMostRecentStaticFieldWrite(staticGet, field);
FieldValue value = new ExistingValue(staticGet.value());
if (isFinal(field)) {
@@ -452,9 +497,26 @@
}
}
+ private void clearMostRecentStaticFieldWrite(StaticGet staticGet, DexClassAndField field) {
+ // If the instruction can throw, we need to clear all most-recent-writes, since subsequent field
+ // writes (if any) are not guaranteed to be executed.
+ if (staticGet.instructionInstanceCanThrow(appView, method)) {
+ activeState.clearMostRecentFieldWrites();
+ } else {
+ activeState.clearMostRecentStaticFieldWrite(field.getReference());
+ }
+ }
+
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);
+
+ // If the instruction can throw, we can't use any previous field stores for store-after-store
+ // elimination.
+ if (staticPut.instructionInstanceCanThrow(appView, method)) {
+ activeState.clearMostRecentFieldWrites();
+ }
+
ExistingValue value = new ExistingValue(staticPut.value());
if (isFinal(field)) {
assert appView.checkForTesting(
@@ -463,13 +525,15 @@
} 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);
+ if (release) {
+ StaticPut mostRecentStaticFieldWrite =
+ activeState.putMostRecentStaticFieldWrite(field.getReference(), staticPut);
+ if (mostRecentStaticFieldWrite != null) {
+ instructionsToRemove
+ .computeIfAbsent(
+ mostRecentStaticFieldWrite.getBlock(), ignoreKey(Sets::newIdentityHashSet))
+ .add(mostRecentStaticFieldWrite);
+ }
}
}
}
@@ -704,10 +768,22 @@
clearMostRecentStaticFieldWrites();
}
+ public void clearMostRecentInstanceFieldWrite(DexField field) {
+ if (mostRecentInstanceFieldWrites != null) {
+ mostRecentInstanceFieldWrites.keySet().removeIf(key -> key.field == field);
+ }
+ }
+
public void clearMostRecentInstanceFieldWrites() {
mostRecentInstanceFieldWrites = null;
}
+ public void clearMostRecentStaticFieldWrite(DexField field) {
+ if (mostRecentStaticFieldWrites != null) {
+ mostRecentStaticFieldWrites.remove(field);
+ }
+ }
+
public void clearMostRecentStaticFieldWrites() {
mostRecentStaticFieldWrites = null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index a735ad9..260a5a3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.optimize;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
@@ -180,7 +178,8 @@
DexEncodedMethod addedMethod =
createSynthesizedMethod(service, classes, methodProcessingContext);
if (appView.options().isGeneratingClassFiles()) {
- addedMethod.upgradeClassFileVersion(code.method().getClassFileVersion());
+ addedMethod.upgradeClassFileVersion(
+ code.context().getDefinition().getClassFileVersion());
}
return addedMethod;
});
@@ -206,10 +205,11 @@
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setProto(proto)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
.setApiLevelForCode(
apiLevelCompute.computeApiLevelForDefinition(
- ListUtils.map(classes, clazz -> clazz.type)))
+ ListUtils.map(classes, clazz -> clazz.type),
+ apiLevelCompute.getPlatformApiLevelOrUnknown(appView)))
.setCode(
m ->
ServiceLoaderSourceCode.generate(
@@ -248,7 +248,7 @@
private final IRCode code;
private final InvokeStatic serviceLoaderLoad;
- private InstructionListIterator iterator;
+ private final InstructionListIterator iterator;
Rewriter(IRCode code, InstructionListIterator iterator, InvokeStatic serviceLoaderLoad) {
this.iterator = iterator;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index aaeb960..5639925 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.optimize;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
@@ -45,8 +43,8 @@
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setClassFileVersion(CfVersion.V1_8)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(method -> getToStringIfNotNullCodeTemplate(method, options))
.setProto(proto));
return new UtilityMethodForCodeOptimizations(syntheticMethod);
@@ -74,8 +72,8 @@
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setClassFileVersion(CfVersion.V1_8)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(
method -> getThrowClassCastExceptionIfNotNullCodeTemplate(method, options))
.setProto(proto));
@@ -104,8 +102,8 @@
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setClassFileVersion(CfVersion.V1_8)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(method -> getThrowIllegalAccessErrorCodeTemplate(method, options))
.setProto(proto));
return new UtilityMethodForCodeOptimizations(syntheticMethod);
@@ -132,8 +130,8 @@
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setClassFileVersion(CfVersion.V1_8)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(
method -> getThrowIncompatibleClassChangeErrorCodeTemplate(method, options))
.setProto(proto));
@@ -162,8 +160,8 @@
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setClassFileVersion(CfVersion.V1_8)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(method -> getThrowNoSuchMethodErrorCodeTemplate(method, options))
.setProto(proto));
return new UtilityMethodForCodeOptimizations(syntheticMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index e392248..9997cc0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.optimize.enums;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.graph.AppView;
@@ -186,8 +185,8 @@
checkNotNullMethod
.getDefinition()
.getClassFileVersionOrElse(null))
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(method -> new CheckNotZeroCode(checkNotNullMethod))
.setOptimizationInfo(
checkNotNullMethod
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
index ddf8c42..ed8958d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.enums;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
import com.android.tools.r8.cf.CfVersion;
@@ -131,8 +130,8 @@
methodBuilder ->
methodBuilder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(codeGenerator)
.setClassFileVersion(CfVersion.V1_6));
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index 90d49ac..182b415 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.optimize.enums;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfArrayStore;
import com.android.tools.r8.cf.code.CfConstNumber;
@@ -149,8 +147,8 @@
methodBuilder ->
methodBuilder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.setCode(codeGenerator)
.setClassFileVersion(CfVersion.V1_6));
}
@@ -232,7 +230,7 @@
dexItemFactory.createField(
sharedUtilityClassType, dexItemFactory.intArrayType, "$VALUES"))
.setAccessFlags(FieldAccessFlags.createPublicStaticFinalSynthetic())
- .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevel(appView.computedMinApiLevel())
.build();
fieldAccessInfoCollectionModifierBuilder
.recordFieldReadInUnknownContext(valuesField.getReference())
@@ -249,8 +247,8 @@
.setAccessFlags(MethodAccessFlags.createForClassInitializer())
.setCode(createClassInitializerCode(sharedUtilityClassType, valuesField))
.setClassFileVersion(CfVersion.V1_6)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.build();
}
@@ -295,8 +293,8 @@
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(createValuesMethodCode(sharedUtilityClassType, valuesField))
.setClassFileVersion(CfVersion.V1_6)
- .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
- .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(appView.computedMinApiLevel())
.build();
this.valuesMethod = valuesMethod;
return valuesMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
index 27fa69a..5cb256c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.optimize.library;
import static com.android.tools.r8.graph.DexLibraryClass.asLibraryClassOrNull;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
@@ -39,7 +38,7 @@
.setAccessFlags(
FieldAccessFlags.fromCfAccessFlags(
Constants.ACC_PRIVATE | Constants.ACC_FINAL))
- .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevel(appView.computedMinApiLevel())
// Will be traced by the enqueuer.
.disableAndroidApiLevelCheck()
.build());
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 6777d57..1a1cf0a 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -4,12 +4,20 @@
package com.android.tools.r8.ir.synthetic;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
+import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode;
+import com.android.tools.r8.cf.code.CfArrayLength;
+import com.android.tools.r8.cf.code.CfArrayLoad;
+import com.android.tools.r8.cf.code.CfArrayStore;
import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.cf.code.CfIf;
+import com.android.tools.r8.cf.code.CfIfCmp;
import com.android.tools.r8.cf.code.CfInstanceFieldRead;
import com.android.tools.r8.cf.code.CfInstanceFieldWrite;
import com.android.tools.r8.cf.code.CfInstanceOf;
@@ -18,12 +26,16 @@
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNewArray;
import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.cf.code.CfReturnVoid;
import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStaticFieldRead;
+import com.android.tools.r8.cf.code.CfStore;
import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -31,6 +43,8 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizer;
@@ -39,6 +53,7 @@
import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -386,6 +401,159 @@
}
}
+ public static class EnumArrayConversionCfCodeProvider extends SyntheticCfCodeProvider {
+
+ private final DexType enumType;
+ private final DexType convertedType;
+
+ public EnumArrayConversionCfCodeProvider(
+ AppView<?> appView, DexType holder, DexType enumType, DexType convertedType) {
+ super(appView, holder);
+ this.enumType = enumType;
+ this.convertedType = convertedType;
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ DexItemFactory factory = appView.dexItemFactory();
+ List<CfInstruction> instructions = new ArrayList<>();
+ DexType enumTypeArray = factory.createArrayType(1, enumType);
+ DexType convertedTypeArray = factory.createArrayType(1, convertedType);
+
+ // if (arg == null) { return null; }
+ instructions.add(new CfLoad(ValueType.fromDexType(enumTypeArray), 0));
+ instructions.add(new CfConstNull());
+ CfLabel nonNull = new CfLabel();
+ instructions.add(new CfIfCmp(If.Type.NE, ValueType.OBJECT, nonNull));
+ instructions.add(new CfConstNull());
+ instructions.add(new CfReturn(ValueType.fromDexType(convertedTypeArray)));
+ instructions.add(nonNull);
+ instructions.add(
+ new CfFrame(
+ ImmutableInt2ReferenceSortedMap.<FrameType>builder()
+ .put(0, FrameType.initialized(enumTypeArray))
+ .build(),
+ ImmutableDeque.of()));
+
+ ImmutableInt2ReferenceSortedMap<FrameType> locals =
+ ImmutableInt2ReferenceSortedMap.<FrameType>builder()
+ .put(0, FrameType.initialized(enumTypeArray))
+ .put(1, FrameType.initialized(factory.intType))
+ .put(2, FrameType.initialized(convertedTypeArray))
+ .put(3, FrameType.initialized(factory.intType))
+ .build();
+
+ // int t1 = arg.length;
+ instructions.add(new CfLoad(ValueType.fromDexType(enumTypeArray), 0));
+ instructions.add(new CfArrayLength());
+ instructions.add(new CfStore(ValueType.INT, 1));
+ // ConvertedType[] t2 = new ConvertedType[t1];
+ instructions.add(new CfLoad(ValueType.INT, 1));
+ instructions.add(new CfNewArray(convertedTypeArray));
+ instructions.add(new CfStore(ValueType.fromDexType(convertedTypeArray), 2));
+ // int t3 = 0;
+ instructions.add(new CfConstNumber(0, ValueType.INT));
+ instructions.add(new CfStore(ValueType.INT, 3));
+ // while (t3 < t1) {
+ CfLabel returnLabel = new CfLabel();
+ CfLabel loopLabel = new CfLabel();
+ instructions.add(loopLabel);
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
+ instructions.add(new CfLoad(ValueType.INT, 3));
+ instructions.add(new CfLoad(ValueType.INT, 1));
+ instructions.add(new CfIfCmp(If.Type.GE, ValueType.INT, returnLabel));
+ // t2[t3] = convert(arg[t3]);
+ instructions.add(new CfLoad(ValueType.fromDexType(convertedTypeArray), 2));
+ instructions.add(new CfLoad(ValueType.INT, 3));
+ instructions.add(new CfLoad(ValueType.fromDexType(enumTypeArray), 0));
+ instructions.add(new CfLoad(ValueType.INT, 3));
+ instructions.add(new CfArrayLoad(MemberType.OBJECT));
+ instructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC,
+ factory.createMethod(
+ getHolder(),
+ factory.createProto(convertedType, enumType),
+ factory.convertMethodName),
+ false));
+ instructions.add(new CfArrayStore(MemberType.OBJECT));
+ // t3 = t3 + 1; }
+ instructions.add(new CfLoad(ValueType.INT, 3));
+ instructions.add(new CfConstNumber(1, ValueType.INT));
+ instructions.add(new CfArithmeticBinop(Opcode.Add, NumericType.INT));
+ instructions.add(new CfStore(ValueType.INT, 3));
+ instructions.add(new CfGoto(loopLabel));
+ // return t2;
+ instructions.add(returnLabel);
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
+ instructions.add(new CfLoad(ValueType.fromDexType(convertedTypeArray), 2));
+ instructions.add(new CfReturn(ValueType.fromDexType(convertedTypeArray)));
+ return standardCfCodeFromInstructions(instructions);
+ }
+ }
+
+ public static class EnumConversionCfCodeProvider extends SyntheticCfCodeProvider {
+
+ private final Iterable<DexEncodedField> enumFields;
+ private final DexType enumType;
+ private final DexType convertedType;
+
+ public EnumConversionCfCodeProvider(
+ AppView<?> appView,
+ DexType holder,
+ Iterable<DexEncodedField> enumFields,
+ DexType enumType,
+ DexType convertedType) {
+ super(appView, holder);
+ this.enumFields = enumFields;
+ this.enumType = enumType;
+ this.convertedType = convertedType;
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ DexItemFactory factory = appView.dexItemFactory();
+ List<CfInstruction> instructions = new ArrayList<>();
+
+ ImmutableInt2ReferenceSortedMap<FrameType> locals =
+ ImmutableInt2ReferenceSortedMap.<FrameType>builder()
+ .put(0, FrameType.initialized(enumType))
+ .build();
+
+ // if (arg == null) { return null; }
+ instructions.add(new CfLoad(ValueType.fromDexType(enumType), 0));
+ instructions.add(new CfConstNull());
+ CfLabel nonNull = new CfLabel();
+ instructions.add(new CfIfCmp(If.Type.NE, ValueType.OBJECT, nonNull));
+ instructions.add(new CfConstNull());
+ instructions.add(new CfReturn(ValueType.fromDexType(convertedType)));
+ instructions.add(nonNull);
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
+
+ // if (arg == enumType.enumField1) { return convertedType.enumField1; }
+ Iterator<DexEncodedField> iterator = enumFields.iterator();
+ while (iterator.hasNext()) {
+ DexEncodedField enumField = iterator.next();
+ CfLabel notEqual = new CfLabel();
+ if (iterator.hasNext()) {
+ instructions.add(new CfLoad(ValueType.fromDexType(enumType), 0));
+ instructions.add(
+ new CfStaticFieldRead(factory.createField(enumType, enumType, enumField.getName())));
+ instructions.add(new CfIfCmp(If.Type.NE, ValueType.OBJECT, notEqual));
+ }
+ instructions.add(
+ new CfStaticFieldRead(
+ factory.createField(convertedType, convertedType, enumField.getName())));
+ instructions.add(new CfReturn(ValueType.fromDexType(convertedType)));
+ if (iterator.hasNext()) {
+ instructions.add(notEqual);
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
+ }
+ }
+ return standardCfCodeFromInstructions(instructions);
+ }
+ }
+
public static class APIConverterConstructorCfCodeProvider extends SyntheticCfCodeProvider {
DexField wrapperField;
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index 129acae..19bbe92 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.shaking;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
@@ -87,7 +86,7 @@
.dexItemFactory()
.createField(clazz.type, clinitField.type, clinitField.name))
.setAccessFlags(accessFlags)
- .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+ .setApiLevel(appView.computedMinApiLevel())
.build();
clazz.appendStaticField(encodedClinitField);
}
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 941bcff..227cbf7 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.code.CfOrDexInstruction;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -21,26 +22,25 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.ListIterator;
public class DefaultEnqueuerUseRegistry extends UseRegistry<ProgramMethod> {
protected final AppView<? extends AppInfoWithClassHierarchy> appView;
protected final Enqueuer enqueuer;
- private final AndroidApiLevelCompute computeApiLevel;
- private AndroidApiLevel maxApiReferenceLevel;
+ private final AndroidApiLevelCompute apiLevelCompute;
+ private ComputedApiLevel maxApiReferenceLevel;
public DefaultEnqueuerUseRegistry(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod context,
Enqueuer enqueuer,
- AndroidApiLevelCompute computeApiLevel) {
+ AndroidApiLevelCompute apiLevelCompute) {
super(appView, context);
this.appView = appView;
this.enqueuer = enqueuer;
- this.computeApiLevel = computeApiLevel;
- this.maxApiReferenceLevel = appView.options().getMinApiLevel();
+ this.apiLevelCompute = apiLevelCompute;
+ maxApiReferenceLevel = appView.computedMinApiLevel();
}
public DexProgramClass getContextHolder() {
@@ -223,14 +223,18 @@
if (reference.isDexMember()) {
maxApiReferenceLevel =
maxApiReferenceLevel.max(
- computeApiLevel.computeApiLevelForDefinition(
- reference.asDexMember(), appView.dexItemFactory()));
+ apiLevelCompute.computeApiLevelForDefinition(
+ reference.asDexMember(),
+ appView.dexItemFactory(),
+ apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
}
maxApiReferenceLevel =
- maxApiReferenceLevel.max(computeApiLevel.computeApiLevelForLibraryReference(reference));
+ maxApiReferenceLevel.max(
+ apiLevelCompute.computeApiLevelForLibraryReference(
+ reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
}
- public AndroidApiLevel getMaxApiReferenceLevel() {
+ public ComputedApiLevel getMaxApiReferenceLevel() {
return maxApiReferenceLevel;
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index fdd1b9f..9f30d38 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -82,8 +82,10 @@
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerFieldAccessAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInvokeAnalysis;
+import com.android.tools.r8.graph.analysis.GetArrayOfMissingTypeVerifyErrorWorkaround;
import com.android.tools.r8.graph.analysis.InvokeVirtualToInterfaceVerifyErrorWorkaround;
import com.android.tools.r8.ir.analysis.proto.ProtoEnqueuerUseRegistry;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoEnqueuerExtension;
@@ -235,11 +237,12 @@
private final boolean forceProguardCompatibility;
private final Mode mode;
- private Set<EnqueuerAnalysis> analyses = new LinkedHashSet<>();
- private Set<EnqueuerInvokeAnalysis> invokeAnalyses = new LinkedHashSet<>();
- private Set<EnqueuerInstanceOfAnalysis> instanceOfAnalyses = new LinkedHashSet<>();
- private Set<EnqueuerExceptionGuardAnalysis> exceptionGuardAnalyses = new LinkedHashSet<>();
- private Set<EnqueuerCheckCastAnalysis> checkCastAnalyses = new LinkedHashSet<>();
+ private final Set<EnqueuerAnalysis> analyses = new LinkedHashSet<>();
+ private final Set<EnqueuerFieldAccessAnalysis> fieldAccessAnalyses = new LinkedHashSet<>();
+ private final Set<EnqueuerInvokeAnalysis> invokeAnalyses = new LinkedHashSet<>();
+ private final Set<EnqueuerInstanceOfAnalysis> instanceOfAnalyses = new LinkedHashSet<>();
+ private final Set<EnqueuerExceptionGuardAnalysis> exceptionGuardAnalyses = new LinkedHashSet<>();
+ private final Set<EnqueuerCheckCastAnalysis> checkCastAnalyses = new LinkedHashSet<>();
// Don't hold a direct pointer to app info (use appView).
private AppInfoWithClassHierarchy appInfo;
@@ -480,6 +483,7 @@
: null;
if (mode.isInitialOrFinalTreeShaking()) {
+ GetArrayOfMissingTypeVerifyErrorWorkaround.register(appView, this);
InvokeVirtualToInterfaceVerifyErrorWorkaround.register(appView, this);
if (options.protoShrinking().enableGeneratedMessageLiteShrinking) {
registerAnalysis(new ProtoEnqueuerExtension(appView));
@@ -498,7 +502,7 @@
liveFields = new LiveFieldsSet(graphReporter::registerField);
apiLevelCompute = AndroidApiLevelCompute.create(appView);
if (mode.isInitialTreeShaking()) {
- desugaring = CfInstructionDesugaringCollection.create(appView, apiLevelCompute);
+ desugaring = CfInstructionDesugaringCollection.create(appView);
interfaceProcessor = new InterfaceProcessor(appView);
} else {
desugaring = CfInstructionDesugaringCollection.empty();
@@ -538,6 +542,11 @@
return this;
}
+ public Enqueuer registerFieldAccessAnalysis(EnqueuerFieldAccessAnalysis analysis) {
+ fieldAccessAnalyses.add(analysis);
+ return this;
+ }
+
public Enqueuer registerInvokeAnalysis(EnqueuerInvokeAnalysis analysis) {
invokeAnalyses.add(analysis);
return this;
@@ -1449,6 +1458,10 @@
}
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
+ fieldAccessAnalyses.forEach(
+ analysis ->
+ analysis.traceInstanceFieldRead(fieldReference, resolutionResult, currentMethod));
+
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must trace the types from the field reference even if it does not exist.
traceFieldReference(fieldReference, resolutionResult, currentMethod);
@@ -1503,6 +1516,10 @@
}
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
+ fieldAccessAnalyses.forEach(
+ analysis ->
+ analysis.traceInstanceFieldWrite(fieldReference, resolutionResult, currentMethod));
+
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must trace the types from the field reference even if it does not exist.
traceFieldReference(fieldReference, resolutionResult, currentMethod);
@@ -1555,6 +1572,9 @@
}
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
+ fieldAccessAnalyses.forEach(
+ analysis -> analysis.traceStaticFieldRead(fieldReference, resolutionResult, currentMethod));
+
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must trace the types from the field reference even if it does not exist.
traceFieldReference(fieldReference, resolutionResult, currentMethod);
@@ -1623,6 +1643,10 @@
}
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
+ fieldAccessAnalyses.forEach(
+ analysis ->
+ analysis.traceStaticFieldWrite(fieldReference, resolutionResult, currentMethod));
+
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must trace the types from the field reference even if it does not exist.
traceFieldReference(fieldReference, resolutionResult, currentMethod);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 4f116de..9396f50 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -8,7 +8,8 @@
import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
-import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -66,7 +67,6 @@
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.FieldSignatureEquivalence;
@@ -222,7 +222,7 @@
private final MethodPoolCollection methodPoolCollection;
private final Timing timing;
private Collection<DexMethod> invokes;
- private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
+ private final AndroidApiLevelCompute apiLevelCompute;
private final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
@@ -261,7 +261,7 @@
this.executorService = executorService;
this.methodPoolCollection = new MethodPoolCollection(appView, subtypingInfo);
this.lensBuilder = new VerticalClassMergerGraphLens.Builder(appView.dexItemFactory());
- this.apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
+ this.apiLevelCompute = AndroidApiLevelCompute.create(appView);
this.timing = timing;
Iterable<DexProgramClass> classes = application.classesWithDeterministicOrder();
@@ -531,11 +531,9 @@
// Only merge if api reference level of source class is equal to target class. The check is
// somewhat expensive.
if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
- AndroidApiLevel sourceApiLevel =
- sourceClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
- AndroidApiLevel targetApiLevel =
- targetClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
- if (sourceApiLevel != targetApiLevel) {
+ ComputedApiLevel sourceApiLevel = sourceClass.getApiReferenceLevel(appView, apiLevelCompute);
+ ComputedApiLevel targetApiLevel = targetClass.getApiReferenceLevel(appView, apiLevelCompute);
+ if (!sourceApiLevel.equals(targetApiLevel)) {
if (Log.ENABLED) {
AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 56a9d20..0af4bf0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -3,8 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -20,7 +19,6 @@
import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.AndroidApiLevel;
public class SyntheticMethodBuilder {
@@ -39,8 +37,8 @@
private MethodTypeSignature genericSignature = MethodTypeSignature.noSignature();
private DexAnnotationSet annotations = DexAnnotationSet.empty();
private ParameterAnnotationsList parameterAnnotationsList = ParameterAnnotationsList.empty();
- private AndroidApiLevel apiLevelForDefinition = NOT_SET;
- private AndroidApiLevel apiLevelForCode = NOT_SET;
+ private ComputedApiLevel apiLevelForDefinition = ComputedApiLevel.notSet();
+ private ComputedApiLevel apiLevelForCode = ComputedApiLevel.notSet();
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
private boolean checkAndroidApiLevels = true;
@@ -109,12 +107,12 @@
return this;
}
- public SyntheticMethodBuilder setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
+ public SyntheticMethodBuilder setApiLevelForDefinition(ComputedApiLevel apiLevelForDefinition) {
this.apiLevelForDefinition = apiLevelForDefinition;
return this;
}
- public SyntheticMethodBuilder setApiLevelForCode(AndroidApiLevel apiLevelForCode) {
+ public SyntheticMethodBuilder setApiLevelForCode(ComputedApiLevel apiLevelForCode) {
this.apiLevelForCode = apiLevelForCode;
return this;
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 0a05c0f..41020e2 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -34,6 +34,7 @@
RETARGET_INTERFACE("RetargetInterface", 21, false, true),
WRAPPER("$Wrapper", 22, false, true),
VIVIFIED_WRAPPER("$VivifiedWrapper", 23, false, true),
+ ENUM_CONVERSION("$EnumConversion", 31, false, true),
LAMBDA("Lambda", 4, false),
INIT_TYPE_ARGUMENT("-IA", 5, false, true),
HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", 6, false, true),
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 6b82664..38934f2 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.utils.structural.Ordered;
import java.util.Arrays;
import java.util.List;
@@ -43,9 +42,7 @@
R(30),
S(31),
Sv2(32),
- ANDROID_PLATFORM(10000),
- UNKNOWN(10001),
- NOT_SET(10002);
+ ANDROID_PLATFORM(10000);
// 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.
@@ -78,13 +75,6 @@
return DexVersion.getDexVersion(this);
}
- public static AndroidApiLevel minApiLevelIfEnabledOrUnknown(AppView<?> appView) {
- InternalOptions options = appView.options();
- return options.apiModelingOptions().enableApiCallerIdentification
- ? options.getMinApiLevel()
- : UNKNOWN;
- }
-
public static List<AndroidApiLevel> getAndroidApiLevelsSorted() {
return Arrays.asList(AndroidApiLevel.values());
}
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 c4a7396..73cf09f 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.ProgramMethod;
@@ -15,10 +16,6 @@
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;
}
@@ -32,12 +29,13 @@
LibraryMethod method,
AndroidApiLevelCompute androidApiLevelCompute,
InternalOptions options) {
- AndroidApiLevel apiLevel =
- androidApiLevelCompute.computeApiLevelForLibraryReference(method.getReference());
- if (apiLevel == AndroidApiLevel.UNKNOWN) {
+ ComputedApiLevel apiLevel =
+ androidApiLevelCompute.computeApiLevelForLibraryReference(
+ method.getReference(), ComputedApiLevel.unknown());
+ if (apiLevel.isUnknownApiLevel()) {
return false;
}
assert options.apiModelingOptions().enableApiCallerIdentification;
- return apiLevel.isLessThanOrEqualTo(options.getMinApiLevel());
+ return apiLevel.asKnownApiLevel().getApiLevel().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 a807bba..624e70e 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -77,7 +77,6 @@
case L_MR1:
case M:
return DexVersion.V35;
- 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 727f9c9..1cbbc21 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -3,6 +3,8 @@
// 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.ANDROID_PLATFORM;
+
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DataResourceConsumer;
@@ -17,6 +19,7 @@
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
import com.android.tools.r8.androidapi.AndroidApiForHashingClass;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Backend;
@@ -424,7 +427,7 @@
}
public boolean isAndroidPlatform() {
- return minApiLevel == AndroidApiLevel.ANDROID_PLATFORM;
+ return minApiLevel == ANDROID_PLATFORM;
}
public boolean isDesugaredLibraryCompilation() {
@@ -564,7 +567,6 @@
public void setMinApiLevel(AndroidApiLevel minApiLevel) {
assert minApiLevel != null;
- assert minApiLevel.isLessThan(AndroidApiLevel.UNKNOWN);
this.minApiLevel = minApiLevel;
}
@@ -1381,8 +1383,6 @@
private boolean ignoreRuntimeTypeChecksForTesting = false;
private boolean restrictToSynthetics = false;
- public int maxGroupSize = 30;
-
public void disable() {
enable = false;
}
@@ -1399,8 +1399,12 @@
this.enable = enable;
}
- public int getMaxGroupSize() {
- return maxGroupSize;
+ public int getMaxClassGroupSize() {
+ return 30;
+ }
+
+ public int getMaxInterfaceGroupSize() {
+ return 100;
}
public boolean isConstructorMergingEnabled() {
@@ -1468,7 +1472,7 @@
public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
- public BiConsumer<MethodReference, AndroidApiLevel> tracedMethodApiLevelCallback = null;
+ public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
public boolean enableApiCallerIdentification = true;
public boolean checkAllApiReferencesAreSet = true;
@@ -1496,7 +1500,7 @@
@Override
public AndroidApiLevel getApiLevel() {
- return classApiMapping.getOrDefault(classReference, AndroidApiLevel.UNKNOWN);
+ return classApiMapping.getOrDefault(classReference, ANDROID_PLATFORM);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 53886e2..2d0cc12 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -867,8 +867,6 @@
case J_MR2:
case K_WATCH:
case ANDROID_PLATFORM:
- case UNKNOWN:
- case NOT_SET:
return false;
default:
return true;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index d7db635..300eed5 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -103,8 +103,7 @@
for (int i = 0; i < integers.size(); i++) {
indices[i] = integers.get(i);
AndroidApiLevel androidApiLevel = apiLevelMap.get(integers.get(i));
- apiLevel[i] =
- (byte) (androidApiLevel == AndroidApiLevel.NOT_SET ? -1 : androidApiLevel.getLevel());
+ apiLevel[i] = (byte) (androidApiLevel == null ? -1 : androidApiLevel.getLevel());
}
try (FileOutputStream fileOutputStream = new FileOutputStream(pathToIndices.toFile());
@@ -160,7 +159,7 @@
return ((reference, apiLevel) -> {
AndroidApiLevel existingMethod = apiLevelMap.put(reference.hashCode(), apiLevel);
if (existingMethod != null) {
- apiLevelMap.put(reference.hashCode(), AndroidApiLevel.NOT_SET);
+ apiLevelMap.put(reference.hashCode(), null);
Pair<DexReference, AndroidApiLevel> existingPair = reverseMap.get(reference.hashCode());
addAmbiguousEntry(existingPair.getSecond(), existingPair.getFirst(), ambiguousMap);
addAmbiguousEntry(apiLevel, reference, ambiguousMap);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 88d53c1..9bbc751 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -126,7 +126,7 @@
AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
DexItemFactory factory = new DexItemFactory();
AndroidApiLevelHashingDatabaseImpl androidApiLevelDatabase =
- new AndroidApiLevelHashingDatabaseImpl(factory, ImmutableList.of());
+ new AndroidApiLevelHashingDatabaseImpl(ImmutableList.of());
parsedApiClasses.forEach(
parsedApiClass -> {
DexType type = factory.createType(parsedApiClass.getClassReference().getDescriptor());
@@ -137,9 +137,13 @@
methodReferences.forEach(
methodReference -> {
DexMethod method = factory.createMethod(methodReference);
- androidApiLevelDatabase
- .getMethodApiLevel(method)
- .isLessThanOrEqualTo(methodApiLevel);
+ AndroidApiLevel androidApiLevel;
+ if (factory.objectMembers.isObjectMember(method)) {
+ androidApiLevel = AndroidApiLevel.B;
+ } else {
+ androidApiLevel = androidApiLevelDatabase.getMethodApiLevel(method);
+ }
+ androidApiLevel.isLessThanOrEqualTo(methodApiLevel);
}));
parsedApiClass.visitFieldReferences(
(fieldApiLevel, fieldReferences) ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelObjectInitTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelObjectInitTest.java
new file mode 100644
index 0000000..e5118ab
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelObjectInitTest.java
@@ -0,0 +1,126 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+
+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.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Constructor;
+import org.junit.Assert;
+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 ApiModelObjectInitTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ boolean addToClassPath =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ Constructor<LibraryClass> declaredConstructor =
+ LibraryClass.class.getDeclaredConstructor(int.class);
+ MethodReference shouldBeL =
+ Reference.methodFromMethod(Main.class.getDeclaredMethod("shouldBeL"));
+ MethodReference shouldBeN =
+ Reference.methodFromMethod(Main.class.getDeclaredMethod("shouldBeN"));
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class)
+ // We replace java.lang.Object.toString with LibraryClass.toString
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (name.equals("toString") && owner.equals(binaryName(Object.class))) {
+ visitor.visitMethodInsn(
+ opcode, binaryName(LibraryClass.class), name, descriptor, isInterface);
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform())
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.L))
+ .apply(setMockApiLevelForMethod(declaredConstructor, AndroidApiLevel.L))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, AndroidApiLevel.N))
+ .apply(
+ ApiModelingTestHelper.addTracedApiReferenceLevelCallBack(
+ (methodReference, apiLevel) -> {
+ AndroidApiLevel currentLevel =
+ parameters.isCfRuntime() ? AndroidApiLevel.B : parameters.getApiLevel();
+ if (methodReference.equals(shouldBeL)) {
+ Assert.assertEquals(AndroidApiLevel.L.max(currentLevel), apiLevel);
+ }
+ if (methodReference.equals(shouldBeN)) {
+ Assert.assertEquals(AndroidApiLevel.N.max(currentLevel), apiLevel);
+ }
+ }))
+ .addKeepMainRule(Main.class)
+ .addAndroidBuildVersion()
+ .enableInliningAnnotations()
+ .compile()
+ .applyIf(addToClassPath, b -> b.addRunClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(!addToClassPath, "Old")
+ .assertSuccessWithOutputLinesIf(addToClassPath, "New");
+ }
+
+ public static class LibraryClass {
+
+ public LibraryClass() {
+ // Default constructor
+ }
+
+ public LibraryClass(int i) {
+ // Non default constructor.
+ }
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static void shouldBeL() {
+ LibraryClass libraryClass = new LibraryClass(1);
+ }
+
+ @NeverInline
+ public static void shouldBeN() {
+ if (new LibraryClass().toString().equals("FOO")) {
+ throw new RuntimeException("Unexpected toString value");
+ }
+ }
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 21) {
+ shouldBeL();
+ shouldBeN();
+ System.out.println("New");
+ } else {
+ System.out.println("Old");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
index c87f2cc..d4127e8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
@@ -63,15 +63,22 @@
}
public static class AccessibilityNodeInfo$AccessibilityAction {
+
+ private int i;
+
+ public AccessibilityNodeInfo$AccessibilityAction(int i, CharSequence sequence) {
+ this.i = i;
+ }
+
int describeContents() {
- return 42;
+ return i;
}
}
public static class Main {
public static void main(String[] args) {
- new AccessibilityNodeInfo$AccessibilityAction().describeContents();
+ new AccessibilityNodeInfo$AccessibilityAction(42, "foobar").describeContents();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 80e9aef..c8ee027 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
@@ -20,6 +21,7 @@
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.function.BiConsumer;
@@ -39,6 +41,20 @@
};
}
+ public static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+ ThrowableConsumer<T> setMockApiLevelForMethod(
+ Constructor<?> constructor, AndroidApiLevel apiLevel) {
+ return compilerBuilder -> {
+ compilerBuilder.addOptionsModification(
+ options -> {
+ options
+ .apiModelingOptions()
+ .methodApiMapping
+ .put(Reference.methodFromMethod(constructor), apiLevel);
+ });
+ };
+ }
+
static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
ThrowableConsumer<T> setMockApiLevelForDefaultInstanceInitializer(
Class<?> clazz, AndroidApiLevel apiLevel) {
@@ -103,7 +119,12 @@
return compilerBuilder -> {
compilerBuilder.addOptionsModification(
options -> {
- options.apiModelingOptions().tracedMethodApiLevelCallback = consumer;
+ options.apiModelingOptions().tracedMethodApiLevelCallback =
+ (methodReference, computedApiLevel) -> {
+ assertTrue(computedApiLevel.isKnownApiLevel());
+ consumer.accept(
+ methodReference, computedApiLevel.asKnownApiLevel().getApiLevel());
+ };
});
};
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 67cce40..645588c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -97,8 +97,16 @@
return parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport());
}
+ protected boolean requiresTimeDesugaring(TestParameters parameters) {
+ return parameters.getApiLevel().getLevel()
+ < (isJDK11DesugaredLibrary() ? AndroidApiLevel.S.getLevel() : AndroidApiLevel.O.getLevel());
+ }
+
protected boolean requiresAnyCoreLibDesugaring(TestParameters parameters) {
- return parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel();
+ return parameters.getApiLevel().getLevel()
+ <= (isJDK11DesugaredLibrary()
+ ? AndroidApiLevel.LATEST.getLevel()
+ : AndroidApiLevel.N.getLevel());
}
protected L8TestBuilder testForL8(AndroidApiLevel apiLevel) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
index 8e1ab72..1245c7e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.LocalDate;
@@ -55,7 +54,7 @@
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Main.class);
- if (shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+ if (shrinkDesugaredLibrary && requiresTimeDesugaring(parameters)) {
runResult.assertFailureWithErrorThatMatches(
containsString("java.lang.NoSuchMethodException"));
} else {
@@ -80,7 +79,7 @@
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Main.class);
- if (shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+ if (shrinkDesugaredLibrary && requiresTimeDesugaring(parameters)) {
runResult.assertFailureWithErrorThatMatches(
containsString("java.lang.NoSuchMethodException"));
} else {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
index 78d3317..139103d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.LocalDate;
@@ -56,7 +55,7 @@
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Main.class);
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
+ if (!requiresTimeDesugaring(parameters)) {
runResult.assertFailureWithErrorThatMatches(
containsString("java.lang.ClassNotFoundException: j$.time.LocalDate"));
} else {
@@ -81,7 +80,7 @@
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Main.class);
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
+ if (!requiresTimeDesugaring(parameters)) {
runResult.assertFailureWithErrorThatMatches(
containsString("java.lang.ClassNotFoundException: j$.time.LocalDate"));
} else {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
index 2411f70..45ec5c9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
@@ -36,15 +36,16 @@
@Parameters(name = "api: {0}")
public static List<Object[]> data() {
return buildParameters(
- range(AndroidApiLevel.K, AndroidApiLevel.ANDROID_PLATFORM),
+ range(AndroidApiLevel.K, AndroidApiLevel.LATEST),
getTestParameters().withNoneRuntime().build());
}
private static List<AndroidApiLevel> range(
- AndroidApiLevel fromIncluding, AndroidApiLevel toExcluding) {
+ AndroidApiLevel fromIncluding, AndroidApiLevel toIncluding) {
ArrayList<AndroidApiLevel> result = new ArrayList<>();
for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
- if (apiLevel.isGreaterThanOrEqualTo(fromIncluding) && apiLevel.isLessThan(toExcluding)) {
+ if (apiLevel.isGreaterThanOrEqualTo(fromIncluding)
+ && apiLevel.isLessThanOrEqualTo(toIncluding)) {
result.add(apiLevel);
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
index 253763b..88f3ca9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.BufferedWriter;
import java.io.File;
@@ -138,7 +137,7 @@
public void testJ$ExtensionDesugaring() throws Exception {
Assume.assumeFalse(parameters.isCfRuntime());
// Above O no desugaring is required.
- Assume.assumeTrue(parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel());
+ Assume.assumeTrue(requiresTimeDesugaring(parameters));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
try {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 0e9d632..6598d6b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -96,7 +96,7 @@
Set<String> expectedCatchGuards;
Set<String> expectedCheckCastType;
String expectedInstanceOfTypes;
- if (parameters.getApiLevel().getLevel() >= 26) {
+ if (!requiresTimeDesugaring(parameters)) {
expectedInvokeHolders =
SetUtils.newHashSet("java.time.Clock", "java.time.LocalDate", "java.time.ZoneId");
if (!isR8) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
index 05282d5..ac25355 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
@@ -124,8 +124,7 @@
allOf(
markerTool(Tool.D8),
markerIsDesugared(),
- markerHasDesugaredLibraryIdentifier(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.O)));
+ markerHasDesugaredLibraryIdentifier(requiresAnyCoreLibDesugaring(parameters)));
assertMarkersMatch(
ExtractMarker.extractMarkerFromDexFile(app), ImmutableList.of(libraryMatcher, d8Matcher));
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MonthTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MonthTest.java
index f5b2096..fbc2ad7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MonthTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MonthTest.java
@@ -41,8 +41,7 @@
return EXPECTED_JAVA_8_OUTPUT;
}
assert parameters.isDexRuntime();
- // Assumes java.time is desugared only if any library desugaring is required, i.e., on 26.
- if (requiresAnyCoreLibDesugaring(parameters)) {
+ if (requiresTimeDesugaring(parameters)) {
return EXPECTED_JAVA_8_OUTPUT;
}
return EXPECTED_JAVA_9_OUTPUT;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
index 10a563d..d392dd8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
import java.nio.file.AccessMode;
import java.nio.file.Path;
import java.util.List;
@@ -29,7 +30,8 @@
private final boolean shrinkDesugaredLibrary;
private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
- private static final String EXPECTED_RESULT = StringUtils.lines("WRITE", "WRITE");
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines("READ", "WRITE", "READ", "WRITE", "EXECUTE");
private static Path CUSTOM_LIB;
@@ -54,28 +56,38 @@
.writeToZip();
}
- private void configureDesugaredLibrary(InternalOptions options) {
- options.desugaredLibraryConfiguration =
+ private void configureDesugaredLibrary(InternalOptions options, boolean l8Compilation) {
+ DesugaredLibraryConfiguration.Builder builder =
DesugaredLibraryConfiguration.builder(
options.itemFactory, options.reporter, Origin.unknown())
+ .setDesugaredLibraryIdentifier("com.tools.android:desugar_jdk_libs:9.99.99")
.putRewritePrefix("java.nio.file.AccessMode", "j$.nio.file.AccessMode")
- .addWrapperConversion("java.nio.file.AccessMode")
- .build();
+ .addWrapperConversion("java.nio.file.AccessMode");
+ if (l8Compilation) {
+ builder.setLibraryCompilation();
+ }
+ options.desugaredLibraryConfiguration = builder.build();
}
@Test
public void testD8() throws Exception {
- Assume.assumeTrue(false);
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addLibraryFiles(getLibraryFile())
.setMinApi(parameters.getApiLevel())
.addProgramClasses(Executor.class)
.addLibraryClasses(CustomLibClass.class)
- .addOptionsModification(this::configureDesugaredLibrary)
+ .addOptionsModification(opt -> this.configureDesugaredLibrary(opt, false))
.compile()
.addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibrary,
+ (apiLevel, keepRules, shrink) ->
+ this.buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ opt -> this.configureDesugaredLibrary(opt, true)),
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
@@ -86,7 +98,7 @@
@Test
public void testR8() throws Exception {
- Assume.assumeTrue(false);
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.addLibraryFiles(getLibraryFile())
@@ -94,11 +106,16 @@
.addKeepMainRule(Executor.class)
.addProgramClasses(Executor.class)
.addLibraryClasses(CustomLibClass.class)
- .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .addOptionsModification(this::configureDesugaredLibrary)
+ .addOptionsModification(opt -> this.configureDesugaredLibrary(opt, false))
.compile()
.addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibrary,
+ (apiLevel, keepRules, shrink) ->
+ this.buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ opt -> this.configureDesugaredLibrary(opt, true)),
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
@@ -111,7 +128,16 @@
public static void main(String[] args) {
System.out.println(CustomLibClass.get(AccessMode.READ));
- System.out.println(CustomLibClass.get(new AccessMode[] {AccessMode.READ})[0]);
+ System.out.println(CustomLibClass.get(AccessMode.WRITE));
+ System.out.println(
+ CustomLibClass.get(
+ new AccessMode[] {AccessMode.READ, AccessMode.WRITE, AccessMode.EXECUTE})[0]);
+ System.out.println(
+ CustomLibClass.get(
+ new AccessMode[] {AccessMode.READ, AccessMode.WRITE, AccessMode.EXECUTE})[1]);
+ System.out.println(
+ CustomLibClass.get(
+ new AccessMode[] {AccessMode.READ, AccessMode.WRITE, AccessMode.EXECUTE})[2]);
}
}
@@ -121,11 +147,11 @@
static class CustomLibClass {
public static AccessMode get(AccessMode mode) {
- return AccessMode.WRITE;
+ return mode;
}
public static AccessMode[] get(AccessMode[] modes) {
- return new AccessMode[] {AccessMode.WRITE};
+ return modes;
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
index a045372..01f9b80 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.nio.file.Path;
@@ -60,14 +59,14 @@
inspector.allClasses().stream()
.filter(c -> c.getOriginalName().contains("Conversions"))
.collect(Collectors.toList());
- if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ if (requiresEmulatedInterfaceCoreLibDesugaring(parameters)) {
assertEquals(5, conversionsClasses.size());
assertTrue(inspector.clazz("j$.util.OptionalConversions").isPresent());
assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
assertTrue(inspector.clazz("j$.util.LongSummaryStatisticsConversions").isPresent());
assertTrue(inspector.clazz("j$.util.IntSummaryStatisticsConversions").isPresent());
assertTrue(inspector.clazz("j$.util.DoubleSummaryStatisticsConversions").isPresent());
- } else if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+ } else if (requiresTimeDesugaring(parameters)) {
assertEquals(1, conversionsClasses.size());
assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
} else {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java
index 6e9d7ca..0874b57 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java
@@ -48,7 +48,7 @@
}
Path desugaredLibJDK11Undesugared = Paths.get("build/libs/desugar_jdk_libs_11_undesugared.jar");
if (Files.exists(desugaredLibJDK11Undesugared)) {
- return desugaredLibJDK11Undesugared;
+ return desugaredLibJDK11Undesugared;
}
return generateUndesugaredJar(desugaredLibJDK11Undesugared);
}
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
index d95f9c8..e16f9e1 100644
--- 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
@@ -44,10 +44,6 @@
@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(getLibraryFile())
@@ -67,10 +63,6 @@
@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(getLibraryFile())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
index 1928523..9d4ad7e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
@@ -234,7 +234,7 @@
for (String issue : formattingProblem) {
D8TestRunResult result =
compileResult.run(parameters.getRuntime(), "TestNGMainRunner", verbosity, issue);
- if (requiresAnyCoreLibDesugaring(parameters)) {
+ if (requiresTimeDesugaring(parameters)) {
// Fails due to formatting differences in desugared library.
assertTrue(result.getStdOut().contains("for style NARROW"));
} else {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index 27e9882..b58b4e1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -118,14 +118,14 @@
false)
.run(parameters.getRuntime(), PKG + ".MainKt")
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
- if (requiresAnyCoreLibDesugaring(parameters)) {
+ if (requiresTimeDesugaring(parameters)) {
d8TestRunResult.inspect(this::inspectRewrittenMetadata);
}
}
@Test
public void testTimeR8() throws Exception {
- boolean desugarLibrary = parameters.isDexRuntime() && requiresAnyCoreLibDesugaring(parameters);
+ boolean desugarLibrary = parameters.isDexRuntime() && requiresTimeDesugaring(parameters);
final R8FullTestBuilder testBuilder =
testForR8(parameters.getBackend())
.addLibraryFiles(getLibraryFile())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/FieldReadBeforeOtherwiseRedundantStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/FieldReadBeforeOtherwiseRedundantStoreTest.java
new file mode 100644
index 0000000..6f8b296
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/FieldReadBeforeOtherwiseRedundantStoreTest.java
@@ -0,0 +1,70 @@
+// 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 com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+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 FieldReadBeforeOtherwiseRedundantStoreTest extends TestBase {
+
+ @Parameter(0)
+ public CompilationMode mode;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, mode: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ CompilationMode.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForD8()
+ .addInnerClasses(FieldReadBeforeOtherwiseRedundantStoreTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .setMode(mode)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess();
+ }
+
+ static class Main {
+
+ public static int field = 0;
+
+ public int testLoop() {
+ int iterations = 50;
+ for (int i = 0; i < iterations; i++) {
+ int a = field;
+ field = a + i;
+ int b = field;
+ field = b + 2 * i;
+ }
+ assertIntEquals(field, 3675);
+ return field;
+ }
+
+ public static void main(String[] args) {
+ Main obj = new Main();
+ obj.testLoop();
+ }
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
index cad2c50..af7854c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
@@ -1,3 +1,7 @@
+// 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;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java
new file mode 100644
index 0000000..4c3f07d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java
@@ -0,0 +1,61 @@
+// 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 com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+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 ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest extends TestBase {
+
+ @Parameter(0)
+ public CompilationMode mode;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, mode: {0}")
+ public static List<Object[]> parameters() {
+ return buildParameters(
+ CompilationMode.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForD8()
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("1");
+ }
+
+ static class Main {
+
+ int sum = 0;
+
+ public static void main(String[] args) {
+ Main main = new Main();
+ try {
+ main.test(new int[] {1});
+ throw new RuntimeException();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println(main.sum);
+ }
+ }
+
+ void test(int[] array) {
+ sum += array[0];
+ sum += array[1];
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java
new file mode 100644
index 0000000..60a0b34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java
@@ -0,0 +1,60 @@
+// 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 com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+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 ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest extends TestBase {
+
+ @Parameter(0)
+ public CompilationMode mode;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, mode: {0}")
+ public static List<Object[]> parameters() {
+ return buildParameters(
+ CompilationMode.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForD8()
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("1");
+ }
+
+ static class Main {
+
+ static int sum = 0;
+
+ public static void main(String[] args) {
+ try {
+ test(new int[] {1});
+ throw new RuntimeException();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println(sum);
+ }
+ }
+
+ static void test(int[] array) {
+ sum += array[0];
+ sum += array[1];
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
index c1da822..89f57c8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -73,7 +73,8 @@
.allMatch(
mergeGroup ->
mergeGroup.size()
- <= defaultHorizontalClassMergerOptions.getMaxGroupSize()));
+ <= defaultHorizontalClassMergerOptions
+ .getMaxClassGroupSize()));
})
.allowDiagnosticWarningMessages()
.compile()
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
index 86ccd24..51bd0cf 100644
--- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -189,7 +189,7 @@
new DataResourceConsumerForTesting(options.dataResourceConsumer);
options.dataResourceConsumer = dataResourceConsumer;
})
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), OtherTestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
diff --git a/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java b/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
new file mode 100644
index 0000000..aba5532
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/workaround/ArrayFieldGetWithMissingBaseTypeTest.java
@@ -0,0 +1,115 @@
+// 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.workaround;
+
+import com.android.tools.r8.NeverClassInline;
+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.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.function.Consumer;
+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 ArrayFieldGetWithMissingBaseTypeTest 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())
+ .applyIf(
+ parameters.isDexRuntime(),
+ testBuilder -> testBuilder.addLibraryFiles(ToolHelper.getMostRecentAndroidJar()))
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Utils.class)
+ .addKeepRules(
+ "-keep class " + Main.class.getTypeName() + " { void notUsedDuringLaunch(); }")
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L),
+ i ->
+ i.assertIsCompleteMergeGroup(
+ UsedDuringLaunch.class, NotUsedDuringLaunch.class))
+ .assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .addOptionsModification(o -> o.apiModelingOptions().disableApiCallerIdentification())
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new UsedDuringLaunch().usedDuringLaunch();
+ }
+
+ // @Keep
+ static void notUsedDuringLaunch() {
+ Consumer<?> emptyConsumer = Utils.getEmptyConsumer();
+ new UsedDuringLaunch().onlyUsedOnHighApiLevels(emptyConsumer);
+ NotUsedDuringLaunch.f = new Consumer<?>[] {emptyConsumer};
+ new NotUsedDuringLaunch().illegalUseOfConsumerArrayOnDalvik();
+ }
+ }
+
+ @NeverClassInline
+ static class UsedDuringLaunch {
+
+ @NeverInline
+ void usedDuringLaunch() {
+ System.out.println("Hello world!");
+ }
+
+ @NeverInline
+ void onlyUsedOnHighApiLevels(Consumer<?> c) {
+ System.out.println(c);
+ }
+ }
+
+ @NeverClassInline
+ static class NotUsedDuringLaunch {
+
+ static Consumer<?>[] f;
+
+ @NeverInline
+ void illegalUseOfConsumerArrayOnDalvik() {
+ Utils.accept(f);
+ }
+ }
+
+ // @Keep
+ static class Utils {
+
+ // @Keep
+ static void accept(Consumer<?>[] array) {
+ System.out.println(array.length);
+ }
+
+ // @Keep
+ public static Consumer<?> getEmptyConsumer() {
+ return ignore -> {};
+ }
+ }
+}