aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Gjesse <sgjesse@google.com>2021-03-15 12:32:51 +0100
committerSøren Gjesse <sgjesse@google.com>2021-03-15 13:19:37 +0000
commit623cbb95d18edccbb57d7fbc01d8980e2927ffb8 (patch)
treeb471de294098901651cd6bbb8597fb7ecd4fae9c
parentef19577b3de8f23b7b4bc711b3a109b383795493 (diff)
downloadr8-623cbb95d18edccbb57d7fbc01d8980e2927ffb8.tar.gz
Fix visiting interfaces for emulated intefaces
The original change aaeee3ceabbe7edf939e96b6a49fae55810cd45d was supposed to only consider generic signatures for cf to cf desugaring. However in some cases that was not the case. This reverts commit bf64f4a66338b743fb47c4144d780f9cd8a86d40 with additional changes. Bug: 181750323, 182702580 Change-Id: I7d675869d72fdbc9456f12b58319592bee83e1e2
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexClass.java121
-rw-r--r--src/main/java/com/android/tools/r8/graph/GenericSignature.java9
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java139
-rw-r--r--src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java159
4 files changed, 405 insertions, 23 deletions
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 0b4b18890..b2bc94824 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -10,6 +10,9 @@ import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
@@ -20,6 +23,7 @@ import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
@@ -31,6 +35,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -780,13 +785,125 @@ public abstract class DexClass extends DexDefinition implements ClassDefinition
Predicate<DexType> ignore,
Set<DexType> seen);
+ public void forEachImmediateInterface(Consumer<DexType> fn) {
+ for (DexType iface : interfaces.values) {
+ fn.accept(iface);
+ }
+ }
+
public void forEachImmediateSupertype(Consumer<DexType> fn) {
if (superType != null) {
fn.accept(superType);
}
- for (DexType iface : interfaces.values) {
- fn.accept(iface);
+ forEachImmediateInterface(fn);
+ }
+
+ public boolean validInterfaceSignatures() {
+ return getClassSignature().superInterfaceSignatures().isEmpty()
+ || interfaces.values.length == getClassSignature().superInterfaceSignatures.size();
+ }
+
+ public void forEachImmediateInterface(BiConsumer<DexType, ClassTypeSignature> consumer) {
+ assert validInterfaceSignatures();
+
+ // If there is no generic signature information don't pass any type arguments.
+ if (getClassSignature().superInterfaceSignatures().isEmpty()) {
+ forEachImmediateInterface(
+ superInterface ->
+ consumer.accept(superInterface, new ClassTypeSignature(superInterface)));
+ return;
+ }
+
+ Iterator<DexType> interfaceIterator = Arrays.asList(interfaces.values).iterator();
+ Iterator<ClassTypeSignature> interfaceSignatureIterator =
+ getClassSignature().superInterfaceSignatures().iterator();
+
+ while (interfaceIterator.hasNext()) {
+ assert interfaceSignatureIterator.hasNext();
+ DexType superInterface = interfaceIterator.next();
+ ClassTypeSignature superInterfaceSignatures = interfaceSignatureIterator.next();
+ consumer.accept(superInterface, superInterfaceSignatures);
+ }
+ }
+
+ public void forEachImmediateSupertype(BiConsumer<DexType, ClassTypeSignature> consumer) {
+ if (superType != null) {
+ consumer.accept(superType, classSignature.superClassSignature);
+ }
+ forEachImmediateInterface(consumer);
+ }
+
+ public void forEachImmediateInterfaceWithAppliedTypeArguments(
+ List<FieldTypeSignature> typeArguments,
+ BiConsumer<DexType, List<FieldTypeSignature>> consumer) {
+ assert validInterfaceSignatures();
+
+ // If there is no generic signature information don't pass any type arguments.
+ if (getClassSignature().superInterfaceSignatures().size() == 0) {
+ forEachImmediateInterface(
+ superInterface -> consumer.accept(superInterface, ImmutableList.of()));
+ return;
+ }
+
+ Iterator<DexType> interfaceIterator = Arrays.asList(interfaces.values).iterator();
+ Iterator<ClassTypeSignature> interfaceSignatureIterator =
+ getClassSignature().superInterfaceSignatures().iterator();
+
+ while (interfaceIterator.hasNext()) {
+ assert interfaceSignatureIterator.hasNext();
+ DexType superInterface = interfaceIterator.next();
+ ClassTypeSignature superInterfaceSignatures = interfaceSignatureIterator.next();
+
+ // With no type arguments erase the signatures.
+ if (typeArguments.isEmpty() && superInterfaceSignatures.hasTypeVariableArguments()) {
+ consumer.accept(superInterface, ImmutableList.of());
+ continue;
+ }
+
+ consumer.accept(superInterface, applyTypeArguments(superInterfaceSignatures, typeArguments));
+ }
+ assert !interfaceSignatureIterator.hasNext();
+ }
+
+ public void forEachImmediateSupertypeWithAppliedTypeArguments(
+ List<FieldTypeSignature> typeArguments,
+ BiConsumer<DexType, List<FieldTypeSignature>> consumer) {
+ if (superType != null) {
+ consumer.accept(
+ superType, applyTypeArguments(getClassSignature().superClassSignature, typeArguments));
+ }
+ forEachImmediateInterfaceWithAppliedTypeArguments(typeArguments, consumer);
+ }
+
+ private List<FieldTypeSignature> applyTypeArguments(
+ ClassTypeSignature superInterfaceSignatures, List<FieldTypeSignature> appliedTypeArguments) {
+ ImmutableList.Builder<FieldTypeSignature> superTypeArgumentsBuilder = ImmutableList.builder();
+ if (superInterfaceSignatures.type.toSourceString().equals("java.util.Map")) {
+ System.currentTimeMillis();
}
+ superInterfaceSignatures
+ .typeArguments()
+ .forEach(
+ typeArgument -> {
+ if (typeArgument.isTypeVariableSignature()) {
+ for (int i = 0; i < getClassSignature().getFormalTypeParameters().size(); i++) {
+ FormalTypeParameter formalTypeParameter =
+ getClassSignature().getFormalTypeParameters().get(i);
+ if (formalTypeParameter
+ .getName()
+ .equals(typeArgument.asTypeVariableSignature().typeVariable())) {
+ if (i >= appliedTypeArguments.size()) {
+ assert false;
+ } else {
+ superTypeArgumentsBuilder.add(appliedTypeArguments.get(i));
+ }
+ }
+ }
+ } else {
+ superTypeArgumentsBuilder.add(typeArgument);
+ }
+ });
+ return superTypeArgumentsBuilder.build();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 1dc1f4ff3..2fa08c842 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -542,6 +542,15 @@ public class GenericSignature {
visitor.visitSimpleClass(innerTypeSignature);
}
}
+
+ public boolean hasTypeVariableArguments() {
+ for (FieldTypeSignature typeArgument : typeArguments) {
+ if (typeArgument.isTypeVariableSignature()) {
+ return true;
+ }
+ }
+ return false;
+ }
}
public static class ArrayTypeSignature extends FieldTypeSignature {
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 3e665db6f..6ef420d78 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
@@ -205,6 +205,40 @@ final class ClassProcessor {
}
}
+ // Emulated interfaces together with the generic signatures.
+ static class EmulatedInterfaces {
+ static EmulatedInterfaces EMPTY = new EmulatedInterfaces(ImmutableSet.of());
+
+ final Set<DexType> emulatedInterfaces;
+
+ EmulatedInterfaces(DexType emulatedInterface) {
+ this.emulatedInterfaces = ImmutableSet.of(emulatedInterface);
+ }
+
+ private EmulatedInterfaces(Set<DexType> emulatedInterfaces) {
+ this.emulatedInterfaces = emulatedInterfaces;
+ }
+
+ boolean isEmpty() {
+ return emulatedInterfaces.isEmpty();
+ }
+
+ boolean contains(DexType type) {
+ return emulatedInterfaces.contains(type);
+ }
+
+ Set<DexType> getEmulatedInterfaces() {
+ return emulatedInterfaces;
+ }
+
+ EmulatedInterfaces merge(EmulatedInterfaces other) {
+ ImmutableSet.Builder<DexType> newEmulatedInterfaces = ImmutableSet.builder();
+ newEmulatedInterfaces.addAll(emulatedInterfaces);
+ newEmulatedInterfaces.addAll(other.emulatedInterfaces);
+ return new EmulatedInterfaces(newEmulatedInterfaces.build());
+ }
+ }
+
// List of emulated interfaces and corresponding signatures which may require forwarding methods.
// If one of the signatures has an override, then the class holding the override is required to
// add the forwarding methods for all signatures, and introduce the corresponding emulated
@@ -214,13 +248,13 @@ final class ClassProcessor {
private static class EmulatedInterfaceInfo {
static final EmulatedInterfaceInfo EMPTY =
- new EmulatedInterfaceInfo(MethodSignatures.EMPTY, ImmutableSet.of());
+ new EmulatedInterfaceInfo(MethodSignatures.EMPTY, EmulatedInterfaces.EMPTY);
final MethodSignatures signatures;
- final ImmutableSet<DexType> emulatedInterfaces;
+ final EmulatedInterfaces emulatedInterfaces;
private EmulatedInterfaceInfo(
- MethodSignatures methodsToForward, ImmutableSet<DexType> emulatedInterfaces) {
+ MethodSignatures methodsToForward, EmulatedInterfaces emulatedInterfaces) {
this.signatures = methodsToForward;
this.emulatedInterfaces = emulatedInterfaces;
}
@@ -232,17 +266,18 @@ final class ClassProcessor {
if (other.isEmpty()) {
return this;
}
- ImmutableSet.Builder<DexType> newEmulatedInterfaces = ImmutableSet.builder();
- newEmulatedInterfaces.addAll(emulatedInterfaces);
- newEmulatedInterfaces.addAll(other.emulatedInterfaces);
return new EmulatedInterfaceInfo(
- signatures.merge(other.signatures), newEmulatedInterfaces.build());
+ signatures.merge(other.signatures), emulatedInterfaces.merge(other.emulatedInterfaces));
}
public boolean isEmpty() {
assert !emulatedInterfaces.isEmpty() || signatures.isEmpty();
return emulatedInterfaces.isEmpty();
}
+
+ boolean contains(DexType type) {
+ return emulatedInterfaces.contains(type);
+ }
}
// Helper to keep track of the direct active subclass and nearest program subclass for reporting.
@@ -376,7 +411,7 @@ final class ClassProcessor {
assert needsLibraryInfo();
MethodSignatures signatures = getDefaultMethods(iface);
EmulatedInterfaceInfo emulatedInterfaceInfo =
- new EmulatedInterfaceInfo(signatures, ImmutableSet.of(iface.type));
+ new EmulatedInterfaceInfo(signatures, new EmulatedInterfaces(iface.type));
return interfaceInfo.withEmulatedInterfaceInfo(emulatedInterfaceInfo);
}
@@ -423,14 +458,13 @@ final class ClassProcessor {
// implement the interface and the emulated one for correct emulated dispatch.
// The class signature won't include the correct type parameters for the duplicated interfaces,
// i.e., there will be foo.A instead of foo.A<K,V>, but such parameters are unused.
- private void duplicateEmulatedInterfaces(
- DexClass clazz, ImmutableSet<DexType> emulatedInterfaces) {
+ private void duplicateEmulatedInterfaces(DexClass clazz, EmulatedInterfaces emulatedInterfaces) {
if (clazz.isNotProgramClass()) {
return;
}
- Set<DexType> filtered = new HashSet<>(emulatedInterfaces);
+ Set<DexType> filtered = new HashSet<>(emulatedInterfaces.getEmulatedInterfaces());
WorkList<DexType> workList = WorkList.newIdentityWorkList();
- for (DexType emulatedInterface : emulatedInterfaces) {
+ for (DexType emulatedInterface : emulatedInterfaces.getEmulatedInterfaces()) {
DexClass iface = appView.definitionFor(emulatedInterface);
if (iface != null) {
assert iface.isLibraryClass()
@@ -448,7 +482,7 @@ final class ClassProcessor {
workList.addIfNotSeen(iface.getInterfaces());
}
- for (DexType emulatedInterface : emulatedInterfaces) {
+ for (DexType emulatedInterface : emulatedInterfaces.getEmulatedInterfaces()) {
DexClass s = appView.definitionFor(emulatedInterface);
if (s != null) {
s = appView.definitionFor(s.superType);
@@ -459,13 +493,17 @@ final class ClassProcessor {
}
}
+ // Collect the signatures for the emulated interfaces to add.
+ Map<DexType, GenericSignature.ClassTypeSignature> signatures = new IdentityHashMap<>();
+ collectEmulatedInterfaces(clazz, filtered, signatures);
// We need to introduce them in deterministic order for deterministic compilation.
ArrayList<DexType> sortedEmulatedInterfaces = new ArrayList<>(filtered);
Collections.sort(sortedEmulatedInterfaces);
List<GenericSignature.ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>();
for (DexType extraInterface : sortedEmulatedInterfaces) {
- extraInterfaceSignatures.add(
- new GenericSignature.ClassTypeSignature(rewriter.getEmulatedInterface(extraInterface)));
+ GenericSignature.ClassTypeSignature signature = signatures.get(extraInterface);
+ assert signature != null;
+ extraInterfaceSignatures.add(signature);
}
// The emulated interface might already be implemented if the input class has gone through
// library desugaring already.
@@ -490,6 +528,75 @@ final class ClassProcessor {
clazz.asProgramClass().addExtraInterfaces(extraInterfaceSignatures);
}
+ private void collectEmulatedInterfaces(
+ DexClass clazz,
+ Set<DexType> emulatesInterfaces,
+ Map<DexType, GenericSignature.ClassTypeSignature> extraInterfaceSignatures) {
+ // TODO(b/182329331): Only handle type arguments for Cf to Cf desugar.
+ if (appView.options().cfToCfDesugar && clazz.validInterfaceSignatures()) {
+ clazz.forEachImmediateSupertype(
+ (type, signature) -> {
+ if (emulatesInterfaces.contains(type)) {
+ extraInterfaceSignatures.put(
+ type,
+ new GenericSignature.ClassTypeSignature(
+ rewriter.getEmulatedInterface(type), signature.typeArguments()));
+ }
+ collectEmulatedInterfacesWithPropagatedTypeArguments(
+ type, signature.typeArguments(), emulatesInterfaces, extraInterfaceSignatures);
+ });
+ } else {
+ clazz.forEachImmediateSupertype(
+ (type) -> {
+ if (emulatesInterfaces.contains(type)) {
+ extraInterfaceSignatures.put(
+ type,
+ new GenericSignature.ClassTypeSignature(rewriter.getEmulatedInterface(type)));
+ }
+ collectEmulatedInterfacesWithPropagatedTypeArguments(
+ type, null, emulatesInterfaces, extraInterfaceSignatures);
+ });
+ }
+ }
+
+ private void collectEmulatedInterfacesWithPropagatedTypeArguments(
+ DexType type,
+ List<GenericSignature.FieldTypeSignature> typeArguments,
+ Set<DexType> emulatesInterfaces,
+ Map<DexType, GenericSignature.ClassTypeSignature> extraInterfaceSignatures) {
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz == null) {
+ return;
+ }
+ // TODO(b/182329331): Only handle type arguments for Cf to Cf desugar.
+ if (appView.options().cfToCfDesugar && clazz.validInterfaceSignatures()) {
+ assert typeArguments != null;
+ clazz.forEachImmediateSupertypeWithAppliedTypeArguments(
+ typeArguments,
+ (iface, signature) -> {
+ if (emulatesInterfaces.contains(iface)) {
+ extraInterfaceSignatures.put(
+ iface,
+ new GenericSignature.ClassTypeSignature(
+ rewriter.getEmulatedInterface(iface), signature));
+ }
+ collectEmulatedInterfacesWithPropagatedTypeArguments(
+ iface, signature, emulatesInterfaces, extraInterfaceSignatures);
+ });
+ } else {
+ assert typeArguments == null;
+ clazz.forEachImmediateSupertype(
+ iface -> {
+ if (emulatesInterfaces.contains(iface)) {
+ extraInterfaceSignatures.put(
+ iface,
+ new GenericSignature.ClassTypeSignature(rewriter.getEmulatedInterface(iface)));
+ }
+ collectEmulatedInterfacesWithPropagatedTypeArguments(
+ iface, null, emulatesInterfaces, extraInterfaceSignatures);
+ });
+ }
+ }
// If any of the signature would lead to a different behavior than the default method on the
// emulated interface, we need to resolve the forwarding methods.
private boolean shouldResolveForwardingMethodsForEmulatedInterfaces(
@@ -502,7 +609,7 @@ final class ClassProcessor {
}
DexClass resolvedHolder = resolutionResult.asSingleResolution().getResolvedHolder();
if (!resolvedHolder.isLibraryClass()
- && !emulatedInterfaceInfo.emulatedInterfaces.contains(resolvedHolder.type)) {
+ && !emulatedInterfaceInfo.contains(resolvedHolder.type)) {
return true;
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java
index a3b768d18..af6ca9e61 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java
@@ -9,15 +9,23 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.Serializable;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.nio.file.Path;
import java.time.LocalDate;
+import java.util.AbstractSequentialList;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.function.UnaryOperator;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,7 +37,6 @@ public class DesugaredGenericSignatureTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
- private static final String EXPECTED = StringUtils.lines("1970", "1", "2");
@Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
@@ -60,7 +67,7 @@ public class DesugaredGenericSignatureTest extends DesugaredLibraryTestBase {
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED);
+ .assertSuccessWithOutput(expected(parameters, false));
}
@Test
@@ -96,13 +103,13 @@ public class DesugaredGenericSignatureTest extends DesugaredLibraryTestBase {
desugaredLibraryKeepRules,
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED);
+ .assertSuccessWithOutput(expected(parameters, true));
} else {
testForJvm()
.addProgramFiles(jar)
.addRunClasspathFiles(getDesugaredLibraryInCF(parameters, options -> {}))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED);
+ .assertSuccessWithOutput(expected(parameters, true));
}
}
@@ -129,7 +136,7 @@ public class DesugaredGenericSignatureTest extends DesugaredLibraryTestBase {
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED);
+ .assertSuccessWithOutput(expected(parameters, false));
}
private void checkRewrittenSignature(CodeInspector inspector) {
@@ -149,6 +156,7 @@ public class DesugaredGenericSignatureTest extends DesugaredLibraryTestBase {
}
public interface Box<T> {
+
T addOne(T t);
}
@@ -161,6 +169,61 @@ public class DesugaredGenericSignatureTest extends DesugaredLibraryTestBase {
}
}
+ private static String expected(
+ TestParameters parameters, boolean genericSignaturesOnEmulatedInterfaces) {
+ final String EXPECTED = StringUtils.lines("Box", "1970", "1", "2");
+ final String STRING_KEY_HASH_MAP_EXPECTED =
+ StringUtils.lines(
+ "StringKeyHashMap", "1", "j$.util.Map<java.lang.String, T>", "2", "true", "true");
+ final String SAME_KEY_AND_VALUE_TYPE_HASH_MAP_EXPECTED =
+ StringUtils.lines(
+ "SameKeyAndValueTypeHashMap", "1", "j$.util.Map<T, T>", "2", "true", "true");
+ final String TRANSFORMING_SEQUENTIAL_LIST_EXPECTED =
+ StringUtils.lines("TransformingSequentialList", "2", "j$.util.List<T>");
+
+ final String EXPECTED_WITH_EMULATED_INTERFACE =
+ STRING_KEY_HASH_MAP_EXPECTED
+ + SAME_KEY_AND_VALUE_TYPE_HASH_MAP_EXPECTED
+ + TRANSFORMING_SEQUENTIAL_LIST_EXPECTED;
+ final String EXPECTED_WITHOUT_EMULATED_INTERFACE_ART_BEFORE_O =
+ StringUtils.lines(
+ "StringKeyHashMap",
+ "1",
+ "interface j$.util.Map",
+ "SameKeyAndValueTypeHashMap",
+ "1",
+ "interface j$.util.Map",
+ "TransformingSequentialList",
+ "2",
+ "interface j$.util.List");
+ final String EXPECTED_WITHOUT_EMULATED_INTERFACE_JVM_AND_ART_FROM_O =
+ StringUtils.lines(
+ "StringKeyHashMap",
+ "0",
+ "SameKeyAndValueTypeHashMap",
+ "0",
+ "TransformingSequentialList",
+ "1");
+
+ return EXPECTED
+ + (genericSignaturesOnEmulatedInterfaces
+ && !parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
+ ? EXPECTED_WITH_EMULATED_INTERFACE
+ : (parameters.isDexRuntime()
+ && (parameters
+ .getRuntime()
+ .asDex()
+ .getMinApiLevel()
+ .isLessThan(AndroidApiLevel.N)
+ || parameters
+ .getApiLevel()
+ .isLessThan(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())))
+ ? EXPECTED_WITHOUT_EMULATED_INTERFACE_ART_BEFORE_O
+ : EXPECTED_WITHOUT_EMULATED_INTERFACE_JVM_AND_ART_FROM_O);
+ }
+
public static class Main {
public static Box<java.time.LocalDate> bar() {
@@ -168,10 +231,96 @@ public class DesugaredGenericSignatureTest extends DesugaredLibraryTestBase {
}
public static void main(String[] args) {
+ testBox();
+ testEmulatedInterfaceGenericSignatureStringKeyHashMap();
+ testEmulatedInterfaceGenericSignatureSameKeyAndValueTypeHashMap();
+ testEmulatedInterfaceGenericSignatureTransformingSequentialList();
+ }
+
+ public static void testBox() {
+ System.out.println("Box");
LocalDate localDate = bar().addOne(LocalDate.of(1970, 1, 1));
System.out.println(localDate.getYear());
System.out.println(localDate.getMonthValue());
System.out.println(localDate.getDayOfMonth());
}
+
+ public static void testEmulatedInterfaceGenericSignatureStringKeyHashMap() {
+ System.out.println("StringKeyHashMap");
+ Class<?> clazz = StringKeyHashMap.class;
+ System.out.println(clazz.getGenericInterfaces().length);
+ if (clazz.getGenericInterfaces().length == 0) {
+ return;
+ }
+ Type genericInterface = clazz.getGenericInterfaces()[0];
+ System.out.println(genericInterface);
+ if (genericInterface instanceof ParameterizedType) {
+ // The j$.util.Map emulated interface has the generic type arguments <String, T>.
+ Type[] actualTypeArguments =
+ ((ParameterizedType) genericInterface).getActualTypeArguments();
+ System.out.println(actualTypeArguments.length);
+ System.out.println(actualTypeArguments[0].equals(String.class));
+ System.out.println(actualTypeArguments[1].equals(clazz.getTypeParameters()[0]));
+ }
+ }
+
+ public static void testEmulatedInterfaceGenericSignatureSameKeyAndValueTypeHashMap() {
+ System.out.println("SameKeyAndValueTypeHashMap");
+ Class<?> clazz = SameKeyAndValueTypeHashMap.class;
+ System.out.println(clazz.getGenericInterfaces().length);
+ if (clazz.getGenericInterfaces().length == 0) {
+ return;
+ }
+ Type genericInterface = clazz.getGenericInterfaces()[0];
+ System.out.println(genericInterface);
+ if (genericInterface instanceof ParameterizedType) {
+ // The j$.util.Map emulated interface has the generic type arguments <T, T>.
+ Type[] actualTypeArguments =
+ ((ParameterizedType) genericInterface).getActualTypeArguments();
+ System.out.println(actualTypeArguments.length);
+ System.out.println(actualTypeArguments[0].equals(clazz.getTypeParameters()[0]));
+ System.out.println(actualTypeArguments[1].equals(clazz.getTypeParameters()[0]));
+ }
+ }
+
+ public static void testEmulatedInterfaceGenericSignatureTransformingSequentialList() {
+ System.out.println("TransformingSequentialList");
+ Class<?> clazz = TransformingSequentialList.class;
+ System.out.println(clazz.getGenericInterfaces().length);
+ if (clazz.getGenericInterfaces().length == 1) {
+ return;
+ }
+ // j$.util.List emulated interface has the generic type argument <T>.
+ System.out.println(clazz.getGenericInterfaces()[1]);
+ }
+ }
+
+ // LinkedHashMap implements Map.
+ abstract static class StringKeyHashMap<T> extends LinkedHashMap<String, T> {
+
+ // Need at least one overridden default method for emulated dispatch.
+ @Override
+ public T getOrDefault(Object key, T defaultValue) {
+ return super.getOrDefault(key, defaultValue);
+ }
+ }
+
+ // LinkedHashMap implements Map.
+ abstract static class SameKeyAndValueTypeHashMap<T> extends LinkedHashMap<T, T> {
+
+ // Need at least one overridden default method for emulated dispatch.
+ @Override
+ public T getOrDefault(Object key, T defaultValue) {
+ return super.getOrDefault(key, defaultValue);
+ }
+ }
+
+ // AbstractSequentialList implements List further up the hierarchy.
+ abstract static class TransformingSequentialList<F, T> extends AbstractSequentialList<T>
+ implements Serializable {
+
+ // Need at least one overridden default method for emulated dispatch.
+ @Override
+ public void replaceAll(UnaryOperator<T> operator) {}
}
}