Desugared library: GSON support
Bug: 167649682
Change-Id: I62efad3bd6a6834cffd0d78ec255c0999e9492a1
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 e61ff61..1a8d506 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_ARGUMENTS;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import static com.google.common.base.Predicates.alwaysTrue;
@@ -480,7 +479,26 @@
methodCollection.addDirectMethod(directMethod);
}
- public void addExtraInterfaces(List<DexType> extraInterfaces, DexItemFactory factory) {
+ public void replaceInterfaces(List<ClassTypeSignature> newInterfaces) {
+ if (newInterfaces.isEmpty()) {
+ return;
+ }
+ clearInterfaces();
+ addExtraInterfaces(newInterfaces);
+ }
+
+ private void clearInterfaces() {
+ interfaces = DexTypeList.empty();
+ if (classSignature.hasSignature()) {
+ classSignature =
+ new ClassSignature(
+ classSignature.formalTypeParameters,
+ classSignature.superClassSignature,
+ ImmutableList.of());
+ }
+ }
+
+ public void addExtraInterfaces(List<ClassTypeSignature> extraInterfaces) {
if (extraInterfaces.isEmpty()) {
return;
}
@@ -488,25 +506,24 @@
addExtraInterfacesToSignatureIfPresent(extraInterfaces);
}
- private void addExtraInterfacesToInterfacesArray(List<DexType> extraInterfaces) {
+ private void addExtraInterfacesToInterfacesArray(List<ClassTypeSignature> extraInterfaces) {
DexType[] newInterfaces =
Arrays.copyOf(interfaces.values, interfaces.size() + extraInterfaces.size());
for (int i = interfaces.size(); i < newInterfaces.length; i++) {
- newInterfaces[i] = extraInterfaces.get(i - interfaces.size());
+ newInterfaces[i] = extraInterfaces.get(i - interfaces.size()).type();
}
interfaces = new DexTypeList(newInterfaces);
}
- private void addExtraInterfacesToSignatureIfPresent(List<DexType> extraInterfaces) {
- // We need to introduce the extra interfaces to the generic signature.
- // At this point we cheat and pretend the extraInterfaces simply don't use any generic types.
+ private void addExtraInterfacesToSignatureIfPresent(List<ClassTypeSignature> extraInterfaces) {
+ // We introduce the extra interfaces to the generic signature.
if (classSignature.hasNoSignature() || extraInterfaces.isEmpty()) {
return;
}
ImmutableList.Builder<ClassTypeSignature> interfacesBuilder =
ImmutableList.<ClassTypeSignature>builder().addAll(classSignature.superInterfaceSignatures);
- for (DexType extraInterface : extraInterfaces) {
- interfacesBuilder.add(new ClassTypeSignature(extraInterface, EMPTY_TYPE_ARGUMENTS));
+ for (ClassTypeSignature extraInterface : extraInterfaces) {
+ interfacesBuilder.add(extraInterface);
}
classSignature =
new ClassSignature(
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 ffb7eed..a92dc75 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -391,7 +391,11 @@
ClassTypeSignature enclosingTypeSignature;
ClassTypeSignature innerTypeSignature;
- ClassTypeSignature(DexType type, List<FieldTypeSignature> typeArguments) {
+ public ClassTypeSignature(DexType type) {
+ this(type, EMPTY_TYPE_ARGUMENTS);
+ }
+
+ public ClassTypeSignature(DexType type, List<FieldTypeSignature> typeArguments) {
this(type, typeArguments, WildcardIndicator.NOT_AN_ARGUMENT);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java
new file mode 100644
index 0000000..6f72091
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2020, 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;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class DesugaredLibraryEmulatedInterfaceDuplicator {
+
+ final AppView<?> appView;
+ final Map<DexType, DexType> emulatedInterfaces;
+
+ public DesugaredLibraryEmulatedInterfaceDuplicator(AppView<?> appView) {
+ this.appView = appView;
+ emulatedInterfaces =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ }
+
+ public void duplicateEmulatedInterfaces() {
+ // All classes implementing an emulated interface now implements the interface and the
+ // emulated one, as well as hidden overrides, for correct emulated dispatch.
+ // We not that duplicated interfaces won't feature the correct type parameters in the
+ // class signature since such signature is expected to be unused.
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.type == appView.dexItemFactory().objectType) {
+ continue;
+ }
+ if (emulatedInterfaces.containsKey(clazz.type)) {
+ transformEmulatedInterfaces(clazz);
+ } else {
+ duplicateEmulatedInterfaces(clazz);
+ }
+ }
+ }
+
+ private void transformEmulatedInterfaces(DexProgramClass clazz) {
+ List<ClassTypeSignature> newInterfaces = new ArrayList<>();
+ GenericSignature.ClassSignature classSignature = clazz.getClassSignature();
+ for (int i = 0; i < clazz.interfaces.size(); i++) {
+ DexType itf = clazz.interfaces.values[i];
+ assert emulatedInterfaces.containsKey(itf);
+ List<FieldTypeSignature> typeArguments;
+ if (classSignature == null) {
+ typeArguments = Collections.emptyList();
+ } else {
+ ClassTypeSignature classTypeSignature = classSignature.superInterfaceSignatures().get(i);
+ assert itf == classTypeSignature.type();
+ typeArguments = classTypeSignature.typeArguments();
+ }
+ newInterfaces.add(new ClassTypeSignature(emulatedInterfaces.get(itf), typeArguments));
+ }
+ clazz.replaceInterfaces(newInterfaces);
+ }
+
+ private void duplicateEmulatedInterfaces(DexProgramClass clazz) {
+ List<DexType> extraInterfaces = new ArrayList<>();
+ LinkedList<DexClass> workList = new LinkedList<>();
+ Set<DexType> processed = Sets.newIdentityHashSet();
+ workList.add(clazz);
+ while (!workList.isEmpty()) {
+ DexClass dexClass = workList.removeFirst();
+ if (processed.contains(dexClass.type)) {
+ continue;
+ }
+ processed.add(dexClass.type);
+ if (dexClass.superType != appView.dexItemFactory().objectType) {
+ processSuperType(clazz.superType, extraInterfaces, workList);
+ }
+ for (DexType itf : dexClass.interfaces) {
+ processSuperType(itf, extraInterfaces, workList);
+ }
+ }
+ extraInterfaces = removeDuplicates(extraInterfaces);
+ List<ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>();
+ for (DexType extraInterface : extraInterfaces) {
+ extraInterfaceSignatures.add(new ClassTypeSignature(extraInterface));
+ }
+ clazz.addExtraInterfaces(extraInterfaceSignatures);
+ }
+
+ private List<DexType> removeDuplicates(List<DexType> extraInterfaces) {
+ if (extraInterfaces.size() <= 1) {
+ return extraInterfaces;
+ }
+ // TODO(b/161399032): It would be nice to remove duplicate based on inheritance, i.e.,
+ // if there is ConcurrentMap<K,V> and Map<K,V>, Map<K,V> can be removed.
+ return new ArrayList<>(new HashSet<>(extraInterfaces));
+ }
+
+ void processSuperType(
+ DexType superType, List<DexType> extraInterfaces, LinkedList<DexClass> workList) {
+ if (emulatedInterfaces.containsKey(superType)) {
+ extraInterfaces.add(emulatedInterfaces.get(superType));
+ } else {
+ DexClass superClass = appView.definitionFor(superType);
+ if (shouldProcessSuperclass(superClass)) {
+ workList.add(superClass);
+ }
+ }
+ }
+
+ private boolean shouldProcessSuperclass(DexClass superclazz) {
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ return false;
+ }
+ // TODO(b/161399032): Pay-as-you-go design: stop duplication on library boundaries.
+ return superclazz != null && superclazz.isLibraryClass();
+ }
+}
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 64dad3e..68a2df3 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
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -375,7 +376,7 @@
// applies up to 24.
for (DexEncodedMethod method : methods) {
clazz.addExtraInterfaces(
- Collections.singletonList(dispatchInterfaceTypeFor(method)), appView.dexItemFactory());
+ Collections.singletonList(new ClassTypeSignature(dispatchInterfaceTypeFor(method))));
if (clazz.lookupVirtualMethod(method.getReference()) == null) {
DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
clazz.addVirtualMethod(newMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 5cda030..61a5c56 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -54,7 +54,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
-import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -923,63 +922,6 @@
return newMethods;
}
- private void duplicateEmulatedInterfaces() {
- // All classes implementing an emulated interface now implements the interface and the
- // emulated one, as well as hidden overrides, for correct emulated dispatch.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (clazz.type == appView.dexItemFactory().objectType) {
- continue;
- }
- List<DexType> extraInterfaces = new ArrayList<>();
- for (DexType type : clazz.interfaces.values) {
- if (emulatedInterfaces.containsKey(type)) {
- extraInterfaces.add(emulatedInterfaces.get(type));
- }
- }
- if (!appView.options().isDesugaredLibraryCompilation()) {
- assert clazz.superType != null;
- DexClass superClazz = appView.definitionFor(clazz.superType);
- if (superClazz != null && superClazz.isLibraryClass()) {
- List<DexType> itfs = emulatedInterfacesOf(superClazz);
- for (DexType itf : itfs) {
- extraInterfaces.add(emulatedInterfaces.get(itf));
- }
- }
- // Remove duplicates.
- if (extraInterfaces.size() > 1) {
- extraInterfaces = new ArrayList<>(new LinkedHashSet<>(extraInterfaces));
- }
- }
- clazz.addExtraInterfaces(extraInterfaces, appView.dexItemFactory());
- }
- }
-
- private List<DexType> emulatedInterfacesOf(DexClass superClazz) {
- if (superClazz.type == factory.objectType) {
- return Collections.emptyList();
- }
- ArrayList<DexType> itfs = new ArrayList<>();
- LinkedList<DexType> workList = new LinkedList<>();
- workList.add(superClazz.type);
- while (!workList.isEmpty()) {
- DexType dexType = workList.removeFirst();
- DexClass dexClass = appView.definitionFor(dexType);
- if (dexClass != null) {
- if (dexClass.superType != factory.objectType) {
- workList.add(dexClass.superType);
- }
- for (DexType itf : dexClass.interfaces.values) {
- if (emulatedInterfaces.containsKey(itf)) {
- itfs.add(itf);
- } else {
- workList.add(itf);
- }
- }
- }
- }
- return itfs;
- }
-
/**
* Move static and default interface methods to companion classes, add missing methods to forward
* to moved default methods implementation.
@@ -992,7 +934,7 @@
if (appView.options().isDesugaredLibraryCompilation()) {
generateEmulateInterfaceLibrary(builder);
}
- duplicateEmulatedInterfaces();
+ new DesugaredLibraryEmulatedInterfaceDuplicator(appView).duplicateEmulatedInterfaces();
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/AllMapsTestClass.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/AllMapsTestClass.java
new file mode 100644
index 0000000..4c15672
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/AllMapsTestClass.java
@@ -0,0 +1,347 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class AllMapsTestClass {
+ // Program class extending ConcurrentHashMap.
+ static class NullableConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
+ NullableConcurrentHashMap() {
+ super();
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ public V put(K key, V value) {
+ if (key == null || value == null) {
+ return null;
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ // Program class extending the library class HashMap, implementing Map.
+ static class NullableHashMap<K, V> extends HashMap<K, V> {
+ NullableHashMap() {
+ super();
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ public V put(K key, V value) {
+ if (key == null || value == null) {
+ return null;
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ // Program class implementing Map.
+ static class NullableMap<K, V> implements Map<K, V> {
+ private HashMap<K, V> map = new HashMap<>();
+
+ NullableMap() {
+ super();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return map.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return map.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ return map.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return map.values();
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof NullableMap)) return false;
+ NullableMap<?, ?> that = (NullableMap<?, ?>) o;
+ return map.equals(that.map);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(map);
+ }
+ }
+ // Program class implementing ConcurrentMap.
+ static class NullableConcurrentMap<K, V> implements ConcurrentMap<K, V> {
+ private HashMap<K, V> map = new HashMap<>();
+
+ NullableConcurrentMap() {
+ super();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return map.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return map.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ return map.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return map.values();
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof NullableConcurrentMap)) return false;
+ NullableConcurrentMap<?, ?> that = (NullableConcurrentMap<?, ?>) o;
+ return map.equals(that.map);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(map);
+ }
+
+ @Override
+ public V putIfAbsent(K key, V value) {
+ return null;
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ return false;
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ return false;
+ }
+
+ @Override
+ public V replace(K key, V value) {
+ return null;
+ }
+ }
+
+ static class Data {
+ final int id;
+ final String name;
+
+ Data(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Data)) return false;
+ Data data = (Data) o;
+ return id == data.id && name.equals(data.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+
+ @Override
+ public String toString() {
+ return "Data{" + "id=" + id + ", name='" + name + '\'' + '}';
+ }
+ }
+
+ public static void main(String[] args) {
+ Gson gson = new Gson();
+
+ HashMap<Integer, Data> hashMap = new HashMap<>();
+ NullableHashMap<Integer, Data> nullableHashMap = new NullableHashMap<>();
+ NullableMap<Integer, Data> nullableMap = new NullableMap<>();
+ NullableConcurrentMap<Integer, Data> nullableConcurrentMap = new NullableConcurrentMap<>();
+ ConcurrentHashMap<Integer, Data> concurrentHashMap = new ConcurrentHashMap<>();
+ NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMap =
+ new NullableConcurrentHashMap<>();
+
+ fillMap(hashMap);
+ fillMap(nullableHashMap);
+ fillMap(nullableMap);
+ fillMap(nullableConcurrentMap);
+ fillMap(concurrentHashMap);
+ fillMap(nullableConcurrentHashMap);
+
+ // Serialization.
+ String hashMapJson = gson.toJson(hashMap);
+ String nullableHashMapJson = gson.toJson(nullableHashMap);
+ String nullableMapJson = gson.toJson(nullableMap);
+ String nullableConcurrentMapJson = gson.toJson(nullableConcurrentMap);
+ String concurrentHashMapJson = gson.toJson(concurrentHashMap);
+ String nullableConcurrentHashMapJson = gson.toJson(nullableConcurrentHashMap);
+
+ // Deserialization.
+ Type hashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
+ HashMap<Integer, Data> hashMapDeserialized = gson.fromJson(hashMapJson, hashMapType);
+ Type nullableHashMapType = new TypeToken<NullableHashMap<Integer, Data>>() {}.getType();
+ NullableHashMap<Integer, Data> nullableHashMapDeserialized =
+ gson.fromJson(nullableHashMapJson, nullableHashMapType);
+ Type nullableMapType = new TypeToken<NullableMap<Integer, Data>>() {}.getType();
+ NullableMap<Integer, Data> nullableMapDeserialized =
+ gson.fromJson(nullableMapJson, nullableMapType);
+ Type nullableConcurrentMapType =
+ new TypeToken<NullableConcurrentMap<Integer, Data>>() {}.getType();
+ NullableConcurrentMap<Integer, Data> nullableConcurrentMapDeserialized =
+ gson.fromJson(nullableConcurrentMapJson, nullableConcurrentMapType);
+ Type concurrentHashMapType = new TypeToken<ConcurrentHashMap<Integer, Data>>() {}.getType();
+ ConcurrentHashMap<Integer, Data> concurrentHashMapDeserialized =
+ gson.fromJson(concurrentHashMapJson, concurrentHashMapType);
+ Type nullableConcurrentHashMapType =
+ new TypeToken<NullableConcurrentHashMap<Integer, Data>>() {}.getType();
+ NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMapDeserialized =
+ gson.fromJson(nullableConcurrentHashMapJson, nullableConcurrentHashMapType);
+
+ // Printing.
+ System.out.println(hashMap.getClass() == hashMapDeserialized.getClass());
+ System.out.println(hashMap.equals(hashMapDeserialized));
+ System.out.println(nullableHashMap.getClass() == nullableHashMapDeserialized.getClass());
+ System.out.println(nullableHashMap.equals(nullableHashMapDeserialized));
+ System.out.println(nullableMap.getClass() == nullableMapDeserialized.getClass());
+ System.out.println(nullableMap.equals(nullableMapDeserialized));
+ System.out.println(
+ nullableConcurrentMap.getClass() == nullableConcurrentMapDeserialized.getClass());
+ System.out.println(nullableConcurrentMap.equals(nullableConcurrentMapDeserialized));
+ System.out.println(concurrentHashMap.getClass() == concurrentHashMapDeserialized.getClass());
+ System.out.println(concurrentHashMap.equals(concurrentHashMapDeserialized));
+ System.out.println(
+ nullableConcurrentHashMap.getClass() == nullableConcurrentHashMapDeserialized.getClass());
+ System.out.println(nullableConcurrentHashMap.equals(nullableConcurrentHashMapDeserialized));
+ }
+
+ public static void fillMap(Map<Integer, Data> map) {
+ map.put(1, new Data(1, "a"));
+ map.put(2, new Data(2, "b"));
+ map.put(3, new Data(3, "c"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
index 29bb1cf..249f26f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
@@ -1,14 +1,14 @@
// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
+// 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.desugar.desugaredlibrary;
+package com.android.tools.r8.desugar.desugaredlibrary.gson;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import dalvik.system.PathClassLoader;
import java.sql.SQLDataException;
@@ -32,7 +32,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class LibrarySubclassInterfaceTest extends DesugaredLibraryTestBase {
+public class GetGenericInterfaceTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
@@ -43,7 +43,7 @@
BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
}
- public LibrarySubclassInterfaceTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ public GetGenericInterfaceTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@@ -53,7 +53,7 @@
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdOut =
testForD8()
- .addInnerClasses(LibrarySubclassInterfaceTest.class)
+ .addInnerClasses(GetGenericInterfaceTest.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
@@ -73,7 +73,7 @@
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdOut =
testForR8(Backend.DEX)
- .addInnerClasses(LibrarySubclassInterfaceTest.class)
+ .addInnerClasses(GetGenericInterfaceTest.class)
.addKeepMainRule(Executor.class)
.noMinification()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java
new file mode 100644
index 0000000..7938465
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GsonAllMapsTest extends GsonDesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final String[] EXPECTED_RESULT =
+ new String[] {
+ "true", "true", "true", "true", "true", "true", "true", "true", "true", "true", "true",
+ "true"
+ };
+
+ @Parameters(name = "shrinkDesugaredLibrary: {0}, runtime: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public GsonAllMapsTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testGsonMapD8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(AllMapsTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get() + GSON_LIBRARY_KEEP_RULES,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), AllMapsTestClass.class)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testGsonMapR8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(AllMapsTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .addKeepMainRule(AllMapsTestClass.class)
+ .addKeepRuleFiles(GSON_CONFIGURATION)
+ .allowUnusedProguardConfigurationRules()
+ .allowDiagnosticMessages()
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get() + GSON_LIBRARY_KEEP_RULES,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), AllMapsTestClass.class)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonDesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonDesugaredLibraryTestBase.java
new file mode 100644
index 0000000..e761248
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonDesugaredLibraryTestBase.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public abstract class GsonDesugaredLibraryTestBase extends DesugaredLibraryTestBase {
+ protected static final Path GSON_CONFIGURATION =
+ Paths.get("src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg");
+ protected static final Path GSON_2_8_1_JAR = Paths.get("third_party/iosched_2019/gson-2.8.1.jar");
+ // We only need here, for all subclasses of java.util.Collection and java.util.Map,
+ // to keep the signature attribute.
+ protected static final String GSON_LIBRARY_KEEP_RULES =
+ "-keepattributes Signature\n"
+ + "-keepattributes EnclosingMethod\n"
+ + "-keepattributes InnerClasses\n";
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java
deleted file mode 100644
index fa5c6d3..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2020, 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.desugar.desugaredlibrary.gson;
-
-import com.android.tools.r8.R8TestRunResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.desugar.desugaredlibrary.gson.TestClasses.TestClass;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import org.junit.Assume;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class GsonMapTest extends DesugaredLibraryTestBase {
-
- private final TestParameters parameters;
- private final boolean shrinkDesugaredLibrary;
- private static final Path GSON_CONFIGURATION =
- Paths.get("src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg");
- private static final Path GSON_2_8_1_JAR = Paths.get("third_party/iosched_2019/gson-2.8.1.jar");
-
- @Parameters(name = "shrinkDesugaredLibrary: {0}, runtime: {1}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
- }
-
- public GsonMapTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
- this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
- this.parameters = parameters;
- }
-
- @Test
- public void testGsonMap() throws Exception {
- Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
- KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
- R8TestRunResult runResult =
- testForR8(parameters.getBackend())
- .addProgramClassesAndInnerClasses(TestClasses.class)
- .addProgramFiles(GSON_2_8_1_JAR)
- .addKeepMainRule(TestClass.class)
- .addKeepRuleFiles(GSON_CONFIGURATION)
- .allowUnusedProguardConfigurationRules()
- .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
- .allowDiagnosticMessages()
- .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .setMinApi(parameters.getApiLevel())
- .compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibrary,
- parameters.getApiLevel(),
- keepRuleConsumer.get(),
- shrinkDesugaredLibrary)
- .run(parameters.getRuntime(), TestClass.class);
- // TODO(b/167649682): Should be always true.
- // .assertSuccessWithOutputLines("true", "true", "true", "true", "true");
- if (shrinkDesugaredLibrary) {
- runResult.assertSuccessWithOutputLines("true", "true", "false", "false", "false");
- } else {
- runResult.assertSuccessWithOutputLines("true", "true", "false", "true", "false");
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java
new file mode 100644
index 0000000..6b75eb9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class GsonOptionalTest extends GsonDesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameterized.Parameters(name = "shrinkDesugaredLibrary: {0}, runtime: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public GsonOptionalTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testGsonOptionalD8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(OptionalTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get() + GSON_LIBRARY_KEEP_RULES,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), OptionalTestClass.class)
+ .assertSuccessWithOutputLines("true", "true");
+ }
+
+ @Test
+ public void testGsonOptionalR8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(OptionalTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .addKeepMainRule(OptionalTestClass.class)
+ .addKeepRuleFiles(GSON_CONFIGURATION)
+ .allowUnusedProguardConfigurationRules()
+ .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .allowDiagnosticMessages()
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get() + GSON_LIBRARY_KEEP_RULES,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), OptionalTestClass.class)
+ .assertSuccessWithOutputLines("true", "true");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/OptionalTestClass.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/OptionalTestClass.java
new file mode 100644
index 0000000..fbc2c31
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/OptionalTestClass.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Objects;
+import java.util.Optional;
+
+public class OptionalTestClass {
+ static class Data {
+ final int id;
+ final String name;
+
+ Data(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Data)) return false;
+ Data data = (Data) o;
+ return id == data.id && name.equals(data.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+
+ @Override
+ public String toString() {
+ return "Data{" + "id=" + id + ", name='" + name + '\'' + '}';
+ }
+ }
+
+ static class OptionalAdapter<T> extends TypeAdapter<Optional<T>> {
+ private final TypeAdapter<T> delegate;
+
+ public OptionalAdapter(TypeAdapter<T> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void write(JsonWriter out, Optional<T> value) throws IOException {
+ if (!value.isPresent()) {
+ out.nullValue();
+ return;
+ }
+ delegate.write(out, value.get());
+ }
+
+ @Override
+ public Optional<T> read(JsonReader in) throws IOException {
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return Optional.empty();
+ }
+ return Optional.of(delegate.read(in));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static OptionalAdapter getInstance(TypeToken typeToken) {
+ TypeAdapter delegate;
+ Type type = typeToken.getType();
+ assert type instanceof ParameterizedType;
+ Type innerType = ((ParameterizedType) type).getActualTypeArguments()[0];
+ delegate = new Gson().getAdapter(TypeToken.get(innerType));
+ return new OptionalAdapter<>(delegate);
+ }
+ }
+
+ public static void main(String[] args) {
+ GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapter(
+ Optional.class, OptionalAdapter.getInstance(new TypeToken<Optional<Data>>() {}));
+ Gson gson = builder.create();
+ Optional<Data> optionalData = Optional.of(new Data(1, "a"));
+ String optionalDataSerialized = gson.toJson(optionalData);
+ Optional<Data> optionalDataDeserialized = gson.fromJson(optionalDataSerialized, Optional.class);
+ System.out.println(optionalData.getClass() == optionalDataDeserialized.getClass());
+ System.out.println(optionalData.equals(optionalDataDeserialized));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java
deleted file mode 100644
index 7b57814..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright (c) 2020, 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.desugar.desugaredlibrary.gson;
-
-import com.google.gson.Gson;
-import com.google.gson.reflect.TypeToken;
-import java.lang.reflect.Type;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class TestClasses {
-
- // Program class extending ConcurrentHashMap.
- static class NullableConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
- NullableConcurrentHashMap() {
- super();
- }
-
- @SuppressWarnings("NullableProblems")
- @Override
- public V put(K key, V value) {
- if (key == null || value == null) {
- return null;
- }
- return super.put(key, value);
- }
-
- @Override
- public void putAll(Map<? extends K, ? extends V> m) {
- for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- // Program class extending the library class HashMap, implementing Map.
- static class NullableHashMap<K, V> extends HashMap<K, V> {
- NullableHashMap() {
- super();
- }
-
- @SuppressWarnings("NullableProblems")
- @Override
- public V put(K key, V value) {
- if (key == null || value == null) {
- return null;
- }
- return super.put(key, value);
- }
-
- @Override
- public void putAll(Map<? extends K, ? extends V> m) {
- for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- // Program class implementing Map.
- static class NullableMap<K, V> implements Map<K, V> {
- private Map<K, V> map = new HashMap<>();
-
- NullableMap() {
- super();
- }
-
- @Override
- public int size() {
- return map.size();
- }
-
- @Override
- public boolean isEmpty() {
- return map.isEmpty();
- }
-
- @Override
- public boolean containsKey(Object key) {
- return map.containsKey(key);
- }
-
- @Override
- public boolean containsValue(Object value) {
- return map.containsValue(value);
- }
-
- @Override
- public V get(Object key) {
- return map.get(key);
- }
-
- @Nullable
- @Override
- public V put(K key, V value) {
- return map.put(key, value);
- }
-
- @Override
- public V remove(Object key) {
- return map.remove(key);
- }
-
- @Override
- public void putAll(@NotNull Map<? extends K, ? extends V> m) {
- for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
-
- @Override
- public void clear() {
- map.clear();
- }
-
- @NotNull
- @Override
- public Set<K> keySet() {
- return map.keySet();
- }
-
- @NotNull
- @Override
- public Collection<V> values() {
- return map.values();
- }
-
- @NotNull
- @Override
- public Set<Entry<K, V>> entrySet() {
- return map.entrySet();
- }
- }
-
- static class Data {
- final int id;
- final String name;
-
- Data(int id, String name) {
- this.id = id;
- this.name = name;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof Data)) return false;
- Data data = (Data) o;
- return id == data.id && name.equals(data.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, name);
- }
-
- @Override
- public String toString() {
- return "Data{" + "id=" + id + ", name='" + name + '\'' + '}';
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- Gson gson = new Gson();
- HashMap<Integer, Data> hashMap = new HashMap<>();
- NullableHashMap<Integer, Data> nullableHashMap = new NullableHashMap<>();
- NullableMap<Integer, Data> nullableMap = new NullableMap<>();
- ConcurrentHashMap<Integer, Data> concurrentHashMap = new ConcurrentHashMap<>();
- NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMap =
- new NullableConcurrentHashMap<>();
-
- fillMap(hashMap);
- fillMap(nullableHashMap);
- fillMap(nullableMap);
- fillMap(concurrentHashMap);
- fillMap(nullableConcurrentHashMap);
-
- String hashMapJson = gson.toJson(hashMap);
- String nullableHashMapJson = gson.toJson(nullableHashMap);
- String nullableMapJson = gson.toJson(nullableMap);
- String concurrentHashMapJson = gson.toJson(concurrentHashMap);
- String nullableConcurrentHashMapJson = gson.toJson(nullableConcurrentHashMap);
-
- Type hashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
- HashMap<Integer, Data> hashMapDeserialized = gson.fromJson(hashMapJson, hashMapType);
- Type nullableHashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
- HashMap<Integer, Data> nullableHashMapDeserialized =
- gson.fromJson(nullableHashMapJson, nullableHashMapType);
- Type nullableMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
- HashMap<Integer, Data> nullableMapDeserialized =
- gson.fromJson(nullableMapJson, nullableMapType);
- Type concurrentHashMapType = new TypeToken<ConcurrentHashMap<Integer, Data>>() {}.getType();
- ConcurrentHashMap<Integer, Data> concurrentHashMapDeserialized =
- gson.fromJson(concurrentHashMapJson, concurrentHashMapType);
- Type nullableConcurrentHashMapType =
- new TypeToken<NullableConcurrentHashMap<Integer, Data>>() {}.getType();
- NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMapDeserialized =
- gson.fromJson(nullableConcurrentHashMapJson, nullableConcurrentHashMapType);
-
- System.out.println(hashMap.equals(hashMapDeserialized));
- System.out.println(nullableHashMap.equals(nullableHashMapDeserialized));
- System.out.println(nullableMap.equals(nullableMapDeserialized));
- System.out.println(concurrentHashMap.equals(concurrentHashMapDeserialized));
- System.out.println(nullableConcurrentHashMap.equals(nullableConcurrentHashMapDeserialized));
- }
-
- public static void fillMap(Map<Integer, Data> map) {
- map.put(1, new Data(1, "a"));
- map.put(2, new Data(2, "b"));
- map.put(3, new Data(3, "c"));
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
index 51f38a6..da7bcba 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
@@ -1,9 +1,8 @@
# Copyright (c) 2020, 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.
-
-# Gson uses generic type information stored in a class file when working with fields. R8
-# removes such information by default, so configure it to keep all of it.
+# Gson uses generic type information stored in a class file when working with fields.
+# R8 removes such information by default, so configure it to keep all of it.
-keepattributes Signature
-keepattributes EnclosingMethod
-keepattributes InnerClasses
@@ -14,10 +13,13 @@
# Gson specific classes
-dontwarn sun.misc.Unsafe
-#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.GsonMapTest.Data { <fields>; }
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.GsonMapTest.NullableConcurrentHashMap { <fields>; }
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$Data { <fields>; }
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableConcurrentHashMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableHashMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableConcurrentMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.OptionalTestClass$Data { <fields>; }
# Prevent R8 from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)