Make desugaring more tolerant on missing hierarchy
This is done by skiping default method desugaring of classes with
only unimplemented defaults methods defined in direct interfaces defined
in libraries. We were already not generating code for those default
methods but we were still checking the hierarchy even there was only
such defaults methods, This change is just removing this hierarchy check
and has no impact on generated code.
This change is not a big relaxation of constraints, it is allowing the
particular case like implementing Iterator but desugaring of classes
with missing hierarchy will still fail if any other default method is
defined outside libraries.
Bug: 73633268
Change-Id: I9dad556de15c939a13ddec13f7ce0fbfd24a0bc7
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index fa0958b..c752fb5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -142,6 +142,7 @@
accumulatedVirtualMethods.addAll(Arrays.asList(clazz.virtualMethods()));
List<DexEncodedMethod> defaultMethodsInDirectInterface = helper.createFullList();
+
List<DexEncodedMethod> toBeImplementedFromDirectInterface =
new ArrayList<>(defaultMethodsInDirectInterface.size());
hideCandidates(accumulatedVirtualMethods,
@@ -209,16 +210,7 @@
// Everything still in candidate list is not hidden.
toBeImplemented.addAll(candidates);
- // NOTE: we also intentionally remove all candidates coming from android.jar
- // since it is only possible in case v24+ version of android.jar is provided.
- // WARNING: This may result in incorrect code on older platforms!
- toBeImplemented.removeIf(
- method -> {
- DexClass holder = rewriter.findDefinitionFor(method.method.holder);
- // Holder of a found method to implement is a defined interface.
- assert holder != null;
- return holder.isLibraryClass();
- });
+
return toBeImplemented;
}
current = superClass;
@@ -283,6 +275,14 @@
+ implementing.toString() + "`.");
}
+ if (definedInterface.isLibraryClass()) {
+ // NOTE: We intentionally ignore all candidates coming from android.jar
+ // since it is only possible in case v24+ version of android.jar is provided.
+ // WARNING: This may result in incorrect code if something else than Android bootclasspath
+ // classes are given as libraries!
+ return helper.wrapInCollection();
+ }
+
// Merge information from all superinterfaces.
for (DexType superinterface : definedInterface.interfaces.values) {
helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest6/MissingSuperImplementIterator.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest6/MissingSuperImplementIterator.java
new file mode 100644
index 0000000..86d2882
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest6/MissingSuperImplementIterator.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package desugaringwithmissingclasstest6;
+
+import desugaringwithmissingclasslib3.C;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class MissingSuperImplementIterator extends C implements Iterator {
+
+ @Override
+ public boolean hasNext() {
+ return false;
+ }
+
+ @Override
+ public Object next() {
+ throw new NoSuchElementException();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index b5802f4..77959d9 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -415,6 +415,34 @@
}
@Test
+ public void testMissingSuperDesugaredWithLibInterfaceAndroidK() throws Throwable {
+ AndroidApiLevel minApi = AndroidApiLevel.K;
+
+ // Reference case: there's nothing to do, we should not complain.
+ // Class MissingSuperImplementIterator extends C implements Iterator should not require
+ // desugaring because Iterator has no default method at this API level.
+ // The test is compiled with incomplete classpath: lib3 is missing so
+ // MissingSuperImplementIterator is missing its super class.
+ test("desugaringwithmissingclasstest6", "desugaringwithmissingclasstest6", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withAndroidJar(AndroidApiLevel.K)
+ .withMinApiLevel(minApi)
+ .build();
+
+ // More litigious case, D8 needs to detect that the default method is part of bootclasspath.
+ // Class MissingSuperImplementIterator extends C implements Iterator should not require
+ // desugaring of Iterator default method because it is part of the Android API and thus is
+ // declaring the default method only when default methods are supported by the runtime.
+ // test is compiled with incomplete classpath: lib3 is missing so
+ // MissingSuperImplementIterator is missing its super class.
+ test("desugaringwithmissingclasstest6", "desugaringwithmissingclasstest6", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withAndroidJar(AndroidApiLevel.N)
+ .withMinApiLevel(minApi)
+ .build();
+ }
+
+ @Test
public void testMissingSuperDesugaredWithProgramCrossImplementationAndroidK() throws Throwable {
AndroidApiLevel minApi = AndroidApiLevel.K;