Merge commit '9f651b6c4bddddc1cb687e4e803f3393e5a1b8c0' into dev-release
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
index 52d878b..089a293 100644
--- a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
@@ -12,7 +12,6 @@
public class CustomConversionAsmRewriteDescription {
static final String WRAP_CONVERT = "wrap_convert";
- static final String INVERTED_WRAP_CONVERT = "inverted_wrap_convert";
static final String CONVERT = "convert";
private static final Set<String> ENUM_WRAP_CONVERT_OWNER =
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriter.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriter.java
index c20de96..0848c09 100644
--- a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriter.java
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriter.java
@@ -6,7 +6,6 @@
import static desugaredlibrary.AsmRewriter.ASM_VERSION;
import static desugaredlibrary.CustomConversionAsmRewriteDescription.CONVERT;
-import static desugaredlibrary.CustomConversionAsmRewriteDescription.INVERTED_WRAP_CONVERT;
import static desugaredlibrary.CustomConversionAsmRewriteDescription.WRAP_CONVERT;
import static desugaredlibrary.CustomConversionAsmRewriter.CustomConversionVersion.LEGACY;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
@@ -133,51 +132,46 @@
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String descriptor, boolean isInterface) {
- if (opcode == INVOKESTATIC) {
- if (name.equals(WRAP_CONVERT)) {
- convertInvoke(
- opcode,
- owner,
- descriptor,
- isInterface,
- javaWrapConvertOwnerMap,
- j$WrapConvertOwnerMap);
- return;
- }
- if (name.equals(INVERTED_WRAP_CONVERT)) {
- convertInvoke(
- opcode,
- owner,
- descriptor,
- isInterface,
- j$WrapConvertOwnerMap,
- javaWrapConvertOwnerMap);
- return;
- }
+ if (opcode == INVOKESTATIC && name.equals(WRAP_CONVERT)) {
+ convertInvoke(opcode, owner, descriptor, isInterface);
+ return;
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
- private void convertInvoke(
- int opcode,
- String owner,
- String descriptor,
- boolean isInterface,
- Map<String, String> javaMap,
- Map<String, String> j$Map) {
- if (!javaMap.containsKey(owner)
- || !j$Map.containsKey(owner)
- || !(owner.startsWith("java") || owner.startsWith("j$"))) {
- throw new RuntimeException("Cannot transform wrap_convert method for " + owner);
+ private String extractFirstArg(String descriptor) {
+ assert descriptor.startsWith("(L");
+ int end = descriptor.indexOf(';');
+ assert end > 2;
+ return descriptor.substring(2, end);
+ }
+
+ private void convertInvoke(int opcode, String owner, String descriptor, boolean isInterface) {
+ String firstArg = extractFirstArg(descriptor);
+ assert sameBaseName(firstArg, owner);
+ if (!javaWrapConvertOwnerMap.containsKey(owner)
+ || !j$WrapConvertOwnerMap.containsKey(owner)
+ || !(firstArg.startsWith("java") || firstArg.startsWith("j$"))) {
+ throw new RuntimeException(
+ "Cannot transform wrap_convert method for " + firstArg + " (owner: " + owner + ")");
}
- if (owner.startsWith("java")) {
- String newOwner = j$Map.get(owner);
+ if (firstArg.startsWith("java")) {
+ String newOwner = javaWrapConvertOwnerMap.get(owner);
super.visitMethodInsn(opcode, newOwner, CONVERT, descriptor, isInterface);
return;
}
- assert owner.startsWith("j$");
- String newOwner = javaMap.get(owner);
+ assert firstArg.startsWith("j$");
+ String newOwner = j$WrapConvertOwnerMap.get(owner);
super.visitMethodInsn(opcode, newOwner, CONVERT, descriptor, isInterface);
}
+
+ private boolean sameBaseName(String firstArg, String owner) {
+ assert owner.startsWith("j$");
+ if (firstArg.equals(owner)) {
+ return true;
+ }
+ String javaName = owner.replace("j$", "java");
+ return firstArg.equals(javaName);
+ }
}
}
diff --git a/compatibility-faq.md b/compatibility-faq.md
index 8ca09d5..5614f6f 100644
--- a/compatibility-faq.md
+++ b/compatibility-faq.md
@@ -25,7 +25,8 @@
- Default methods are not implicitly kept as abstract methods.
- Attributes (such as `Signature`) and annotations are only kept for classes,
methods and fields which are matched by keep rules even when `-keepattributes`
-is specified.
+is specified. The weakest rule that will keep annotations and attributes is
+`-keep[classmembers],allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class-specification`
Additionally, for attributes describing a relationship such as `InnerClass` and
`EnclosingMethod`, non-compat mode requires both endpoints being kept.
diff --git a/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java b/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
index 8db22b3..510d621 100644
--- a/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
+++ b/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
@@ -5,7 +5,7 @@
package j$.nio.file.spi;
public class FileSystemProvider {
- public static java.nio.file.spi.FileSystemProvider inverted_wrap_convert(
+ public static java.nio.file.spi.FileSystemProvider wrap_convert(
j$.nio.file.spi.FileSystemProvider provider) {
// Rewritten in ASM to the wrapper method.
return null;
diff --git a/src/library_desugar/java/j$/util/stream/Stream.java b/src/library_desugar/java/j$/util/stream/Stream.java
index dc5861d..7e06e4a 100644
--- a/src/library_desugar/java/j$/util/stream/Stream.java
+++ b/src/library_desugar/java/j$/util/stream/Stream.java
@@ -6,14 +6,6 @@
public class Stream<T> {
- public static java.util.stream.Stream<?> inverted_wrap_convert(j$.util.stream.Stream<?> stream) {
- return null;
- }
-
- public static j$.util.stream.Stream<?> inverted_wrap_convert(java.util.stream.Stream<?> stream) {
- return null;
- }
-
public static java.util.stream.Stream<?> wrap_convert(j$.util.stream.Stream<?> stream) {
return null;
}
diff --git a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
index 5be05a8..e34e571 100644
--- a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
+++ b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
@@ -29,7 +29,7 @@
Class.forName("java.nio.file.FileSystems");
j$.nio.file.FileSystem fileSystem = FileSystems.getDefault();
j$.nio.file.spi.FileSystemProvider provider = fileSystem.provider();
- return j$.nio.file.spi.FileSystemProvider.inverted_wrap_convert(provider);
+ return j$.nio.file.spi.FileSystemProvider.wrap_convert(provider);
} catch (ClassNotFoundException ignored) {
// We reach this path is API < 26.
}
diff --git a/src/library_desugar/java/java/util/stream/FlatMapApiFlips.java b/src/library_desugar/java/java/util/stream/FlatMapApiFlips.java
index b9e1b9e..ac81093 100644
--- a/src/library_desugar/java/java/util/stream/FlatMapApiFlips.java
+++ b/src/library_desugar/java/java/util/stream/FlatMapApiFlips.java
@@ -37,6 +37,7 @@
this.function = function;
}
+ @SuppressWarnings("unchecked")
private R flipStream(R maybeStream) {
if (maybeStream == null) {
return null;
@@ -89,6 +90,7 @@
this.function = function;
}
+ @SuppressWarnings("unchecked")
private R flipStream(R maybeStream) {
if (maybeStream == null) {
return null;
@@ -115,6 +117,7 @@
this.function = function;
}
+ @SuppressWarnings("unchecked")
private R flipStream(R maybeStream) {
if (maybeStream == null) {
return null;
@@ -143,6 +146,7 @@
this.function = function;
}
+ @SuppressWarnings("unchecked")
private R flipStream(R maybeStream) {
if (maybeStream == null) {
return null;
diff --git a/src/library_desugar/java/java/util/stream/StackWalkerApiFlips.java b/src/library_desugar/java/java/util/stream/StackWalkerApiFlips.java
index 300c5ad..0951704 100644
--- a/src/library_desugar/java/java/util/stream/StackWalkerApiFlips.java
+++ b/src/library_desugar/java/java/util/stream/StackWalkerApiFlips.java
@@ -22,17 +22,16 @@
this.function = function;
}
+ @SuppressWarnings("unchecked")
private T flipStream(T maybeStream) {
if (maybeStream == null) {
return null;
}
if (maybeStream instanceof java.util.stream.Stream<?>) {
- return (T)
- j$.util.stream.Stream.inverted_wrap_convert((java.util.stream.Stream<?>) maybeStream);
+ return (T) j$.util.stream.Stream.wrap_convert((java.util.stream.Stream<?>) maybeStream);
}
if (maybeStream instanceof j$.util.stream.Stream<?>) {
- return (T)
- j$.util.stream.Stream.inverted_wrap_convert((j$.util.stream.Stream<?>) maybeStream);
+ return (T) j$.util.stream.Stream.wrap_convert((j$.util.stream.Stream<?>) maybeStream);
}
throw exception("java.util.stream.Stream", maybeStream.getClass());
}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 9332de7..9cc01cc 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -22,7 +22,85 @@
]
},
{
+ "api_level_below_or_equal": 29,
+ "rewrite_prefix": {
+ "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
+ },
+ "wrapper_conversion": [
+ "java.util.concurrent.Flow$Publisher",
+ "java.util.concurrent.Flow$Subscriber",
+ "java.util.concurrent.Flow$Subscription"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 32,
+ "api_level_greater_or_equal": 24,
+ "emulate_interface": {
+ "java.util.Collection": "j$.util.Collection"
+ },
+ "dont_rewrite": [
+ "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
+ "java.util.Spliterator java.util.Collection#spliterator()",
+ "java.util.stream.Stream java.util.Collection#parallelStream()",
+ "java.util.stream.Stream java.util.Collection#stream()",
+ "void java.util.Collection#forEach(java.util.function.Consumer)"
+ ],
+ "rewrite_prefix": {
+ "java.util.stream.DesugarCollectors": "j$.util.stream.DesugarCollectors"
+ },
+ "retarget_method": {
+ "java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()": "java.util.stream.DesugarCollectors"
+ },
+ "amend_library_method": [
+ "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
+ ]
+ },
+ {
"api_level_below_or_equal": 30,
+ "api_level_greater_or_equal": 26,
+ "rewrite_prefix": {
+ "java.time.DesugarDuration": "j$.time.DesugarDuration",
+ "java.time.DesugarLocalTime": "j$.time.DesugarLocalTime"
+ },
+ "retarget_method": {
+ "long java.time.Duration#dividedBy(java.time.Duration)": "java.time.DesugarDuration",
+ "long java.time.Duration#toDaysPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toHoursPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toMillisPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toMinutesPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toNanosPart()": "java.time.DesugarDuration",
+ "long java.time.Duration#toSeconds()": "java.time.DesugarDuration",
+ "int java.time.Duration#toSecondsPart()": "java.time.DesugarDuration",
+ "java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)": "java.time.DesugarDuration",
+ "java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)": "java.time.DesugarLocalTime",
+ "long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)": "java.time.DesugarLocalTime"
+ },
+ "amend_library_method": [
+ "public long java.time.Duration#dividedBy(java.time.Duration)",
+ "public long java.time.Duration#toDaysPart()",
+ "public int java.time.Duration#toHoursPart()",
+ "public int java.time.Duration#toMillisPart()",
+ "public int java.time.Duration#toMinutesPart()",
+ "public int java.time.Duration#toNanosPart()",
+ "public long java.time.Duration#toSeconds()",
+ "public int java.time.Duration#toSecondsPart()",
+ "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
+ "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
+ "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 25,
"rewrite_prefix": {
"java.time.": "j$.time.",
"java.util.Desugar": "j$.util.Desugar"
@@ -50,31 +128,6 @@
}
},
{
- "api_level_below_or_equal": 29,
- "rewrite_prefix": {
- "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
- },
- "wrapper_conversion": [
- "java.util.concurrent.Flow$Publisher",
- "java.util.concurrent.Flow$Subscriber",
- "java.util.concurrent.Flow$Subscription"
- ]
- },
- {
- "api_level_below_or_equal": 32,
- "api_level_greater_or_equal": 24,
- "emulate_interface": {
- "java.util.Collection": "j$.util.Collection"
- },
- "dont_rewrite": [
- "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
- "java.util.Spliterator java.util.Collection#spliterator()",
- "java.util.stream.Stream java.util.Collection#parallelStream()",
- "java.util.stream.Stream java.util.Collection#stream()",
- "void java.util.Collection#forEach(java.util.function.Consumer)"
- ]
- },
- {
"api_level_below_or_equal": 23,
"rewrite_prefix": {
"java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
@@ -192,7 +245,7 @@
]
},
{
- "api_level_below_or_equal": 30,
+ "api_level_below_or_equal": 25,
"retarget_method": {
"java.time.Instant java.util.Calendar#toInstant()": "java.util.DesugarCalendar",
"java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate",
@@ -229,9 +282,6 @@
"sun.misc.Desugar": "j$.sun.misc.Desugar"
},
"rewrite_derived_prefix": {
- "java.time.": {
- "j$.time.": "java.time."
- },
"sun.misc.DesugarUnsafe": {
"jdk.internal.misc.Unsafe": "j$.sun.misc.DesugarUnsafe"
}
@@ -250,8 +300,13 @@
},
"amend_library_method": [
"private static boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)"
- ]
- },
+ ],
+ "rewrite_derived_prefix": {
+ "java.time.": {
+ "j$.time.": "java.time."
+ }
+ }
+ },
{
"api_level_below_or_equal": 23,
"rewrite_prefix": {
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
index c29c213..91f9a56 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
@@ -1,8 +1,8 @@
{
"configuration_format_version": 5,
- "group_id" : "com.tools.android",
- "artifact_id" : "desugar_jdk_libs",
- "version": "1.2.3",
+ "group_id": "com.tools.android",
+ "artifact_id": "desugar_jdk_libs",
+ "version": "1.2.4",
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -47,29 +47,9 @@
{
"api_level_below_or_equal": 23,
"rewrite_prefix": {
- "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic"
- }
- },
- {
- "api_level_below_or_equal": 23,
- "rewrite_prefix": {
+ "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
"java.util.function.": "j$.util.function."
- },
- "emulate_interface": {
- "java.lang.Iterable": "j$.lang.Iterable",
- "java.util.Collection": "j$.util.Collection",
- "java.util.Comparator": "j$.util.Comparator",
- "java.util.Iterator": "j$.util.Iterator",
- "java.util.List": "j$.util.List",
- "java.util.Map": "j$.util.Map",
- "java.util.Map$Entry": "j$.util.Map$Entry",
- "java.util.Set": "j$.util.Set",
- "java.util.SortedSet": "j$.util.SortedSet",
- "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
- },
- "dont_rewrite": [
- "java.util.Iterator#remove"
- ]
+ }
},
{
"api_level_below_or_equal": 10000,
@@ -90,6 +70,21 @@
"java.util.Arrays#stream": "java.util.DesugarArrays",
"java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet"
},
+ "emulate_interface": {
+ "java.lang.Iterable": "j$.lang.Iterable",
+ "java.util.Collection": "j$.util.Collection",
+ "java.util.Comparator": "j$.util.Comparator",
+ "java.util.Iterator": "j$.util.Iterator",
+ "java.util.List": "j$.util.List",
+ "java.util.Map": "j$.util.Map",
+ "java.util.Map$Entry": "j$.util.Map$Entry",
+ "java.util.Set": "j$.util.Set",
+ "java.util.SortedSet": "j$.util.SortedSet",
+ "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
+ },
+ "dont_rewrite": [
+ "java.util.Iterator#remove"
+ ],
"wrapper_conversion": [
"java.util.Spliterator",
"java.util.function.IntUnaryOperator",
@@ -234,4 +229,4 @@
"-dontwarn sun.misc.Unsafe",
"-dontwarn wrapper.**"
]
-}
\ No newline at end of file
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index 5a1d513..c62cb34 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -29,7 +29,85 @@
]
},
{
+ "api_level_below_or_equal": 29,
+ "rewrite_prefix": {
+ "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
+ },
+ "wrapper_conversion": [
+ "java.util.concurrent.Flow$Publisher",
+ "java.util.concurrent.Flow$Subscriber",
+ "java.util.concurrent.Flow$Subscription"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 32,
+ "api_level_greater_or_equal": 24,
+ "emulate_interface": {
+ "java.util.Collection": "j$.util.Collection"
+ },
+ "dont_rewrite": [
+ "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
+ "java.util.Spliterator java.util.Collection#spliterator()",
+ "java.util.stream.Stream java.util.Collection#parallelStream()",
+ "java.util.stream.Stream java.util.Collection#stream()",
+ "void java.util.Collection#forEach(java.util.function.Consumer)"
+ ],
+ "rewrite_prefix": {
+ "java.util.stream.DesugarCollectors": "j$.util.stream.DesugarCollectors"
+ },
+ "retarget_method": {
+ "java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)": "java.util.stream.DesugarCollectors",
+ "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()": "java.util.stream.DesugarCollectors"
+ },
+ "amend_library_method": [
+ "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
+ ]
+ },
+ {
"api_level_below_or_equal": 30,
+ "api_level_greater_or_equal": 26,
+ "rewrite_prefix": {
+ "java.time.DesugarDuration": "j$.time.DesugarDuration",
+ "java.time.DesugarLocalTime": "j$.time.DesugarLocalTime"
+ },
+ "retarget_method": {
+ "long java.time.Duration#dividedBy(java.time.Duration)": "java.time.DesugarDuration",
+ "long java.time.Duration#toDaysPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toHoursPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toMillisPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toMinutesPart()": "java.time.DesugarDuration",
+ "int java.time.Duration#toNanosPart()": "java.time.DesugarDuration",
+ "long java.time.Duration#toSeconds()": "java.time.DesugarDuration",
+ "int java.time.Duration#toSecondsPart()": "java.time.DesugarDuration",
+ "java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)": "java.time.DesugarDuration",
+ "java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)": "java.time.DesugarLocalTime",
+ "long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)": "java.time.DesugarLocalTime"
+ },
+ "amend_library_method": [
+ "public long java.time.Duration#dividedBy(java.time.Duration)",
+ "public long java.time.Duration#toDaysPart()",
+ "public int java.time.Duration#toHoursPart()",
+ "public int java.time.Duration#toMillisPart()",
+ "public int java.time.Duration#toMinutesPart()",
+ "public int java.time.Duration#toNanosPart()",
+ "public long java.time.Duration#toSeconds()",
+ "public int java.time.Duration#toSecondsPart()",
+ "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
+ "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
+ "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 25,
"rewrite_prefix": {
"java.time.": "j$.time.",
"java.util.Desugar": "j$.util.Desugar"
@@ -57,31 +135,6 @@
}
},
{
- "api_level_below_or_equal": 29,
- "rewrite_prefix": {
- "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
- },
- "wrapper_conversion": [
- "java.util.concurrent.Flow$Publisher",
- "java.util.concurrent.Flow$Subscriber",
- "java.util.concurrent.Flow$Subscription"
- ]
- },
- {
- "api_level_below_or_equal": 32,
- "api_level_greater_or_equal": 24,
- "emulate_interface": {
- "java.util.Collection": "j$.util.Collection"
- },
- "dont_rewrite": [
- "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
- "java.util.Spliterator java.util.Collection#spliterator()",
- "java.util.stream.Stream java.util.Collection#parallelStream()",
- "java.util.stream.Stream java.util.Collection#stream()",
- "void java.util.Collection#forEach(java.util.function.Consumer)"
- ]
- },
- {
"api_level_below_or_equal": 25,
"rewrite_prefix": {
"java.io.DesugarFile": "j$.io.DesugarFile",
@@ -193,13 +246,6 @@
}
},
{
- "api_level_below_or_equal": 32,
- "api_level_greater_or_equal": 24,
- "rewrite_prefix": {
- "java.util.stream.Collectors": "j$.util.stream.Collectors"
- }
- },
- {
"api_level_below_or_equal": 23,
"rewrite_prefix": {
"java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
@@ -344,7 +390,7 @@
]
},
{
- "api_level_below_or_equal": 30,
+ "api_level_below_or_equal": 25,
"retarget_method": {
"java.time.Instant java.util.Calendar#toInstant()": "java.util.DesugarCalendar",
"java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate",
@@ -390,19 +436,10 @@
"rewrite_prefix": {
"desugar.": "j$.desugar.",
"libcore.": "j$.libcore.",
- "java.lang.Desugar": "j$.lang.Desugar",
"sun.security.action.": "j$.sun.security.action."
}
},
{
- "api_level_below_or_equal": 30,
- "rewrite_derived_prefix": {
- "java.time.": {
- "j$.time.": "java.time."
- }
- }
- },
- {
"api_level_below_or_equal": 25,
"rewrite_prefix": {
"java.nio.channels.Desugar": "j$.nio.channels.Desugar",
@@ -450,6 +487,12 @@
},
"java.nio.file.Path": {
"j$.nio.file.Path": "java.nio.file.Path"
+ },
+ "java.time.": {
+ "j$.time.": "java.time."
+ },
+ "java.nio.file.WatchEvent": {
+ "j$.nio.file.WatchEvent": "java.nio.file.WatchEvent"
}
},
"retarget_method": {
@@ -510,6 +553,9 @@
},
{
"api_level_below_or_equal": 18,
+ "rewrite_prefix": {
+ "java.lang.DesugarCharacter": "j$.lang.DesugarCharacter"
+ },
"retarget_static_field": {
"sun.nio.cs.US_ASCII sun.nio.cs.US_ASCII#INSTANCE": "java.nio.charset.Charset java.nio.charset.StandardCharsets#US_ASCII",
"sun.nio.cs.ISO_8859_1 sun.nio.cs.ISO_8859_1#INSTANCE": "java.nio.charset.Charset java.nio.charset.StandardCharsets#ISO_8859_1",
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
index 021a904..8c90b56 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Timing.TimingMerger;
@@ -145,6 +146,8 @@
fileTiming.end();
timings.add(fileTiming);
}
+ updateStringIdsSizeAndOffset(dexOutputBuffer, sections);
+
merger.add(timings);
merger.end();
@@ -162,6 +165,39 @@
}
}
+ private void updateStringIdsSizeAndOffset(
+ DexOutputBuffer dexOutputBuffer, List<DexContainerSection> sections) {
+ // The last section has the shared string_ids table. Now it is written the final size and
+ // offset is known and the remaining sections can be updated to point to the shared table.
+ // This updates both the size and offset in the header and in the map.
+ DexContainerSection lastSection = ListUtils.last(sections);
+ int stringIdsSize = lastSection.getFileWriter().getMixedSectionOffsets().getStringData().size();
+ int stringIdsOffset = lastSection.getLayout().stringIdsOffset;
+ for (DexContainerSection section : sections) {
+ if (section != lastSection) {
+ dexOutputBuffer.moveTo(section.getLayout().headerOffset + Constants.STRING_IDS_SIZE_OFFSET);
+ dexOutputBuffer.putInt(stringIdsSize);
+ dexOutputBuffer.putInt(stringIdsOffset);
+ dexOutputBuffer.moveTo(section.getLayout().getMapOffset());
+ // Skip size.
+ dexOutputBuffer.getInt();
+ while (dexOutputBuffer.position() < section.getLayout().getEndOfFile()) {
+ int sectionType = dexOutputBuffer.getShort();
+ dexOutputBuffer.getShort(); // Skip unused.
+ if (sectionType == Constants.TYPE_STRING_ID_ITEM) {
+ dexOutputBuffer.putInt(stringIdsSize);
+ dexOutputBuffer.putInt(stringIdsOffset);
+ break;
+ } else {
+ // Skip size and offset for this type.
+ dexOutputBuffer.getInt();
+ dexOutputBuffer.getInt();
+ }
+ }
+ }
+ }
+ }
+
private DexContainerSection writeVirtualFileSection(
VirtualFile virtualFile,
Timing timing,
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
index e552456..d9cabaa 100644
--- a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -126,6 +126,14 @@
byteBuffer.putInt(anInteger);
}
+ public short getShort() {
+ return byteBuffer.getShort();
+ }
+
+ public int getInt() {
+ return byteBuffer.getInt();
+ }
+
public boolean assertZero() {
int pos = byteBuffer.position();
int i = byteBuffer.getInt();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index d05ea55..0db5825 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -131,7 +131,8 @@
field -> {
FieldAccessInfo accessInfo = fieldAccessInfos.get(field.getReference());
KeepFieldInfo keepInfo = appView.getKeepInfo(field);
- if (keepInfo.isPinned(appView.options()) || accessInfo.isWrittenFromMethodHandle()) {
+ if (keepInfo.isPinned(appView.options())
+ || (accessInfo != null && accessInfo.isWrittenFromMethodHandle())) {
fieldStates.put(field.getDefinition(), FieldState.unknown());
}
});
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
index f83ab04..167b91d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassResolutionResult;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
@@ -107,7 +108,18 @@
private DexLibraryClass getLibraryClass(DexReference reference) {
DexClass dexClass = definitions.contextIndependentDefinitionFor(reference.getContextType());
if (dexClass == null || !dexClass.isLibraryClass()) {
- // Consider just throwing an error.
+ // We can end up in situation where a class is rewritten at some API level but is used as
+ // a library class with a different min API level. We check here if there is a multiple
+ // resolution result.
+ ClassResolutionResult result =
+ definitions.contextIndependentDefinitionForWithResolutionResult(
+ reference.getContextType());
+ if (result.isMultipleClassResolutionResult()) {
+ DexClass alternativeClass = result.toAlternativeClass();
+ if (alternativeClass != null && alternativeClass.isLibraryClass()) {
+ return alternativeClass.asLibraryClass();
+ }
+ }
reporter.warning(
"Desugared library: Cannot amend library reference "
+ reference
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index eb710e9..91da4d9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -1,10 +1,11 @@
// Copyright (c) 2021, 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.itf;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.ALL;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.NONE;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfNew;
@@ -386,9 +387,10 @@
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.helper = new InterfaceDesugaringSyntheticHelper(appView);
- needsLibraryInfo =
- !appView.options().canUseDefaultAndStaticInterfaceMethods()
- && !appView.options().machineDesugaredLibrarySpecification.isEmpty();
+ assert desugaringMode != NONE;
+ assert desugaringMode == ALL
+ || !appView.options().machineDesugaredLibrarySpecification.isEmpty();
+ needsLibraryInfo = !appView.options().machineDesugaredLibrarySpecification.isEmpty();
this.isLiveMethod = isLiveMethod;
this.desugaringMode = desugaringMode;
}
@@ -454,7 +456,7 @@
}
// Add non-library default methods as well as those for desugared library classes.
if (!iface.isLibraryClass() || (needsLibraryInfo() && helper.isInDesugaredLibrary(iface))) {
- MethodSignatures signatures = getDefaultMethods(iface);
+ MethodSignatures signatures = getDefaultMethodsMatching(iface, m -> true);
interfaceInfo = interfaceInfo.withSignatures(signatures);
}
return interfaceInfo;
@@ -466,18 +468,29 @@
assert iface.superType == dexItemFactory.objectType;
assert helper.isEmulatedInterface(iface.type);
assert needsLibraryInfo();
- MethodSignatures signatures = getDefaultMethods(iface);
+ MethodSignatures signatures =
+ getDefaultMethodsMatching(
+ iface,
+ m ->
+ appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getEmulatedInterfaceEmulatedDispatchMethodDescriptor(m)
+ != null);
EmulatedInterfaceInfo emulatedInterfaceInfo =
new EmulatedInterfaceInfo(signatures, new EmulatedInterfaces(iface.type));
return interfaceInfo.withEmulatedInterfaceInfo(emulatedInterfaceInfo);
}
- private MethodSignatures getDefaultMethods(DexClass iface) {
+ private MethodSignatures getDefaultMethodsMatching(
+ DexClass iface, Predicate<DexMethod> predicate) {
assert iface.isInterface();
Set<Wrapper<DexMethod>> defaultMethods =
new HashSet<>(iface.getMethodCollection().numberOfVirtualMethods());
for (DexEncodedMethod method : iface.virtualMethods(DexEncodedMethod::isDefaultMethod)) {
- defaultMethods.add(equivalence.wrap(method.getReference()));
+ if (predicate.test(method.getReference())) {
+ defaultMethods.add(equivalence.wrap(method.getReference()));
+ }
}
return MethodSignatures.create(defaultMethods);
}
@@ -653,6 +666,7 @@
});
}
}
+
// 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(
@@ -803,7 +817,6 @@
}
// Construction of actual forwarding methods.
-
private void addSyntheticMethod(DexProgramClass clazz, DexEncodedMethod method) {
newSyntheticMethods
.computeIfAbsent(clazz, key -> ProgramMethodSet.create())
@@ -893,7 +906,6 @@
}
// Topological order traversal and its helpers.
-
private DexClass definitionOrNull(DexType type, ReportingContext context) {
// No forwards at the top of the class hierarchy (assuming java.lang.Object is never amended).
if (type == null || type == dexItemFactory.objectType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
index c764377..fd607ee 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.IterableUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -59,9 +60,8 @@
}
DexType newType = emulatedInterfaces.get(emulatedInterface.type);
assert newType != null;
- DexEncodedMethod[] newVirtualMethods =
- renameHolder(emulatedInterface.virtualMethods(), newType);
- DexEncodedMethod[] newDirectMethods = renameHolder(emulatedInterface.directMethods(), newType);
+ DexEncodedMethod[] newVirtualMethods = computeNewVirtualMethods(emulatedInterface, newType);
+ DexEncodedMethod[] newDirectMethods = DexEncodedMethod.EMPTY_ARRAY;
assert emulatedInterface.getSuperType() == appView.dexItemFactory().objectType;
assert !emulatedInterface.hasFields();
assert emulatedInterface.getNestHost() == null;
@@ -116,11 +116,24 @@
return newInterfaces;
}
- private DexEncodedMethod[] renameHolder(Iterable<DexEncodedMethod> methods, DexType newName) {
- List<DexEncodedMethod> methodArray = IterableUtils.toNewArrayList(methods);
+ private DexEncodedMethod[] computeNewVirtualMethods(
+ DexProgramClass emulatedInterface, DexType newName) {
+ Iterable<ProgramMethod> emulatedMethods =
+ emulatedInterface.virtualProgramMethods(
+ m ->
+ appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getEmulatedInterfaceEmulatedDispatchMethodDescriptor(m.getReference())
+ != null);
+ List<ProgramMethod> methodArray = IterableUtils.toNewArrayList(emulatedMethods);
DexEncodedMethod[] newMethods = new DexEncodedMethod[methodArray.size()];
for (int i = 0; i < newMethods.length; i++) {
- newMethods[i] = methodArray.get(i).toRenamedHolderMethod(newName, appView.dexItemFactory());
+ newMethods[i] =
+ methodArray
+ .get(i)
+ .getDefinition()
+ .toRenamedHolderMethod(newName, appView.dexItemFactory());
}
return newMethods;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 4d31397..6099074 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1980,8 +1980,13 @@
Map<BasicBlock, LinkedHashMap<Value, Instruction>> addConstantInBlock,
Predicate<Instruction> selector) {
InstructionListIterator iterator = block.listIterator(code);
+ boolean seenCompareExit = false;
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
+ if (options.canHaveCmpIfFloatBug() && instruction.isCmp()) {
+ seenCompareExit = true;
+ }
+
if (instruction.hasUnusedOutValue() || instruction.outValue().hasLocalInfo()) {
continue;
}
@@ -2008,6 +2013,7 @@
Instruction uniqueUse = instruction.outValue().singleUniqueUser();
Instruction next = iterator.next();
if (uniqueUse == next) {
+ iterator.previous();
continue;
}
if (next.hasOutValue()
@@ -2017,6 +2023,7 @@
Instruction nextNext = iterator.peekNext();
Instruction uniqueUseNext = next.outValue().singleUniqueUser();
if (uniqueUse == nextNext && uniqueUseNext == nextNext) {
+ iterator.previous();
continue;
}
}
@@ -2053,6 +2060,14 @@
}
}
+ // If the dominator block has a potential compare exit we will chose that as the insertion
+ // point. Uniquely for instructions having invalues this can be before the definition of them.
+ // Bail-out when this is the case. See b/251015885 for more information.
+ if (seenCompareExit
+ && Iterables.any(instruction.inValues(), x -> x.getBlock() == dominator)) {
+ continue;
+ }
+
Instruction copy;
switch (instruction.opcode()) {
case CONST_CLASS:
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 0184def..ea58acd 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -221,11 +221,13 @@
this.retraceMethodResult = retraceMethodResult;
this.methodReference = methodReference;
this.mapping = mapping;
- assert mapping != null;
}
@Override
public boolean isCompilerSynthesized() {
+ if (mapping == null) {
+ return false;
+ }
if (mapping.getMemberNaming() != null) {
return mapping.getMemberNaming().isCompilerSynthesized();
} else {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 09c87e2..30a8723 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2226,6 +2226,19 @@
return;
}
+ if (!clazz.isInterface()) {
+ throw appView
+ .reporter()
+ .fatalError(
+ "The class "
+ + implementer
+ + " implements the interface "
+ + type
+ + " but "
+ + type
+ + " is not an interface.");
+ }
+
if (!appView.options().enableUnusedInterfaceRemoval
|| rootSet.noUnusedInterfaceRemoval.contains(type)
|| mode.isMainDexTracing()) {
diff --git a/src/test/examplesJava10/collectors/Main.java b/src/test/examplesJava10/collectors/Main.java
new file mode 100644
index 0000000..a1b3d71
--- /dev/null
+++ b/src/test/examplesJava10/collectors/Main.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2022, 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 collectors;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class Main {
+
+ public static void main(String[] args) {
+ Collector<Object, ?, List<Object>> filtering =
+ Collectors.filtering(Objects::nonNull, Collectors.toList());
+ System.out.println(Stream.of(null, 1).collect(filtering).get(0));
+
+ Collector<List<?>, ?, List<Object>> collector =
+ Collectors.flatMapping(Collection::stream, Collectors.toList());
+ System.out.println(Stream.of(List.of(1)).collect(collector).get(0));
+
+ Collector<Object, ?, List<Object>> toList = Collectors.toUnmodifiableList();
+ System.out.println(Stream.of(1).collect(toList).get(0));
+ Collector<Object, ?, Set<Object>> toSet = Collectors.toUnmodifiableSet();
+ System.out.println(Stream.of(1).collect(toSet).iterator().next());
+ Collector<Object, ?, Map<String, Integer>> toMap1 =
+ Collectors.toUnmodifiableMap(Object::toString, Object::hashCode);
+ System.out.println(Stream.of(1).collect(toMap1).keySet().iterator().next());
+ Collector<Object, ?, Map<String, Integer>> toMap2 =
+ Collectors.toUnmodifiableMap(Object::toString, Object::hashCode, (x, y) -> x);
+ System.out.println(Stream.of(1).collect(toMap2).keySet().iterator().next());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java
new file mode 100644
index 0000000..50edd76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java
@@ -0,0 +1,139 @@
+// Copyright (c) 2022, 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a regression test for b/252558440. */
+@RunWith(Parameterized.class)
+public class ApiModelMockDalvikVerifyErrorTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private final AndroidApiLevel mockLevel = AndroidApiLevel.M;
+
+ private boolean isGreaterOrEqualToMockLevel() {
+ return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class, LibrarySub.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), mockLevel));
+ }
+
+ private boolean addToBootClasspath() {
+ return parameters.isCfRuntime()
+ || parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
+ .compile()
+ .inspect(this::inspect)
+ .applyIf(
+ addToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, LibrarySub.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (isGreaterOrEqualToMockLevel()) {
+ runResult.assertFailureWithErrorThatThrows(ClassCastException.class);
+ } else {
+ runResult.assertSuccessWithOutputLines("Hello World!");
+ }
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ }
+
+ public abstract static class LibraryClass {
+
+ public abstract void foo();
+ }
+
+ public static class LibrarySub extends LibraryClass {
+
+ @Override
+ public void foo() {
+ System.out.println("LibrarySub::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ LibraryClass librarySub = (LibraryClass) new Object();
+ librarySub.foo();
+ } else {
+ System.out.println("Hello World!");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index 872ca85..df80365 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -5,16 +5,20 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
-import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_LEGACY;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -22,6 +26,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
+import java.util.function.Predicate;
import java.util.stream.Stream;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,7 +44,7 @@
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
- getJdk8Jdk11(),
+ ImmutableList.of(JDK8, JDK11, JDK11_LEGACY),
SPECIFICATIONS_WITH_CF2CF);
}
@@ -82,16 +87,17 @@
}
MethodSubject direct =
inspector.clazz(Executor.class).uniqueMethodWithOriginalName("directTypes");
+ Predicate<InstructionSubject> matcher =
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
+ ? (instr ->
+ instr.toString().contains("$-EL")
+ || instr.toString().contains("Comparator$-CC")
+ || instr.toString().contains("Stream$-CC"))
+ : (instr ->
+ instr.toString().contains("$-EL") || instr.toString().contains("Comparator"));
if (libraryDesugaringSpecification.hasEmulatedInterfaceDesugaring(parameters)) {
assertTrue(
- direct
- .streamInstructions()
- .filter(InstructionSubject::isInvokeStatic)
- .allMatch(
- instr ->
- instr.toString().contains("$-EL")
- || instr.toString().contains("Comparator$-CC")
- || instr.toString().contains("Stream$-CC")));
+ direct.streamInstructions().filter(InstructionSubject::isInvokeStatic).allMatch(matcher));
} else {
assertTrue(direct.streamInstructions().noneMatch(instr -> instr.toString().contains("$-EL")));
}
@@ -106,11 +112,7 @@
inherited
.streamInstructions()
.filter(InstructionSubject::isInvokeStatic)
- .allMatch(
- instr ->
- instr.toString().contains("$-EL")
- || instr.toString().contains("Comparator$-CC")
- || instr.toString().contains("Stream$-CC")));
+ .allMatch(matcher));
inherited.streamInstructions().forEach(this::assertInheritedDispatchCorrect);
}
@@ -118,16 +120,15 @@
if (!instructionSubject.isConstString(JumboStringMode.ALLOW)) {
for (String s : new String[] {">stream", "spliterator", "sort"}) {
if (instructionSubject.toString().contains(s)) {
- assertTrue(instructionSubject.isInvokeStatic());
- assertTrue(
- instructionSubject.toString().contains("$-EL")
- || instructionSubject.toString().contains("Comparator$-CC"));
+ assertTrue(instructionSubject.isInvokeStatic());
+ assertTrue(
+ instructionSubject.toString().contains("$-EL")
+ || instructionSubject.toString().contains("Comparator$-CC"));
}
}
}
}
-
static class Executor {
// In directTypes() the collections use directly their type which implements a j$ interface
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index 2838f2c..76bd656 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -15,20 +15,39 @@
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.hamcrest.CoreMatchers;
import org.junit.Assume;
import org.junit.Test;
@@ -39,6 +58,30 @@
@RunWith(Parameterized.class)
public class DesugaredLibraryContentTest extends DesugaredLibraryTestBase {
+ // The class sun.misc.Unsafe is runnable on Android despite not being in android.jar.
+ private static final Set<String> ALLOWED_MISSING_HOLDER = ImmutableSet.of("sun.misc.Unsafe");
+ private static final Set<String> ALLOWED_MISSING_METHOD =
+ ImmutableSet.of(
+ // The takeWhile/dropWhile methods are present in the wrappers but not yet present on
+ // android.jar
+ // leading to NoSuchMethod errors, yet, we keep them for subsequent android versions.
+ "java.util.stream.IntStream"
+ + " java.util.stream.IntStream.dropWhile(java.util.function.IntPredicate)",
+ "java.util.stream.Stream java.util.stream.Stream.takeWhile(java.util.function.Predicate)",
+ "java.util.stream.LongStream"
+ + " java.util.stream.LongStream.dropWhile(java.util.function.LongPredicate)",
+ "java.util.stream.DoubleStream"
+ + " java.util.stream.DoubleStream.takeWhile(java.util.function.DoublePredicate)",
+ "java.util.stream.IntStream"
+ + " java.util.stream.IntStream.takeWhile(java.util.function.IntPredicate)",
+ "java.util.stream.Stream java.util.stream.Stream.dropWhile(java.util.function.Predicate)",
+ "java.util.stream.LongStream"
+ + " java.util.stream.LongStream.takeWhile(java.util.function.LongPredicate)",
+ "java.util.stream.DoubleStream"
+ + " java.util.stream.DoubleStream.dropWhile(java.util.function.DoublePredicate)",
+ // FileStore.getBlockSize() was added in 33 which confuses the required library (30).
+ "long java.nio.file.FileStore.getBlockSize()");
+
private final TestParameters parameters;
private final CompilationSpecification compilationSpecification;
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -67,7 +110,63 @@
.apply(libraryDesugaringSpecification::configureL8TestBuilder)
.compile()
.assertNoMessages()
- .inspect(this::assertCorrect);
+ .inspect(this::assertCorrect)
+ .inspect(this::assertAllInvokeResolve);
+ }
+
+ private void assertAllInvokeResolve(CodeInspector inspector) throws IOException {
+ AndroidApp build =
+ AndroidApp.builder()
+ .addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
+ .build();
+ DirectMappedDexApplication libHolder =
+ new ApplicationReader(build, inspector.getApplication().options, Timing.empty())
+ .read()
+ .toDirect();
+ DirectMappedDexApplication finalApp =
+ inspector
+ .getApplication()
+ .toDirect()
+ .builder()
+ .replaceLibraryClasses(libHolder.libraryClasses())
+ .build();
+ AppInfoWithClassHierarchy appInfo =
+ AppView.createForD8(
+ AppInfo.createInitialAppInfo(
+ finalApp, GlobalSyntheticsStrategy.forNonSynthesizing()))
+ .appInfoForDesugaring();
+ Map<DexMethod, Object> failures = new IdentityHashMap<>();
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ for (FoundMethodSubject method : clazz.allMethods()) {
+ if (method.hasCode()) {
+ for (InstructionSubject instruction : method.instructions(InstructionSubject::isInvoke)) {
+ assertInvokeResolution(instruction, appInfo, method, failures);
+ }
+ }
+ }
+ }
+ for (DexMethod dexMethod : new HashSet<>(failures.keySet())) {
+ if (ALLOWED_MISSING_HOLDER.contains(dexMethod.getHolderType().toString())) {
+ failures.remove(dexMethod);
+ } else if (ALLOWED_MISSING_METHOD.contains(dexMethod.toString())) {
+ failures.remove(dexMethod);
+ }
+ }
+ assertTrue(failures.isEmpty());
+ }
+
+ private void assertInvokeResolution(
+ InstructionSubject instruction,
+ AppInfoWithClassHierarchy appInfo,
+ FoundMethodSubject context,
+ Map<DexMethod, Object> failures) {
+ DexMethod method = instruction.getMethod();
+ assert method != null;
+ MethodResolutionResult methodResolutionResult =
+ appInfo.unsafeResolveMethodDueToDexFormatLegacy(method);
+ if (methodResolutionResult.isFailedResolution()) {
+ failures.put(method, new Pair(context, methodResolutionResult));
+ }
}
@Test
@@ -110,7 +209,7 @@
assertThat(
clazz.getOriginalName(),
CoreMatchers.anyOf(startsWith("j$."), startsWith("java."))));
- if (parameters.getApiLevel().getLevel() <= AndroidApiLevel.R.getLevel()) {
+ if (parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel()) {
assertThat(inspector.clazz("j$.time.Clock"), isPresent());
}
// Above N the following classes are removed instead of being desugared.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java
index cf2079e..09bb322 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java
@@ -6,13 +6,20 @@
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -36,6 +43,21 @@
@RunWith(Parameterized.class)
public class JavaTimeJDK11Test extends DesugaredLibraryTestBase {
+ private static AndroidApiLevel INTRODUCTION_LEVEL = AndroidApiLevel.S;
+ private static Set<String> DURATION_VIRTUAL_INVOKES =
+ ImmutableSet.of(
+ "toDaysPart",
+ "toHoursPart",
+ "toMillisPart",
+ "toMinutesPart",
+ "toNanosPart",
+ "toSeconds",
+ "toSecondsPart",
+ "dividedBy",
+ "truncatedTo");
+ private static Set<String> LOCAL_TIME_VIRTUAL_INVOKES = ImmutableSet.of("toEpochSecond");
+ private static Set<String> LOCAL_TIME_STATIC_INVOKES = ImmutableSet.of("ofInstant");
+
private final TestParameters parameters;
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
private final CompilationSpecification compilationSpecification;
@@ -47,7 +69,7 @@
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
- ImmutableList.of(JDK11),
+ ImmutableList.of(JDK11, JDK11_PATH),
DEFAULT_SPECIFICATIONS);
}
@@ -61,26 +83,48 @@
}
@Test
- public void test() throws Exception {
+ public void test() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramClassFileData(getProgramClassFileData())
.addKeepMainRule(TestClass.class)
+ .compile()
+ .inspect(this::assertCalls)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
+ private void assertCalls(CodeInspector inspector) {
+ MethodSubject mainMethod = inspector.clazz(TestClass.class).mainMethod();
+ assertTrue(mainMethod.isPresent());
+ mainMethod
+ .streamInstructions()
+ .forEach(
+ i -> {
+ if (i.isInvoke()) {
+ if (libraryDesugaringSpecification.hasTimeDesugaring(parameters)) {
+ checkInvokeTime(i, "j$.time.Duration", "j$.time.LocalTime");
+ return;
+ }
+ if (parameters.getApiLevel().isLessThan(INTRODUCTION_LEVEL)) {
+ checkInvokeTime(i, "j$.time.DesugarDuration", "j$.time.DesugarLocalTime");
+ return;
+ }
+ checkInvokeTime(i, "java.time.Duration", "java.time.LocalTime");
+ }
+ });
+ }
+
+ private void checkInvokeTime(InstructionSubject i, String duration, String localTime) {
+ String name = i.getMethod().getName().toString();
+ if (DURATION_VIRTUAL_INVOKES.contains(name)) {
+ assertEquals(duration, i.getMethod().getHolderType().toString());
+ }
+ if (LOCAL_TIME_STATIC_INVOKES.contains(name) || LOCAL_TIME_VIRTUAL_INVOKES.contains(name)) {
+ assertEquals(localTime, i.getMethod().getHolderType().toString());
+ }
+ }
+
private Collection<byte[]> getProgramClassFileData() throws IOException {
- Set<String> methodsToRewriteToDurationVirtualInvoke =
- ImmutableSet.of(
- "toDaysPart",
- "toHoursPart",
- "toMillisPart",
- "toMinutesPart",
- "toNanosPart",
- "toSeconds",
- "toSecondsPart",
- "dividedBy",
- "truncatedTo");
return ImmutableList.of(
transformer(TestClass.class)
.addMethodTransformer(
@@ -92,29 +136,30 @@
String name,
String descriptor,
boolean isInterface) {
- if (opcode == Opcodes.INVOKESTATIC
- && methodsToRewriteToDurationVirtualInvoke.contains(name)) {
- super.visitMethodInsn(
- Opcodes.INVOKEVIRTUAL,
- "java/time/Duration",
- name,
- withoutFirstObjectArg(descriptor),
- isInterface);
- return;
- }
- if (opcode == Opcodes.INVOKESTATIC && name.equals("ofInstant")) {
- super.visitMethodInsn(
- opcode, "java/time/LocalTime", name, descriptor, isInterface);
- return;
- }
- if (opcode == Opcodes.INVOKESTATIC && name.equals("toEpochSecond")) {
- super.visitMethodInsn(
- Opcodes.INVOKEVIRTUAL,
- "java/time/LocalTime",
- name,
- withoutFirstObjectArg(descriptor),
- isInterface);
- return;
+ if (opcode == Opcodes.INVOKESTATIC) {
+ if (DURATION_VIRTUAL_INVOKES.contains(name)) {
+ super.visitMethodInsn(
+ Opcodes.INVOKEVIRTUAL,
+ "java/time/Duration",
+ name,
+ withoutFirstObjectArg(descriptor),
+ isInterface);
+ return;
+ }
+ if (LOCAL_TIME_VIRTUAL_INVOKES.contains(name)) {
+ super.visitMethodInsn(
+ Opcodes.INVOKEVIRTUAL,
+ "java/time/LocalTime",
+ name,
+ withoutFirstObjectArg(descriptor),
+ isInterface);
+ return;
+ }
+ if (LOCAL_TIME_STATIC_INVOKES.contains(name)) {
+ super.visitMethodInsn(
+ opcode, "java/time/LocalTime", name, descriptor, isInterface);
+ return;
+ }
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/NewCollectorsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/NewCollectorsTest.java
new file mode 100644
index 0000000..4c77e47
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/NewCollectorsTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2022, 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.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+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 NewCollectorsTest extends DesugaredLibraryTestBase {
+
+ private static final AndroidApiLevel NEW_COLLECTORS_LEVEL = AndroidApiLevel.T;
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final Path INPUT_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR + "collectors.jar");
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("1", "1", "1", "1", "1", "1");
+ private static final String MAIN_CLASS = "collectors.Main";
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ ImmutableList.of(JDK11, JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public NewCollectorsTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .compile()
+ .inspect(this::assertCollectors)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private void assertCollectors(CodeInspector inspector) {
+ MethodSubject methodSubject = inspector.clazz(MAIN_CLASS).mainMethod();
+ assertTrue(methodSubject.isPresent());
+ if (libraryDesugaringSpecification.hasEmulatedInterfaceDesugaring(parameters)) {
+ // Collectors is not present, all calls to the j$ version.
+ assertTrue(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.Collectors"));
+ assertFalse(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.DesugarCollectors"));
+ assertFalse(anyStaticInvokeToHolder(methodSubject, "java.util.stream.Collectors"));
+ return;
+ }
+ if (parameters.getApiLevel().isLessThan(NEW_COLLECTORS_LEVEL)) {
+ // Collectors is present, but partially, calls to java Collectors and DesugarCollectors.
+ assertFalse(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.Collectors"));
+ assertTrue(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.DesugarCollectors"));
+ assertTrue(anyStaticInvokeToHolder(methodSubject, "java.util.stream.Collectors"));
+ return;
+ }
+ // Collectors is fully present, all calls to java Collectors.
+ assertFalse(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.Collectors"));
+ assertFalse(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.DesugarCollectors"));
+ assertTrue(anyStaticInvokeToHolder(methodSubject, "java.util.stream.Collectors"));
+ }
+
+ private boolean anyStaticInvokeToHolder(MethodSubject methodSubject, String holder) {
+ return methodSubject
+ .streamInstructions()
+ .anyMatch(
+ i -> i.isInvokeStatic() && i.getMethod().getHolderType().toString().equals(holder));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
index 9ccafc8..8085bf6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
@@ -34,12 +34,13 @@
public class LibraryDesugaringSpecification {
public static Descriptor JDK8_DESCRIPTOR = new Descriptor(24, 26, -1, 26, 24);
- public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 30, -1, 32, -1);
+ public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 26, -1, 32, -1);
public static Descriptor EMPTY_DESCRIPTOR_24 = new Descriptor(-1, -1, -1, 24, -1);
- public static Descriptor JDK11_PATH_DESCRIPTOR = new Descriptor(24, 30, 26, 32, -1);
- public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(24, 10000, -1, 10000, 24);
+ public static Descriptor JDK11_PATH_DESCRIPTOR = new Descriptor(24, 26, 26, 32, -1);
+ public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(10000, 10000, -1, 10000, 24);
private static class Descriptor {
+
// Above this level emulated interface are not *entirely* desugared.
private final int emulatedInterfaceDesugaring;
// Above this level java.time is not *entirely* desugared.
diff --git a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
index af1ee96..6f68f53 100644
--- a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
+++ b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
@@ -3,6 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex.container;
+import static com.android.tools.r8.dex.Constants.DATA_OFF_OFFSET;
+import static com.android.tools.r8.dex.Constants.DATA_SIZE_OFFSET;
+import static com.android.tools.r8.dex.Constants.MAP_OFF_OFFSET;
+import static com.android.tools.r8.dex.Constants.STRING_IDS_OFF_OFFSET;
+import static com.android.tools.r8.dex.Constants.STRING_IDS_SIZE_OFFSET;
+import static com.android.tools.r8.dex.Constants.TYPE_STRING_ID_ITEM;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ByteDataView;
@@ -10,6 +16,9 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dex.CompatByteBuffer;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.maindexlist.MainDexListTests;
import com.android.tools.r8.transformers.ClassTransformer;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -17,7 +26,10 @@
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
import java.io.IOException;
+import java.nio.ByteOrder;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -91,8 +103,7 @@
options -> options.getTestingOptions().dexContainerExperiment = true)
.compile()
.writeToZip();
- List<byte[]> dexFromDexing = unzipContent(outputFromDexing);
- assertEquals(1, dexFromDexing.size());
+ validateSingleContainerDex(outputFromDexing);
}
@Test
@@ -105,7 +116,7 @@
options -> options.getTestingOptions().dexContainerExperiment = true)
.compile()
.writeToZip();
- assertEquals(1, unzipContent(outputA).size());
+ validateSingleContainerDex(outputA);
Path outputB =
testForD8(Backend.DEX)
@@ -115,7 +126,84 @@
options -> options.getTestingOptions().dexContainerExperiment = true)
.compile()
.writeToZip();
- assertEquals(1, unzipContent(outputB).size());
+ validateSingleContainerDex(outputB);
+ }
+
+ private void validateSingleContainerDex(Path output) throws IOException {
+ List<byte[]> dexes = unzipContent(output);
+ assertEquals(1, dexes.size());
+ validateStringIdsSizeAndOffsets(dexes.get(0));
+ }
+
+ private void validateStringIdsSizeAndOffsets(byte[] dex) {
+ CompatByteBuffer buffer = CompatByteBuffer.wrap(dex);
+ setByteOrder(buffer);
+
+ IntList sections = new IntArrayList();
+ int offset = 0;
+ while (offset < buffer.capacity()) {
+ sections.add(offset);
+ int dataSize = buffer.getInt(offset + DATA_SIZE_OFFSET);
+ int dataOffset = buffer.getInt(offset + DATA_OFF_OFFSET);
+ offset = dataOffset + dataSize;
+ }
+ assertEquals(buffer.capacity(), offset);
+
+ int lastOffset = sections.getInt(sections.size() - 1);
+ int stringIdsSize = buffer.getInt(lastOffset + STRING_IDS_SIZE_OFFSET);
+ int stringIdsOffset = buffer.getInt(lastOffset + STRING_IDS_OFF_OFFSET);
+
+ for (Integer sectionOffset : sections) {
+ assertEquals(stringIdsSize, buffer.getInt(sectionOffset + STRING_IDS_SIZE_OFFSET));
+ assertEquals(stringIdsOffset, buffer.getInt(sectionOffset + STRING_IDS_OFF_OFFSET));
+ assertEquals(stringIdsSize, getSizeFromMap(TYPE_STRING_ID_ITEM, buffer, sectionOffset));
+ assertEquals(stringIdsOffset, getOffsetFromMap(TYPE_STRING_ID_ITEM, buffer, sectionOffset));
+ }
+ }
+
+ private int getSizeFromMap(int type, CompatByteBuffer buffer, int offset) {
+ int mapOffset = buffer.getInt(offset + MAP_OFF_OFFSET);
+ buffer.position(mapOffset);
+ int mapSize = buffer.getInt();
+ for (int i = 0; i < mapSize; i++) {
+ int sectionType = buffer.getShort();
+ buffer.getShort(); // Skip unused.
+ int sectionSize = buffer.getInt();
+ buffer.getInt(); // Skip offset.
+ if (type == sectionType) {
+ return sectionSize;
+ }
+ }
+ throw new RuntimeException("Not found");
+ }
+
+ private int getOffsetFromMap(int type, CompatByteBuffer buffer, int offset) {
+ int mapOffset = buffer.getInt(offset + MAP_OFF_OFFSET);
+ buffer.position(mapOffset);
+ int mapSize = buffer.getInt();
+ for (int i = 0; i < mapSize; i++) {
+ int sectionType = buffer.getShort();
+ buffer.getShort(); // Skip unused.
+ buffer.getInt(); // SKip size.
+ int sectionOffset = buffer.getInt();
+ if (type == sectionType) {
+ return sectionOffset;
+ }
+ }
+ throw new RuntimeException("Not found");
+ }
+
+ private void setByteOrder(CompatByteBuffer buffer) {
+ // Make sure we set the right endian for reading.
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ int endian = buffer.getInt(Constants.ENDIAN_TAG_OFFSET);
+ if (endian == Constants.REVERSE_ENDIAN_CONSTANT) {
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ } else {
+ if (endian != Constants.ENDIAN_CONSTANT) {
+ throw new CompilationError("Unable to determine endianess for reading dex file.");
+ }
+ }
}
private List<byte[]> unzipContent(Path zip) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnCheckCastCompareLongTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnCheckCastCompareLongTest.java
index c2721f1..f94bba3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnCheckCastCompareLongTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnCheckCastCompareLongTest.java
@@ -4,16 +4,10 @@
package com.android.tools.r8.ir.optimize.canonicalization;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThrows;
-
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -49,29 +43,15 @@
// For R8 we will try to shorten the live range and we have a bailout to find an insertion
// point if we see a cmp instruction and the api is lower than 23. The receiver of the instance
// get is not defined on the insertion point which is why this regression test was made.
- R8FullTestBuilder r8FullTestBuilder =
- testForR8(parameters.getBackend())
- .addProgramClasses(I.class, Main.class)
- .addProgramClassFileData(getTestClassWithRewrittenLongCompareToLCmp())
- .addKeepMainRule(Main.class)
- .addKeepClassRules(I.class)
- .setMinApi(parameters.getApiLevel())
- .enableInliningAnnotations();
- if (parameters.getApiLevel().isLessThan(AndroidApiLevel.M)) {
- // TODO(b/251015885). We should not fail compilation.
- assertThrows(
- CompilationFailedException.class,
- () ->
- r8FullTestBuilder.compileWithExpectedDiagnostics(
- diagnostics ->
- diagnostics.assertErrorMessageThatMatches(
- containsString("Unexpected values live at entry to first block"))));
-
- } else {
- r8FullTestBuilder
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(EXPECTED);
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, Main.class)
+ .addProgramClassFileData(getTestClassWithRewrittenLongCompareToLCmp())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(I.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
}
private byte[] getTestClassWithRewrittenLongCompareToLCmp() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageKeepClassMembersTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageKeepClassMembersTest.java
index a49759f..57f650e 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageKeepClassMembersTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageKeepClassMembersTest.java
@@ -6,15 +6,15 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.testclasses.RepackageForKeepClassMembers;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,64 +22,57 @@
@RunWith(Parameterized.class)
public class RepackageKeepClassMembersTest extends RepackageTestBase {
+ private final String EXPECTED = "Could not find " + typeName(RepackageForKeepClassMembers.class);
+
public RepackageKeepClassMembersTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
+ public void testPG() throws Exception {
+ assumeTrue(parameters.isOrSimulateNoneRuntime());
+ testForProguard(ProguardVersion.V7_0_0)
+ .addProgramClasses(RepackageForKeepClassMembers.class, Main.class)
.setMinApi(parameters.getApiLevel())
.apply(this::configureRepackaging)
.addKeepRules(
- "-keepclassmembers class * extends " + typeName(Base.class) + " { <fields>; }")
+ "-keepclassmembers class " + typeName(RepackageForKeepClassMembers.class) + " { *; }")
+ .addKeepMainRule(Main.class)
+ .addDontWarn(RepackageKeepClassMembersTest.class, NeverClassInline.class)
+ .run(parameters.getRuntime(), Main.class, typeName(RepackageForKeepClassMembers.class))
+ .inspect(this::inspect)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(RepackageForKeepClassMembers.class, Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(this::configureRepackaging)
+ .addKeepRules(
+ "-keepclassmembers class " + typeName(RepackageForKeepClassMembers.class) + " { *; }")
.addKeepMainRule(Main.class)
.enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .run(parameters.getRuntime(), Main.class, typeName(Sub.class))
+ .run(parameters.getRuntime(), Main.class, typeName(RepackageForKeepClassMembers.class))
.inspect(this::inspect)
- // TODOD(b/250671873): Should not repackage class.
- .assertSuccessWithOutputLines("Could not find " + typeName(Sub.class));
+ .assertSuccessWithOutputLines(EXPECTED);
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(Sub.class);
+ ClassSubject clazz = inspector.clazz(RepackageForKeepClassMembers.class);
assertThat(clazz, isPresent());
- // TODOD(b/250671873): Should not repackage class.
- assertThat(Sub.class, isRepackaged(inspector));
+ assertThat(RepackageForKeepClassMembers.class, isRepackaged(inspector));
assertThat(clazz.uniqueFieldWithOriginalName("hashCodeCache"), isPresentAndNotRenamed());
- assertThat(clazz.uniqueMethodWithOriginalName("calculateHashCode"), isPresentAndRenamed());
- }
-
- public static class Base {}
-
- @NeverClassInline
- public static class Sub extends Base {
-
- public int hashCodeCache;
-
- @NeverInline
- public int calculateHashCode() {
- if (hashCodeCache != -1) {
- return hashCodeCache;
- }
- hashCodeCache = Objects.hash(Sub.class);
- return hashCodeCache;
- }
-
- @Override
- public int hashCode() {
- return calculateHashCode();
- }
+ assertThat(clazz.uniqueMethodWithOriginalName("calculateHashCode"), isPresentAndNotRenamed());
}
public static class Main {
public static void main(String[] args) {
if (System.currentTimeMillis() == 0) {
- System.out.println(new Sub().hashCode());
+ System.out.println(new RepackageForKeepClassMembers().hashCode());
}
try {
Class<?> subClass = Class.forName(args[0]);
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java
index a208857..a6c9ea3 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java
@@ -5,15 +5,18 @@
package com.android.tools.r8.repackage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.testclasses.RepackageForKeepClassMembers;
+import com.android.tools.r8.transformers.ClassFileTransformer.FieldPredicate;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,66 +25,82 @@
@RunWith(Parameterized.class)
public class RepackageProtectedInSamePackageTest extends RepackageTestBase {
+ private final String EXPECTED = "Hello World!";
+
public RepackageProtectedInSamePackageTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
+ public void testPG() throws Exception {
+ assumeTrue(parameters.isOrSimulateNoneRuntime());
+ testForProguard(ProguardVersion.V7_0_0)
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getRepackageForKeepClassMembersWithProtectedAccess())
.setMinApi(parameters.getApiLevel())
.apply(this::configureRepackaging)
.addKeepRules(
- "-keepclassmembers,allowobfuscation class * extends "
- + typeName(Base.class)
- + " { <fields>; }")
+ "-keepclassmembers class " + typeName(RepackageForKeepClassMembers.class) + " { *; }")
+ .addKeepMainRule(Main.class)
+ .addDontWarn(RepackageProtectedInSamePackageTest.class, NeverClassInline.class)
+ .run(parameters.getRuntime(), Main.class, typeName(RepackageForKeepClassMembers.class))
+ .inspect(inspector -> inspect(inspector, true))
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getRepackageForKeepClassMembersWithProtectedAccess())
+ .setMinApi(parameters.getApiLevel())
+ .apply(this::configureRepackaging)
+ .addKeepRules(
+ "-keepclassmembers class " + typeName(RepackageForKeepClassMembers.class) + " { *; }")
.addKeepMainRule(Main.class)
.enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
.run(parameters.getRuntime(), Main.class)
- .inspect(this::inspect)
- .assertSuccessWithOutputLines("Hello World!");
+ .inspect(inspector -> inspect(inspector, false))
+ .assertSuccessWithOutputLines(EXPECTED);
}
- private void inspect(CodeInspector inspector) {
+ private byte[] getRepackageForKeepClassMembersWithProtectedAccess() throws Exception {
+ return transformer(RepackageForKeepClassMembers.class)
+ .setAccessFlags(
+ MethodPredicate.onName("calculateHashCode"),
+ methodAccessFlags -> {
+ methodAccessFlags.unsetPublic();
+ methodAccessFlags.setProtected();
+ })
+ .setAccessFlags(
+ FieldPredicate.onName("hashCodeCache"),
+ fieldAccessFlags -> {
+ fieldAccessFlags.unsetPublic();
+ fieldAccessFlags.setProtected();
+ })
+ .transform();
+ }
+
+ private void inspect(CodeInspector inspector, boolean isProguard) {
+ ClassSubject clazz = inspector.clazz(RepackageForKeepClassMembers.class);
+ assertThat(clazz, isPresent());
// TODO(b/250671873): We should be able to repackage the Sub class since the only reference
// to Sub.class is in the same package and we have allowobfuscation.
- ClassSubject clazz = inspector.clazz(Sub.class);
- assertThat(clazz, isPresent());
- assertThat(Sub.class, isNotRepackaged(inspector));
- assertThat(clazz.uniqueFieldWithOriginalName("hashCodeCache"), isPresentAndRenamed());
- assertThat(clazz.uniqueMethodWithOriginalName("calculateHashCode"), isPresentAndRenamed());
- }
-
- public static class Base {}
-
- @NeverClassInline
- public static class Sub extends Base {
-
- protected int hashCodeCache;
-
- @NeverInline
- protected int calculateHashCode() {
- if (hashCodeCache != -1) {
- return hashCodeCache;
- }
- hashCodeCache = Objects.hash(Sub.class);
- return hashCodeCache;
+ if (isProguard) {
+ assertThat(RepackageForKeepClassMembers.class, isRepackaged(inspector));
+ } else {
+ assertThat(RepackageForKeepClassMembers.class, isNotRepackaged(inspector));
}
-
- @Override
- public int hashCode() {
- return calculateHashCode();
- }
+ assertThat(clazz.uniqueFieldWithOriginalName("hashCodeCache"), isPresentAndNotRenamed());
+ assertThat(clazz.uniqueMethodWithOriginalName("calculateHashCode"), isPresentAndNotRenamed());
}
public static class Main {
public static void main(String[] args) {
if (System.currentTimeMillis() == 0) {
- System.out.println(new Sub().hashCode());
+ System.out.println(new RepackageForKeepClassMembers().hashCode());
}
System.out.println("Hello World!");
}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/RepackageForKeepClassMembers.java b/src/test/java/com/android/tools/r8/repackage/testclasses/RepackageForKeepClassMembers.java
new file mode 100644
index 0000000..48781e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/RepackageForKeepClassMembers.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2022, 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.repackage.testclasses;
+
+import com.android.tools.r8.NeverClassInline;
+import java.util.Objects;
+
+@NeverClassInline
+public class RepackageForKeepClassMembers {
+
+ public int hashCodeCache;
+
+ public int calculateHashCode() {
+ if (hashCodeCache != -1) {
+ return hashCodeCache;
+ }
+ hashCodeCache = Objects.hash(RepackageForKeepClassMembers.class);
+ return hashCodeCache;
+ }
+
+ @Override
+ public int hashCode() {
+ return calculateHashCode();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceNoProguardMapTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceNoProguardMapTest.java
new file mode 100644
index 0000000..2deccf8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceNoProguardMapTest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, 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.retrace;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetraceNoProguardMapTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testNoLine() {
+ Retracer retracer =
+ Retracer.createDefault(ProguardMapProducer.fromString(""), new DiagnosticsHandler() {});
+ MethodReference methodReference =
+ Reference.method(Reference.classFromTypeName("a"), "foo", Collections.emptyList(), null);
+ Optional<RetraceMethodElement> first =
+ retracer.retraceMethod(methodReference).stream().findFirst();
+ assertTrue(first.isPresent());
+ assertEquals(methodReference, first.get().getRetracedMethod().asKnown().getMethodReference());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 9dc6120..47c49fc 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -550,6 +550,14 @@
private ClassFileTransformer setAccessFlags(
FieldReference fieldReference, Consumer<FieldAccessFlags> setter) {
+ return setAccessFlags(
+ FieldPredicate.onNameAndDescriptor(
+ fieldReference.getFieldName(), fieldReference.getFieldType().getDescriptor()),
+ setter);
+ }
+
+ public ClassFileTransformer setAccessFlags(
+ FieldPredicate predicate, Consumer<FieldAccessFlags> setter) {
return addClassTransformer(
new ClassTransformer() {
@@ -557,8 +565,7 @@
public FieldVisitor visitField(
int access, String name, String descriptor, String signature, Object value) {
FieldAccessFlags accessFlags = FieldAccessFlags.fromCfAccessFlags(access);
- if (name.equals(fieldReference.getFieldName())
- && descriptor.equals(fieldReference.getFieldType().getDescriptor())) {
+ if (predicate.test(access, name, descriptor, signature, value)) {
setter.accept(accessFlags);
}
return super.visitField(
@@ -631,7 +638,7 @@
return (access, name, descriptor, signature, value) -> true;
}
- static FieldPredicate onNameAndSignature(String name, String descriptor) {
+ static FieldPredicate onNameAndDescriptor(String name, String descriptor) {
return (access, otherName, otherDescriptor, signature, value) ->
name.equals(otherName) && descriptor.equals(otherDescriptor);
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index aeb419e..b569a57 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -178,6 +178,10 @@
}
}
+ public DexApplication getApplication() {
+ return application;
+ }
+
public DexItemFactory getFactory() {
return dexItemFactory;
}
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index 6bb56ca..6a3c83d 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-ed1a6a57d899d32cc749160872cad5c2130566b7
\ No newline at end of file
+9f148123a86ba0f40e58a71f9ad59c723ecb4607
\ No newline at end of file