aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Gjesse <sgjesse@google.com>2021-03-11 16:27:06 +0100
committerSøren Gjesse <sgjesse@google.com>2021-03-11 15:28:00 +0000
commitaaeee3ceabbe7edf939e96b6a49fae55810cd45d (patch)
tree1cbac6bd6dc34b76aaa5ee04d58d4891ddd40836
parentf6c349d326bb75eceec858245e27aa0e1b313cc5 (diff)
downloadr8-aaeee3ceabbe7edf939e96b6a49fae55810cd45d.tar.gz
Add generic signature to emulated interfaces
For each emulated interface added to a class the generic signature will be added as well. As the emulated interface is added lower in the hierarchy than the implementation of the original interface the generic arguments from where the emulated interface is added is evaluated. This is done by propagating the type arguments up the inheritance hierarchy to where the original interface was implemented. Bug: 181750323 Change-Id: I4ba3b8881610b6372473a5c3f0a814245eecf368
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexClass.java112
-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.java129
-rw-r--r--src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java159
4 files changed, 386 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..7a7fa38e2 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,116 @@ 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 void forEachImmediateInterface(BiConsumer<DexType, ClassTypeSignature> consumer) {
+ // 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) {
+ // 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..9de1141ce 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,65 @@ final class ClassProcessor {
clazz.asProgramClass().addExtraInterfaces(extraInterfaceSignatures);
}
+ private void collectEmulatedInterfaces(
+ DexClass clazz,
+ Set<DexType> emulatesInterfaces,
+ Map<DexType, GenericSignature.ClassTypeSignature> extraInterfaceSignatures) {
+ clazz.forEachImmediateSupertype(
+ (type, signature) -> {
+ if (emulatesInterfaces.contains(type)) {
+ extraInterfaceSignatures.put(
+ type,
+ new GenericSignature.ClassTypeSignature(
+ rewriter.getEmulatedInterface(type), signature.typeArguments()));
+ }
+ // TODO(b/182329331): Only handle type arguments for Cf to Cf desugar.
+ collectEmulatedInterfacesWithPropagatedTypeArguments(
+ type,
+ appView.options().cfToCfDesugar ? signature.typeArguments() : 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) {
+ 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 +599,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) {}
}
}