Version 2.1.47
Cherry-pick: Desugared library: fix static rewrites
CL: https://r8-review.googlesource.com/c/r8/+/52406
Cherry-pick: Allow R8 markers without a library desugaring identifier when merging
CL: https://r8-review.googlesource.com/c/r8/+/52411
Bug: 159441805
Bug: 158644894
Bug: 160282413
Change-Id: I3761a50fa949e61cbf99caf27098249971d25724
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index 0b1fead..c8d6295 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -49,6 +49,11 @@
return extractMarker(appBuilder.build());
}
+ public static Collection<Marker> extractMarkerFromJarFile(Path file)
+ throws IOException, ExecutionException {
+ return extractMarker(AndroidApp.builder().addProgramFile(file).build());
+ }
+
public static int extractDexSize(Path file) throws IOException, ResourceException {
AndroidApp.Builder appBuilder = AndroidApp.builder();
addDexResources(appBuilder, file);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 5a52f49..8024a8a 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "2.1.46";
+ public static final String LABEL = "2.1.47";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 1484f02..e15a4eb 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -88,6 +88,9 @@
new StringDiagnostic(
"Merging program compiled with multiple desugared libraries."));
}
+ if (identifier.equals(NO_LIBRARY_DESUGARING) && marker.tool == Tool.R8) {
+ continue;
+ }
desugaredLibraryIdentifiers.add(identifier);
}
}
@@ -131,6 +134,10 @@
return this;
}
+ public boolean hasMinApi() {
+ return jsonObject.has(MIN_API);
+ }
+
public Long getMinApi() {
return jsonObject.get(MIN_API).getAsLong();
}
@@ -141,6 +148,10 @@
return this;
}
+ public boolean hasDesugaredLibraryIdentifiers() {
+ return jsonObject.has(DESUGARED_LIBRARY_IDENTIFIERS);
+ }
+
public String[] getDesugaredLibraryIdentifiers() {
if (jsonObject.has(DESUGARED_LIBRARY_IDENTIFIERS)) {
JsonArray array = jsonObject.get(DESUGARED_LIBRARY_IDENTIFIERS).getAsJsonArray();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 53ff67e..316f299 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -50,10 +50,10 @@
private final AppView<?> appView;
private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
- // Map virtualRewrites hold a methodName->method mapping for virtual methods which are
- // rewritten while the holder is non final but no superclass implement the method. In this case
- // d8 needs to force resolution of given methods to see if the invoke needs to be rewritten.
- private final Map<DexString, List<DexMethod>> virtualRewrites = new IdentityHashMap<>();
+ // Map nonFinalRewrite hold a methodName -> method mapping for methods which are rewritten while
+ // the holder is non final. In this case d8 needs to force resolution of given methods to see if
+ // the invoke needs to be rewritten.
+ private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites = new IdentityHashMap<>();
// Non final virtual library methods requiring generation of emulated dispatch.
private final Set<DexMethod> emulatedDispatchMethods = Sets.newHashSet();
@@ -107,7 +107,7 @@
InvokeMethod invoke = instruction.asInvokeMethod();
DexMethod retarget = getRetargetLibraryMember(invoke.getInvokedMethod());
if (retarget == null) {
- if (!matchesVirtualRewrite(invoke.getInvokedMethod())) {
+ if (!matchesNonFinalHolderRewrite(invoke.getInvokedMethod())) {
continue;
}
// We need to force resolution, even on d8, to know if the invoke has to be rewritten.
@@ -128,7 +128,7 @@
// Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
// infinite loops. We do direct resolution. This is a very uncommon case.
- if (invoke.isInvokeSuper() && matchesVirtualRewrite(invoke.getInvokedMethod())) {
+ if (invoke.isInvokeSuper() && matchesNonFinalHolderRewrite(invoke.getInvokedMethod())) {
DexEncodedMethod dexEncodedMethod =
appView
.appInfoForDesugaring()
@@ -163,8 +163,8 @@
return retargetLibraryMember.get(method);
}
- private boolean matchesVirtualRewrite(DexMethod method) {
- List<DexMethod> dexMethods = virtualRewrites.get(method.name);
+ private boolean matchesNonFinalHolderRewrite(DexMethod method) {
+ List<DexMethod> dexMethods = nonFinalHolderRewrites.get(method.name);
if (dexMethods == null) {
return false;
}
@@ -188,17 +188,19 @@
DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
List<DexEncodedMethod> found = findDexEncodedMethodsWithName(methodName, typeClass);
for (DexEncodedMethod encodedMethod : found) {
- if (!encodedMethod.isStatic()) {
- virtualRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
- virtualRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
- if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
- // In this case interface method rewriter takes care of it.
- continue;
- } else if (!encodedMethod.isFinal()) {
- // Virtual rewrites require emulated dispatch for inheritance.
- // The call is rewritten to the dispatch holder class instead.
- handleEmulateDispatch(appView, encodedMethod.method);
- newHolder = dispatchHolderTypeFor(encodedMethod.method);
+ if (!typeClass.isFinal()) {
+ nonFinalHolderRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
+ nonFinalHolderRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
+ if (!encodedMethod.isStatic()) {
+ if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
+ // In this case interface method rewriter takes care of it.
+ continue;
+ } else if (!encodedMethod.isFinal()) {
+ // Virtual rewrites require emulated dispatch for inheritance.
+ // The call is rewritten to the dispatch holder class instead.
+ handleEmulateDispatch(appView, encodedMethod.method);
+ newHolder = dispatchHolderTypeFor(encodedMethod.method);
+ }
}
}
DexProto proto = encodedMethod.method.proto;
diff --git a/src/test/java/com/android/tools/r8/MarkerMatcher.java b/src/test/java/com/android/tools/r8/MarkerMatcher.java
index 90aec54..13065b7 100644
--- a/src/test/java/com/android/tools/r8/MarkerMatcher.java
+++ b/src/test/java/com/android/tools/r8/MarkerMatcher.java
@@ -117,6 +117,20 @@
};
}
+ public static Matcher<Marker> markerHasMinApi() {
+ return new MarkerMatcher() {
+ @Override
+ protected boolean eval(Marker marker) {
+ return marker.hasMinApi();
+ }
+
+ @Override
+ protected void explain(Description description) {
+ description.appendText(Marker.MIN_API + " found");
+ }
+ };
+ }
+
public static Matcher<Marker> markerHasChecksums(boolean value) {
return new MarkerMatcher() {
@Override
@@ -165,6 +179,25 @@
};
}
+ public static Matcher<Marker> markerHasDesugaredLibraryIdentifier() {
+ return markerHasDesugaredLibraryIdentifier(true);
+ }
+
+ public static Matcher<Marker> markerHasDesugaredLibraryIdentifier(boolean value) {
+ return new MarkerMatcher() {
+ @Override
+ protected boolean eval(Marker marker) {
+ return marker.hasDesugaredLibraryIdentifiers() == value;
+ }
+
+ @Override
+ protected void explain(Description description) {
+ description.appendText(
+ Marker.DESUGARED_LIBRARY_IDENTIFIERS + (value ? " found" : " not found"));
+ }
+ };
+ }
+
@Override
protected boolean matchesSafely(Marker marker) {
return eval(marker);
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 03052cc..f485a25 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
@@ -4,22 +4,35 @@
package com.android.tools.r8.desugar.desugaredlibrary;
+import static com.android.tools.r8.MarkerMatcher.assertMarkersMatch;
+import static com.android.tools.r8.MarkerMatcher.markerCompilationMode;
+import static com.android.tools.r8.MarkerMatcher.markerHasDesugaredLibraryIdentifier;
+import static com.android.tools.r8.MarkerMatcher.markerHasMinApi;
+import static com.android.tools.r8.MarkerMatcher.markerTool;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.ExtractMarker;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11DesugaredLibraryTestBase;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
+import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -69,6 +82,47 @@
.assertSuccessWithOutputLines(JAVA_RESULT);
}
+ @Test
+ public void testMergeDesugaredWithShrunkenLib() throws Exception {
+ // Compile a library with R8 to CF.
+ Path shrunkenLib =
+ testForR8(Backend.CF)
+ .addProgramClasses(Part2.class)
+ .addKeepClassRules(Part2.class)
+ .compile()
+ .writeToZip();
+
+ // R8 class file output marker has no library desugaring identifier.
+ Matcher<Marker> libraryMatcher =
+ allOf(
+ markerTool(Tool.R8),
+ markerCompilationMode(CompilationMode.RELEASE),
+ not(markerHasMinApi()),
+ not(markerHasDesugaredLibraryIdentifier()));
+ assertMarkersMatch(
+ ExtractMarker.extractMarkerFromJarFile(shrunkenLib), ImmutableList.of(libraryMatcher));
+
+ // Build an app with the R8 compiled library.
+ Path app =
+ testForD8()
+ .addProgramFiles(buildPart1DesugaredLibrary(), shrunkenLib)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+
+ // The app has both the R8 marker from the library compilation and the D8 marker from dexing.
+ Matcher<Marker> d8Matcher =
+ allOf(
+ markerTool(Tool.D8),
+ markerHasMinApi(),
+ markerHasDesugaredLibraryIdentifier(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O)));
+ assertMarkersMatch(
+ ExtractMarker.extractMarkerFromDexFile(app), ImmutableList.of(libraryMatcher, d8Matcher));
+ }
+
private void assertError(TestDiagnosticMessages m) {
List<Diagnostic> errors = m.getErrors();
if (expectError()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index 32bbe65..c2d88b0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -123,19 +123,12 @@
System.out.println(myAtomicInteger.updateAndGet(x -> x + 100));
System.out.println("145");
- try {
- MyDateNoOverride.from(myCal.toInstant());
- System.out.println("b/159441805 fixed");
- } catch (NoSuchMethodError e) {
- // TODO(b/159441805): Should not throw.
- }
-
- try {
- MyDateOverride.from(myCal.toInstant());
- System.out.println("b/159441805 fixed");
- } catch (NoSuchMethodError e) {
- // TODO(b/159441805): Should not throw.
- }
+ Date date1 = MyDateNoOverride.from(myCal.toInstant());
+ System.out.println(date1.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ Date date2 = MyDateOverride.from(myCal.toInstant());
+ System.out.println(date2.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
System.out.println(MyDateDoubleOverride.from(myCal.toInstant()).toInstant());
System.out.println("1970-01-02T10:17:36.788Z");